-
Notifications
You must be signed in to change notification settings - Fork 136
chore(spanner): support mutation only operation for read-write mux #3423
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
Changes from 7 commits
3f87f79
e942e35
6d1b6dc
9ec3a6c
65b3234
5907ad5
042ef7e
dfc2a39
be71459
f12729e
01c0314
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -22,13 +22,15 @@ | |||||
| import com.google.common.collect.ImmutableList; | ||||||
| import com.google.protobuf.ListValue; | ||||||
| import java.io.Serializable; | ||||||
| import java.util.ArrayList; | ||||||
| import java.util.Collections; | ||||||
| import java.util.HashSet; | ||||||
| import java.util.LinkedHashMap; | ||||||
| import java.util.List; | ||||||
| import java.util.Map; | ||||||
| import java.util.Objects; | ||||||
| import java.util.Set; | ||||||
| import java.util.concurrent.ThreadLocalRandom; | ||||||
| import javax.annotation.Nullable; | ||||||
|
|
||||||
| /** | ||||||
|
|
@@ -402,20 +404,41 @@ private boolean isFloat32NaN(Value value) { | |||||
| return value.getType().equals(Type.float32()) && Float.isNaN(value.getFloat32()); | ||||||
| } | ||||||
|
|
||||||
| static void toProto(Iterable<Mutation> mutations, List<com.google.spanner.v1.Mutation> out) { | ||||||
| // Converts Mutation and returns a random Mutation from the available mutation list based on the | ||||||
| // following heuristics: | ||||||
| // 1. Prefer mutations other than INSERT, since INSERT mutations may contain autogenerated columns | ||||||
| // whose information is unavailable on the client. | ||||||
| // 2. If the list only contains INSERT mutations, select the one with the highest number of | ||||||
| // values. | ||||||
| static com.google.spanner.v1.Mutation toProtoGetRandomMutation( | ||||||
harshachinta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| Iterable<Mutation> mutations, List<com.google.spanner.v1.Mutation> out) { | ||||||
| Mutation last = null; | ||||||
| // The mutation currently being built. | ||||||
| com.google.spanner.v1.Mutation.Builder proto = null; | ||||||
| // The "write" (!= DELETE) or "keySet" (==DELETE) for the last mutation encoded, for coalescing. | ||||||
| com.google.spanner.v1.Mutation.Write.Builder write = null; | ||||||
| com.google.spanner.v1.KeySet.Builder keySet = null; | ||||||
|
|
||||||
| // Store all the mutations excluding INSERT. | ||||||
harshachinta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| List<com.google.spanner.v1.Mutation> allMutationsExcludingInsert = new ArrayList<>(); | ||||||
| // Stores INSERT mutation with large number of values. | ||||||
harshachinta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| com.google.spanner.v1.Mutation largeInsertMutation = | ||||||
|
||||||
| com.google.spanner.v1.Mutation largeInsertMutation = | |
| com.google.spanner.v1.Mutation largestInsertMutation = |
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.
One very peculiar, but not impossible, case is that the list of mutations only contains empty insert mutations. This could for example be possible for a table that has a default value for the primary key (or allows null for the primary key), and also either has a default value for all other columns, or allows them to be null. Such a case would lead to this method returning Mutation.getDefaultInstance() as the largest insert mutation.
The heuristics should therefore also be changed to prefer an insert mutation with zero columns and a table name, over an insert mutation with zero columns and no table name. (Any other non-empty property of a real mutation can also be used instead of the table name for this check).
Can we also add a test for this specific scenario? It is typically something that could also backslide in the future.
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.
Thanks Knut for pointing out to this edge case.
I have updated the code to handle this case by adding below lines, and added a test for empty insert mutation scenario.
// If largestInsertMutation is a default instance of Mutation, replace it with the current
// INSERT mutation, even if it contains zero values.
if (!largestInsertMutation.hasInsert()) {
return true;
}
Talking out loud of all the edge cases,
- If mutations are empty, then this logic never gets called.
- Table name in a mutation can not be empty.
- If there are mutations other than INSERT operation, then once of them is chosen at random.
- If only INSERT mutations, then one with largest value is chosen
- If only INSERT mutation, with no columns/values set, then this mutation will be chosen as random mutation
Please let me know if there is anything missing.
Outdated
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.
You can skip most of this, as the if statement on line 429 already guarantees that this is a DELETE mutation. So you can just directly add it to allMutationsExcludingInsert.
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.
We have a coalescing mechanism to group similar consecutive mutations together.
As a result of this logic, the if statement ensures that the current mutation selected from the list is a DELETE mutation. However, the proto being built is based on prior iterations and will not necessarily be a DELETE mutation.
For example, consider the following mutation list:
Mutation.newInsertBuilder("T1").set("C").to("V1").build(),
Mutation.delete("T1", Key.of("k1")),
Mutation.newInsertBuilder("T2").set("C").to("V1").build()
In this case, the if condition will match the DELETE operation on line 2, but the proto will be an INSERT mutation from line 1.
We have a test case toProtoCoalescingDeleteChanges to verify this behavior.
Outdated
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.
This code block is repeated a couple of times. Could we extract that to a separate method?
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.
I have extracted them to seperate methods. Please let me know if there are any other better ways.
Uh oh!
There was an error while loading. Please reload this page.