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

*: stale reads compatible with prepare #25156

Merged
merged 17 commits into from
Jun 11, 2021
Merged

Conversation

xhebox
Copy link
Contributor

@xhebox xhebox commented Jun 4, 2021

What problem does this PR solve?

Issue Number: close #24932

Problem Summary: Prepare is now compatible with stale reads.

What is changed and how it works?

What's Changed:

snapshotTS != 0 is used to indicate that it is a stale read.

  1. CachedPrepareStmt has a new field SnapshotTS to store the result of preprocessing in PrepareExec.
  2. newExecutorBuilder has a new parameter, snapshotTS. It is passed from session.ExecutePreparedStmt, along cachedPlanExec or CompileExecutePreparedStmt. The one in session comes from CachedPrepareStmt.
  3. Skip infoschema check in IsCachedExecOk and OptimizePreparedPlan, if snapshotTS != 0.
  4. plannercore.Execute plan has a new field SnapshotTS to store the timestamp from CachedPrepareStmt.
  5. executorExec.buildExecute will assign snapshotTS from plannercore.Execute plan to executorBuilder.

1-3 covered the prepared execution directly from session/mysql-protocol, and 4-5 covered the execution from plain SQL.

NEW CHANGES: to support dynamic timestamp like NOW() - INTERVAL 1 SECOND:

  1. CachedPrepareStmt do not store infoschema anymore. You need to recheck infoschema before execute to ensure consistency anyway.
  2. As Of statements evaluation is stored/prepared as a closure to support dynamic re-evaluation. For non-prepare statements, they can use LastSnapshotTS to avoid re-evaluation.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)

Release note

  • Support prepare stale reads SQL

@xhebox xhebox requested review from a team as code owners June 4, 2021 08:45
@xhebox xhebox requested review from qw4990 and removed request for a team June 4, 2021 08:45
@ti-chi-bot ti-chi-bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jun 4, 2021
@xhebox xhebox force-pushed the stale_3 branch 2 times, most recently from 05ebf02 to 5ba47c9 Compare June 4, 2021 09:06
@github-actions github-actions bot added sig/execution SIG execution sig/sql-infra SIG: SQL Infra labels Jun 4, 2021
tk.MustExec("insert into t values (3)")

staleRows := testkit.Rows("1")
staleSQL := fmt.Sprintf(`select * from t as of timestamp '%s'`, time1.Format("2006-1-2 15:04:05.000"))
Copy link
Member

@nolouch nolouch Jun 4, 2021

Choose a reason for hiding this comment

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

What will happen in this situation?

create table t (id int)
insert into t values (1)                                            ---- 1s
prepare s from  select * from t as of timestamp Now() - INTERVAL 2 Second  -----2s
execute s ;                                                         ----- 4s
alter table t add column age int;                                ----- 5s
insert into t values (2, 2)                                      --------5s
execute s;                                                         ----- 8s

maybe we need to store the expression rather than SanpshotTS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to prepare the handle of infoschema in #25080, but that is not true anymore. The handle of infoschema is now added.

Copy link
Member

@nolouch nolouch Jun 4, 2021

Choose a reason for hiding this comment

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

I want to get the result (2, 2) in the time point 8s. but I guess you implement will always get the result 1.

Copy link
Contributor Author

@xhebox xhebox Jun 4, 2021

Choose a reason for hiding this comment

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

Yeah, the timestamp is fixed once preprocessed now.

Your expectation needs a re-preprocessing, which drops all the prepared efforts. It is not a simple reason of my implementation. If timestamp is changed, schema and snapshot is changed, the result from previous prepare statements is meaningless.

Its semantic may be intuitive, but it does not make much sense under our current framework of prepare statements.

A full support, I guess, will need to store the expr of as of into the plan. And the optimizer should consider infoschema and snapshot in their optimization process. Then it will be much easier to implement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could provide a hack solution, if you really want it to be true.

Copy link
Contributor Author

@xhebox xhebox Jun 4, 2021

Choose a reason for hiding this comment

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

