Skip to main content

ff_engine/
partition_router.rs

1//! Partition-aware dispatch for cross-partition operations.
2//!
3//! The PartitionRouter resolves an ExecutionId to its partition and provides
4//! key contexts for Valkey operations. For Phase 1, a single ferriskey::Client
5//! connection is shared across all partitions (the client handles cluster routing
6//! internally via hash tags).
7
8use ff_core::keys::{ExecKeyContext, IndexKeys};
9use ff_core::partition::{
10    Partition, PartitionConfig, PartitionFamily, execution_partition,
11};
12use ff_core::types::ExecutionId;
13
14/// Routes execution operations to the correct partition.
15///
16/// In a Valkey Cluster deployment, the ferriskey client handles slot-level
17/// routing transparently — all keys for a partition share the same `{p:N}`
18/// hash tag, so they land on the same shard. The router's job is partition
19/// computation and key context construction, not connection selection.
20pub struct PartitionRouter {
21    config: PartitionConfig,
22}
23
24impl PartitionRouter {
25    pub fn new(config: PartitionConfig) -> Self {
26        Self { config }
27    }
28
29    /// Resolve an execution ID to its partition.
30    pub fn partition_for(&self, eid: &ExecutionId) -> Partition {
31        execution_partition(eid, &self.config)
32    }
33
34    /// Build an ExecKeyContext for the given execution.
35    pub fn exec_keys(&self, eid: &ExecutionId) -> ExecKeyContext {
36        let partition = self.partition_for(eid);
37        ExecKeyContext::new(&partition, eid)
38    }
39
40    /// Build IndexKeys for a given partition index.
41    pub fn index_keys(&self, partition_index: u16) -> IndexKeys {
42        let partition = Partition {
43            family: PartitionFamily::Execution,
44            index: partition_index,
45        };
46        IndexKeys::new(&partition)
47    }
48
49    /// The partition config.
50    pub fn config(&self) -> &PartitionConfig {
51        &self.config
52    }
53
54    /// Total number of flow partitions.
55    ///
56    /// Post-RFC-011: exec keys co-locate with their parent flow's partition
57    /// under hash-tag routing, so this count governs exec routing too.
58    /// There is no separate `num_execution_partitions`.
59    pub fn num_flow_partitions(&self) -> u16 {
60        self.config.num_flow_partitions
61    }
62}
63
64// ── Cluster 4 relocation (PR-7b) ────────────────────────────────────
65//
66// The pre-PR-7b `dispatch_dependency_resolution` +
67// `dispatch_via_postgres` free functions + `is_child_skipped_result`
68// helper lived here. They have been moved behind
69// `EngineBackend::cascade_completion`:
70//
71// - Valkey: `ff_backend_valkey::cascade::run_cascade`
72// - Postgres: `PostgresBackend::cascade_completion` →
73//   `ff_backend_postgres::dispatch::dispatch_completion`
74//
75// The single caller, `completion_listener::spawn_dispatch_loop`, now
76// trait-routes via `Arc<dyn EngineBackend>`.
77