Skip to content

feat(wiki): add exponential backoff retry for +node-create lock contention#1076

Merged
fangshuyu-768 merged 1 commit into
mainfrom
fix/wiki-node-create-lock-contention-retry
May 25, 2026
Merged

feat(wiki): add exponential backoff retry for +node-create lock contention#1076
fangshuyu-768 merged 1 commit into
mainfrom
fix/wiki-node-create-lock-contention-retry

Conversation

@fangshuyu-768
Copy link
Copy Markdown
Collaborator

@fangshuyu-768 fangshuyu-768 commented May 25, 2026

Summary

Closes #1012

When creating wiki nodes under the same parent concurrently, the API returns error code 131009 (lock contention) ~5-15% of the time. This adds automatic retry with exponential backoff (250ms, 500ms; max 2 retries) so callers no longer need to implement retry logic themselves.

Changes

  • shortcuts/wiki/wiki_node_create.go: retry loop on code 131009 with exponential backoff, context cancellation support, retry-exhaustion hint preserving Detail.Code/Err/Raw
  • shortcuts/wiki/wiki_node_create_test.go: 6 retry unit tests
  • tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go: dry-run E2E tests for wiki +node-create

Test plan

go test ./shortcuts/wiki/ -run TestRunWikiNodeCreate -v
go test ./tests/cli_e2e/wiki/ -run TestWikiNodeCreateDryRun -v

Summary by CodeRabbit

  • New Features

    • Wiki node creation now automatically retries on detected lock contention, emits retry status messages, respects cancellation, and preserves original error codes while adding a retry-exhaustion hint when retries fail.
    • No retries occur for non-contention errors.
  • Tests

    • Added unit tests covering retry scenarios (success after retries, exhaustion, no-retry cases, cancellation).
    • Added CLI end-to-end dry-run tests validating node-create behavior and flag validation.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 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 interruptible exponential-backoff retries for wiki node creation on lock-contention (error 131009), routes stderr for retry logs, classifies contention errors, wraps exhausted retry errors retaining original metadata, and adds unit and dry-run e2e tests covering retry and validation cases.

Changes

Wiki Node Creation Retry Support

Layer / File(s) Summary
Retry configuration and imports
shortcuts/wiki/wiki_node_create.go
Import errors and add constants for wikiNodeCreateMaxRetries and wikiNodeCreateRetryBaseDelay.
CLI wiring of stderr to runWikiNodeCreate
shortcuts/wiki/wiki_node_create.go
Pass runtime.IO().ErrOut into runWikiNodeCreate so retry status messages are written to stderr.
Retry loop and execution flow
shortcuts/wiki/wiki_node_create.go
Implement an interruptible exponential-backoff retry loop in runWikiNodeCreate that retries on lock-contention, prints retry attempts to errOut, respects ctx.Done(), aborts on non-retryable errors, and stops after the max attempts.
Error classification and retry-exhaustion wrapping
shortcuts/wiki/wiki_node_create.go
Add isWikiNodeLockContention to detect contention via output.ExitError detail code and wrapWikiNodeCreateRetryError to append a retries-exhausted hint while preserving original error code/detail.
Tests: fake client, invocation updates, and retry tests
shortcuts/wiki/wiki_node_create_test.go
Extend fakeWikiNodeCreateClient with createErrs []error, consume sequential errors in CreateNode, pass io.Discard for stderr in updated tests, and add tests verifying retry-then-success with logs, retry exhaustion wrapped as *output.ExitError, and no retry for non-contention errors.
CLI e2e dry-run tests
tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go
Add dry-run e2e tests asserting generated HTTP requests and CLI flag validation errors for multiple wiki +node-create scenarios, plus a helper to extract structured error messages.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • larksuite/cli#320: Earlier changes to shortcuts/wiki/wiki_node_create.go that relate to the same create path and tests.
  • larksuite/cli#1014: Introduces/depends on output.LarkErrWikiLockContention classification used by the retry detection and wrapping.
  • larksuite/cli#470: Modifies the same wiki create flow (auto-grant / nil handling), overlapping area of change.

"I am a rabbit, I retry with grace,
When locks refuse, I slow my pace.
Backoff beats the frantic spree,
Stderr whispers 'attempt: two, three'.
Now nodes appear — hop, hop, embrace!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding exponential backoff retry logic for wiki node creation when lock contention (code 131009) occurs.
Description check ✅ Passed The description follows the template structure with all required sections: Summary (with issue reference), Changes (listing all three files), and Test Plan with specific commands.
Linked Issues check ✅ Passed All coding requirements from #1012 are met: retry on 131009 only, exponential backoff (250ms/500ms), context cancellation support, error preservation, and comprehensive unit/e2e tests.
Out of Scope Changes check ✅ Passed All changes are directly related to the retry-on-lock-contention objective. The addition of E2E dry-run tests is appropriate validation and not out of scope.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/wiki-node-create-lock-contention-retry

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.

@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact labels May 25, 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)
shortcuts/wiki/wiki_node_create.go (1)

375-386: ⚡ Quick win

Preserve Err and Raw when rewrapping ExitError.

Lines 375-386 rebuild the envelope but drop the original Err and Raw fields, which can strip the causal chain or change downstream enrichment behavior. Copy those through when appending the retry hint.

Suggested change
 	return &output.ExitError{
 		Code: exitErr.Code,
+		Err:  exitErr.Err,
+		Raw:  exitErr.Raw,
 		Detail: &output.ErrDetail{
 			Type:       exitErr.Detail.Type,
 			Code:       exitErr.Detail.Code,
 			Message:    exitErr.Detail.Message,
 			Hint:       hint,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/wiki/wiki_node_create.go` around lines 375 - 386, The current
rewrap of an existing exitErr into a new &output.ExitError recreates Code and
Detail but drops the original causal fields Err and Raw; update the return to
copy exitErr.Err and exitErr.Raw into the new &output.ExitError so the causal
chain and raw payload are preserved while still appending the retry hint into
Detail (i.e., keep Code, Detail.*, plus Err and Raw from the original exitErr).
shortcuts/wiki/wiki_node_create_test.go (1)

785-899: ⚡ Quick win

Add one test for the canceled-context branch.

The new suite doesn't exercise the ctx.Done() path in shortcuts/wiki/wiki_node_create.go Lines 319-323, so the "abort during backoff and return ctx.Err()" behavior can still regress unnoticed. A small cancellation test would close that gap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/wiki/wiki_node_create_test.go` around lines 785 - 899, Add a test
that exercises the ctx.Done() branch in runWikiNodeCreate by creating a
fakeWikiNodeCreateClient that initially returns a lock-contention error (use
output.ErrAPI(output.LarkErrWikiLockContention,...)), run runWikiNodeCreate with
a context.WithCancel in a goroutine, cancel the context while the function is in
its backoff/retry loop, and assert the call returned ctx.Err() (or
context.Canceled), that no additional creates were invoked after cancellation,
and that stderr/log contains the expected abort/retry message; reference the
runWikiNodeCreate function and fakeWikiNodeCreateClient to locate where to add
this test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@shortcuts/wiki/wiki_node_create_test.go`:
- Around line 785-899: Add a test that exercises the ctx.Done() branch in
runWikiNodeCreate by creating a fakeWikiNodeCreateClient that initially returns
a lock-contention error (use
output.ErrAPI(output.LarkErrWikiLockContention,...)), run runWikiNodeCreate with
a context.WithCancel in a goroutine, cancel the context while the function is in
its backoff/retry loop, and assert the call returned ctx.Err() (or
context.Canceled), that no additional creates were invoked after cancellation,
and that stderr/log contains the expected abort/retry message; reference the
runWikiNodeCreate function and fakeWikiNodeCreateClient to locate where to add
this test.

In `@shortcuts/wiki/wiki_node_create.go`:
- Around line 375-386: The current rewrap of an existing exitErr into a new
&output.ExitError recreates Code and Detail but drops the original causal fields
Err and Raw; update the return to copy exitErr.Err and exitErr.Raw into the new
&output.ExitError so the causal chain and raw payload are preserved while still
appending the retry hint into Detail (i.e., keep Code, Detail.*, plus Err and
Raw from the original exitErr).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a4da265-dce4-4171-a0e1-135578a73c87

📥 Commits

Reviewing files that changed from the base of the PR and between ac06eaa and 1752a34.

📒 Files selected for processing (2)
  • shortcuts/wiki/wiki_node_create.go
  • shortcuts/wiki/wiki_node_create_test.go

@codecov
Copy link
Copy Markdown

codecov Bot commented May 25, 2026

Codecov Report

❌ Patch coverage is 88.23529% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.85%. Comparing base (ac06eaa) to head (b1da213).

Files with missing lines Patch % Lines
shortcuts/wiki/wiki_node_create.go 88.23% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1076      +/-   ##
==========================================
+ Coverage   67.83%   67.85%   +0.01%     
==========================================
  Files         592      592              
  Lines       55327    55373      +46     
==========================================
+ Hits        37532    37574      +42     
- Misses      14683    14685       +2     
- Partials     3112     3114       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 25, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@b1da213623d84e19406ed37c5ed824173668219f

🧩 Skill update

npx skills add larksuite/cli#fix/wiki-node-create-lock-contention-retry -y -g

@fangshuyu-768 fangshuyu-768 force-pushed the fix/wiki-node-create-lock-contention-retry branch from 1752a34 to b9fbbac Compare May 25, 2026 10:24
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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/wiki/wiki_node_create_test.go`:
- Around line 831-869: The test TestRunWikiNodeCreateRetriesExhausted asserts a
3-attempt (2-retry) behavior but runWikiNodeCreate should perform initial
attempt + 4 retries for lock contention; update the fake client and assertions:
set client.createErrs to five lockErr entries, assert len(client.createInvoked)
== 5, and update the hint expectation to look for "failed after 4 retries" while
still verifying output.LarkErrWikiLockContention and that the original "lock
contention" hint is preserved; reference runWikiNodeCreate,
fakeWikiNodeCreateClient.createErrs and createInvoked, and
output.LarkErrWikiLockContention when making the changes.

In `@shortcuts/wiki/wiki_node_create.go`:
- Around line 374-385: When reconstructing the ExitError (the returned
&output.ExitError) you're dropping the original error chain and raw payload;
update the constructor to copy exitErr.Err and exitErr.Raw into the new
ExitError so errors.Unwrap() and any enrichment using Raw still work—i.e.,
include the Err and Raw fields from the original exitErr when building the
returned *output.ExitError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 506c6da4-cb58-448c-b2f8-7c68b8016058

📥 Commits

Reviewing files that changed from the base of the PR and between 1752a34 and b9fbbac.

📒 Files selected for processing (2)
  • shortcuts/wiki/wiki_node_create.go
  • shortcuts/wiki/wiki_node_create_test.go

Comment thread shortcuts/wiki/wiki_node_create_test.go
Comment thread shortcuts/wiki/wiki_node_create.go
@fangshuyu-768 fangshuyu-768 force-pushed the fix/wiki-node-create-lock-contention-retry branch from b9fbbac to 4886efe Compare May 25, 2026 10:42
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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
shortcuts/wiki/wiki_node_create_test.go (1)

840-865: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align exhausted-retry assertions with the configured retry budget.

This case still validates 3 total attempts / 2 retries, so it won’t catch under-retrying against the intended initial attempt + 4 retries behavior.

🔧 Suggested test fix
-		createErrs: []error{lockErr, lockErr, lockErr}, // all 3 attempts fail
+		createErrs: []error{lockErr, lockErr, lockErr, lockErr, lockErr}, // all 5 attempts fail
@@
-	if len(client.createInvoked) != 3 {
-		t.Fatalf("create invoked %d times, want 3", len(client.createInvoked))
+	if len(client.createInvoked) != 5 {
+		t.Fatalf("create invoked %d times, want 5", len(client.createInvoked))
 	}
@@
-	if !strings.Contains(exitErr.Detail.Hint, "failed after 2 retries") {
+	if !strings.Contains(exitErr.Detail.Hint, "failed after 4 retries") {
 		t.Fatalf("hint = %q, want retry exhaustion message", exitErr.Detail.Hint)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/wiki/wiki_node_create_test.go` around lines 840 - 865, Test
currently asserts 3 total attempts (2 retries) but the retry budget should be
initial attempt + 4 retries; update the test setup and assertions accordingly:
make client.createErrs contain 5 errors (e.g., lockErr repeated 5 times) so
runWikiNodeCreate will be invoked 5 times, change the check that inspects
len(client.createInvoked) to expect 5, and update the expected retry-exhaustion
hint asserted against exitErr.Detail.Hint to reference "failed after 4 retries"
(keep references to runWikiNodeCreate, wikiNodeCreateSpec, client.createInvoked,
lockErr, output.ExitError, and output.LarkErrWikiLockContention to locate
changes).
🧹 Nitpick comments (1)
tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go (1)

199-204: ⚡ Quick win

Make error-message extraction resilient to non-JSON stderr/stdout formats.

validateWikiErrorMessage currently returns "" when error.message is absent in both streams. Add a fallback to r.Stdout + r.Stderr so validation-reject assertions remain stable even if envelope formatting changes.

Suggested patch
 func validateWikiErrorMessage(r *clie2e.Result) string {
 	if msg := gjson.Get(r.Stdout, "error.message").String(); msg != "" {
 		return msg
 	}
-	return gjson.Get(r.Stderr, "error.message").String()
+	if msg := gjson.Get(r.Stderr, "error.message").String(); msg != "" {
+		return msg
+	}
+	return r.Stdout + r.Stderr
 }

Based on learnings: in tests/cli_e2e/*_dryrun_test.go, Validate-stage failures should be asserted from result.Stdout + result.Stderr with non-zero exit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go` around lines 199 - 204,
The validateWikiErrorMessage helper currently only returns JSON-parsed
"error.message" from r.Stdout or r.Stderr and can return an empty string if
neither stream contains that field; change it to fall back to the raw
concatenated output when parsing yields empty, i.e., after attempting gjson.Get
on r.Stdout and r.Stderr in validateWikiErrorMessage, if both results are empty
return r.Stdout + r.Stderr so tests assert against the combined raw output
(refer to function validateWikiErrorMessage and the r.Stdout / r.Stderr
variables).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/wiki/wiki_node_create.go`:
- Around line 30-38: The retry budget is too low — update the constants so
wikiNodeCreateMaxRetries allows 4 retry attempts (so total attempts = initial +
4 retries) and keep wikiNodeCreateRetryBaseDelay at 250*time.Millisecond so
exponential backoff yields 250ms, 500ms, 1s, 2s; locate and update every
occurrence of wikiNodeCreateMaxRetries and wikiNodeCreateRetryBaseDelay
(including the duplicate declaration around the other occurrence) so the retry
loop and any backoff logic use the new maxRetries value.

---

Duplicate comments:
In `@shortcuts/wiki/wiki_node_create_test.go`:
- Around line 840-865: Test currently asserts 3 total attempts (2 retries) but
the retry budget should be initial attempt + 4 retries; update the test setup
and assertions accordingly: make client.createErrs contain 5 errors (e.g.,
lockErr repeated 5 times) so runWikiNodeCreate will be invoked 5 times, change
the check that inspects len(client.createInvoked) to expect 5, and update the
expected retry-exhaustion hint asserted against exitErr.Detail.Hint to reference
"failed after 4 retries" (keep references to runWikiNodeCreate,
wikiNodeCreateSpec, client.createInvoked, lockErr, output.ExitError, and
output.LarkErrWikiLockContention to locate changes).

---

Nitpick comments:
In `@tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go`:
- Around line 199-204: The validateWikiErrorMessage helper currently only
returns JSON-parsed "error.message" from r.Stdout or r.Stderr and can return an
empty string if neither stream contains that field; change it to fall back to
the raw concatenated output when parsing yields empty, i.e., after attempting
gjson.Get on r.Stdout and r.Stderr in validateWikiErrorMessage, if both results
are empty return r.Stdout + r.Stderr so tests assert against the combined raw
output (refer to function validateWikiErrorMessage and the r.Stdout / r.Stderr
variables).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54db8c0b-2985-4ac9-9921-fd10512209bd

📥 Commits

Reviewing files that changed from the base of the PR and between b9fbbac and 4886efe.

📒 Files selected for processing (3)
  • shortcuts/wiki/wiki_node_create.go
  • shortcuts/wiki/wiki_node_create_test.go
  • tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go

Comment thread shortcuts/wiki/wiki_node_create.go
@fangshuyu-768 fangshuyu-768 force-pushed the fix/wiki-node-create-lock-contention-retry branch from efac5cb to 7005935 Compare May 25, 2026 11:23
…ntion (#1012)

When creating wiki nodes under the same parent concurrently, the API
returns error code 131009 (lock contention) ~5-15% of the time. This
adds automatic retry with exponential backoff (250ms, 500ms; max 2
retries) so callers no longer need to implement retry logic themselves.

- Retry loop in runWikiNodeCreate: only retries on code 131009, respects
  context cancellation, prints progress to stderr
- wrapWikiNodeCreateRetryError preserves Err/Raw/Detail.Code in ExitError
- 6 unit tests covering retry success, exhaustion, non-contention error,
  single-retry success, context cancellation, no-retry on success
- 8 dry-run E2E tests for wiki +node-create request shape and validation
@fangshuyu-768 fangshuyu-768 force-pushed the fix/wiki-node-create-lock-contention-retry branch from 7005935 to b1da213 Compare May 25, 2026 11:26
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.

♻️ Duplicate comments (1)
shortcuts/wiki/wiki_node_create.go (1)

30-38: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Increase lock-contention retries to match the expected backoff contract.

wikiNodeCreateMaxRetries is still 2 (250ms, 500ms), which under-delivers the expected lock-contention retry sequence (250ms, 500ms, 1s, 2s) for this flow and can cause avoidable failures under burst creates.

Suggested patch
 const (
 	// wikiNodeCreateMaxRetries is the maximum number of retry attempts after
 	// the initial request when the API returns lock contention (code 131009).
-	wikiNodeCreateMaxRetries = 2
+	wikiNodeCreateMaxRetries = 4
 
 	// wikiNodeCreateRetryBaseDelay is the initial backoff delay for lock
-	// contention retries. Subsequent retries double the delay (250ms, 500ms).
+	// contention retries. Subsequent retries double the delay
+	// (250ms, 500ms, 1s, 2s).
 	wikiNodeCreateRetryBaseDelay = 250 * time.Millisecond
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/wiki/wiki_node_create.go` around lines 30 - 38, The lock-contention
retry count is too low: update the constant wikiNodeCreateMaxRetries from 2 to 4
so the backoff sequence using wikiNodeCreateRetryBaseDelay (250ms) yields 250ms,
500ms, 1s, 2s retries; keep wikiNodeCreateRetryBaseDelay as-is and update the
associated comment to reflect the new 4 retry attempts and the expanded delay
sequence to ensure burst creates get the expected backoff behavior.
🧹 Nitpick comments (1)
shortcuts/wiki/wiki_node_create_test.go (1)

834-868: ⚡ Quick win

Assert ExitError.Err and ExitError.Raw preservation in the exhaustion test.

On Line 856-868 the test only validates Detail.Code/Hint, so a regression in wrapped Err/Raw could slip through.

Suggested test hardening
 func TestRunWikiNodeCreateRetriesExhausted(t *testing.T) {
 	t.Parallel()

-	lockErr := output.ErrAPI(output.LarkErrWikiLockContention, "lock contention", nil)
+	cause := errors.New("upstream lock contention")
+	lockErr := &output.ExitError{
+		Code: 1,
+		Detail: &output.ErrDetail{
+			Type:    "api",
+			Code:    output.LarkErrWikiLockContention,
+			Message: "lock contention",
+			Hint:    "lock contention",
+		},
+		Err: cause,
+		Raw: true,
+	}
@@
 	if !strings.Contains(exitErr.Detail.Hint, "lock contention") {
 		t.Fatalf("hint = %q, want original classification hint preserved", exitErr.Detail.Hint)
 	}
+	if !exitErr.Raw {
+		t.Fatalf("Raw = %v, want true", exitErr.Raw)
+	}
+	if !errors.Is(exitErr.Err, cause) {
+		t.Fatalf("Err = %v, want wrapped cause %v", exitErr.Err, cause)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/wiki/wiki_node_create_test.go` around lines 834 - 868, The test
only checks exitErr.Detail fields; also assert that the original lockErr from
createErrs is preserved in the returned ExitError by verifying exitErr.Err and
exitErr.Raw reference or wrap the original lockErr from the test (the lockErr
used to populate client.createErrs). Update the test around the
runWikiNodeCreate call to assert errors.Is(exitErr.Err, lockErr) (or compare
equality if Raw is expected) and that exitErr.Raw == lockErr (or
errors.Is(exitErr.Raw, lockErr)) so both Err and Raw are validated alongside
Detail.Code and Detail.Hint; locate symbols runWikiNodeCreate,
fakeWikiNodeCreateClient, client.createErrs, lockErr and ExitError/exitErr to
add these assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@shortcuts/wiki/wiki_node_create.go`:
- Around line 30-38: The lock-contention retry count is too low: update the
constant wikiNodeCreateMaxRetries from 2 to 4 so the backoff sequence using
wikiNodeCreateRetryBaseDelay (250ms) yields 250ms, 500ms, 1s, 2s retries; keep
wikiNodeCreateRetryBaseDelay as-is and update the associated comment to reflect
the new 4 retry attempts and the expanded delay sequence to ensure burst creates
get the expected backoff behavior.

---

Nitpick comments:
In `@shortcuts/wiki/wiki_node_create_test.go`:
- Around line 834-868: The test only checks exitErr.Detail fields; also assert
that the original lockErr from createErrs is preserved in the returned ExitError
by verifying exitErr.Err and exitErr.Raw reference or wrap the original lockErr
from the test (the lockErr used to populate client.createErrs). Update the test
around the runWikiNodeCreate call to assert errors.Is(exitErr.Err, lockErr) (or
compare equality if Raw is expected) and that exitErr.Raw == lockErr (or
errors.Is(exitErr.Raw, lockErr)) so both Err and Raw are validated alongside
Detail.Code and Detail.Hint; locate symbols runWikiNodeCreate,
fakeWikiNodeCreateClient, client.createErrs, lockErr and ExitError/exitErr to
add these assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7da8d7a3-e27a-494d-b8b0-e3aa15aabe55

📥 Commits

Reviewing files that changed from the base of the PR and between 7005935 and b1da213.

📒 Files selected for processing (3)
  • shortcuts/wiki/wiki_node_create.go
  • shortcuts/wiki/wiki_node_create_test.go
  • tests/cli_e2e/wiki/wiki_node_create_dryrun_test.go

@fangshuyu-768 fangshuyu-768 merged commit aea9f37 into main May 25, 2026
21 checks passed
@fangshuyu-768 fangshuyu-768 deleted the fix/wiki-node-create-lock-contention-retry branch May 25, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wiki +node-create 批量调用频发 131009 lock contention,客户端能否内置重试?

2 participants