No, it must be re-preprocessed. Infoschema may change. Even if it is not changed, a check for stale infoschema is needed, which leads to one more tikv metadata access.

Otherwise, the result is inconsistent.

Agree with you.

Glad to hear that but it can not be true for now. I can only hack around. The solution will be tricky.

Copy link
Member

@nolouch nolouch Jun 7, 2021

Choose a reason for hiding this comment

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

Yes, I mean I can accept re-preprocessed, cannot accept the result is fixed on the point in mine example. BTW, Even if we have the infoschema cache, still we need to access TiKV to get the schema version for every request?

Copy link
Contributor Author

@xhebox xhebox Jun 7, 2021

Choose a reason for hiding this comment

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

No for the current solution. Because it is already ensured when prepare. If you want to ensure the consistency for NOW() - 2 seconds, however, yes, you need extra request. You don't know the schema version before 2 seconds. It is not prepared due to NOW(). If you don't check, it may be outdated.

FYI, check

tidb/domain/domain.go

Lines 103 to 107 in 5c95062

m := meta.NewSnapshotMeta(snapshot)
neededSchemaVersion, err := m.GetSchemaVersion()
if err != nil {
return nil, false, 0, nil, err
}
for the code of getting SchemaVersion.

Copy link
Member

Choose a reason for hiding this comment

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

I have checked it, the implementation is different from what I understand. I think the schema version is guaranteed to increase, it could be optimized to reduce the extra request by check the schema version is not changed between the older request and the up-to-date schema version.

Copy link
Member

@nolouch nolouch Jun 7, 2021

Choose a reason for hiding this comment

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

Otherwise, it is easy to have a single point of bottleneck with the current implementation, because the meta information is always read from a certain TiKV node.

Comment on lines 1929 to 1946
var is infoschema.InfoSchema
if preparedStmt.ForUpdateRead {
is = domain.GetDomain(s).InfoSchema()
} else if preparedStmt.InfoSchema != nil {
is = preparedStmt.InfoSchema.(infoschema.InfoSchema)
} else {
is = s.GetInfoSchema().(infoschema.InfoSchema)
}
Copy link
Contributor Author

@xhebox xhebox Jun 4, 2021

Choose a reason for hiding this comment

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

While the change of infoschema for stale reads are expected, you may argue that preparedStmtExec does not have such logic, i.e. change infoschema for updateRead.

But it does. It will eventually invoke planner.Optimize, which does get the latest infoschema for preparedStmt.ForUpdateRead. It is located at https://github.com/pingcap/tidb/blob/master/planner/optimize.go#L240-L250.

executor/adapter.go Outdated Show resolved Hide resolved
@nolouch
Copy link
Member

nolouch commented Jun 7, 2021

PTAL @JmPotato @Yisaer

Copy link
Member

@JmPotato JmPotato left a comment

Choose a reason for hiding this comment

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

Except for the problem @nolouch said, the rest LGTM.

