-
Notifications
You must be signed in to change notification settings - Fork 220
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
Support setting assertions and receiving assertion errors for Prewrite requests #311
Conversation
txnkv/transaction/2pc.go
Outdated
isPessimisticLock []bool | ||
isAssertExist []bool | ||
isAssertNotExist []bool |
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.
It looks a bit wasteful to me... Maybe also define "flags" here as the lowest 3 bits of UserData
?
txnkv/transaction/2pc.go
Outdated
func (m *memBufferMutations) Slice(from, to int) CommitterMutations { | ||
return &memBufferMutations{ | ||
handles: m.handles[from:to], | ||
storage: m.storage, | ||
} | ||
} | ||
|
||
func (m *memBufferMutations) Push(op kvrpcpb.Op, isPessimisticLock bool, handle unionstore.MemKeyHandle) { | ||
aux := uint16(op) << 1 | ||
func (m *memBufferMutations) Push(op kvrpcpb.Op, isPessimisticLock bool, assertExist, assertNotExist bool, handle unionstore.MemKeyHandle) { |
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 think it is better to add comments about the format of aux
and add unit tests for it.
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.
Ah.. sorry, this PR is not ready yet and it was supposed to be a draft PR, and currently this PR only did some minor changes based on #273 .
txnkv/transaction/2pc.go
Outdated
ts, err1 := c.store.GetTimestampWithRetry(retry.NewBackofferWithVars(ctx, TsoMaxBackoff, c.txn.vars), c.txn.GetScope()) | ||
if err1 == nil { | ||
_, _, err2 := c.checkSchemaValid(ctx, ts, c.txn.schemaVer, false) | ||
if err2 != nil { | ||
err = err2 | ||
} | ||
} |
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 guess it is to avoid some false positives with online DDL? Could you add some comments?
Closing for no activity. Please reopen after ready. |
Even it's using Github's draft PR functionality? |
sorry, I misunderstood the time of last commit |
Signed-off-by: lysu <sulifx@gmail.com> Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
ccd06e8
to
e22a4eb
Compare
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
3c89534
to
6369c44
Compare
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
// TODO: Check if it's safe to use `val.Exists` instead of assuming empty value. | ||
if !val.Exists { | ||
valExists = tikv.SetKeyLockedValueNotExists | ||
} |
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.
@cfzjywxk Is this safe? Previously when a key is locked without ReturnValue
option, we assume it must exist since TiDB will only lock the key after it's found in scanning. However this changed the behavior. I'm thinking if I'd better add two new flags for storing whether it exists or not, that the new flags will only be used in assertion, to avoid changing the normal behavior.
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 decide to add new flags for it. Even it's safe, it will introduce difficulties when we meet problem about the LockedValueExists flag (which affects the behavior about "duplicated entry") and try to diagnose it.
Nevermind.
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.
@MyonKeminta
The meanings seems to be the same, use pessimistic lock response to record existing key value, I think it'ok if the judging condition is handled correctly.
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
b11e6c2
to
084762b
Compare
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
I think the keys and values in error messages can be encoded as hex for readability. |
I usually use the hex encoded format printing keys and values. |
Did you mean However, by the way, I'm afraid things about redacting is questionable here, for example, the not-redacted keys might be printed when printing these kinds of errors in logs. |
Yes. It comes from the protobuf marshaller. I agree on leaving it for now.
Then the problem also exists for other error types? client-go doesn't care about log redaction at all. |
Yes. I mean, it's possible that TiDB prints an error from client-go, and the error might be of these kinds, and as a result it will introduce the risk of leaking sensitive information to the log. And I think even if there's no such case in TiDB, it can still be easily introduced. |
// If ReturnValue is disabled and CheckExistence is requested, it's still possible that the TiKV's version | ||
// is too old and CheckExistence is not supported. |
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'm not clear about what this comment mean... Do we really need to pay attention to it?
TiDB should not be used with an old version of TiKV, so I don't think we should care about it. And I don't think a client-go user will set assertions (hopefully...)
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.
It's not necessary if we assume TiDB is never used with an old version of TiKV. I'm feeling afraid since we are supporting downgrading, and I'm not sure if there's any special cases about that, so I added this...🤔
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.
Emm, downgrading... I'm thinking when downgrading, the order should also reverse? First TiDB, then TiKV and PD?
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'm ok with the comment. It shows a requirement for minimal TiKV version.
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.
Emm, downgrading... I'm thinking when downgrading, the order should also reverse? First TiDB, then TiKV and PD?
I think so, but I'm not sure how it really is.
83e686c
to
e78d46c
Compare
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
@@ -725,6 +734,9 @@ func checkConflictValue(iter *Iterator, m *kvrpcpb.Mutation, forUpdateTS uint64, | |||
return nil, err | |||
} | |||
if !ok { | |||
if m.Assertion == kvrpcpb.Assertion_Exist && assertionLevel != kvrpcpb.AssertionLevel_Off { | |||
logutil.BgLogger().Error("ASSERTION FAIL!!! non-exist for must exist key", zap.Stringer("mutation", m)) | |||
} | |||
return nil, nil |
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.
Why doesn't it return an error
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.
Ah, this is old code before my modification and assertion is not supported in mocktikv (so there are only tests based on unistore). Since there's now too much difference between mocktikv and tikv, it might be complicated to implement assertion in mocktikv. I think there changes can be removed. How do you think?
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.
Wait a minute, I need to double check it
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.
Sorry I forgot it. To write tests for pessimistic lock returning existence, the related assertion logic is actually already half-implemented. Let's fix this return value for completeness.
@@ -739,7 +751,8 @@ func checkConflictValue(iter *Iterator, m *kvrpcpb.Mutation, forUpdateTS uint64, | |||
} | |||
|
|||
needGetVal := getVal | |||
needCheckAssertion := m.Assertion == kvrpcpb.Assertion_NotExist | |||
needCheckShouldNotExistForPessimisticLock := m.Assertion == kvrpcpb.Assertion_NotExist && m.Op == kvrpcpb.Op_PessimisticLock | |||
needCheckAssertionForPrewerite := m.Assertion != kvrpcpb.Assertion_None && m.Op != kvrpcpb.Op_PessimisticLock && assertionLevel != kvrpcpb.AssertionLevel_Off |
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.
needCheckAssertionForPrewerite := m.Assertion != kvrpcpb.Assertion_None && m.Op != kvrpcpb.Op_PessimisticLock && assertionLevel != kvrpcpb.AssertionLevel_Off | |
needCheckAssertionForPrewrite := m.Assertion != kvrpcpb.Assertion_None && m.Op != kvrpcpb.Op_PessimisticLock && assertionLevel != kvrpcpb.AssertionLevel_Off |
Do we need to consider Assertion_Unknown here? Though in the current implementation it doesn't make a difference.
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.
No. Unknown will not be passed via TiKV RPC, so Assertion_Unknown even doesn't exist in kvproto.
txnkv/transaction/2pc.go
Outdated
func (m *memBufferMutations) Push(op kvrpcpb.Op, isPessimisticLock, assertExist, assertNotExist bool, handle unionstore.MemKeyHandle) { | ||
// The format to put to the handle.UserData: | ||
// MSB LSB | ||
// [13 bits: Op][1 bit: assertNotExist][1 bit: assertExist][1 bit: isPessimisticLock] |
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 think it'd be better to place the comments next to its definition.
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.
The format is defined by the user of memdb instead of memdb itself. I'll move the comment to near the definition of memBufferMutations.handles
field.
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.
Makes sense. It's just that when I read the usage of the type, I would jump to its definition and hope to find some explanations there.
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
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.
The rest LGTM
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
// Handle the case that the TiKV's version is too old and doesn't support `CheckExistence`. | ||
// If `CheckExistence` is set, `ReturnValues` is not set and `CheckExistence` is not supported, skip | ||
// retrieving value totally (indicated by `skipRetrievingValue`) to avoid panicking. | ||
skipRetrievingValue := !action.ReturnValues && action.CheckExistence && len(lockResp.NotFounds) == 0 |
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.
Could the len(lockResp.NotFounds) == 0
represent such situation accurately?
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.
The previous version of TiKV already supports setting the NotFounds
field when ReturnValues
is set. So when ReturnValues
is set, both new and old versions of TiKV should work. But if CheckExistence
is set and ReturnValues
is unset, since old TiKV cannot recognize the new field CheckExistence
, the NotFounds
returned from TiKV will be empty. We cannot retrieve existence in this case, and it should be the only case.
|
||
// InitCheckExistence creates the map to store whether each key exists or not. | ||
func (ctx *LockCtx) InitCheckExistence(capacity int) { | ||
ctx.CheckExistence = true |
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 field will be set to true only in tests?
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.
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
This PR is part of pingcap/tidb#26833 and is a continuation of #273 .