Skip to content

V0.9.10#16

Merged
bartvanbenthem merged 10 commits into
mainfrom
v0.9.10
Jun 12, 2026
Merged

V0.9.10#16
bartvanbenthem merged 10 commits into
mainfrom
v0.9.10

Conversation

@bartvanbenthem

Copy link
Copy Markdown
Owner

What's New

Exponential backoff on poll failures

watch_external no longer retries immediately on error. Failed polls now wait
using exponential backoff: the retry delay starts at the base interval,
doubles on each consecutive failure, and is capped at interval × 32.
A successful poll resets the wait back to interval.

Previously, a transient failure caused an immediate retry on the next tick with
only a warning log. Now the watcher backs off progressively, reducing noise and
load during extended outages.

New WatchConfig type and watch_external_with_config entry point

WatchConfig exposes interval and max_backoff as explicit fields and is
constructed with a fluent builder:

use std::time::Duration;
use koprs_external::watcher::WatchConfig;

let config = WatchConfig::new(Duration::from_secs(30))
    .with_max_backoff(Duration::from_secs(300)); // cap retries at 5 min

watch_external_with_config accepts a WatchConfig instead of a bare
Duration, giving full control over the retry ceiling:

let _handle = watch_external_with_config(poller, config, tx);

watch_external remains unchanged as a convenience wrapper; it calls
watch_external_with_config with WatchConfig::new(interval) and a default
max_backoff of interval × 32.

Public re-exports

WatchConfig and watch_external_with_config are now re-exported from the
crate root alongside the existing ExternalEvent and watch_external.

use koprs_external::{ExternalEvent, WatchConfig, watch_external, watch_external_with_config};

Correct feature flag name in documentation

The object-store feature is "object-store", not "s3". The README and
code examples have been corrected.

# correct
koprs-external = { version = "0.9.1", features = ["object-store"] }

Internal changes

  • watcher.rs: replaced the tokio::time::interval ticker with explicit
    sleep calls so backoff waits can vary between iterations.
  • store.rs: removed duplicate inline doc examples that duplicated the
    README; module-level and struct-level docs now point to the crate README for
    backend-specific setup.
  • lib.rs: doc-link syntax updated ([crate::watcher::ExternalSource]
    [watcher::ExternalSource]) to fix cross-reference resolution.
  • tests/watcher.rs: added FlakySource — a test double that fails a
    configurable number of times then succeeds — covering the backoff recovery
    path. Three new async tests verify recovery after N errors, survival through
    five consecutive failures, and watch_external_with_config behaviour.
  • tests/watcher.rs: two unit tests cover WatchConfig construction:
    default max_backoff equals interval × 32, and with_max_backoff
    overrides the default correctly.

Migration

watch_external is source-compatible. No changes required for existing callers.

To set a custom backoff ceiling, switch to watch_external_with_config:

// before
let _handle = watch_external(poller, Duration::from_secs(30), tx);

// after — custom ceiling
let config = WatchConfig::new(Duration::from_secs(30))
    .with_max_backoff(Duration::from_secs(300));
let _handle = watch_external_with_config(poller, config, tx);

@bartvanbenthem bartvanbenthem merged commit 15e427d into main Jun 12, 2026
1 check passed
@bartvanbenthem bartvanbenthem deleted the v0.9.10 branch June 12, 2026 10:43
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.

1 participant