This document describes the refactoring of high-frequency telemetry tracking to use Soroban Temporary Storage instead of persistent storage, significantly reducing long-term ledger rent burdens.
Previously, incoming telemetry tracking (heartbeat timestamps) was stored in a way that could accumulate ledger rent costs. High-frequency, short-lived price feeds should not burden persistent storage nodes long-term.
The heartbeat tracking system now uses Soroban Temporary Storage with proper TTL (Time-To-Live) configuration:
const HEARTBEAT_TTL_LEDGERS: u32 = 17_280; // ~24 hours at 5s/ledger
const HEARTBEAT_TTL_THRESHOLD: u32 = 5_000; // Extend when < 5000 ledgers remainfn _record_heartbeat(env: &Env, asset: Symbol) {
let mut timestamps: Map<Symbol, u64> = env.storage().temporary().get(&HEARTBEAT_KEY).unwrap_or_else(|| Map::new(env));
timestamps.set(asset, env.ledger().timestamp());
env.storage().temporary().set(&HEARTBEAT_KEY, ×tamps);
// Set TTL to ensure entries expire naturally after validation window
env.storage().temporary().extend_ttl(
&HEARTBEAT_KEY,
HEARTBEAT_TTL_THRESHOLD,
HEARTBEAT_TTL_LEDGERS,
);
}Key Features:
- Uses
env.storage().temporary()for all telemetry data - Automatically extends TTL when entries are accessed and have < 5000 ledgers remaining
- Entries expire after ~24 hours (17,280 ledgers at 5 seconds per ledger)
- No manual cleanup required - ledger automatically purges expired entries
Temporary storage entries expire naturally from ledger state once their validation time window closes:
- Initial TTL: 17,280 ledgers (~24 hours)
- Auto-extension: When accessed with < 5,000 ledgers remaining, TTL resets
- Natural cleanup: Soroban automatically removes expired entries from ledger state
The following public functions interact with temporary telemetry storage:
update_heartbeat()- Records timestamp for an assetget_last_update_timestamp()- Retrieves last update timeis_data_fresh()- Checks if data is within the configured intervalfinalize_consensus()- Cleans up temporary cache and telemetry
- No long-term storage costs for high-frequency telemetry data
- Old entries automatically expire without manual intervention
- Temporary storage is optimized for short-lived data
- No accumulation of historical telemetry data
- Soroban's ledger state automatically purges expired entries
- No need for manual maintenance or archival processes
- Temporary storage has significantly lower rent costs
- Predictable costs based on TTL configuration
| Storage Type | Use Case | Rent Cost | Expiration |
|---|---|---|---|
| Instance | Contract configuration (admin, intervals) | Medium | Manual/explicit |
| Persistent | Long-term data (stakes, node profiles) | High | Manual/explicit |
| Temporary | High-frequency telemetry, caches | Low | Automatic via TTL |
Default: 5 minutes (300 seconds)
const DEFAULT_HEARTBEAT_INTERVAL: u64 = 5 * 60;Can be customized via:
pub fn set_heartbeat_interval(env: Env, interval: u64, admin: Address)// Entries live for ~24 hours before automatic expiration
const HEARTBEAT_TTL_LEDGERS: u32 = 17_280;
// Auto-extend TTL when < 5000 ledgers remain
const HEARTBEAT_TTL_THRESHOLD: u32 = 5_000;// Record a heartbeat (automatically sets TTL)
TimeLockedUpgradeContract::_record_heartbeat(&env, symbol_short!("VALUE"));
// Check if data is fresh
let is_fresh = TimeLockedUpgradeContract::is_data_fresh(env.clone(), symbol_short!("NGN"));
// Get last update timestamp (returns Option<u64>)
let last_update = TimeLockedUpgradeContract::get_last_update_timestamp(env.clone(), symbol_short!("NGN"));
// Data automatically expires after TTL window - no manual cleanup neededThe test suite in src/test.rs validates the refactored behavior:
test_heartbeat_fresh_data()- Verifies immediate freshness after updatetest_heartbeat_stale_data()- Validates expiration after intervaltest_heartbeat_never_updated()- Handles missing entries gracefullytest_heartbeat_custom_interval()- Tests configurable intervalstest_stake_updates_heartbeat()- Verifies stake operations record telemetrytest_set_value_updates_heartbeat()- Verifies value updates record telemetry
This refactoring maintains the same public API. Existing integrations continue to work without modification.
- Old persistent heartbeat data (if any) will remain until manually removed
- New heartbeat data uses temporary storage exclusively
- Automatic expiration ensures no long-term accumulation
const HEARTBEAT_KEY: Symbol = symbol_short!("HBEAT");
const HB_INTERVAL_KEY: Symbol = symbol_short!("HBINTV");
const CONSENSUS_CACHE_KEY: Symbol = symbol_short!("CACHE");// Map of asset symbols to their last update timestamps
Map<Symbol, u64>-
Use temporary storage for:
- High-frequency price feeds
- Telemetry and heartbeat data
- Consensus cache data
- Any data with natural expiration windows
-
TTL Configuration:
- Set TTL longer than your validation window
- Use threshold-based extension for active data
- Balance between availability and cost
-
Monitoring:
- Track
is_data_fresh()for data availability - Monitor heartbeat intervals for optimal freshness
- Adjust TTL based on actual usage patterns
- Track
Potential improvements for consideration:
- Dynamic TTL: Adjust TTL based on asset activity levels
- Metrics: Add telemetry for storage usage patterns
- Archival: Optional off-chain archival for historical analysis
- Multi-tier Storage: Automatic migration from temporary to persistent for important events
This refactoring successfully moves high-frequency telemetry tracking to Soroban's temporary storage, eliminating long-term ledger rent burdens while maintaining full functionality. Old entries now expire naturally from ledger state registers once their validation time window closes.
Implementation Date: June 2026
Contract: TimeLockedUpgradeContract
Modified Files: src/lib.rs