Skip to content

tso, server: add debug logs for TSO sync, closure, and forwarding paths#10439

Merged
ti-chi-bot[bot] merged 10 commits intotikv:masterfrom
YuhaoZhang00:add-tso-debug-logs
Apr 9, 2026
Merged

tso, server: add debug logs for TSO sync, closure, and forwarding paths#10439
ti-chi-bot[bot] merged 10 commits intotikv:masterfrom
YuhaoZhang00:add-tso-debug-logs

Conversation

@YuhaoZhang00
Copy link
Copy Markdown
Contributor

@YuhaoZhang00 YuhaoZhang00 commented Mar 17, 2026

What problem does this PR solve?

Issue Number: ref #10329

When investigating TSO fallback and dual-writer incidents such as #10329, the existing logs and
metrics are not enough to reconstruct:

  1. which persisted TSO window was written to etcd,
  2. when PD/embedded TSO state changed during switching, and
  3. how many TSO streams were forwarded by PD to the independent TSO service.

What is changed and how does it work?

This PR improves observability for TSO persistence and forwarding paths.

  1. Add persistence-focused logs for successful TSO window writes to etcd:

    • syncTimestamp
    • updateTimestamp
    • resetUserTimestamp
  2. Improve TSO switching related logs:

    • clarify allocator reset warning when UpdateTSO fails and the allocator resigns leadership
    • rename the shutdown log to closing the embedded TSO allocator
    • replace the noisy per-check skip log with transition-based logs when TSO dynamic switching
      is disabled or re-enabled
  3. Replace the forwarded TSO stream debug log with a Prometheus metric:

    • add pd_server_tso_forward_stream_total
    • increment it when PD forwards a TSO stream to the independent TSO service
  4. Add a Grafana panel for the new metric in the PD dashboard:

    • Forward TSO streams count

All new logs on hot paths use debug level to avoid overwhelming logs under burst requests.

Check List

Tests

  • Manual test
    • Grafana panel working with new metric locally
fwd-tso-streams-count

Code changes

N/A

Side effects

  • Increased observability/log volume in TSO persistence paths

Related changes

N/A

Release note

None.

Summary by CodeRabbit

  • Chores
    • Enhanced TSO logging: clearer allocator-reset notice on leadership resignation and new success logs when TSO windows are persisted (including reason, member and time details).
    • Improved dynamic TSO mode visibility with explicit resume/skip messages when mode state changes.
  • New Features
    • Added a Prometheus counter for forwarded TSO streams and incremented it when forwarding occurs.
    • Updated Grafana dashboard: resized panels and added a “Forward TSO streams count” graph.

Add observability to TSO operations that were previously silent:
- Log each successful etcd timestamp save on the normal TSO update path (debug)
- Clarify the Reset(true) path logs to indicate leadership resignation
- Distinguish embedded TSO allocator closure from microservice Close()
- Log when TSO dynamic switching is disabled and service check is skipped
- Log when PD forwards a TSO stream to independent TSO service

Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@ti-chi-bot ti-chi-bot bot added release-note-none Denotes a PR that doesn't merit a release note. do-not-merge/needs-triage-completed dco-signoff: yes Indicates the PR's author has signed the dco. contribution This PR is from a community contributor. needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. labels Mar 17, 2026
@ti-chi-bot
Copy link
Copy Markdown
Contributor

ti-chi-bot bot commented Mar 17, 2026

Hi @YuhaoZhang00. Thanks for your PR.

I'm waiting for a tikv member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

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 kubernetes-sigs/prow repository.

@ti-chi-bot ti-chi-bot bot added the first-time-contributor Indicates that the PR was contributed by an external member and is a first-time contributor. label Mar 17, 2026
@ti-chi-bot
Copy link
Copy Markdown
Contributor

ti-chi-bot bot commented Mar 17, 2026

Welcome @YuhaoZhang00!

It looks like this is your first PR to tikv/pd 🎉.

I'm the bot to help you request reviewers, add labels and more, See available commands.

We want to make sure your contribution gets all the attention it needs!



Thank you, and welcome to tikv/pd. 😃

@ti-chi-bot ti-chi-bot bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Mar 17, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds structured persistence logs for TSO operations, tracks TSO dynamic-switching state with an atomic in RaftCluster to suppress redundant messages and skip checks when disabled, increments a new Prometheus counter when forwarding TSO streams, and updates Grafana layout adding a panel for forwarded TSO streams.