@ti-chi-bot ti-chi-bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jun 7, 2021
@@ -245,7 +245,7 @@ func (a *ExecStmt) PointGet(ctx context.Context, is infoschema.InfoSchema) (*rec
}
}
if a.PsStmt.Executor == nil {
b := newExecutorBuilder(a.Ctx, is, a.Ti)
b := newExecutorBuilder(a.Ctx, is, a.Ti, a.SnapshotTS, a.ExplicitStaleness)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

a.ExplicitStaleness is added as a parameter, found #25206 lost the point get case, just like me.

@@ -101,14 +101,11 @@ func Preprocess(ctx sessionctx.Context, node ast.Node, preprocessOpt ...Preproce
v.PreprocessorReturn = &PreprocessorReturn{}
}
node.Accept(&v)
readTS := ctx.GetSessionVars().TxnReadTS.UseTxnReadTS()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to handleAsOfAndReadTS to center the initialize of LastSnapshotTS and other parameters.

@ti-chi-bot ti-chi-bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jun 7, 2021
executor/stale_txn_test.go Outdated Show resolved Hide resolved
planner/core/preprocess.go Outdated Show resolved Hide resolved
@ti-chi-bot
Copy link
Member

@JmPotato: Thanks for your review. The bot only counts approvals from reviewers and higher roles in list, but you're still welcome to leave your comments.

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@nolouch nolouch removed sig/execution SIG execution sig/sql-infra SIG: SQL Infra labels Jun 11, 2021
@ti-chi-bot
Copy link
Member

[REVIEW NOTIFICATION]

This pull request has been approved by:

  • JmPotato
  • nolouch

To complete the pull request process, please ask the reviewers in the list to review by filling /cc @reviewer in the comment.
After your PR has acquired the required number of LGTMs, you can assign this pull request to the committer in the list by filling /assign @committer in the comment to help you merge this pull request.

The full list of commands accepted by this bot can be found here.

Reviewer can indicate their review by submitting an approval review.
Reviewer can cancel approval by submitting a request changes review.

@ti-chi-bot ti-chi-bot added status/LGT2 Indicates that a PR has LGTM 2. and removed status/LGT1 Indicates that a PR has LGTM 1. labels Jun 11, 2021
@nolouch
Copy link
Member

nolouch commented Jun 11, 2021

/merge

@ti-chi-bot
Copy link
Member

This pull request has been accepted and is ready to merge.

Commit hash: 703a67d

@ti-chi-bot ti-chi-bot added the status/can-merge Indicates a PR has been approved by a committer. label Jun 11, 2021
@github-actions github-actions bot added sig/execution SIG execution sig/sql-infra SIG: SQL Infra labels Jun 11, 2021
@nolouch
Copy link
Member

nolouch commented Jun 11, 2021

/run-unit-test
/run-check_dev_2

@nolouch
Copy link
Member

nolouch commented Jun 11, 2021

/run-integration-common-test

Comment on lines +817 to +824
tk.MustExec(fmt.Sprintf(`start transaction read only as of timestamp '%s'`, time2.Format("2006-1-2 15:04:05.000")))
c.Assert(tk.ExecToErr(staleSQL), NotNil)
tk.MustExec("commit")

// test prepared stale select in stale txn
tk.MustExec(fmt.Sprintf(`start transaction read only as of timestamp '%s'`, time2.Format("2006-1-2 15:04:05.000")))
tk.MustQuery("execute s").Check(staleRows)
tk.MustExec("commit")
Copy link
Contributor

Choose a reason for hiding this comment

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

Is execute s expected to be executed successfully in staleness transaction while the staleSQL are not?

Copy link
Member

Choose a reason for hiding this comment

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

How about not in prepare?

Copy link
Contributor Author

@xhebox xhebox Jun 11, 2021

Choose a reason for hiding this comment

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

Yes, it is expected. And it should be possible to throw an error in OptimizePreparedPlan.

Copy link
Contributor

Choose a reason for hiding this comment

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

IMO this is not expected. We should avoid multiple snapshotTS in a transaction as users may think it's data inconsistency error during transaction.

@ti-chi-bot ti-chi-bot merged commit 9189ec6 into pingcap:master Jun 11, 2021
ti-srebot pushed a commit to ti-srebot/tidb that referenced this pull request Jun 11, 2021
@ti-srebot
Copy link
Contributor

cherry pick to release-5.1 in PR #25371

// handleAsOfAndReadTS tries to handle as of closure, or possibly read_ts.
// If read_ts is not nil, it will be consumed.
// If as of is not nil, timestamp is used to get the history infoschema from the infocache.
func (p *preprocessor) handleAsOfAndReadTS(node *ast.AsOfClause) {
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 handleAsOfAndReadTS is much complex now. We need to add example to explain it and add unit test to ensured logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-cherry-pick-release-5.1 sig/execution SIG execution sig/sql-infra SIG: SQL Infra size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. status/can-merge Indicates a PR has been approved by a committer. status/LGT2 Indicates that a PR has LGTM 2.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make AsOf compatible with Prepare
6 participants