-
Notifications
You must be signed in to change notification settings - Fork 177
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
Add partitioned tables support #2197
base: main
Are you sure you want to change the base?
Conversation
5e6a8f1
to
8aafed5
Compare
✅ Deploy Preview for electric-next ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
65582f7
to
b9e1bd0
Compare
|
b9e1bd0
to
b517c38
Compare
@robacourt situation recovered. review away. great start to the year! |
Nice work! We should document the two possible ways to subscribe to partitioned tables. |
@balegas I've added basic docs highlighting the behaviour of shapes on partitioned tables - pls review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! I've left a few comments.
I've also been thinking there might be an alternative implementation, whereby the replication steam is processed (by a PartitionTransformer for want of a better name) before it gets to the filter, and if it sees a change for a partition it inserts a change into the stream for the logical table. That way, all the partition logic can go in the PartitionTransformer. Shape, Filter, Consumer etc. would remain unchanged. Sure it will create some changes that might not be used, but that seems a good tradeoff for the simplicity. Is there a reason why this is not possible? Or undesirable?
{:error, _} -> | ||
# just ignore errors here, they're unlikely anyway | ||
:ok |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surely the ShapeLogCollector should crash if there's an error? An error could be something as simple as loss of network connectivity to the database, which if we just swallow the error we won't clean the relation leading to stale cache and an inconsistent state, or have I missed something?
{:ok, _} -> | ||
# probably a malformed value from a test inspector | ||
:ok |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some of our mocks don't return the full table info, including a parent value. rather than update all our existing tests and add the burden of adding all these extra keys to all mock results in perpetuity i just added a clause that handles an implicit parent: nil
clause in the inspector result.
@@ -56,6 +56,36 @@ This is the root table of the shape. All shapes must specify a table and it must | |||
|
|||
The value can be just a tablename like `projects`, or can be a qualified tablename prefixed by the database schema using a `.` delimiter, such as `foo.projects`. If you don't provide a schema prefix, then the table is assumed to be in the `public.` schema. | |||
|
|||
#### Partitioned Tables |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
@robacourt that's a good idea and does remove undesirable complexity from the Filter. it does add a coordination point where we have to add information about partitions to the partition "transformer" on shape creation. currently this is nicely encapsulated by the Shapes.Dispatcher so though I like the encapsulation you suggest, I think it ends up as more complex as shape information has to be threaded into more places and fundamentally we end up with more moving parts. I will look at the integration with the filter to see if I can encapsulate the partition handling a bit more. update: thinking a bit more, I could maybe model the partition handling in the filter itself using this transformer approach. |
e2ab17d
to
a566465
Compare
I can see the appeal of putting the logic in the filter as it needs a similar interface to the Filter. But it would be nice to keep the Filter ignorant of partitions. One possibility would be for the Dispatcher to call both, as in: filter = Filter.add_shape(state.filter, from, shape)
partition_transformer = PartitionTransformer.register_table(state.transformer, shape.root_table) then later {partition_transformer, events} = PartitionTransformer.process_events([event])
...
Filter.affected_shapes(state.filter, event) Ideally then the only thing that knows about partitions would be the PartitionTransformer. |
changes to partitions are now passed by expanding writes to a partition into that write plus a write to the matching partition root
@robacourt you're absolutely right! have pushed a version that works exactly as you propose, except I dropped the this version is much simpler as we don't have manage relations as well as changes. pls have a look. pr is not ready to merge as i have some serious issues with truncation to work out |
That's looks great! Nice one! There's various things I could point out, like reverting the Filter tests, but as you're not ready to merge I assume things like that are on your list so I won't point them out right now. In terms of the new structure it looks great 🚀 |
and fix problem when attempting to remove a subscription to a partitioned table
Allows for subscribing to partitions of a partitioned table (which you could already do) but also subscribing to the root, partitioned, table and receiving all updates across all partitions.
Fixes #2118
Major changes:
Filter
so that it can correctly pass on a change on a partition table to a shape on the partition root.key
s are not being re-written - I don't think this is an issue as the keys are guaranteed to be unique and that's enough.Minor:
{filter, shape_ids}
so that the filter's state can be updated