Changes

Cohort / File(s) Summary
TSO allocator & persistence
pkg/tso/allocator.go, pkg/tso/tso.go
Enhanced logging around UpdateTSO failures and successful persistence of TSO windows to etcd (adds clearer messages and structured fields like member-name / saved-time). No control-flow behavior changes.
Cluster TSO dynamic switching
server/cluster/cluster.go
Added tsoDynamicSwitchingState (atomic.Int32) and state constants; atomically track transitions and log “resuming”/“skipping” messages only on state change; skip embedded TSO discovery/start/stop when dynamic switching disabled.
gRPC forwarding & metrics
server/grpc_service.go, server/metrics.go
Added pd_server_tso_forward_stream_total counter and increment (tsoForwardStreamCounter.Inc()) when forwarding TSO streams to an independent service.
Grafana dashboard
metrics/grafana/pd.json
Adjusted panel widths/positions and added new panel Forward TSO streams count (query uses pd_server_tso_forward_stream_total).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GrpcServer
  participant RaftCluster
  participant TSOService
  participant Etcd

  Client->>GrpcServer: open TSO stream
  GrpcServer->>RaftCluster: checkTSOService()
  RaftCluster->>RaftCluster: atomic read/update tsoDynamicSwitchingState
  alt dynamic switching disabled
    RaftCluster-->>GrpcServer: log "skipping TSO service checks"
    GrpcServer->>TSOService: forward stream (independent) / Inc metric
  else dynamic switching enabled
    RaftCluster-->>GrpcServer: log "resuming TSO service checks"
    GrpcServer->>TSOService: forward or serve embedded / Inc metric
  end
  TSOService->>Etcd: persist TSO window (save)
  Etcd-->>TSOService: ack
  TSOService-->>GrpcServer: stream responses
  GrpcServer-->>Client: stream responses
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

size/M

Suggested reviewers

  • JmPotato
  • okJiang
  • rleungx
  • liyishuai

Poem

