11package com .linkedin .venice .endToEnd ;
22
3+ import static com .linkedin .venice .ConfigKeys .CLIENT_SYSTEM_STORE_REPOSITORY_REFRESH_INTERVAL_SECONDS ;
34import static com .linkedin .venice .ConfigKeys .CONTROLLER_DEFERRED_VERSION_SWAP_SERVICE_ENABLED ;
45import static com .linkedin .venice .ConfigKeys .CONTROLLER_DEFERRED_VERSION_SWAP_SLEEP_MS ;
6+ import static com .linkedin .venice .ConfigKeys .DATA_BASE_PATH ;
7+ import static com .linkedin .venice .ConfigKeys .LOCAL_REGION_NAME ;
58import static com .linkedin .venice .utils .IntegrationTestPushUtils .createStoreForJob ;
69import static com .linkedin .venice .utils .TestWriteUtils .NAME_RECORD_V3_SCHEMA ;
710import static com .linkedin .venice .utils .TestWriteUtils .getTempDataDirectory ;
811import static com .linkedin .venice .vpj .VenicePushJobConstants .TARGETED_REGION_PUSH_WITH_DEFERRED_SWAP ;
12+ import static org .testng .Assert .assertNotNull ;
13+ import static org .testng .Assert .assertNull ;
914
15+ import com .linkedin .davinci .client .DaVinciClient ;
16+ import com .linkedin .davinci .client .DaVinciConfig ;
1017import com .linkedin .venice .controllerapi .ControllerClient ;
1118import com .linkedin .venice .controllerapi .UpdateStoreQueryParams ;
19+ import com .linkedin .venice .integration .utils .DaVinciTestContext ;
1220import com .linkedin .venice .integration .utils .ServiceFactory ;
21+ import com .linkedin .venice .integration .utils .VeniceClusterWrapper ;
22+ import com .linkedin .venice .integration .utils .VeniceMultiClusterWrapper ;
1323import com .linkedin .venice .integration .utils .VeniceMultiRegionClusterCreateOptions ;
1424import com .linkedin .venice .integration .utils .VeniceTwoLayerMultiRegionMultiClusterWrapper ;
1525import com .linkedin .venice .meta .StoreInfo ;
1929import com .linkedin .venice .utils .TestUtils ;
2030import com .linkedin .venice .utils .TestWriteUtils ;
2131import com .linkedin .venice .utils .Utils ;
32+ import com .linkedin .venice .utils .VeniceProperties ;
2233import java .io .File ;
2334import java .io .IOException ;
35+ import java .util .List ;
2436import java .util .Map ;
2537import java .util .Properties ;
2638import java .util .concurrent .TimeUnit ;
2739import java .util .stream .IntStream ;
40+ import org .apache .logging .log4j .LogManager ;
41+ import org .apache .logging .log4j .Logger ;
2842import org .testng .Assert ;
2943import org .testng .annotations .AfterClass ;
3044import org .testng .annotations .BeforeClass ;
@@ -40,6 +54,9 @@ public class TestDeferredVersionSwap {
4054 private static final String [] CLUSTER_NAMES =
4155 IntStream .range (0 , NUMBER_OF_CLUSTERS ).mapToObj (i -> "venice-cluster" + i ).toArray (String []::new );
4256
57+ private static final int TEST_TIMEOUT = 120_000 ;
58+ private static final Logger LOGGER = LogManager .getLogger (TestDeferredVersionSwap .class );
59+
4360 @ BeforeClass
4461 public void setUp () {
4562 Properties controllerProps = new Properties ();
@@ -68,7 +85,7 @@ public void cleanUp() {
6885 Utils .closeQuietlyWithErrorLogged (multiRegionMultiClusterWrapper );
6986 }
7087
71- @ Test
88+ @ Test ( timeOut = TEST_TIMEOUT )
7289 public void testDeferredVersionSwap () throws IOException {
7390 File inputDir = getTempDataDirectory ();
7491 TestWriteUtils .writeSimpleAvroFileWithStringToV3Schema (inputDir , 100 , 100 );
@@ -131,4 +148,140 @@ public void testDeferredVersionSwap() throws IOException {
131148 }
132149 }
133150
151+ @ Test (timeOut = TEST_TIMEOUT * 2 )
152+ public void testDvcDelayedIngestionWithTargetRegion () throws Exception {
153+ // Setup job properties
154+ UpdateStoreQueryParams storeParms = new UpdateStoreQueryParams ().setUnusedSchemaDeletionEnabled (true );
155+ storeParms .setTargetRegionSwapWaitTime (1 );
156+ storeParms .setTargetRegionSwap (TARGET_REGION );
157+ String parentControllerURLs = multiRegionMultiClusterWrapper .getControllerConnectString ();
158+ String keySchemaStr = "\" int\" " ;
159+ String valueSchemaStr = "\" int\" " ;
160+
161+ // Create store + start a normal push
162+ int keyCount = 100 ;
163+ File inputDir = getTempDataDirectory ();
164+ TestWriteUtils .writeSimpleAvroFileWithIntToIntSchema (inputDir , keyCount );
165+ String inputDirPath = "file://" + inputDir .getAbsolutePath ();
166+ String storeName = Utils .getUniqueString ("testDvcDelayedIngestionWithTargetRegion" );
167+ Properties props =
168+ IntegrationTestPushUtils .defaultVPJProps (multiRegionMultiClusterWrapper , inputDirPath , storeName );
169+ try (ControllerClient parentControllerClient = new ControllerClient (CLUSTER_NAMES [0 ], parentControllerURLs )) {
170+ createStoreForJob (CLUSTER_NAMES [0 ], keySchemaStr , valueSchemaStr , props , storeParms ).close ();
171+ LOGGER .info ("DvcDeferredVersionSwap starting normal push job" );
172+ TestWriteUtils .runPushJob ("Test push job" , props );
173+ TestUtils .waitForNonDeterministicPushCompletion (
174+ Version .composeKafkaTopic (storeName , 1 ),
175+ parentControllerClient ,
176+ 30 ,
177+ TimeUnit .SECONDS );
178+
179+ // Version should only be swapped in all regions
180+ TestUtils .waitForNonDeterministicAssertion (1 , TimeUnit .MINUTES , () -> {
181+ Map <String , Integer > coloVersions =
182+ parentControllerClient .getStore (storeName ).getStore ().getColoToCurrentVersions ();
183+
184+ coloVersions .forEach ((colo , version ) -> {
185+ Assert .assertEquals ((int ) version , 1 );
186+ });
187+ });
188+ }
189+
190+ // Create dvc client in target region
191+ List <VeniceMultiClusterWrapper > childDatacenters = multiRegionMultiClusterWrapper .getChildRegions ();
192+ VeniceClusterWrapper cluster1 = childDatacenters .get (0 ).getClusters ().get (CLUSTER_NAMES [0 ]);
193+ VeniceProperties backendConfig = DaVinciTestContext .getDaVinciPropertyBuilder (cluster1 .getZk ().getAddress ())
194+ .put (DATA_BASE_PATH , Utils .getTempDataDirectory ().getAbsolutePath ())
195+ .put (LOCAL_REGION_NAME , TARGET_REGION )
196+ .put (CLIENT_SYSTEM_STORE_REPOSITORY_REFRESH_INTERVAL_SECONDS , 1 )
197+ .build ();
198+ DaVinciClient <Object , Object > client1 =
199+ ServiceFactory .getGenericAvroDaVinciClient (storeName , cluster1 , new DaVinciConfig (), backendConfig );
200+ client1 .subscribeAll ().get ();
201+
202+ // Check that v1 is ingested
203+ for (int i = 1 ; i <= keyCount ; i ++) {
204+ assertNotNull (client1 .get (i ).get ());
205+ }
206+
207+ // Do another push with target region enabled
208+ int keyCount2 = 200 ;
209+ File inputDir2 = getTempDataDirectory ();
210+ String inputDirPath2 = "file://" + inputDir2 .getAbsolutePath ();
211+ TestWriteUtils .writeSimpleAvroFileWithIntToIntSchema (inputDir2 , keyCount2 );
212+ Properties props2 =
213+ IntegrationTestPushUtils .defaultVPJProps (multiRegionMultiClusterWrapper , inputDirPath2 , storeName );
214+ try (ControllerClient parentControllerClient = new ControllerClient (CLUSTER_NAMES [0 ], parentControllerURLs )) {
215+ props2 .put (TARGETED_REGION_PUSH_WITH_DEFERRED_SWAP , true );
216+ LOGGER .info ("DvcDeferredVersionSwap starting target region push job" );
217+ TestWriteUtils .runPushJob ("Test push job" , props2 );
218+ TestUtils .waitForNonDeterministicPushCompletion (
219+ Version .composeKafkaTopic (storeName , 2 ),
220+ parentControllerClient ,
221+ 30 ,
222+ TimeUnit .SECONDS );
223+
224+ // Version should only be swapped in the target region
225+ TestUtils .waitForNonDeterministicAssertion (1 , TimeUnit .MINUTES , () -> {
226+ Map <String , Integer > coloVersions =
227+ parentControllerClient .getStore (storeName ).getStore ().getColoToCurrentVersions ();
228+
229+ coloVersions .forEach ((colo , version ) -> {
230+ if (colo .equals (TARGET_REGION )) {
231+ Assert .assertEquals ((int ) version , 2 );
232+ } else {
233+ Assert .assertEquals ((int ) version , 1 );
234+ }
235+ });
236+ });
237+
238+ // Data should be automatically ingested in target region for dvc
239+ TestUtils .waitForNonDeterministicAssertion (30 , TimeUnit .SECONDS , () -> {
240+ for (int i = 101 ; i <= keyCount2 ; i ++) {
241+ assertNotNull (client1 .get (i ).get ());
242+ }
243+ });
244+
245+ // Close dvc client in target region
246+ client1 .close ();
247+
248+ // Create dvc client in non target region
249+ VeniceClusterWrapper cluster2 = childDatacenters .get (1 ).getClusters ().get (CLUSTER_NAMES [0 ]);
250+ VeniceProperties backendConfig2 = DaVinciTestContext .getDaVinciPropertyBuilder (cluster2 .getZk ().getAddress ())
251+ .put (DATA_BASE_PATH , Utils .getTempDataDirectory ().getAbsolutePath ())
252+ .put (LOCAL_REGION_NAME , "dc-1" )
253+ .put (CLIENT_SYSTEM_STORE_REPOSITORY_REFRESH_INTERVAL_SECONDS , 1 )
254+ .build ();
255+ DaVinciClient <Object , Object > client2 =
256+ ServiceFactory .getGenericAvroDaVinciClient (storeName , cluster2 , new DaVinciConfig (), backendConfig2 );
257+ client2 .subscribeAll ().get ();
258+
259+ // Check that v2 is not ingested
260+ TestUtils .waitForNonDeterministicAssertion (30 , TimeUnit .SECONDS , () -> {
261+ for (int i = 101 ; i <= keyCount2 ; i ++) {
262+ assertNull (client2 .get (i ).get ());
263+ }
264+ });
265+
266+ // Version should be swapped in all regions
267+ LOGGER .info ("DvcDeferredVersionSwap check that target region push is complete" );
268+ TestUtils .waitForNonDeterministicAssertion (1 , TimeUnit .MINUTES , () -> {
269+ Map <String , Integer > coloVersions =
270+ parentControllerClient .getStore (storeName ).getStore ().getColoToCurrentVersions ();
271+
272+ coloVersions .forEach ((colo , version ) -> {
273+ Assert .assertEquals ((int ) version , 2 );
274+ });
275+ });
276+
277+ // Check that v2 is ingested in dvc non target region
278+ TestUtils .waitForNonDeterministicAssertion (30 , TimeUnit .SECONDS , () -> {
279+ for (int i = 101 ; i <= keyCount2 ; i ++) {
280+ assertNotNull (client2 .get (i ).get ());
281+ }
282+ });
283+
284+ client2 .close ();
285+ }
286+ }
134287}
0 commit comments