rototo
DocsReference
Reference

SDK Refresh Reference

Configuration is deployed separately from the application binary. A long-running service should be able to pick up a newly reviewed workspace version without restarting, while continuing to serve the last known good workspace when refresh fails.

RefreshingWorkspace is the SDK type for that model.

Load With Refresh

use std::time::Duration;
use rototo::{RefreshOptions, RefreshingWorkspace};

let refresh = RefreshOptions::new() .with_period(Duration::from_secs(30));

let workspace = RefreshingWorkspace::load(source, refresh).await?;

import rototo

workspace = await rototo.RefreshingWorkspace.load(
    source,
    period_seconds=30,
)
import { RefreshingWorkspace } from "rototo";

const workspace = await RefreshingWorkspace.load(source, {
  periodSeconds: 30,
});
RefreshingWorkspaceOptions options = RefreshingWorkspaceOptions.builder()
    .periodSeconds(30.0)
    .build();

RefreshingWorkspace workspace = RefreshingWorkspace
    .load(source, options)
    .get();
periodSeconds := 30.0

workspace, err := rototo.LoadRefreshing(
    ctx,
    source,
    &rototo.RefreshingWorkspaceOptions{
        PeriodSeconds: &periodSeconds,
    },
)
if err != nil {
    return err
}
defer workspace.Close(ctx)

Initial load stages the source, lints it, compiles the runtime model, and makes that workspace current. If initial load fails, the service has no active workspace and the call returns an error.

Resolution

RefreshingWorkspace exposes the same runtime resolution methods:

let resolution = workspace
    .resolve_variable("account-limits", &context)
    .await?;
resolution = await workspace.resolve_variable(
    "account-limits",
    context,
)
const resolution = await workspace.resolveVariable(
  "account-limits",
  context,
);
VariableResolution resolution = workspace
    .resolveVariable("account-limits", context)
    .get();
resolution, err := workspace.ResolveVariable(
    ctx,
    "account-limits",
    resolveContext,
    nil,
)

Each call resolves against the current successfully loaded workspace. A successful refresh affects future resolutions. It does not mutate a resolution already returned to application code.

Manual Refresh

let outcome = workspace.refresh_now().await?;
outcome = await workspace.refresh_now()
const outcome = await workspace.refreshNow();
String outcome = workspace.refreshNow().get();
outcome, err := workspace.RefreshNow(ctx)

Refresh outcomes:

OutcomeMeaning
unchangedSource fingerprint did not change.
refreshedA changed source loaded, linted, and replaced the current workspace.
immutableSource is pinned to an immutable commit.

Periodic Refresh

Periodic refresh starts a background refresh loop for mutable sources.

let refresh = RefreshOptions::new()
    .with_period(Duration::from_secs(60))
    .with_failure_backoff(Duration::from_secs(5), Duration::from_secs(300));
workspace = await rototo.RefreshingWorkspace.load(
    source,
    period_seconds=60,
)
const workspace = await RefreshingWorkspace.load(source, {
  periodSeconds: 60,
});
RefreshingWorkspaceOptions options = RefreshingWorkspaceOptions.builder()
    .periodSeconds(60.0)
    .build();

RefreshingWorkspace workspace = RefreshingWorkspace .load(source, options) .get();

periodSeconds := 60.0

workspace, err := rototo.LoadRefreshing(
    ctx,
    source,
    &rototo.RefreshingWorkspaceOptions{
        PeriodSeconds: &periodSeconds,
    },
)

On refresh failure, rototo keeps the current workspace active and backs off before the next attempt. The default Rust backoff starts at 5 seconds and caps at 300 seconds. The first Python and Go SDK releases use the Rust defaults.

Immutable Sources

A git source pinned to a full 40-character commit SHA is immutable. Periodic refresh is disabled for immutable sources, because there is no later workspace version to discover from that source string.

Commit-pinned sources are good for reproducible jobs and tests. Branch or tag sources are the usual fit for long-running services that should receive reviewed configuration updates.

Status

let status = workspace.status().await;
status = await workspace.status()
const status = await workspace.status();
RefreshStatus status = workspace.status().get();
status, err := workspace.Status(ctx)

RefreshStatus contains:

FieldMeaning
current_fingerprintFingerprint of the active workspace source.
last_successLast successful initial load or refresh time.
last_attemptLast refresh attempt time.
consecutive_failuresCount of refresh failures since the last success.
last_errorLast refresh error message, if any.
refreshingWhether a refresh attempt is currently running.
immutableWhether the current source is immutable.

Rust also exposes RefreshStatus::stale(max_staleness).

Shutdown

workspace.shutdown().await;
await workspace.shutdown()
await workspace.shutdown();
workspace.shutdown().get();
err := workspace.Shutdown(ctx)

shutdown stops the background refresh loop and waits for it. Dropping the refreshing workspace also signals shutdown and aborts the task if needed, but explicit shutdown gives services a cleaner stop path.

Observability

A service should expose the active fingerprint, refresh failures, and staleness state. Those are the fields that tell an operator whether the service is using the latest reviewed workspace or deliberately serving the last known good version.