Skip to main content

orlando_cluster/
placement.rs

1use crate::hash_ring::{HashRing, SiloAddress};
2
3/// Strategy for determining which silo should host a grain activation.
4pub trait PlacementStrategy: Send + Sync + 'static {
5    /// Given a grain type and key, return which silo should own it.
6    /// Returns `None` if no silo is available (empty cluster).
7    fn place(
8        &self,
9        grain_type: &str,
10        grain_key: &str,
11        local_silo_id: &str,
12        ring: &HashRing,
13    ) -> Option<SiloAddress>;
14}
15
16/// Default strategy: consistent hashing via the hash ring.
17/// This is what Orleans uses for most grains.
18#[derive(Debug)]
19pub struct HashBasedPlacement;
20
21impl PlacementStrategy for HashBasedPlacement {
22    fn place(
23        &self,
24        grain_type: &str,
25        grain_key: &str,
26        _local_silo_id: &str,
27        ring: &HashRing,
28    ) -> Option<SiloAddress> {
29        let ring_key = format!("{}/{}", grain_type, grain_key);
30        ring.get(&ring_key).cloned()
31    }
32}
33
34/// Prefer-local placement: always activate grains on the silo that receives
35/// the first call. Useful for cache grains or compute-local workloads.
36#[derive(Debug)]
37pub struct PreferLocalPlacement;
38
39impl PlacementStrategy for PreferLocalPlacement {
40    fn place(
41        &self,
42        _grain_type: &str,
43        _grain_key: &str,
44        local_silo_id: &str,
45        ring: &HashRing,
46    ) -> Option<SiloAddress> {
47        ring.members()
48            .into_iter()
49            .find(|s| s.silo_id == local_silo_id)
50    }
51}
52
53/// Random placement: distribute grains randomly across the cluster.
54/// Useful for stateless compute grains where you want even load distribution
55/// without the determinism of consistent hashing.
56#[derive(Debug)]
57pub struct RandomPlacement;
58
59impl PlacementStrategy for RandomPlacement {
60    fn place(
61        &self,
62        _grain_type: &str,
63        _grain_key: &str,
64        _local_silo_id: &str,
65        ring: &HashRing,
66    ) -> Option<SiloAddress> {
67        let members = ring.members();
68        if members.is_empty() {
69            return None;
70        }
71        let idx = fastrand::usize(..members.len());
72        Some(members[idx].clone())
73    }
74}