v3: migration support for lookup vindexes#3600
v3: migration support for lookup vindexes#3600alainjobart merged 6 commits intovitessio:masterfrom sougou:v3
Conversation
|
@tirsen: This is phase 1, which is the hardest part of the feature.
|
rafael
left a comment
There was a problem hiding this comment.
Should we add one test to show that the index is not a scatter when the rows are in the lookup table?
This is looking good to me. Added some small comments.
| uniqueShards := make(map[string]bool) | ||
| for _, kr := range krs { | ||
| ResolveKeyRangeToShards(allShards, uniqueShards, kr) | ||
| keyRangeToShardMap(allShards, uniqueShards, kr) |
There was a problem hiding this comment.
Maybe we can remove keyRangeToShardMap altogether and do something like:
func MapKeyRangesToShards(ctx context.Context, topoServ Server, cell, keyspace string, tabletType topodatapb.TabletType, krs []*topodatapb.KeyRange) (string, []string, error) {
keyspace, _, allShards, err := GetKeyspaceShards(ctx, topoServ, cell, keyspace, tabletType)
if err != nil {
return "", nil, err
}
var res []string
for _, kr := range krs {
res = append(res, GetShardsForKeyRange(allShards, kr)...)
}
return keyspace, res, nil
}
There was a problem hiding this comment.
I was about to do it. Then realized that we can eventually deprecate this (all V2). So, left it mostly untouched.
|
|
||
| // Ksids represents keyspace ids. It's either a list of keyspace ids | ||
| // or a keyrange. | ||
| type Ksids struct { |
There was a problem hiding this comment.
I was thinking if a different approach could be possible here. It seems like this struct is behaving like an algebraic data type, where later on there is alway a check to see if it's being used as a Range or as IDs.
I wonder if there is a way to encode this as methods in the interface, in that way the behavior will be more explicit and clearer. I went down this exercise and it's not trivial, but doesn't seem undoable at first glance.
There was a problem hiding this comment.
I think the best abstraction would be for this struct to compute the list of shards, or a wrapper that did the same. However, the shards get added directly to another data structure (while iterating on IDs). The abstraction would have caused an extra allocation. So, I decided to keep the existing (and common) code paths efficient.
|
Remind me why upsert on create is needed? |
|
upsert_on_create is for this use case:
With upsert_on_insert turned on, it will just replace the existing entry to match the new insert. The downside: In other words, upsert_on_insert should be used only if you have other ways to guarantee uniqueness, and you want the ability to re-insert a previously failed insert. |
| // Context returns the context of the current request. | ||
| Context() context.Context | ||
|
|
||
| // V3 functions. |
There was a problem hiding this comment.
For the newcomer to the code it would be great to provide some more context on what "V3" means -- something like "Routing functions used by Vindex operations" would be clearer than "V3".
Perhaps you eventually plan to have the VCursor used in other contexts, but AFAIK Execute (and now ExecuteAutocommit) are only used by the vindex implementations so having this be clear in the interface would definitely help those of us who have tried to wade through what's going on here.
|
Some comments/questions more about the overall approach than the code specifics:
It seems to me the only way to handle a transitional non-unique lookup vindex is to always scatter on read until you are sure the vindex is fully populated. Note that the current behavior seems correct for unique lookup vindexes.
|
go/vt/vtgate/executor_dml_test.go
Outdated
There was a problem hiding this comment.
It seems weird to use a string boolean value "true" when json supports boolean natively...
|
"Autocommit" is for "transactional" integrity. That means we can't have a committed transaction that inserts a new row with a "lookup vindexed column" without the lookup table being updated since the lookup table insert has to commit before the transaction that inserts the vindexed column. It's a crucial part of "productionizing" vindexes. More details in my final comment here: #3537 Regarding atomicity of non-unique lookup tables: I think if you need atomicity during the backfill you have to insert all the lookup rows for a given key in the same transaction. That can be accomplished using a map/reduce style backfill (where we shuffle to the target column value and then insert all of those in one transaction). We will backfill in our application code for now but I think this is how Vitess native backfills have to work to ensure correctness. I think Sugu has been toying with the idea of a Vitess native MR implementation, this may be a good first use case. I think we can also very easily add a "silent vindex" option where we simply keep the lookup table up to date but don't use it for routing. This seems like a useful option if you know your backfill isn't atomic. |
|
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed, please reply here (e.g.
|
|
I signed it! |
|
CLAs look good, thanks! |
|
@sougou Congrats on your first PR to the Vitess project! |
This change allows non-unique vindexes to return a keyrange instead of a list of keyspace ids. Initially, this feature will be used for implementing a migration mode vindex. When we add a vindex to a column, there are no values for existing rows, and entries for those must be backfilled. Until the backfill happens, a vindex could return the full keyrange asking the engine to perform a full scatter. Once the backfill is done, the vindex can be switched to a more strict one. In the future, this approach can also be used for more sophisticated vindexes for mapping inequalities to keyranges.
Change non-unique lookup vindexes to return a full keyrange if no match is found. This mode can be used when adding a new vindex to a column. With this flag turned on new entries will use the lookup while old ones will result in a scatter. Once the backfill is done, the flag can be turned off.
With the Update function now part of Lookup, such vindexes can now find more efficient implementations than the previous Delete and Create sequence. Currently, the Update implementations just do the Delete and Create, but we'll soon improve those. This abstraction also allows some vindexes to reject Updates if it's not supported. While making changes, I found a couple of bugs that I fixed: * updateChangedVindexes fromIds computation was incorrect. It would have fetched incorrect values if the table had multiple multi-column and owned lookup vindexes. * The Values for changing columns in planbuilder was getting filled out in the SET order, and not in the column order of the vindex, which was causing the updates to set the wrong values. The above are not practical use cases, but it's better to strive for correctness.
Added autocommit support for lookup vindex. In this mode, as described in #3642, inserts are executed as upserts, deletes are ignored, and updates are disallowed.
scatterIfAbsent was an unsafe flag because it could return incorrect results if a lookup vindex was partially populated. The only safe behavior is to always return a full scatter until backfill is fully completed. The flag is accordingly renamed to write_only
| changedVindexes := make(map[string][]sqltypes.PlanValue) | ||
| for i, vindex := range colVindexes { | ||
| for _, assignment := range update.Exprs { | ||
| for _, vcol := range vindex.Columns { |
go/vt/vtgate/vindexes/lookup.go
Outdated
There was a problem hiding this comment.
Shouldn't be something like? When present, queries will be sent to all shards (i.e the lookup table won't be used).
This part if an entry is missing doesn't seem to reflect what the code is doing.
There was a problem hiding this comment.
I think we should add a comment here, explaining why we are doing this.
The new behavior for autocommit has been updated to reflect comments in #3642: * We never delete * All db operations use autocommit to prevent deadlocks * Non-unique vindexes upsert * Unique vindexes always insert * Updates are allowed as long as the underlying operations succeed
|
I went couple times through this. LGTM. Looking forward to having this throughly documented in vitess.io. It will be tricky to reason about all these different behaviors. |
Commits: