Skip to content

feat: Retry getBlockTimestamp#3166

Merged
alpe merged 5 commits intomainfrom
alex/3141_getTimestamp
Mar 18, 2026
Merged

feat: Retry getBlockTimestamp#3166
alpe merged 5 commits intomainfrom
alex/3141_getTimestamp

Conversation

@alpe
Copy link
Contributor

@alpe alpe commented Mar 16, 2026

Resolves #3141

Overview

Retry fetching timestamp from DA with backoff on failure.
Fails with error instead of defaulting to current time.

Summary by CodeRabbit

  • Bug Fixes

    • Improved block timestamp handling with retries, deterministic timestamp propagation, and explicit errors instead of falling back to current time.
  • New Features

    • Added a dedicated blob-only retrieval path to better separate header vs data fetches and improve retrieval behavior.
    • Tracing now records blob retrieval metrics.
  • Tests

    • Expanded tests and mocks covering timestamp retry, cache behavior, retrieval paths, and tracing.
  • Breaking Changes

    • Submission API signature changed — callers must adapt to the new parameter.

(cherry picked from commit a6573fe6c40082380759f55018a47c8da5bf97bd)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

Adds a retry-with-backoff loop for fetching block timestamps, caches timestamps, introduces a RetrieveBlobs API/path that omits header timestamp, makes Retrieve fail with structured errors instead of using current time on timestamp resolution failure, and updates Submit to accept an options []byte parameter. Tests and mocks updated.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Added "Retry fetching the timestamp on error in da-client" under Unreleased > Fixed.
DA client core & timestamp cache
block/internal/da/client.go
Added timestamp cache struct and constants; implemented getBlockTimestamp with retry/backoff and logging; introduced cache helpers and deterministic timestamp resolution; Retrieve now uses strict timestamp handling and returns structured errors when timestamp resolution fails; Submit signature changed to accept options []byte.
Client interface & tracing
block/internal/da/interface.go, block/internal/da/tracing.go
Added RetrieveBlobs to the DA client interface and a traced RetrieveBlobs wrapper with span attributes.
DA client tests
block/internal/da/client_test.go, block/internal/da/tracing_test.go
Added tests covering timestamp fetch retry, cache behavior, and tracing for RetrieveBlobs; expanded mocks in tracing tests.
Mocks & test DA
test/mocks/da.go, test/testda/dummy.go, apps/evm/server/force_inclusion_test.go
Added RetrieveBlobs mock plumbing and delegate implementations to satisfy new method; small mock method added for tests.
Syncing retriever updates
block/internal/syncing/da_retriever.go, block/internal/syncing/da_retriever_test.go
Switched DA client calls from Retrieve to RetrieveBlobs in blob fetching and updated tests accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant Client as DA Client
    participant Cache as BlockTimestampCache (local)
    participant Header as DA Header Service
    participant Store as Result / Caller

    Client->>Cache: cachedBlockTimestamp(height)?
    alt cache hit
        Cache-->>Client: timestamp
    else cache miss
        Client->>Header: fetch header by height (attempt 1)
        alt header returns timestamp
            Header-->>Client: header with timestamp
            Client->>Cache: storeBlockTimestamp(height, ts)
        else header error
            Client->>Client: wait (backoff)
            Client->>Header: fetch header by height (attempt 2)
            alt header returns timestamp
                Header-->>Client: header with timestamp
                Client->>Cache: storeBlockTimestamp(height, ts)
            else header error
                Client->>Client: wait (backoff)
                Client->>Header: fetch header by height (attempt 3)
                alt header returns timestamp
                    Header-->>Client: header with timestamp
                    Client->>Cache: storeBlockTimestamp(height, ts)
                else retries exhausted
                    Header-->>Client: error
                    Client-->>Store: return structured ResultRetrieve error (no fallback to now)
                end
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

T:testing

Suggested reviewers

  • julienrbrt
  • chatton
  • tac0turtle

Poem

