@@ -123,7 +123,24 @@ mod test {
123123 Ok ( ( ) )
124124 }
125125
126+ /// Stops the downstairs task, return a `(port, rport)` tuple
127+ pub async fn stop ( & mut self ) -> Result < ( u16 , u16 ) > {
128+ let ds = self . downstairs . take ( ) . unwrap ( ) ;
129+ let port = ds. address ( ) . port ( ) ;
130+ let rport = ds. repair_address ( ) . port ( ) ;
131+ ds. stop ( ) . await ?;
132+ Ok ( ( port, rport) )
133+ }
134+
126135 pub async fn reboot_read_write ( & mut self ) -> Result < ( ) > {
136+ self . reboot_read_write_with_ports ( 0 , 0 ) . await
137+ }
138+
139+ pub async fn reboot_read_write_with_ports (
140+ & mut self ,
141+ port : u16 ,
142+ rport : u16 ,
143+ ) -> Result < ( ) > {
127144 let downstairs =
128145 Downstairs :: new_builder ( self . tempdir . path ( ) , false )
129146 . set_logger ( csl ( ) )
@@ -134,6 +151,8 @@ mod test {
134151 downstairs,
135152 DownstairsClientSettings {
136153 address : self . address ,
154+ port,
155+ rport,
137156 ..DownstairsClientSettings :: default ( )
138157 } ,
139158 )
@@ -5839,4 +5858,156 @@ mod test {
58395858 // Make sure everything worked
58405859 volume. activate ( ) . await . unwrap ( ) ;
58415860 }
5861+
5862+ #[ tokio:: test]
5863+ async fn connect_two_ds_then_deactivate ( ) {
5864+ const BLOCK_SIZE : usize = 512 ;
5865+
5866+ // Spin off three downstairs, build our Crucible struct.
5867+ let mut tds = TestDownstairsSet :: small ( false ) . await . unwrap ( ) ;
5868+ let opts = tds. opts ( ) ;
5869+ tds. downstairs1 . stop ( ) . await . unwrap ( ) ;
5870+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_secs ( 1 ) ) . await ;
5871+
5872+ let ( guest, io) = Guest :: new ( None ) ;
5873+ let _join_handle = up_main ( opts, 1 , None , io, None ) . unwrap ( ) ;
5874+ guest. activate ( ) . await . unwrap ( ) ;
5875+
5876+ let res = guest
5877+ . write (
5878+ BlockIndex ( 0 ) ,
5879+ BytesMut :: from ( vec ! [ 0x55 ; BLOCK_SIZE * 2 ] . as_slice ( ) ) ,
5880+ )
5881+ . await ;
5882+ assert ! ( res. is_ok( ) ) ;
5883+
5884+ guest. deactivate ( ) . await . unwrap ( ) ;
5885+ }
5886+
5887+ #[ tokio:: test]
5888+ async fn connect_two_ds_then_another ( ) {
5889+ const BLOCK_SIZE : usize = 512 ;
5890+
5891+ // Spin off three downstairs, build our Crucible struct.
5892+ let mut tds = TestDownstairsSet :: small ( false ) . await . unwrap ( ) ;
5893+ let opts = tds. opts ( ) ;
5894+ let ( ds1_port, ds1_rport) = tds. downstairs1 . stop ( ) . await . unwrap ( ) ;
5895+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_secs ( 1 ) ) . await ;
5896+
5897+ let ( guest, io) = Guest :: new ( None ) ;
5898+ let _join_handle = up_main ( opts, 1 , None , io, None ) . unwrap ( ) ;
5899+ guest. activate ( ) . await . unwrap ( ) ;
5900+
5901+ let res = guest
5902+ . write (
5903+ BlockIndex ( 0 ) ,
5904+ BytesMut :: from ( vec ! [ 0x55 ; BLOCK_SIZE * 2 ] . as_slice ( ) ) ,
5905+ )
5906+ . await ;
5907+ assert ! ( res. is_ok( ) ) ;
5908+
5909+ // Restart downstairs1, which should use live-repair to join the quorum
5910+ //
5911+ // We have to wait a while here, because there's a 10-second reconnect
5912+ // delay.
5913+ tds. downstairs1
5914+ . reboot_read_write_with_ports ( ds1_port, ds1_rport)
5915+ . await
5916+ . unwrap ( ) ;
5917+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_secs ( 15 ) ) . await ;
5918+ guest. deactivate ( ) . await . unwrap ( ) ;
5919+
5920+ // Reconnect with only ds1 running, then confirm that it received the
5921+ // writes. We'll come up in read-only mode so that we can connect with
5922+ // just a single Downstairs, to make sure the reads go to DS1.
5923+ tds. downstairs1 . reboot_read_only ( ) . await . unwrap ( ) ;
5924+ tds. downstairs2 . stop ( ) . await . unwrap ( ) ;
5925+ tds. downstairs3 . stop ( ) . await . unwrap ( ) ;
5926+ tds. crucible_opts . read_only = true ;
5927+ tds. crucible_opts . target [ 0 ] = tds. downstairs1 . address ( ) ;
5928+ let opts = tds. opts ( ) ;
5929+ let ( guest, io) = Guest :: new ( None ) ;
5930+ let _join_handle = up_main ( opts, 1 , None , io, None ) . unwrap ( ) ;
5931+ guest. activate ( ) . await . unwrap ( ) ;
5932+ let mut buf = Buffer :: new ( 2 , BLOCK_SIZE ) ;
5933+ guest. read ( BlockIndex ( 0 ) , & mut buf) . await . unwrap ( ) ;
5934+
5935+ assert_eq ! ( buf. to_vec( ) , vec![ 0x55 ; BLOCK_SIZE * 2 ] ) ;
5936+ }
5937+
5938+ #[ tokio:: test]
5939+ async fn min_quorum_live_repair ( ) {
5940+ const BLOCK_SIZE : usize = 512 ;
5941+
5942+ // Spin off three downstairs, build our Crucible struct.
5943+ let mut tds = TestDownstairsSet :: small ( false ) . await . unwrap ( ) ;
5944+
5945+ // Stop downstairs 1 before constructing the guest, so it won't be
5946+ // included and we'll do min-quorum reconciliation.
5947+ let ( port, rport) = tds. downstairs1 . stop ( ) . await . unwrap ( ) ;
5948+
5949+ // Start the guest and do a write to ds 2 and 3.
5950+ let ( guest, io) = Guest :: new ( None ) ;
5951+ let opts = tds. opts ( ) ;
5952+ let _join_handle = up_main ( opts, 1 , None , io, None ) . unwrap ( ) ;
5953+ guest. activate ( ) . await . unwrap ( ) ;
5954+ let res = guest
5955+ . write (
5956+ BlockIndex ( 0 ) ,
5957+ BytesMut :: from ( vec ! [ 0x55 ; BLOCK_SIZE * 2 ] . as_slice ( ) ) ,
5958+ )
5959+ . await ;
5960+ assert ! ( res. is_ok( ) ) ;
5961+
5962+ // Deactivate the guest, all without downstairs 1 participating
5963+ guest. deactivate ( ) . await . unwrap ( ) ;
5964+
5965+ // At this point, the data has been written to DS 2 and 3. We'll start
5966+ // up again with DS 1 and 2, so min-quorum should do reconciliation.
5967+
5968+ tds. downstairs1
5969+ . reboot_read_write_with_ports ( port, rport)
5970+ . await
5971+ . unwrap ( ) ;
5972+ tds. downstairs2 . stop ( ) . await . unwrap ( ) ;
5973+ guest. activate_with_gen ( 2 ) . await . unwrap ( ) ;
5974+
5975+ let mut buf = Buffer :: new ( 2 , BLOCK_SIZE ) ;
5976+ guest. read ( BlockIndex ( 0 ) , & mut buf) . await . unwrap ( ) ;
5977+
5978+ assert_eq ! ( buf. to_vec( ) , vec![ 0x55 ; BLOCK_SIZE * 2 ] ) ;
5979+ }
5980+
5981+ #[ tokio:: test]
5982+ async fn min_quorum_cancel ( ) {
5983+ // Spin off three downstairs, build our Crucible struct.
5984+ let mut tds = TestDownstairsSet :: small ( false ) . await . unwrap ( ) ;
5985+
5986+ // Stop downstairs 1 before constructing the guest, so it won't be
5987+ // included and we'll do min-quorum reconciliation.
5988+ let ( port, rport) = tds. downstairs1 . stop ( ) . await . unwrap ( ) ;
5989+
5990+ // Start the guest and do a write to ds 2 and 3.
5991+ let ( guest, io) = Guest :: new ( None ) ;
5992+ let opts = tds. opts ( ) ;
5993+ let _join_handle = up_main ( opts, 1 , None , io, None ) . unwrap ( ) ;
5994+ let s = tokio:: spawn ( async move { guest. activate ( ) . await } ) ;
5995+
5996+ // Get into our min-quorum wait, which is 500 ms
5997+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 100 ) ) . await ;
5998+
5999+ // Stop DS2
6000+ tds. downstairs2 . stop ( ) . await . unwrap ( ) ;
6001+
6002+ // Wait for the min-quorum timer to go off; it shouldn't panic!
6003+ tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) . await ;
6004+
6005+ // Restart DS1, we're now eligible for min-quorum negotiation again
6006+ tds. downstairs1
6007+ . reboot_read_write_with_ports ( port, rport)
6008+ . await
6009+ . unwrap ( ) ;
6010+
6011+ s. await . unwrap ( ) . unwrap ( )
6012+ }
58426013}
0 commit comments