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(electric): Sync rows that were in the table prior to electrification #1108

Merged
merged 6 commits into from
Apr 9, 2024

Conversation

alco
Copy link
Member

@alco alco commented Mar 28, 2024

When a table is electrified, its newly created shadow table is empty. If the user table had any rows prior to electrification, they would remain invisible to Electric due to the JOIN between the user table and its shadow table performed when querying for initial subscription data.

Fixes #1106, VAX-1750.

Copy link
Contributor

@magnetised magnetised left a comment

Choose a reason for hiding this comment

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

This probably works for the case you're testing, but I hit a related problem in my permissions e2e test.

https://github.com/electric-sql/electric/pull/993/files#diff-eb1e9904c46985b04740fdb3c781a86be013de57652d796dea7db7e40095ffb6R36

In this test if I swap the order of the inserts and electrification I get an error later in the test when inserting into the project_memberships table caused by logical replication message with nil tags.

Details here: https://linear.app/electric-sql/issue/VAX-1750/inserting-into-a-table-fails-when-electrifying-a-table-with-existing

might be worth adding an e2e test that reproduces this situation

end

defp unwrap_quotes("\"" <> _ = name), do: String.slice(name, 1..-2//1)
Copy link
Contributor

Choose a reason for hiding this comment

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

to be entirely over-defensive about it we should also be replacing any "" with ". Wouldn't String.trim_trailing\2be a little more explicit about what we're doing here too?

Copy link
Contributor

Choose a reason for hiding this comment

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

Technically not equivalent given that a double-quote inside the quoted string will be represented by ""

Copy link
Member Author

@alco alco Apr 7, 2024

Choose a reason for hiding this comment

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

I started hating this "defensive" function definition the moment wrote it. I have since changed the approach to use unquoted {<schema>, <table>} tuples and only produce quoted identifiers immediately prior to using them in an SQL string.

@alco
Copy link
Member Author

alco commented Apr 5, 2024

@magnetised Good catch!

We could try addressing this issue in a general way by creating a view over the shadow table that would return default values for all columns if a shadow row is missing. But then those defaults would need to work in different contexts where shadow rows are queried.

Right now we just have these two places in the code where only _tags are queried from the shadow table. So I'm inclined to apply a fix to the problem you've pointed out and, going forward, keep in mind the possibility that a row from a user table might not have a corresponding shadow row.

@icehaunter thoughts?

@icehaunter
Copy link
Contributor

I think addressing it in those places is good enough

@alco alco force-pushed the alco/vax-1768-initial-subscription-data branch from 10e51a9 to 50ad6e2 Compare April 5, 2024 22:12
@alco alco changed the base branch from main to alco/restore-electric-publication April 5, 2024 22:13
@alco alco force-pushed the alco/vax-1768-initial-subscription-data branch 4 times, most recently from 8ec5298 to bbe03f8 Compare April 7, 2024 19:59
Copy link
Contributor

@magnetised magnetised left a comment

Choose a reason for hiding this comment

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

Nice

schema_version
|> SchemaVersion.table!(layer.target_table)
|> Schema.single_table_info(schema_version.schema)

columns = Enum.map_join(table.columns, ", ", &~s|this."#{&1.name}"|)
Copy link
Contributor

Choose a reason for hiding this comment

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

we should maybe document a restriction that electrified tables or columns in electrified tables can't contain " in their names... seems a) reasonable and b) that it would save us a lot of time defensively escaping quotes everywhere...

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure about the reasonable part. People have all kinds of weird things in their schemas when those are generated by a tool instead of being written by hand.

We should pick some SQL fragment builder utility and use it pervasively. Switching to using Ecto's query API is one option. That will get easier to do if #1136 is accepted it merged (once it's done).

]
|> Enum.reject(&(is_nil(&1) or &1 == ""))
|> Enum.intersperse(" AND ")

where = if where_clauses != [], do: ["WHERE ", where_clauses], else: ""

user_table = {table_info.schema, table_info.name}
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't this just layer.target_table?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, great point!

Copy link
Contributor

@icehaunter icehaunter left a comment

Choose a reason for hiding this comment

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

Very nice job!

Base automatically changed from alco/restore-electric-publication to main April 9, 2024 11:53
alco added 6 commits April 9, 2024 14:57
When a table is electrified, its newly created shadow table is empty.
If the table had any rows prior to electrification, they would remain
invisible to Electric due to the JOIN between the user table and its
shadow table performed when querying for initial subscription data.
@alco alco force-pushed the alco/vax-1768-initial-subscription-data branch from 7856b97 to 13e3b72 Compare April 9, 2024 11:57
@alco alco merged commit f200734 into main Apr 9, 2024
5 checks passed
@alco alco deleted the alco/vax-1768-initial-subscription-data branch April 9, 2024 12:26
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.

Client doesn't receive rows if those rows existed on the table prior to electrification
3 participants