Repository navigation
localbackend: add write timeout handling for object store region jobs #68148
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 all commits
8d4b518
f2f9739
4a06c1f
b5434fd
8f2b2ef
1226785
f4cb5fb
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 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,7 @@ package local | |||||||||||||||||
|
|
||||||||||||||||||
| import ( | ||||||||||||||||||
| "context" | ||||||||||||||||||
| goerrors "errors" | ||||||||||||||||||
| "io" | ||||||||||||||||||
| "strings" | ||||||||||||||||||
| "sync" | ||||||||||||||||||
|
|
@@ -197,7 +198,7 @@ func (w *regionJobBaseWorker) runJob(ctx context.Context, job *regionJob) error | |||||||||||||||||
| job.convertStageTo(needRescan) | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
| res, err := w.writeFn(ctx, job) | ||||||||||||||||||
| res, err := w.writeWithTimeout(ctx, job) | ||||||||||||||||||
| err = injectfailpoint.DXFRandomErrorWithOnePercentWrapper(err) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| if !w.isRetryableImportTiKVError(err) { | ||||||||||||||||||
|
|
@@ -266,6 +267,34 @@ func (w *regionJobBaseWorker) runJob(ctx context.Context, job *regionJob) error | |||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (w *regionJobBaseWorker) writeWithTimeout( | ||||||||||||||||||
| ctx context.Context, | ||||||||||||||||||
| job *regionJob, | ||||||||||||||||||
| ) (ret *tikvWriteResult, err error) { | ||||||||||||||||||
| // set a timeout for the write operation, if it takes too long, we will | ||||||||||||||||||
| // return with common.ErrWriteTooSlow and let caller retry the whole job | ||||||||||||||||||
| // instead of being stuck forever. | ||||||||||||||||||
| timeout := 15 * time.Minute | ||||||||||||||||||
| failpoint.Inject("shortWaitNTimeout", func(val failpoint.Value) { | ||||||||||||||||||
| ms := val.(int) | ||||||||||||||||||
| timeout = time.Duration(ms) * time.Millisecond | ||||||||||||||||||
| }) | ||||||||||||||||||
| ctx, cancel := context.WithTimeoutCause(ctx, timeout, common.ErrWriteTooSlow) | ||||||||||||||||||
| defer cancel() | ||||||||||||||||||
|
|
||||||||||||||||||
| ret, err = w.writeFn(ctx, job) | ||||||||||||||||||
| if err == nil { | ||||||||||||||||||
| return ret, nil | ||||||||||||||||||
| } | ||||||||||||||||||
| if errors.Cause(err) == context.DeadlineExceeded { | ||||||||||||||||||
| if cause := context.Cause(ctx); goerrors.Is(cause, common.ErrWriteTooSlow) { | ||||||||||||||||||
|
D3Hunter marked this conversation as resolved.
Comment on lines
+289
to
+290
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. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect how writeFn implementations propagate context.DeadlineExceeded.
rg -nP -C3 '\bfunc\s+\([^)]*\)\s+(doWrite|write)\s*\(' pkg/lightning/backend/local
rg -nP -C2 'DeadlineExceeded|status\.FromContextError|context\.Cause' pkg/lightning/backend/local pkg/ingestorRepository: pingcap/tidb Length of output: 3167 🏁 Script executed: # First, let's examine the doWrite function implementation
sed -n '331,450p' pkg/lightning/backend/local/region_job.go | head -100Repository: pingcap/tidb Length of output: 3077 🏁 Script executed: # Examine the write function implementation
sed -n '355,420p' pkg/lightning/backend/local/job_worker.goRepository: pingcap/tidb Length of output: 1741 🏁 Script executed: # Check how errors are imported and used in these files
head -50 pkg/lightning/backend/local/job_worker.go | grep -E 'import|errors|goerrors'Repository: pingcap/tidb Length of output: 164 🏁 Script executed: # Look for gRPC status error handling in the codebase
rg -n 'status\.FromContextError|status\.FromError' pkg/lightning/backend/local pkg/ingestorRepository: pingcap/tidb Length of output: 141 🏁 Script executed: # Get full context around job_worker.go:289-290
sed -n '270,310p' pkg/lightning/backend/local/job_worker.goRepository: pingcap/tidb Length of output: 1425 🏁 Script executed: # Check if errors are wrapped with %w anywhere in writeFn implementations
rg '%w' pkg/lightning/backend/local/job_worker.go pkg/lightning/backend/local/region_job.goRepository: pingcap/tidb Length of output: 38 🏁 Script executed: # Look for where gRPC stream errors might be handled before reaching line 289
sed -n '270,350p' pkg/lightning/backend/local/job_worker.go | grep -A30 'func (w \*localRegionJobWorker)'Repository: pingcap/tidb Length of output: 38 🏁 Script executed: # Check how writeWithTimeout works and what writeFn is
sed -n '250,290p' pkg/lightning/backend/local/job_worker.goRepository: pingcap/tidb Length of output: 1392 🏁 Script executed: # Check if pingcap/errors types implement Unwrap() for Is() compatibility
rg -n 'func.*Unwrap|func.*Cause' pkg/vendor/github.com/pingcap/errors 2>/dev/null || \
rg -n 'pingcap/errors' go.modRepository: pingcap/tidb Length of output: 125 🏁 Script executed: # Look for any %w wrapping in region_job.go doWrite implementation
sed -n '331,550p' pkg/lightning/backend/local/region_job.go | grep -n '%w\|Wrap'Repository: pingcap/tidb Length of output: 38 🏁 Script executed: # Check what Annotate and Trace functions do
rg -A5 'func.*Annotate|func.*Trace' pkg/lightning/backend/local/job_worker.go | head -20Repository: pingcap/tidb Length of output: 38 🏁 Script executed: # Look at the complete doWrite to see all error returns
sed -n '331,600p' pkg/lightning/backend/local/region_job.go | grep -n 'return.*error\|errors\.\|err :='Repository: pingcap/tidb Length of output: 1077 🏁 Script executed: # Check if there are any gRPC status error wrappings in doWrite
sed -n '331,600p' pkg/lightning/backend/local/region_job.go | grep -n 'status\|Status'Repository: pingcap/tidb Length of output: 38 🏁 Script executed: # Look at the test to understand expected behavior
sed -n '310,330p' pkg/lightning/backend/local/job_worker_test.goRepository: pingcap/tidb Length of output: 945 🏁 Script executed: # Check if pingcap/errors is implemented with Unwrap support by looking at actual usage patterns in tests
rg -B5 -A5 'goerrors\.Is.*DeadlineExceeded' pkg/lightning/backend/local/Repository: pingcap/tidb Length of output: 38 🏁 Script executed: # Let's check the actual implementation path for how errors flow through WriteClient
rg -n 'WriteClient' pkg/lightning/backend/local/job_worker.go | head -5Repository: pingcap/tidb Length of output: 131 🏁 Script executed: # Look at ingestCli to see what WriteClient returns
find pkg -name '*ingest*' -type f | head -10Repository: pingcap/tidb Length of output: 173 🏁 Script executed: # Check how errors.Trace wraps errors - does it implement Unwrap?
rg -B3 -A10 'type.*Error|func.*Unwrap' pkg/vendor/github.com/pingcap/errors/errors.go 2>/dev/null || \
python3 << 'EOF'
# Try to understand if pingcap/errors preserves error chains
import subprocess
result = subprocess.run(['find', 'pkg', '-name', 'errors.go', '-path', '*/pingcap/errors/*'], capture_output=True, text=True)
print("Found error files:")
print(result.stdout)
EOFRepository: pingcap/tidb Length of output: 76 🏁 Script executed: # Let's check the ingestcli WriteClient implementation to see what errors it returns
sed -n '1,50p' pkg/ingestor/ingestcli/ingest_err.goRepository: pingcap/tidb Length of output: 1539 🏁 Script executed: # Check what WriteClient interface/method looks like
rg -B5 -A10 'WriteClient.*func' pkg/ingestor/Repository: pingcap/tidb Length of output: 38 🏁 Script executed: # Most importantly, let's verify if pingcap/errors implements Unwrap for goerrors.Is compatibility
python3 << 'EOF'
# Check if pingcap/errors in go.mod and look for its implementation
import subprocess
import os
# Find pingcap/errors package in vendor
result = subprocess.run(
['find', 'pkg/vendor/github.com/pingcap/errors', '-name', '*.go'],
capture_output=True, text=True
)
if result.stdout:
files = result.stdout.strip().split('\n')[:3]
for f in files:
print(f"\n=== {f} ===")
with open(f) as fp:
content = fp.read()
# Look for Unwrap method
if 'Unwrap' in content:
lines = content.split('\n')
for i, line in enumerate(lines):
if 'Unwrap' in line:
start = max(0, i-2)
end = min(len(lines), i+8)
print('\n'.join(lines[start:end]))
print('---')
EOFRepository: pingcap/tidb Length of output: 38 🏁 Script executed: # Final check: verify the fix would work by checking if errors.Trace returns an error that satisfies goerrors.Is
rg -n 'func.*Trace.*error' pkg/ | grep pingcap || \
grep -r 'func.*Trace' pkg/vendor/github.com/pingcap/errors/ 2>/dev/null | head -5Repository: pingcap/tidb Length of output: 38 Use Line 289 uses Suggested change- if errors.Cause(err) == context.DeadlineExceeded {
+ if goerrors.Is(err, context.DeadlineExceeded) {
if cause := context.Cause(ctx); goerrors.Is(cause, common.ErrWriteTooSlow) {
tidblogutil.Logger(ctx).Info("experiencing a wait timeout while writing to TiKV")
err = errors.Trace(cause)
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| tidblogutil.Logger(ctx).Info("experiencing a wait timeout while writing to TiKV") | ||||||||||||||||||
| err = errors.Trace(cause) | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| return ret, err | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (*regionJobBaseWorker) isRetryableImportTiKVError(err error) bool { | ||||||||||||||||||
| err = errors.Cause(err) | ||||||||||||||||||
| // io.EOF is not retryable in normal case | ||||||||||||||||||
|
|
||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.