feat: allow source and destination clusters to overlap - #1473
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
Next on the TODO:
|
| let overlap_check = OverlappingShardsCheck::new(&self.source); | ||
|
|
||
| for shard in self.cluster.shards() { | ||
| let primary = shard | ||
| .pools_with_roles() | ||
| .iter() | ||
| .find(|(r, _)| r == &Role::Primary) | ||
| .ok_or(Error::NoPrimary)? | ||
| .1 | ||
| .standalone(ConnectReason::Replication) | ||
| .await?; | ||
| conns.push(primary); | ||
| let dest_shards = self.dest.shards(); | ||
|
|
||
| if dest_shards.is_empty() { | ||
| return Err(Error::DestinationNoShards); | ||
| } | ||
|
|
||
| for (shard_number, shard) in self.dest.shards().iter().enumerate() { | ||
| if overlap_check.overlaps(shard)? { | ||
| warn!( | ||
| "skipping replication to shard {} because it overlaps with source cluster", | ||
| shard.primary_address()? | ||
| ); | ||
| continue; | ||
| } | ||
|
|
||
| let primary = shard.primary_standalone(ConnectReason::Replication).await?; | ||
| conns.push((shard_number, primary)); | ||
| } | ||
|
|
||
| if conns.is_empty() { | ||
| return Err(Error::SourceDestinationIdentical); | ||
| } |
There was a problem hiding this comment.
this is very similar code how it's done in the copy.rs, we may consider to put this inside OverlappingShardsCheck and reuse
| // Connect to all shards. | ||
| pub(crate) async fn connect(&mut self) -> Result<(), Error> { | ||
| let mut conns: Vec<Server> = vec![]; | ||
| let mut conns = vec![]; |
There was a problem hiding this comment.
oh, when the len of conns do not correspond to the len of destination shards it'll be more error prone if for any reason we'll use the conns index instead of shard_number inside for shard identification. I'll put the exact places right now what I found with this problem, but I wonder if we should cover it somehow on the type level:
- use wrapper structure for conns
- create empty Conn instead that do nothing to have the same len
- maybe operate on Shard or similar instead of passing shard_number along the data
| } | ||
|
|
||
| let partition = self.partition; | ||
| let n_conns = self.connections.len(); |
There was a problem hiding this comment.
here should be the dest.shards().len() I believe instead, since this relies on the behavior of shard routing that sees all shards
|
|
||
| self.connections = servers; | ||
| for (shard_number, shard) in destination_shards.iter().enumerate() { | ||
| if overlap_check.overlaps(shard)? { |
There was a problem hiding this comment.
I wonder is we should use the Shard type or similar everywhere instead of tuples (shard_number, smth). There is a number already inside the Shard and we could simplify it in places like this and maybe reuse instead of adding shard_number to other structs
| warn!( | ||
| "skipping data sync to {} because it is part of the source cluster", | ||
| shard.primary_address()?, | ||
| ); | ||
| continue; |
There was a problem hiding this comment.
some edge case to validate maybe: check if the shard number is different in source/destination cluster, just in case
| let cancel = cancel.clone(); | ||
| handles.push(tasks::spawn("parallel sync manager", async move { | ||
| let manager = ParallelSyncManager::new(tables, replicas, source, dest)?; | ||
| let tables = manager.run(cancel).await?; |
There was a problem hiding this comment.
ParallelSync looks like using all the shards for destination_has_rows check, so for any error we'll stop retrying since we'll find the entries on the existing shards
|
Sounds like to this to work some invariants should hold, because if we change the number of shards the routing will expect the data be reorganized differently on the destination comparing to source and since we leave the data on source shards as is we should guarantee that the routing will work the same way as it was before. |
|
This should have some integration test |
Allow source and destination clusters to overlap. Matching databases in the destination cluster are ignored and data is only routed to shards not specified in the source cluster. This little hack allows us to:
Example
Using
data-sync, we can copy:tenant_id = 4from source to destination, without touching the original node.