Skip to content

Conversation

@martintmk
Copy link
Member

@martintmk martintmk commented Jan 16, 2026

Introduces seatbelt, a production-ready resilience middleware crate providing:

  • Retry: Automatic retries with configurable backoff (constant, linear, exponential) and jitter
  • Timeout: Cancellation of long-running operations with per-request overrides
  • Circuit Breaker: Prevents cascading failures by blocking requests when failure rates exceed thresholds

Key features:

  • Runtime-agnostic design (works with any async runtime via tick::Clock)
  • Type-state pattern enforces correct configuration at compile time
  • OpenTelemetry metrics and structured logging support
  • Composable layers compatible with tower/layered middleware stacks

Example:

let clock = Clock::new_tokio();
  let context = ResilienceContext::new(&clock).name("my_service");

  let stack = (
      // Retry: recover from transient failures
      Retry::layer("retry", &context)
          .clone_input()
          .max_retry_attempts(3)
          .backoff(Backoff::Exponential)
          .recovery_with(|output: &Result<String, String>, _| match output {
              Ok(_) => RecoveryInfo::never(),
              Err(_) => RecoveryInfo::retry(),
          }),
      // Circuit breaker: stop calling failing services
      Breaker::layer("circuit", &context)
          .recovery_with(|output: &Result<String, String>, _| match output {
              Ok(_) => RecoveryInfo::never(),
              Err(_) => RecoveryInfo::retry(),
          })
          .rejected_input_error(|_, _| "circuit open".to_string()),
      // Timeout: bound operation duration
      Timeout::layer("timeout", &context)
          .timeout(Duration::from_secs(5))
          .timeout_output(|_| Err("timeout".to_string())),
      Execute::new(call_external_service),
  );

  let service = stack.into_service();
  let result = service.execute("request".to_string()).await;

@codecov
Copy link

codecov bot commented Jan 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (fa3d815) to head (f989b49).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##             main     #203     +/-   ##
=========================================
  Coverage   100.0%   100.0%             
=========================================
  Files         105      135     +30     
  Lines        6783     8161   +1378     
=========================================
+ Hits         6783     8161   +1378     

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

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

⚠️ Breaking Changes Detected

error: failed to retrieve local crate data from git revision

Caused by:
    0: failed to retrieve manifest file from git revision source
    1: possibly due to errors: [
         failed when reading /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/1101a22c99c13f720565990cb0cdf10dabd2cf12/scripts/crate-template/Cargo.toml: TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       : TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       ,
         failed to parse /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/1101a22c99c13f720565990cb0cdf10dabd2cf12/Cargo.toml: no `package` table,
       ]
    2: package `seatbelt` not found in /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/1101a22c99c13f720565990cb0cdf10dabd2cf12

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
   1: cargo_semver_checks::rustdoc_gen::RustdocFromProjectRoot::get_crate_source
   2: cargo_semver_checks::rustdoc_gen::StatefulRustdocGenerator<cargo_semver_checks::rustdoc_gen::CoupledState>::prepare_generator
   3: cargo_semver_checks::Check::check_release::{{closure}}
   4: cargo_semver_checks::Check::check_release
   5: cargo_semver_checks::exit_on_error
   6: cargo_semver_checks::main
   7: std::sys::backtrace::__rust_begin_short_backtrace
   8: main
   9: <unknown>
  10: __libc_start_main
  11: _start

If the breaking changes are intentional then everything is fine - this message is merely informative.

Remember to apply a version number bump with the correct severity when publishing a version with breaking changes (1.x.x -> 2.x.x or 0.1.x -> 0.2.x).

Copy link
Member

@geeknoid geeknoid left a comment

Choose a reason for hiding this comment

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

Check out the "Alerts" tab in GitHub. It mentions public APIs which are referencing items whose docs are being hidden, which seems like an anti-pattern. I don't know why these items are showing up in that tab instead of breaking the build...

@geeknoid
Copy link
Member

Uh ho, the CLA check is failing. You might need to close and reopen the PR...

@martintmk martintmk closed this Jan 27, 2026
@martintmk martintmk reopened this Jan 27, 2026
@martintmk martintmk enabled auto-merge (squash) January 27, 2026 18:25
@martintmk martintmk merged commit ddab918 into main Jan 27, 2026
34 checks passed
@martintmk martintmk deleted the u/mtomka/seatbelt branch January 27, 2026 18:48
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.

4 participants