launchdarkly_server_sdk/migrations/
mod.rs

1use core::fmt;
2use std::fmt::{Display, Formatter};
3
4use launchdarkly_server_sdk_evaluation::FlagValue;
5use serde::{Deserialize, Serialize};
6
7#[non_exhaustive]
8#[derive(Debug, Copy, Clone, Serialize, Eq, Hash, PartialEq)]
9#[serde(rename_all = "lowercase")]
10/// Origin represents the source of origin for a migration-related operation.
11pub enum Origin {
12    /// Old represents the technology source we are migrating away from.
13    Old,
14    /// New represents the technology source we are migrating towards.
15    New,
16}
17
18#[non_exhaustive]
19#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
20#[serde(rename_all = "lowercase")]
21/// Operation represents a type of migration operation; namely, read or write.
22pub enum Operation {
23    /// Read denotes a read-related migration operation.
24    Read,
25    /// Write denotes a write-related migration operation.
26    Write,
27}
28
29#[non_exhaustive]
30#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "lowercase")]
32/// Stage denotes one of six possible stages a technology migration could be a
33/// part of, progressing through the following order.
34///
35/// Off -> DualWrite -> Shadow -> Live -> RampDown -> Complete
36pub enum Stage {
37    /// Off - migration hasn't started, "old" is authoritative for reads and writes
38    Off,
39    /// DualWrite - write to both "old" and "new", "old" is authoritative for reads
40    DualWrite,
41    /// Shadow - both "new" and "old" versions run with a preference for "old"
42    Shadow,
43    /// Live - both "new" and "old" versions run with a preference for "new"
44    Live,
45    /// RampDown - only read from "new", write to "old" and "new"
46    Rampdown,
47    /// Complete - migration is done
48    Complete,
49}
50
51impl Display for Stage {
52    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53        match self {
54            Stage::Off => write!(f, "off"),
55            Stage::DualWrite => write!(f, "dualwrite"),
56            Stage::Shadow => write!(f, "shadow"),
57            Stage::Live => write!(f, "live"),
58            Stage::Rampdown => write!(f, "rampdown"),
59            Stage::Complete => write!(f, "complete"),
60        }
61    }
62}
63
64impl From<Stage> for FlagValue {
65    fn from(stage: Stage) -> FlagValue {
66        FlagValue::Str(stage.to_string())
67    }
68}
69
70impl TryFrom<FlagValue> for Stage {
71    type Error = String;
72
73    fn try_from(value: FlagValue) -> Result<Self, Self::Error> {
74        if let FlagValue::Str(value) = value {
75            match value.as_str() {
76                "off" => Ok(Stage::Off),
77                "dualwrite" => Ok(Stage::DualWrite),
78                "shadow" => Ok(Stage::Shadow),
79                "live" => Ok(Stage::Live),
80                "rampdown" => Ok(Stage::Rampdown),
81                "complete" => Ok(Stage::Complete),
82                _ => Err(format!("Invalid stage: {}", value)),
83            }
84        } else {
85            Err("Cannot convert non-string value to Stage".to_string())
86        }
87    }
88}
89
90#[non_exhaustive]
91#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
92#[serde(rename_all = "lowercase")]
93/// ExecutionOrder represents the various execution modes this SDK can operate under while
94/// performing migration-assisted reads.
95pub enum ExecutionOrder {
96    /// Serial execution ensures the authoritative read will always complete execution before
97    /// executing the non-authoritative read.
98    Serial,
99    /// Random execution randomly decides if the authoritative read should execute first or second.
100    Random,
101    /// Concurrent executes both concurrently, waiting until both calls have finished before
102    /// proceeding.
103    Concurrent,
104}
105
106pub use migrator::Migrator;
107pub use migrator::MigratorBuilder;
108pub use tracker::MigrationOpTracker;
109
110mod migrator;
111mod tracker;