🐇 I hop through logs and count each stream,
I mark the flips where dynamic modes gleam,
Saved times whisper from etcd's shore,
Streams forwarded — one, two, then more,
A tiny hop, and metrics sing — hooray!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'tso, server: add debug logs for TSO sync, closure, and forwarding paths' directly and accurately summarizes the main changes: adding debug logs across TSO and server components for sync, closure, and forwarding operations.
Description check ✅ Passed The PR description provides a clear problem statement (ref #10329), detailed explanation of changes across 4 areas (persistence logs, switching logs, metrics, Grafana panel), test approach (manual), and release note status, largely following the repository template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@YuhaoZhang00
Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot
Copy link
Copy Markdown
Contributor

ti-chi-bot bot commented Mar 19, 2026

@YuhaoZhang00: Cannot trigger testing until a trusted user reviews the PR and leaves an /ok-to-test message.

Details

In response to this:

/retest

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 kubernetes-sigs/prow repository.

@JmPotato
Copy link
Copy Markdown
Member

/ok-to-test

@ti-chi-bot ti-chi-bot bot added ok-to-test Indicates a PR is ready to be tested. and removed needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. labels Mar 19, 2026
@JmPotato
Copy link
Copy Markdown
Member

/retest

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 19, 2026

Codecov Report

❌ Patch coverage is 81.81818% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.89%. Comparing base (033398b) to head (ec48604).
⚠️ Report is 28 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10439      +/-   ##
==========================================
+ Coverage   78.86%   78.89%   +0.03%     
==========================================
  Files         529      532       +3     
  Lines       71102    71883     +781     
==========================================
+ Hits        56072    56714     +642     
- Misses      11014    11130     +116     
- Partials     4016     4039      +23     
Flag Coverage Δ
unittests 78.89% <81.81%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@YuhaoZhang00
Copy link
Copy Markdown
Contributor Author

/retest

pkg/tso/tso.go Outdated
t.metrics.updateSaveDuration.Observe(time.Since(start).Seconds())
saveDuration := time.Since(start)
t.metrics.updateSaveDuration.Observe(saveDuration.Seconds())
log.Debug("saved timestamp to etcd",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What do you think about only logging this and setting it to the INFO level when the saveDuration exceeds a certain threshold?

We don't really need to know every time it saves; we are much more concerned with how long the saving process actually takes.

c.SetServiceIndependent(constant.TSOServiceName)
}
if !c.opt.GetMicroserviceConfig().IsTSODynamicSwitchingEnabled() {
log.Debug("TSO dynamic switching is disabled, skipping TSO service check")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It isn’t on a hot path here, or it won’t be printed that frequently. So I think using INFO-level logging is fine, and it can give us some information about the timing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Under isKeyspaceGroupEnabled = true and isTSODynamicSwitchingEnabled = false config, the log will be printed once per 100ms. Maybe worth keeping it in Debug level, but would be better eliminating it. For this method, logs on successful/failed state transition is just enough.

defer done()
}
if s.IsServiceIndependent(constant.TSOServiceName) {
log.Debug("forwarding TSO stream to independent TSO service")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I feel this indicator would be better suited as a Grafana metric. This way, we can observe the count on a monitoring dashboard.

@ti-chi-bot ti-chi-bot bot added dco-signoff: no Indicates the PR's author has not signed dco. and removed dco-signoff: yes Indicates the PR's author has signed the dco. labels Mar 19, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
pkg/tso/tso.go (1)

289-305: Minor: getLastSavedTime() called twice.

t.getLastSavedTime() is called once in the condition (line 289) and again at line 290. Consider capturing it once before the condition check to avoid the redundant atomic load, though this is a minor point since user-reset is infrequent.

♻️ Suggested refactor
 // save into etcd only if nextPhysical is close to lastSavedTime
+lastSavedTime := t.getLastSavedTime()
-if typeutil.SubRealTimeByWallClock(t.getLastSavedTime(), nextPhysical) <= updateTimestampGuard {
-	lastSavedTime := t.getLastSavedTime()
+if typeutil.SubRealTimeByWallClock(lastSavedTime, nextPhysical) <= updateTimestampGuard {
 	save := nextPhysical.Add(t.saveInterval)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tso/tso.go` around lines 289 - 305, The code calls t.getLastSavedTime()
twice (in the condition using typeutil.SubRealTimeByWallClock and immediately
after), causing redundant atomic loads; capture the value once into a local
variable (e.g., lastSavedTime := t.getLastSavedTime()) before evaluating the
condition, use that local in the SubRealTimeByWallClock comparison and later
when logging/updating, and keep the existing logic that computes save :=
nextPhysical.Add(t.saveInterval) and calls t.saveTimestamp(save) and
t.lastSavedTime.Store(save).
server/cluster/cluster.go (1)

472-497: Good state transition logging, minor asymmetry on startup.

The implementation correctly addresses the concern about logging every 100ms by only logging on state transitions. However, there's a subtle asymmetry:

  • Startup with dynamic switching enabled: no log (Unknown→Enabled doesn't trigger)
  • Startup with dynamic switching disabled: logs "skipping TSO service checks" (Unknown→Disabled triggers)

This may be intentional since logging when checks are being skipped is useful information. If symmetric behavior is desired, the enabled path could also log on Unknown→Enabled:

♻️ Optional: symmetric startup logging
 prev := c.tsoDynamicSwitchingState.Swap(tsoDynamicSwitchingStateEnabled)
-if prev == tsoDynamicSwitchingStateDisabled {
+if prev != tsoDynamicSwitchingStateEnabled {
     log.Info("TSO dynamic switching is enabled, resuming TSO service checks")
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/cluster/cluster.go` around lines 472 - 497, The logging is asymmetric
on startup: when swapping to tsoDynamicSwitchingStateEnabled you only log if
prev == tsoDynamicSwitchingStateDisabled, but when swapping to
tsoDynamicSwitchingStateDisabled you log for any non-disabled previous state
(including unknown); make the enabled-path symmetric by treating the unknown
state the same as disabled for logging. Update the swap branch that uses
c.tsoDynamicSwitchingState.Swap(tsoDynamicSwitchingStateEnabled) (the block that
currently checks prev == tsoDynamicSwitchingStateDisabled) to also log when prev
== tsoDynamicSwitchingStateUnknown so startup Unknown→Enabled emits a log
message similar to Unknown→Disabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/tso/tso.go`:
- Around line 289-305: The code calls t.getLastSavedTime() twice (in the
condition using typeutil.SubRealTimeByWallClock and immediately after), causing
redundant atomic loads; capture the value once into a local variable (e.g.,
lastSavedTime := t.getLastSavedTime()) before evaluating the condition, use that
local in the SubRealTimeByWallClock comparison and later when logging/updating,
and keep the existing logic that computes save :=
nextPhysical.Add(t.saveInterval) and calls t.saveTimestamp(save) and
t.lastSavedTime.Store(save).

In `@server/cluster/cluster.go`:
- Around line 472-497: The logging is asymmetric on startup: when swapping to
tsoDynamicSwitchingStateEnabled you only log if prev ==
tsoDynamicSwitchingStateDisabled, but when swapping to
tsoDynamicSwitchingStateDisabled you log for any non-disabled previous state
(including unknown); make the enabled-path symmetric by treating the unknown
state the same as disabled for logging. Update the swap branch that uses
c.tsoDynamicSwitchingState.Swap(tsoDynamicSwitchingStateEnabled) (the block that
currently checks prev == tsoDynamicSwitchingStateDisabled) to also log when prev
== tsoDynamicSwitchingStateUnknown so startup Unknown→Enabled emits a log
message similar to Unknown→Disabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0b66f8a8-61f3-4177-923b-6f6fdc57001f

📥 Commits

Reviewing files that changed from the base of the PR and between 33ff440 and 31de270.

📒 Files selected for processing (2)
  • pkg/tso/tso.go
  • server/cluster/cluster.go

@ti-chi-bot ti-chi-bot bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Mar 19, 2026
@YuhaoZhang00 YuhaoZhang00 requested a review from JmPotato March 19, 2026 15:51
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@ti-chi-bot ti-chi-bot bot added needs-1-more-lgtm Indicates a PR needs 1 more LGTM. approved labels Apr 7, 2026
pkg/tso/tso.go Outdated

t.metrics.syncOKEvent.Inc()
log.Info("sync and save timestamp",
log.Info("persisted tso window to etcd (sync)",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a critical path log that has existed for a long time, and I tend to avoid changing all the fields, as that might affect some alerting systems.

@ti-chi-bot ti-chi-bot bot added lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Apr 8, 2026
@JmPotato
Copy link
Copy Markdown
Member

JmPotato commented Apr 8, 2026

/hold

@ti-chi-bot ti-chi-bot bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Apr 8, 2026
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@JmPotato
Copy link
Copy Markdown
Member

JmPotato commented Apr 8, 2026

/unhold

@ti-chi-bot ti-chi-bot bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Apr 8, 2026
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@ti-chi-bot ti-chi-bot bot removed the lgtm label Apr 8, 2026
@YuhaoZhang00 YuhaoZhang00 requested a review from JmPotato April 8, 2026 04:31
@ti-chi-bot ti-chi-bot bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Apr 8, 2026
@YuhaoZhang00
Copy link
Copy Markdown
Contributor Author

/retest

@YuhaoZhang00
Copy link
Copy Markdown
Contributor Author

/retest

1 similar comment
@YuhaoZhang00
Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot ti-chi-bot bot added the lgtm label Apr 9, 2026
@ti-chi-bot
Copy link
Copy Markdown
Contributor

ti-chi-bot bot commented Apr 9, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bufferflies, JmPotato

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

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [JmPotato,bufferflies]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot bot removed the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Apr 9, 2026
@ti-chi-bot
Copy link
Copy Markdown
Contributor

ti-chi-bot bot commented Apr 9, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-04-07 08:31:22.797488104 +0000 UTC m=+858688.002848161: ☑️ agreed by bufferflies.
  • 2026-04-08 03:22:24.307946767 +0000 UTC m=+926549.513306824: ☑️ agreed by JmPotato.
  • 2026-04-08 04:01:25.934448429 +0000 UTC m=+928891.139808485: ✖️🔁 reset by JmPotato.
  • 2026-04-08 04:32:50.02854806 +0000 UTC m=+930775.233908117: ☑️ agreed by JmPotato.
  • 2026-04-09 02:07:23.372785194 +0000 UTC m=+1008448.578145251: ☑️ agreed by bufferflies.

@ti-chi-bot ti-chi-bot bot merged commit b21a183 into tikv:master Apr 9, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved contribution This PR is from a community contributor. dco-signoff: yes Indicates the PR's author has signed the dco. first-time-contributor Indicates that the PR was contributed by an external member and is a first-time contributor. lgtm ok-to-test Indicates a PR is ready to be tested. release-note-none Denotes a PR that doesn't merit a release note. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants