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