Skip to content

Commit ac5fb7a

Browse files
committed
refactor(fortuna): streamline keeper configuration and improve event processing
- Updated config.sample.yaml by removing unnecessary blank lines. - Changed backup_delay_seconds in README.md from 45 to 30 for consistency. - Refactored run_keeper_threads to accept KeeperConfig directly instead of private_key. - Enhanced run function to handle keeper configuration more effectively. - Added comments in process_event_with_backoff to clarify primary and backup replica logic.
1 parent 631e71d commit ac5fb7a

File tree

6 files changed

+34
-23
lines changed

6 files changed

+34
-23
lines changed

apps/fortuna/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ a database to be available at build time. Create a `.env` file in the root of th
1717
DATABASE_URL="sqlite:fortuna.db?mode=rwc"
1818
```
1919

20+
Install sqlx for cargo with:
21+
```bash
22+
cargo install sqlx
23+
```
24+
2025
Next, you need to create the database and apply the schema migrations. You can do this by running:
2126

2227
```bash
@@ -62,7 +67,7 @@ keeper:
6267
total_replicas: 2
6368
backup_delay_seconds: 30
6469

65-
# Replica 1 (Green) - handles odd sequence numbers (1, 3, 5, ...)
70+
# Replica 1 (Green) - handles odd sequence numbers (1, 3, 5, ...)
6671
keeper:
6772
replica_config:
6873
replica_id: 1
@@ -77,7 +82,7 @@ keeper:
7782
replica_config:
7883
replica_id: 0
7984
total_replicas: 3
80-
backup_delay_seconds: 45
85+
backup_delay_seconds: 30
8186
```
8287
8388
### Deployment Considerations

apps/fortuna/config.sample.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ chains:
4747
# blocks after 5 blocks, then again after 10 blocks, and finally after 20 blocks.
4848
block_delays: [5, 10, 20]
4949

50-
51-
5250
# Historical commitments -- delete this block for local development purposes
5351
commitments:
5452
# prettier-ignore
@@ -88,8 +86,8 @@ keeper:
8886
value: 0xabcd
8987
# For production, you can store the private key in a file.
9088
# file: keeper-key.txt
91-
# Multi-replica configuration documentation
9289

90+
# Multi-replica configuration
9391
# Optional: Multi-replica configuration for high availability and load distribution
9492
# Uncomment and configure for production deployments with multiple Fortuna instances
9593
# replica_config:
@@ -98,7 +96,7 @@ keeper:
9896
# backup_delay_seconds: 30 # Seconds to wait before processing other replicas' requests
9997
#
10098
# Example configurations:
101-
#
99+
#
102100
# Two-replica setup (Blue/Green):
103101
# - Replica 0: handles even sequence numbers (0, 2, 4, ...)
104102
# - Replica 1: handles odd sequence numbers (1, 3, 5, ...)

apps/fortuna/src/command/run.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use {
33
api::{self, ApiBlockChainState, BlockchainState, ChainId},
44
chain::ethereum::InstrumentedPythContract,
55
command::register_provider::CommitmentMetadata,
6-
config::{Commitment, Config, EthereumConfig, ProviderConfig, RunOptions},
6+
config::{Commitment, Config, EthereumConfig, KeeperConfig, ProviderConfig, RunOptions},
77
eth_utils::traced_client::RpcMetrics,
88
history::History,
99
keeper::{self, keeper_metrics::KeeperMetrics},
@@ -98,6 +98,11 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
9898
if keeper_private_key_option.is_none() {
9999
tracing::info!("Not starting keeper service: no keeper private key specified. Please add one to the config if you would like to run the keeper service.")
100100
}
101+
let keeper_config_option = if keeper_private_key_option.is_some() {
102+
Some(config.keeper.clone())
103+
} else {
104+
None
105+
};
101106
let chains: Arc<RwLock<HashMap<ChainId, ApiBlockChainState>>> = Arc::new(RwLock::new(
102107
config
103108
.chains
@@ -109,7 +114,7 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
109114
for (chain_id, chain_config) in config.chains.clone() {
110115
keeper_metrics.add_chain(chain_id.clone(), config.provider.address);
111116
let keeper_metrics = keeper_metrics.clone();
112-
let keeper_private_key_option = keeper_private_key_option.clone();
117+
let keeper_config_option = keeper_config_option.clone();
113118
let chains = chains.clone();
114119
let secret_copy = secret.clone();
115120
let rpc_metrics = rpc_metrics.clone();
@@ -122,7 +127,7 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
122127
&chain_id,
123128
chain_config.clone(),
124129
keeper_metrics.clone(),
125-
keeper_private_key_option.clone(),
130+
keeper_config_option.clone(),
126131
chains.clone(),
127132
&secret_copy,
128133
history.clone(),
@@ -172,7 +177,7 @@ async fn setup_chain_and_run_keeper(
172177
chain_id: &ChainId,
173178
chain_config: EthereumConfig,
174179
keeper_metrics: Arc<KeeperMetrics>,
175-
keeper_private_key_option: Option<String>,
180+
keeper_config: Option<KeeperConfig>,
176181
chains: Arc<RwLock<HashMap<ChainId, ApiBlockChainState>>>,
177182
secret_copy: &str,
178183
history: Arc<History>,
@@ -192,9 +197,9 @@ async fn setup_chain_and_run_keeper(
192197
chain_id.clone(),
193198
ApiBlockChainState::Initialized(state.clone()),
194199
);
195-
if let Some(keeper_private_key) = keeper_private_key_option {
200+
if let Some(keeper_config) = keeper_config {
196201
keeper::run_keeper_threads(
197-
keeper_private_key,
202+
keeper_config,
198203
chain_config,
199204
state,
200205
keeper_metrics.clone(),

apps/fortuna/src/keeper.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub enum RequestState {
5656
/// handle any events for the new blocks.
5757
#[tracing::instrument(name = "keeper", skip_all, fields(chain_id = chain_state.id))]
5858
pub async fn run_keeper_threads(
59-
private_key: String,
59+
keeper_config: crate::config::KeeperConfig,
6060
chain_eth_config: EthereumConfig,
6161
chain_state: BlockchainState,
6262
metrics: Arc<KeeperMetrics>,
@@ -67,6 +67,11 @@ pub async fn run_keeper_threads(
6767
let latest_safe_block = get_latest_safe_block(&chain_state).in_current_span().await;
6868
tracing::info!("Latest safe block: {}", &latest_safe_block);
6969

70+
let private_key = keeper_config
71+
.private_key
72+
.load()?
73+
.ok_or(anyhow::anyhow!("Keeper private key must be provided"))?;
74+
7075
let contract = Arc::new(InstrumentedSignablePythContract::from_config(
7176
&chain_eth_config,
7277
&private_key,
@@ -85,13 +90,7 @@ pub async fn run_keeper_threads(
8590
contract: contract.clone(),
8691
gas_limit,
8792
escalation_policy: chain_eth_config.escalation_policy.to_policy(),
88-
keeper_config: crate::config::KeeperConfig {
89-
private_key: crate::config::SecretString {
90-
value: None,
91-
file: None,
92-
},
93-
replica_config: None,
94-
},
93+
keeper_config,
9594
metrics: metrics.clone(),
9695
fulfilled_requests_cache,
9796
history,

apps/fortuna/src/keeper/block.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use {
22
crate::{
33
api::BlockchainState,
44
chain::{ethereum::InstrumentedSignablePythContract, reader::BlockNumber},
5+
config::KeeperConfig,
56
eth_utils::utils::EscalationPolicy,
67
history::History,
78
keeper::{
@@ -45,7 +46,7 @@ pub struct ProcessParams {
4546
pub gas_limit: U256,
4647
pub escalation_policy: EscalationPolicy,
4748
pub chain_state: BlockchainState,
48-
pub keeper_config: crate::config::KeeperConfig,
49+
pub keeper_config: KeeperConfig,
4950
pub metrics: Arc<KeeperMetrics>,
5051
pub history: Arc<History>,
5152
pub fulfilled_requests_cache: Arc<RwLock<HashSet<u64>>>,

apps/fortuna/src/keeper/process_event.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub async fn process_event_with_backoff(
3535
return Ok(());
3636
}
3737

38+
// Check if we are the primary replica for this request.
39+
// If not, we will wait for a delay before processing the request.
3840
let is_primary_replica =
3941
if let Some(replica_config) = &process_param.keeper_config.replica_config {
4042
let assigned_replica = event.sequence_number % replica_config.total_replicas;
@@ -66,6 +68,8 @@ pub async fn process_event_with_backoff(
6668
.await;
6769
}
6870

71+
// Check if the request is still open after the delay.
72+
// If it is, we will process it as a backup replica.
6973
match chain_state
7074
.contract
7175
.get_request(event.provider_address, event.sequence_number)
@@ -88,9 +92,8 @@ pub async fn process_event_with_backoff(
8892
tracing::warn!(
8993
sequence_number = event.sequence_number,
9094
error = ?e,
91-
"Error checking request status after delay, skipping"
95+
"Error checking request status after delay, processing as backup replica"
9296
);
93-
return Ok(());
9497
}
9598
}
9699
}

0 commit comments

Comments
 (0)