Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Handle 400 errors as unrecoverable #1880

Merged
merged 4 commits into from
Oct 23, 2024
Merged

Conversation

msfstef
Copy link
Contributor

@msfstef msfstef commented Oct 22, 2024

Fixes #1876

The issue was that we were sending must-refetch messages with some of our 400s, although they were unrecoverable and re-fetching would trigger an infinite loop.

I've made the server send 400 errors for the cases where the shape_id and shape_definition do not match, with an explanatory message, and I've made the client terminate when receiving such an error.

As a temporary measure, the error is not thrown into the ether but stored in the ShapeStream (accessible via stream.error).

We have had some discussions with @balegas about making errors more "handleable" by changing our approach to the ShapeStream instantiation. I think this warrants opening a new issue and corresponding PR to discuss more thoroughly.

@@ -236,7 +241,7 @@ defmodule Electric.Plug.ServeShapePlug do
# thus the shape ID does not match the shape definition
# and we return a 400 bad request status code
conn
|> send_resp(400, @must_refetch)
|> send_resp(400, @shape_definition_mismatch)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this a 409? If the shape definition is valid perhaps they just have a really old shape_id.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where people were hitting this problem (myself included) is when we'd request an invalid shape — e.g. the table didn't exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the case where the shape_id is valid and exists (so there is an active shape that matches it), but the provided shape definition does not match that. That is a bad request and cannot be resolved with a must-refetch.

You need to:

  • remove the shape_id so we start serving you the shape that matches the shape definition provided, OR
  • provide the correct shape definition that matches this active shape_id that you are providing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm no — when a shape is deleted on the server (for whatever reason), the next request for that shape would re-create the shape with a new shape_handle — then the next person to request the shape would have a mis-match between their shape_handle and the shape definition because their handle is pointing to a shape log that no longer exists. Which isn't a problem as a new shape now exists so they just need to refetch it.

This is the main use case for must-refetch.

400 is for a bad shape definition e.g. we can't parse the where clause or whatever.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(re: shape_handle #1771)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok — yeah this shouldn't happen unless the client has a bug.

Did you check the "request a shape where the table doesn't exist"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So nice seeing all those green checkboxes now!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(see the integration test I added to the client)

when there is a mismatch, there are two cases:

  1. the shape_id no longer exists, so we return a 409 with the new shape handle for the given definition
  2. the shape_id exists and matches a shape definition that is not the provided one in the URL, so we return a 400

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you check the "request a shape where the table doesn't exist"?

This case will return a 400 at the shape validation stage - I didn't add an integration test for this but as far as the client is concerned right now any 400 will terminate the stream so we should be fine

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok great!

Copy link
Contributor

@KyleAMathews KyleAMathews left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@msfstef msfstef merged commit 7de9f1d into main Oct 23, 2024
23 checks passed
@msfstef msfstef deleted the msfstef/handle-400s-in-client branch October 23, 2024 10:59
KyleAMathews pushed a commit that referenced this pull request Nov 1, 2024
Fixes #1876

The issue was that we were sending `must-refetch` messages with some of
our 400s, although they were unrecoverable and re-fetching would trigger
an infinite loop.

I've made the server send 400 errors for the cases where the `shape_id`
and `shape_definition` do not match, with an explanatory message, and
I've made the client terminate when receiving such an error.

As a temporary measure, the error is not thrown into the ether but
stored in the `ShapeStream` (accessible via `stream.error`).

We have had some discussions with @balegas about making errors more
"handleable" by changing our approach to the `ShapeStream`
instantiation. I think this warrants opening a new issue and
corresponding PR to discuss more thoroughly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix infinite loop when the typescript client requests a non-existing table
3 participants