🐰 I hopped three times to fetch that time,
Backoff between each careful climb.
No fake "now" when retries are through—
I cache the truth and pass it to you.
🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes significant out-of-scope additions beyond the timestamp retry feature: a new RetrieveBlobs method added across multiple files (interface, implementation, tests, mocks) that is not mentioned in issue #3141. Remove or defer the RetrieveBlobs method additions to a separate PR, as they appear to be a distinct feature unrelated to the timestamp retry requirement specified in issue #3141.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Retry getBlockTimestamp' accurately and concisely summarizes the main change—implementing retry logic for timestamp fetching—and is specific enough to understand the primary objective.
Description check ✅ Passed The pull request description appropriately references the linked issue (#3141) and provides a clear overview of the changes: implementing retry logic with backoff and failing with an error instead of defaulting to current time.
Linked Issues check ✅ Passed The pull request fully satisfies the requirements from issue #3141: implements retry logic for timestamp fetching with backoff, and returns hard errors instead of defaulting to current time when retries are exhausted.

✏️ 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 alex/3141_getTimestamp
📝 Coding Plan
  • Generate coding plan for human review comments

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
Copy link
Contributor

github-actions bot commented Mar 16, 2026

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedMar 18, 2026, 10:51 AM

@claude
Copy link
Contributor

claude bot commented Mar 16, 2026

Claude finished @alpe's task in 2m 39s —— View job


PR Review: feat: Retry getBlockTimestamp

  • Gather context and read changed files
  • Review code quality & architecture
  • Review error handling & resilience
  • Review testing
  • Post final review

Summary

The core objective (retry with backoff + hard error instead of time.Now() fallback) is sound and well-implemented. A few issues worth addressing before merge:


Issues

1. Subscribe goroutine captures outer err variableclient.go:528

// Outer function scope:
rawCh, err := c.blobAPI.Subscribe(ctx, ns)
...
go func() {
    ...
    blockTime, err = c.getBlockTimestamp(ctx, resp.Height) // captures outer `err` by reference

The goroutine captures err from the enclosing Subscribe function scope via closure. While safe after Subscribe returns (no one else reads it), this is a code smell and could mislead future readers into thinking there's shared state. Prefer a local variable:

var getErr error
blockTime, getErr = c.getBlockTimestamp(ctx, resp.Height)
if getErr != nil { ... }

Fix this →


2. Subscribe emits events with zero timestamp on retry exhaustionclient.go:531

blockTime, err = c.getBlockTimestamp(ctx, resp.Height)
if err != nil {
    c.logger.Error()...
    blockTime = time.Time{}  // zero time emitted downstream
}

time.Time{} is emitted in the subscription event. This was improved from the old time.Now() fallback, but consumers receiving a zero-value timestamp may not handle it gracefully (block time ordering, metrics, etc.). The existing TODO links to #3142, but consider whether zero time is actually safer than time.Now() for the based sequencer path — zero time is less obviously wrong but could be harder to detect. This is worth a brief comment explaining the explicit choice and its implications.


3. Missing test: context cancellation during retryclient_test.go

getBlockTimestamp has a context cancellation path (lines 285–288):

select {
case <-ctx.Done():
    return time.Time{}, fmt.Errorf("fetching header timestamp for block %d: %w", height, ctx.Err())
case <-time.After(backoff):
}

But there is no test verifying this branch. A cancelled context mid-retry should return quickly with a wrapped ctx.Err() — worth covering given the retry+backoff complexity.


4. resolveBlockTimestamp silently returns zero time in non-strict modeclient.go:310

if !strict {
    if blockTime, ok := c.cachedBlockTimestamp(height); ok {
        return blockTime, nil
    }
    return time.Time{}, nil  // silent zero time, no error
}

This is by design for RetrieveBlobs, but the zero-time result is not documented at the call site or in the method's comment. Callers of RetrieveBlobs who check res.Timestamp.IsZero() would need to know this is expected. Worth a doc comment.


Observations

Retry logic is correct — exponential backoff, no sleep after last attempt, and context cancellation handled properly in the select. The error wrapping at line 294 is clean.

Duplicate test is resolved — the pre-merge check flagged a duplicate TestClient_Retrieve_TimestampFetchRetry; this is not present in the current HEAD (cf8e8eb). The concern in the automated check is stale.

Interface break is propagated cleanlySubmit with options []byte is updated across interface.go, tracing.go, tracing_test.go, dummy.go, and the mock, with no missed implementations found.

Test backoff timing — tests run against the real blockTimestampFetchBackoff constant (100ms initial), so the failure test path sleeps ~300ms. This is acceptable but consider a future test helper to override retry parameters for speed.

resolveBlockTimestamp naming — the strict boolean is clear in context, but the function could benefit from a doc comment explaining the two modes.


Verdict

The critical fix (deterministic block timestamps via retry + hard error) is correct. Address the err variable capture (item 1) and consider adding the ctx-cancellation test (item 3) before merge. Items 2 and 4 can be deferred to #3142.

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 82.40741% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.13%. Comparing base (6e0bd9a) to head (cf8e8eb).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
block/internal/da/client.go 83.51% 12 Missing and 3 partials ⚠️
block/internal/da/tracing.go 73.33% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3166      +/-   ##
==========================================
+ Coverage   60.75%   61.13%   +0.37%     
==========================================
  Files         117      117              
  Lines       11988    12082      +94     
==========================================
+ Hits         7283     7386     +103     
+ Misses       3886     3870      -16     
- Partials      819      826       +7     
Flag Coverage Δ
combined 61.13% <82.40%> (+0.37%) ⬆️

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

☔ 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.

@alpe alpe changed the title Retry getBlockTimestamp feat: Retry getBlockTimestamp Mar 16, 2026
@alpe alpe marked this pull request as ready for review March 16, 2026 16:04
Copy link
Member

@julienrbrt julienrbrt left a comment

Choose a reason for hiding this comment

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

This makes sense. The sequencer should retry eventually if this happens on the force inclusion da fetching? We should make sure it cannot kill a sequencer. Although we do need this fix, as it could produce non deterministic blocks otherwise.

alpe added 2 commits March 17, 2026 08:20
* main:
  chore: minor changes from audit tool (#3165)
  build(deps): Bump actions/download-artifact from 8.0.0 to 8.0.1 in the patch-updates group (#3169)
  fix: Strict raft state (#3167)
* main:
  feat: Subscribe to forced inclusion ns events (#3146)
  build(deps): Bump the all-go group across 3 directories with 3 updates (#3168)
Copy link
Contributor

@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)
block/internal/da/client.go (2)

432-437: Inconsistency: Subscribe still falls back to time.Now() on error.

The Retrieve method now properly returns an error when timestamp fetch fails, but Subscribe still falls back to time.Now(). The TODO comment acknowledges this, but it creates an inconsistency in timestamp handling between the two code paths.

Is this intentional for this PR, deferring Subscribe fixes to a follow-up? If so, consider linking the TODO to issue #3142 explicitly.

📝 Link TODO to issue
 				if err != nil {
 					c.logger.Error().Uint64("height", resp.Height).Err(err).Msg("failed to get DA block timestamp for subscription event")
 					blockTime = time.Now()
-					// TODO: we should retry fetching the timestamp. Current time may mess block time consistency for based sequencers.
+					// TODO(`#3142`): we should retry fetching the timestamp. Current time may mess block time consistency for based sequencers.
 				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@block/internal/da/client.go` around lines 432 - 437, Subscribe currently
falls back to time.Now() when getBlockTimestamp fails, causing inconsistency
with Retrieve which returns an error; update the Subscribe code path in the
block subscription handler (the blockTime assignment block that calls
c.getBlockTimestamp and logs via c.logger.Error().Uint64(...).Err(err).Msg(...))
to stop silently using time.Now(): either propagate the error to the
subscription caller (return or send an error into the subscription channel) or
fail the subscription event instead of substituting the current time, and remove
or update the TODO accordingly; if you intentionally defer this change, update
the TODO to reference issue `#3142` explicitly (e.g., "TODO: address in `#3142`") so
the inconsistency is tracked.

45-48: Consider making retry parameters configurable.

The hardcoded constants work for an initial implementation, but you may want to expose these via Config for environments with different latency characteristics or reliability needs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@block/internal/da/client.go` around lines 45 - 48, The retry constants
blockTimestampFetchMaxAttempts and blockTimestampFetchBackoff should be made
configurable via the client's Config: add fields (e.g.,
BlockTimestampFetchMaxAttempts int and BlockTimestampFetchBackoff time.Duration)
to the Config struct, initialize them with the current defaults (3 and
100*time.Millisecond) when constructing the client (e.g., in NewClient or config
validation), and replace direct uses of the constants with Config references
wherever block timestamp fetching logic runs (references to
blockTimestampFetchMaxAttempts and blockTimestampFetchBackoff). Ensure
nil/zero-value handling preserves the existing defaults so behavior is unchanged
unless the caller overrides the config.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@block/internal/da/client.go`:
- Around line 432-437: Subscribe currently falls back to time.Now() when
getBlockTimestamp fails, causing inconsistency with Retrieve which returns an
error; update the Subscribe code path in the block subscription handler (the
blockTime assignment block that calls c.getBlockTimestamp and logs via
c.logger.Error().Uint64(...).Err(err).Msg(...)) to stop silently using
time.Now(): either propagate the error to the subscription caller (return or
send an error into the subscription channel) or fail the subscription event
instead of substituting the current time, and remove or update the TODO
accordingly; if you intentionally defer this change, update the TODO to
reference issue `#3142` explicitly (e.g., "TODO: address in `#3142`") so the
inconsistency is tracked.
- Around line 45-48: The retry constants blockTimestampFetchMaxAttempts and
blockTimestampFetchBackoff should be made configurable via the client's Config:
add fields (e.g., BlockTimestampFetchMaxAttempts int and
BlockTimestampFetchBackoff time.Duration) to the Config struct, initialize them
with the current defaults (3 and 100*time.Millisecond) when constructing the
client (e.g., in NewClient or config validation), and replace direct uses of the
constants with Config references wherever block timestamp fetching logic runs
(references to blockTimestampFetchMaxAttempts and blockTimestampFetchBackoff).
Ensure nil/zero-value handling preserves the existing defaults so behavior is
unchanged unless the caller overrides the config.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f6f2ee7-12b4-46b5-a4f7-e262388bfcb5

📥 Commits

Reviewing files that changed from the base of the PR and between b14a9f8 and 03152fd.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • block/internal/da/client.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

Copy link
Contributor

@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

🧹 Nitpick comments (2)
block/internal/syncing/da_retriever_test.go (1)

262-269: Use a fixed timestamp in mocked RetrieveBlobs responses.

time.Now() here is unnecessary for assertions and introduces avoidable nondeterminism in test fixtures.

As per coding guidelines, "Ensure tests are deterministic."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@block/internal/syncing/da_retriever_test.go` around lines 262 - 269, The
mocked RetrieveBlobs responses use time.Now(), causing nondeterministic tests;
replace those timestamps with a fixed time variable (e.g., ts := time.Unix(...))
and use that ts in the BaseResult.Timestamp for both datypes.ResultRetrieve
returns in the test so assertions are deterministic; update the two
client.On("RetrieveBlobs", ...).Return(...) calls to set BaseResult.Timestamp to
the fixed ts instead of time.Now() and run the test.
block/internal/da/client_test.go (1)

349-351: Guard event receive with a timeout to avoid hanging tests.

A timeout-based select keeps this test failure mode deterministic if subscription delivery regresses.

As per coding guidelines, "Ensure tests are deterministic."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@block/internal/da/client_test.go` around lines 349 - 351, The test currently
blocks on a direct receive from the events channel (ev := <-events) which can
hang if delivery regresses; change this to a timeout-guarded select that waits
for either an event from the events channel (assign to ev and run the existing
assertions on ev.Height and ev.Timestamp) or a time.After timeout branch that
fails the test (use t.Fatalf or require.FailNow with a clear timeout message).
Reference the existing events channel and ev variable in the select and pick a
reasonable timeout (e.g., 1s or configurable) to make the test deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@block/internal/syncing/da_retriever.go`:
- Around line 87-94: The current callers of RetrieveBlobs (in the block using
r.client.RetrieveBlobs with r.client.GetHeaderNamespace()/GetDataNamespace())
rely on validateBlobResponse returning nil for unknown non-success DA statuses
which can mask real failures; update validateBlobResponse to return a non-nil
error for any unhandled/default DA status (rather than nil), and change the
callers around RetrieveBlobs to propagate and return that error immediately (do
not convert it into ErrBlobNotFound). Specifically, modify validateBlobResponse
to handle the default case with an explicit error describing the unexpected DA
status and ensure both call sites that invoke validateBlobResponse after
RetrieveBlobs (the header/data branch and the other similar block) check and
return that error instead of proceeding.

---

Nitpick comments:
In `@block/internal/da/client_test.go`:
- Around line 349-351: The test currently blocks on a direct receive from the
events channel (ev := <-events) which can hang if delivery regresses; change
this to a timeout-guarded select that waits for either an event from the events
channel (assign to ev and run the existing assertions on ev.Height and
ev.Timestamp) or a time.After timeout branch that fails the test (use t.Fatalf
or require.FailNow with a clear timeout message). Reference the existing events
channel and ev variable in the select and pick a reasonable timeout (e.g., 1s or
configurable) to make the test deterministic.

In `@block/internal/syncing/da_retriever_test.go`:
- Around line 262-269: The mocked RetrieveBlobs responses use time.Now(),
causing nondeterministic tests; replace those timestamps with a fixed time
variable (e.g., ts := time.Unix(...)) and use that ts in the
BaseResult.Timestamp for both datypes.ResultRetrieve returns in the test so
assertions are deterministic; update the two client.On("RetrieveBlobs",
...).Return(...) calls to set BaseResult.Timestamp to the fixed ts instead of
time.Now() and run the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: daeec9e5-12d4-4cf5-b8bf-86c3bda137f2

📥 Commits

Reviewing files that changed from the base of the PR and between 03152fd and cf8e8eb.

📒 Files selected for processing (10)
  • apps/evm/server/force_inclusion_test.go
  • block/internal/da/client.go
  • block/internal/da/client_test.go
  • block/internal/da/interface.go
  • block/internal/da/tracing.go
  • block/internal/da/tracing_test.go
  • block/internal/syncing/da_retriever.go
  • block/internal/syncing/da_retriever_test.go
  • test/mocks/da.go
  • test/testda/dummy.go

Comment on lines +87 to +94
headerRes := r.client.RetrieveBlobs(ctx, daHeight, r.client.GetHeaderNamespace())

// If namespaces are the same, return header result
if bytes.Equal(r.client.GetHeaderNamespace(), r.client.GetDataNamespace()) {
return headerRes, r.validateBlobResponse(headerRes, daHeight)
}

dataRes := r.client.Retrieve(ctx, daHeight, r.client.GetDataNamespace())
dataRes := r.client.RetrieveBlobs(ctx, daHeight, r.client.GetDataNamespace())
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not silently accept unknown non-success DA statuses.

With both paths now using RetrieveBlobs, validateBlobResponse returning nil in default can mask real failures and later return ErrBlobNotFound incorrectly.

Suggested fix
 func (r *daRetriever) validateBlobResponse(res datypes.ResultRetrieve, daHeight uint64) error {
 	switch res.Code {
 	case datypes.StatusError:
 		return fmt.Errorf("DA retrieval failed: %s", res.Message)
 	case datypes.StatusHeightFromFuture:
 		return fmt.Errorf("%w: height from future", datypes.ErrHeightFromFuture)
 	case datypes.StatusNotFound:
 		return fmt.Errorf("%w: blob not found", datypes.ErrBlobNotFound)
 	case datypes.StatusSuccess:
 		r.logger.Debug().Uint64("da_height", daHeight).Msg("successfully retrieved from DA")
 		return nil
 	default:
-		return nil
+		return fmt.Errorf("DA retrieval failed with status code %d: %s", res.Code, res.Message)
 	}
 }

Also applies to: 141-154

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@block/internal/syncing/da_retriever.go` around lines 87 - 94, The current
callers of RetrieveBlobs (in the block using r.client.RetrieveBlobs with
r.client.GetHeaderNamespace()/GetDataNamespace()) rely on validateBlobResponse
returning nil for unknown non-success DA statuses which can mask real failures;
update validateBlobResponse to return a non-nil error for any unhandled/default
DA status (rather than nil), and change the callers around RetrieveBlobs to
propagate and return that error immediately (do not convert it into
ErrBlobNotFound). Specifically, modify validateBlobResponse to handle the
default case with an explicit error describing the unexpected DA status and
ensure both call sites that invoke validateBlobResponse after RetrieveBlobs (the
header/data branch and the other similar block) check and return that error
instead of proceeding.

Copy link
Member

@julienrbrt julienrbrt left a comment

Choose a reason for hiding this comment

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

ACK

@alpe alpe added this pull request to the merge queue Mar 18, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Mar 18, 2026
@alpe alpe added this pull request to the merge queue Mar 18, 2026
Merged via the queue into main with commit d27d099 Mar 18, 2026
33 of 34 checks passed
@alpe alpe deleted the alex/3141_getTimestamp branch March 18, 2026 15:47
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.

[FEATURE] retry fetching the timestamp on error in da/client

2 participants