Skip to content

feat: Extend lock on timeout#87

Merged
zeljkoX merged 3 commits intomainfrom
extend-lock-on-timeout
Feb 23, 2026
Merged

feat: Extend lock on timeout#87
zeljkoX merged 3 commits intomainfrom
extend-lock-on-timeout

Conversation

@zeljkoX
Copy link
Contributor

@zeljkoX zeljkoX commented Feb 20, 2026

This PR will:

  • Minimize time bounds to prevent long retries on relayer side and to match TIMEOUT + LOCK EXTEND time
  • Extend lock for additional default lock time(30) in order to allow retry or resolving tx without dropping lock.

Dropping lock on timeout means that another request could pick it up while previous tx is still unresolved.

Summary by CodeRabbit

  • Bug Fixes

    • Improved lock management on timeout errors—locks now extend their lifetime instead of being immediately released, preventing premature unavailability.
  • Configuration Updates

    • Reduced maximum time boundary thresholds from 120 to 60 seconds for faster system responsiveness.
  • Tests

    • Added comprehensive test coverage for lock extension behavior and timeout/error scenarios.

Copilot AI review requested due to automatic review settings February 20, 2026 14:33
@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Changes reduce time boundary constants from 120 to 60 seconds and add lock extension functionality to the handler pool. When a WAIT_TIMEOUT error occurs during transaction submission, the system now extends the pool lock instead of releasing it, with corresponding test coverage for this behavior.

Changes

Cohort / File(s) Summary
Time Boundary Configuration
src/plugin/constants.ts
Reduced three time boundary constants (TIME.MAX_TIME_BOUND_OFFSET_SECONDS, SIMULATION.MAX_TIME_BOUND_OFFSET_SECONDS, SIMULATION.MAX_FUTURE_TIME_BOUND_SECONDS) from 120 to 60 seconds.
Lock Extension Implementation
src/plugin/handler.ts, src/plugin/pool.ts
Added extendLock method to ChannelPool to refresh lock TTL with token ownership verification. Handler updated to invoke extendLock and skip release on WAIT_TIMEOUT errors.
Lock Extension Test Coverage
test/handler.sequence-cache.test.ts, test/pool.test.ts
Added tests validating lock extension behavior: timeout vs non-timeout error paths, successful submissions, token matching, and error handling in KV operations.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Handler as Handler
    participant Pool as ChannelPool
    participant KV as Storage (KV)

    Client->>Handler: Submit transaction
    Handler->>Pool: acquire(lock)
    Pool->>KV: Check and lock channel
    KV-->>Pool: Lock acquired
    Pool-->>Handler: poolLock returned

    Handler->>Handler: Attempt submit
    Note over Handler: Submit fails with WAIT_TIMEOUT

    Handler->>Pool: extendLock(poolLock)
    Pool->>KV: Read lock entry
    KV-->>Pool: Lock entry (verify token)
    alt Token matches (ownership confirmed)
        Pool->>KV: Update lock with new timestamp & TTL
        KV-->>Pool: Lock updated
    else Token mismatch
        Note over Pool: No-op, swallow error
    end
    Pool-->>Handler: extendLock complete

    Note over Handler: Skip release, keep lock extended
    Handler-->>Client: Return with extended lock
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • dylankilkenny
  • collins-w

Poem

🐰 Hop, hop! The locks now stretch their paws,
When timeouts knock, we pause, we pause,
Extend the grip, don't let it fall,
Sixty seconds—that's all, that's all! 🔒✨

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Extend lock on timeout' directly and clearly summarizes the primary change: extending channel locks when timeout occurs, which is the main feature objective described in the PR.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch extend-lock-on-timeout

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

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements lock extension on transaction timeout to prevent race conditions where another request could acquire a channel while a previous transaction is still unresolved. The changes reduce transaction time bounds from 120s to 60s to better align with the timeout (25s) + lock extension (30s) pattern, totaling 55s which fits within the new 60s time bound.

Changes:

  • Reduced time bound constants from 120s to 60s to match timeout and lock extension timing
  • Added extendLock method to ChannelPool that refreshes lock TTL with token-based ownership verification
  • Modified error handling in handler.ts to extend locks on WAIT_TIMEOUT instead of releasing them

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/plugin/constants.ts Reduced MAX_TIME_BOUND_OFFSET_SECONDS from 120s to 60s in TIME and SIMULATION constants
src/plugin/pool.ts Added extendLock method with token-based ownership verification and silent error handling
src/plugin/handler.ts Modified WAIT_TIMEOUT error handling to extend lock and skip release in finally block
test/pool.test.ts Added comprehensive tests for extendLock functionality including token validation and error handling
test/handler.sequence-cache.test.ts Added integration tests verifying lock extension behavior on WAIT_TIMEOUT vs. normal release on other errors

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@zeljkoX zeljkoX requested a review from tirumerla February 20, 2026 14:38
Copy link

@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 (1)
test/pool.test.ts (1)

62-77: Consider resetting the spy before extendLock for clarity.

The test logic is correct—the spy is created after acquire(), so filtering by the key works. However, calling setSpy.mockClear() before extendLock would make the assertion simpler and the intent clearer.

♻️ Optional improvement
     // Spy on kv.set to verify it's not called for the extend
     const setSpy = vi.spyOn(kv, 'set');
+    setSpy.mockClear();
     await pool.extendLock(wrongLock);
 
-    // set should not have been called (token mismatch)
-    const extendCalls = setSpy.mock.calls.filter((c) => c[0] === `testnet:channel:in-use:${lock.relayerId}`);
-    expect(extendCalls).toHaveLength(0);
+    // set should not have been called at all (token mismatch)
+    expect(setSpy).not.toHaveBeenCalled();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/pool.test.ts` around lines 62 - 77, Reset the kv.set spy before calling
pool.extendLock to ensure only calls caused by extendLock are observed: after
creating the spy (vi.spyOn(kv, 'set')) call setSpy.mockClear() so prior set
calls from pool.acquire() are cleared, then call pool.extendLock(wrongLock) and
assert that no sets were made for the key
`testnet:channel:in-use:${lock.relayerId}`; this involves the FakeKV instance,
ChannelPool.acquire, ChannelPool.extendLock, and the setSpy mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/pool.test.ts`:
- Around line 62-77: Reset the kv.set spy before calling pool.extendLock to
ensure only calls caused by extendLock are observed: after creating the spy
(vi.spyOn(kv, 'set')) call setSpy.mockClear() so prior set calls from
pool.acquire() are cleared, then call pool.extendLock(wrongLock) and assert that
no sets were made for the key `testnet:channel:in-use:${lock.relayerId}`; this
involves the FakeKV instance, ChannelPool.acquire, ChannelPool.extendLock, and
the setSpy mock.

@zeljkoX zeljkoX merged commit 3bdf6da into main Feb 23, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants