Skip to content
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

Merged
merged 38 commits into from
Feb 10, 2022

Conversation

MyonKeminta
Copy link
Contributor

This PR is part of pingcap/tidb#26833 and is a continuation of #273 .

Comment on lines 267 to 269
isPessimisticLock []bool
isAssertExist []bool
isAssertNotExist []bool
Copy link
Collaborator

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?

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) {
Copy link
Collaborator

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.

Copy link
Contributor Author

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 .

Comment on lines 1284 to 1335
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
}
}
Copy link
Collaborator

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?

@disksing
Copy link
Collaborator

Closing for no activity. Please reopen after ready.

@disksing disksing closed this Oct 20, 2021
@MyonKeminta
Copy link
Contributor Author

Closing for no activity. Please reopen after ready.

Even it's using Github's draft PR functionality?

@disksing
Copy link
Collaborator

sorry, I misunderstood the time of last commit

@disksing disksing reopened this Oct 20, 2021
lysu and others added 10 commits October 20, 2021 19:59
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>
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>
// TODO: Check if it's safe to use `val.Exists` instead of assuming empty value.
if !val.Exists {
valExists = tikv.SetKeyLockedValueNotExists
}
Copy link
Contributor Author

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.

Copy link
Contributor Author

@MyonKeminta MyonKeminta Dec 15, 2021

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.

Copy link
Contributor

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>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
@ekexium
Copy link
Contributor

ekexium commented Dec 17, 2021

I think the keys and values in error messages can be encoded as hex for readability.

@cfzjywxk
Copy link
Contributor

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.

@MyonKeminta
Copy link
Contributor Author

I think the keys and values in error messages can be encoded as hex for readability.

Did you mean ErrAssertionFailed defined in error/errors.go? The Error method is implemented in the same way as other kinds of errors defined in this file.

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.

@ekexium
Copy link
Contributor

ekexium commented Dec 20, 2021

Did you mean ErrAssertionFailed defined in error/errors.go? The Error method is implemented in the same way as other kinds of errors defined in this file.

Yes. It comes from the protobuf marshaller. I agree on leaving it for now.

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.

Then the problem also exists for other error types? client-go doesn't care about log redaction at all.

@MyonKeminta
Copy link
Contributor Author

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.

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.
It's not related to this PR though. I'm just thinking there might be a problem.

Comment on lines +687 to +688
// 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.
Copy link
Collaborator

@sticnarf sticnarf Dec 20, 2021

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...)

Copy link
Contributor Author

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...🤔

Copy link
Collaborator

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?

Copy link
Collaborator

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.

Copy link
Contributor Author

@MyonKeminta MyonKeminta Dec 20, 2021

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.

Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
@MyonKeminta MyonKeminta marked this pull request as ready for review January 20, 2022 09:48
@@ -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
Copy link
Contributor

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

Copy link
Contributor Author

@MyonKeminta MyonKeminta Jan 21, 2022

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?

Copy link
Contributor Author

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

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Contributor Author

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.

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]
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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>
Copy link
Contributor

@ekexium ekexium left a 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>
@ekexium ekexium requested a review from sticnarf February 7, 2022 04:20
// 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
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

@MyonKeminta MyonKeminta Feb 8, 2022

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>
Signed-off-by: MyonKeminta <MyonKeminta@users.noreply.github.com>
@MyonKeminta MyonKeminta merged commit b5eb031 into tikv:master Feb 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants