-
Notifications
You must be signed in to change notification settings - Fork 6.2k
sessionctx: add cluster-wide read-only status variable #68853
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
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 |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright 2026 PingCAP, Inc. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package vardef | ||
|
|
||
| import ( | ||
| "context" | ||
| "sync/atomic" | ||
| ) | ||
|
|
||
| // ClusterReadOnlyChecker returns whether all live TiDB instances are effectively read-only. | ||
| type ClusterReadOnlyChecker func(context.Context) (bool, error) | ||
|
|
||
| // ReadOnlyStatusReporter publishes the local TiDB read-only status. | ||
| type ReadOnlyStatusReporter func(context.Context) error | ||
|
|
||
| var ( | ||
| clusterReadOnlyChecker atomic.Value | ||
| readOnlyStatusReporter atomic.Value | ||
| ) | ||
|
|
||
| // LocalTiDBReadOnlyStatus returns the local TiDB effective read-only status. | ||
| func LocalTiDBReadOnlyStatus() bool { | ||
| return RestrictedReadOnly.Load() || VarTiDBSuperReadOnly.Load() | ||
| } | ||
|
|
||
| // SetClusterReadOnlyChecker registers the checker used by the derived tidb_is_read_only variable. | ||
| func SetClusterReadOnlyChecker(checker ClusterReadOnlyChecker) { | ||
| clusterReadOnlyChecker.Store(checker) | ||
| } | ||
|
|
||
| // GetClusterReadOnlyStatus returns whether the whole TiDB cluster is effectively read-only. | ||
| func GetClusterReadOnlyStatus(ctx context.Context) (bool, error) { | ||
| checker, ok := clusterReadOnlyChecker.Load().(ClusterReadOnlyChecker) | ||
| if !ok || checker == nil { | ||
| return LocalTiDBReadOnlyStatus(), nil | ||
| } | ||
| return checker(ctx) | ||
| } | ||
|
|
||
| // SetReadOnlyStatusReporter registers a reporter for the local TiDB read-only status. | ||
| func SetReadOnlyStatusReporter(reporter ReadOnlyStatusReporter) { | ||
| readOnlyStatusReporter.Store(reporter) | ||
| } | ||
|
|
||
| // ReportReadOnlyStatus publishes the local TiDB read-only status when a reporter is available. | ||
| func ReportReadOnlyStatus(ctx context.Context) error { | ||
| reporter, ok := readOnlyStatusReporter.Load().(ReadOnlyStatusReporter) | ||
| if !ok || reporter == nil { | ||
| return nil | ||
| } | ||
| return reporter(ctx) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,6 +81,12 @@ | |
| return func(sv *SysVar) { sv.MinValue = minVal } | ||
| } | ||
|
|
||
| func reportTiDBReadOnlyStatus(ctx context.Context) { | ||
| if err := vardef.ReportReadOnlyStatus(ctx); err != nil { | ||
|
Check failure on line 85 in pkg/sessionctx/variable/sysvar.go
|
||
| logutil.BgLogger().Warn("update TiDB read-only status failed", zap.Error(err)) | ||
| } | ||
| } | ||
|
Comment on lines
+84
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the Crossbuild is failing here because Also applies to: 1014-1020 🧰 Tools🪛 GitHub Check: Bazel Crossbuild (ubuntu-24.04-arm)[failure] 85-85: 🤖 Prompt for AI Agents |
||
|
|
||
| // newExecConcurrencySysVar creates a session/global SysVar for executor concurrency settings. | ||
| func newExecConcurrencySysVar(name string, defValue int, setter concurrencySetter, opts ...execConcurrencySysVarOption) *SysVar { | ||
| sv := &SysVar{ | ||
|
|
@@ -971,7 +977,7 @@ | |
| tikvstore.TxnCommitBatchSize.Store(uint64(TidbOptInt64(val, int64(tikvstore.DefTxnCommitBatchSize)))) | ||
| return nil | ||
| }}, | ||
| {Scope: vardef.ScopeGlobal, Name: vardef.TiDBRestrictedReadOnly, Value: BoolToOnOff(vardef.DefTiDBRestrictedReadOnly), Type: vardef.TypeBool, SetGlobal: func(_ context.Context, s *SessionVars, val string) error { | ||
| {Scope: vardef.ScopeGlobal, Name: vardef.TiDBRestrictedReadOnly, Value: BoolToOnOff(vardef.DefTiDBRestrictedReadOnly), Type: vardef.TypeBool, SetGlobal: func(ctx context.Context, s *SessionVars, val string) error { | ||
| on := TiDBOptOn(val) | ||
| // For user initiated SET GLOBAL, also change the value of TiDBSuperReadOnly | ||
| if on && s.StmtCtx.StmtType == "Set" { | ||
|
|
@@ -985,6 +991,7 @@ | |
| } | ||
| } | ||
| vardef.RestrictedReadOnly.Store(on) | ||
| reportTiDBReadOnlyStatus(ctx) | ||
| return nil | ||
| }}, | ||
| {Scope: vardef.ScopeGlobal, Name: vardef.TiDBSuperReadOnly, Value: BoolToOnOff(vardef.DefTiDBSuperReadOnly), Type: vardef.TypeBool, Validation: func(s *SessionVars, normalizedValue string, _ string, _ vardef.ScopeFlag) (string, error) { | ||
|
|
@@ -999,10 +1006,18 @@ | |
| } | ||
| } | ||
| return normalizedValue, nil | ||
| }, SetGlobal: func(_ context.Context, s *SessionVars, val string) error { | ||
| }, SetGlobal: func(ctx context.Context, s *SessionVars, val string) error { | ||
| vardef.VarTiDBSuperReadOnly.Store(TiDBOptOn(val)) | ||
| reportTiDBReadOnlyStatus(ctx) | ||
| return nil | ||
| }}, | ||
| {Scope: vardef.ScopeGlobal, Name: vardef.TiDBIsReadOnly, Value: vardef.Off, Type: vardef.TypeBool, ReadOnly: true, GetGlobal: func(ctx context.Context, s *SessionVars) (string, error) { | ||
| on, err := vardef.GetClusterReadOnlyStatus(ctx) | ||
|
Check failure on line 1015 in pkg/sessionctx/variable/sysvar.go
|
||
| if err != nil { | ||
| return vardef.Off, err | ||
| } | ||
| return BoolToOnOff(on), nil | ||
| }}, | ||
| {Scope: vardef.ScopeGlobal, Name: vardef.TiDBEnableGOGCTuner, Value: BoolToOnOff(vardef.DefTiDBEnableGOGCTuner), Type: vardef.TypeBool, SetGlobal: func(_ context.Context, s *SessionVars, val string) error { | ||
| on := TiDBOptOn(val) | ||
| gctuner.EnableGOGCTuner.Store(on) | ||
|
|
||
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.
There is race here. UpdateServerLabel and UpdateServerReadOnlyStatus both clone DynamicInfo, change one field group, then write the whole struct back.
Bug case: label update clones read_only=false; read-only update writes read_only=true; label update then writes its old snapshot with new labels, restoring read_only=false. After that, tidb_is_read_only may return OFF even though this TiDB is already read-only.