44
55namespace LaunchDarkly \Migrations ;
66
7+ use LaunchDarkly \Impl \Migrations \Executor ;
78use LaunchDarkly \LDClient ;
89use LaunchDarkly \LDContext ;
910use LaunchDarkly \LDUser ;
10- use LaunchDarkly \Types \Result ;
1111
1212/**
1313 * Migrator is a class for performing a technology migration.
@@ -22,8 +22,8 @@ public function __construct(
2222 private ExecutionOrder $ executionOrder ,
2323 private MigrationConfig $ readConfig ,
2424 private MigrationConfig $ writeConfig ,
25- private bool $ measureLatency ,
26- private bool $ measureErrors ,
25+ private bool $ trackLatency ,
26+ private bool $ trackErrors ,
2727 ) {
2828 }
2929
@@ -36,9 +36,28 @@ public function read(
3636 Stage $ defaultStage ,
3737 mixed $ payload = null
3838 ): OperationResult {
39- // TODO(sc-219376): Implement later
39+ $ variationResult = $ this ->client ->migrationVariation ($ key , $ context , $ defaultStage );
40+ /** @var Stage */
41+ $ stage = $ variationResult ['stage ' ];
42+ /** @var OpTracker */
43+ $ tracker = $ variationResult ['tracker ' ];
44+ $ tracker ->operation (Operation::READ );
4045
41- return new OperationResult (Origin::OLD , Result::success (null ));
46+ $ old = new Executor (Origin::OLD , $ this ->readConfig ->old , $ tracker , $ this ->trackLatency , $ this ->trackErrors , $ payload );
47+ $ new = new Executor (Origin::NEW , $ this ->readConfig ->new , $ tracker , $ this ->trackLatency , $ this ->trackErrors , $ payload );
48+
49+ $ result = match ($ stage ) {
50+ Stage::OFF => $ old ->run (),
51+ Stage::DUALWRITE => $ old ->run (),
52+ Stage::SHADOW => $ this ->readBoth ($ old , $ new , $ tracker ),
53+ Stage::LIVE => $ this ->readBoth ($ new , $ old , $ tracker ),
54+ Stage::RAMPDOWN => $ new ->run (),
55+ Stage::COMPLETE => $ new ->run (),
56+ };
57+
58+ // TODO(sc-219377): Emit the event here
59+
60+ return $ result ;
4261 }
4362
4463 /**
@@ -49,9 +68,65 @@ public function write(
4968 LDContext |LDUser $ context ,
5069 Stage $ defaultStage ,
5170 mixed $ payload = null
52- ): OperationResult {
53- // TODO(sc-219376): Implement later
54- //
55- return new OperationResult (Origin::OLD , Result::success (null ));
71+ ): WriteResult {
72+ $ variationResult = $ this ->client ->migrationVariation ($ key , $ context , $ defaultStage );
73+ /** @var Stage */
74+ $ stage = $ variationResult ['stage ' ];
75+ /** @var OpTracker */
76+ $ tracker = $ variationResult ['tracker ' ];
77+ $ tracker ->operation (Operation::READ );
78+
79+ $ old = new Executor (Origin::OLD , $ this ->writeConfig ->old , $ tracker , $ this ->trackLatency , $ this ->trackErrors , $ payload );
80+ $ new = new Executor (Origin::NEW , $ this ->writeConfig ->new , $ tracker , $ this ->trackLatency , $ this ->trackErrors , $ payload );
81+
82+ $ writeResult = match ($ stage ) {
83+ Stage::OFF => new WriteResult ($ old ->run ()),
84+ Stage::DUALWRITE => $ this ->writeBoth ($ old , $ new , $ tracker ),
85+ Stage::SHADOW => $ this ->writeBoth ($ old , $ new , $ tracker ),
86+ Stage::LIVE => $ this ->writeBoth ($ new , $ old , $ tracker ),
87+ Stage::RAMPDOWN => $ this ->writeBoth ($ new , $ old , $ tracker ),
88+ Stage::COMPLETE => new WriteResult ($ new ->run ()),
89+ };
90+
91+ // TODO(sc-219377): Emit the event here
92+
93+ return $ writeResult ;
94+ }
95+
96+ private function readBoth (Executor $ authoritative , Executor $ nonauthoritative , OpTracker $ tracker ): OperationResult
97+ {
98+ // TODO(sc-219378): Add sampling to limit to 50% chance
99+ if ($ this ->executionOrder == ExecutionOrder::RANDOM ) {
100+ $ nonauthoritativeResult = $ nonauthoritative ->run ();
101+ $ authoritativeResult = $ authoritative ->run ();
102+ } else {
103+ $ authoritativeResult = $ authoritative ->run ();
104+ $ nonauthoritativeResult = $ nonauthoritative ->run ();
105+ }
106+
107+ if ($ this ->readConfig ->comparison === null ) {
108+ return $ authoritativeResult ;
109+ }
110+
111+ if ($ authoritativeResult ->isSuccessful () && $ nonauthoritativeResult ->isSuccessful ()) {
112+ $ tracker ->consistent (fn (): bool => ($ this ->readConfig ->comparison )($ authoritativeResult ->value , $ nonauthoritativeResult ->value ));
113+ }
114+
115+ return $ authoritativeResult ;
116+ }
117+
118+ private function writeBoth (Executor $ authoritative , Executor $ nonauthoritative , OpTracker $ tracker ): WriteResult
119+ {
120+ $ authoritativeResult = $ authoritative ->run ();
121+ $ tracker ->invoked ($ authoritative ->origin );
122+
123+ if (!$ authoritativeResult ->isSuccessful ()) {
124+ return new WriteResult ($ authoritativeResult );
125+ }
126+
127+ $ nonauthoritativeResult = $ nonauthoritative ->run ();
128+ $ tracker ->invoked ($ nonauthoritative ->origin );
129+
130+ return new WriteResult ($ authoritativeResult , $ nonauthoritativeResult );
56131 }
57132}
0 commit comments