Skip to content

Commit fbfbad3

Browse files
committed
add withPartitionedOrdinalMap into blob header and forcing restore to fail, if producer and dataset don't have the same partition setting;
1 parent feb60fc commit fbfbad3

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

hollow/src/main/java/com/netflix/hollow/api/producer/AbstractHollowProducer.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,22 @@ private HollowProducer.ReadState restore(
327327
readState = ReadStateHelper.newReadState(client.getCurrentVersionId(), client.getStateEngine());
328328
readStates = ReadStateHelper.restored(readState);
329329

330+
String datasetTag = readStates.current().getStateEngine()
331+
.getHeaderTag(HollowStateEngine.HEADER_TAG_PARTITIONED_ORDINAL_MAP);
332+
boolean datasetPartitionedOrdinalMap = Boolean.parseBoolean(datasetTag); // null → false
333+
if (partitionedOrdinalMap && !datasetPartitionedOrdinalMap) {
334+
throw new IllegalStateException(
335+
"trying to restore a HollowProducer with partitionedOrdinalMap enabled to a dataset " +
336+
"that is produced with this feature disabled. In order to enable partitionedOrdinalMap " +
337+
"feature, please start a new delta chain.");
338+
}
339+
if (!partitionedOrdinalMap && datasetPartitionedOrdinalMap) {
340+
throw new IllegalStateException(
341+
"trying to restore a HollowProducer with partitionedOrdinalMap disabled to a dataset " +
342+
"that is produced with this feature enabled. In order to disable partitionedOrdinalMap " +
343+
"feature, please start a new delta chain.");
344+
}
345+
330346
// Need to restore data to new ObjectMapper since can't restore to non empty Write State Engine
331347
Collection<HollowSchema> schemas = objectMapper.getStateEngine().getSchemas();
332348
HollowWriteStateEngine writeEngine = hashCodeFinder == null
@@ -591,6 +607,7 @@ private void updateHeaderTags(HollowWriteStateEngine writeEngine, long toVersion
591607
}
592608
long deltaChainVersionCounter = prevDeltaChainVersionCounter + 1;
593609
writeEngine.addHeaderTag(HEADER_TAG_DELTA_CHAIN_VERSION_COUNTER, String.valueOf(deltaChainVersionCounter));
610+
writeEngine.addHeaderTag(HollowStateEngine.HEADER_TAG_PARTITIONED_ORDINAL_MAP, Boolean.toString(partitionedOrdinalMap));
594611
}
595612

596613
void populate(

hollow/src/main/java/com/netflix/hollow/core/HollowStateEngine.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ public interface HollowStateEngine extends HollowDataset {
8080
*/
8181
String HEADER_TAG_DELTA_CHAIN_VERSION_COUNTER = "hollow.delta.chain.version.counter";
8282

83+
/**
84+
* A header tag indicating whether the producer used partitioned ordinal maps.
85+
*/
86+
String HEADER_TAG_PARTITIONED_ORDINAL_MAP = "hollow.partitioned.ordinal.map";
87+
8388
@Override
8489
List<HollowSchema> getSchemas();
8590

hollow/src/test/java/com/netflix/hollow/api/producer/HollowProducerTest.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,93 @@ private void restoreAndAssert(HollowProducer producer, long version, int size, i
511511
System.out.println("Asserted Correctness of version:" + version + "\n\n");
512512
}
513513

514+
@Test(expected = IllegalStateException.class)
515+
public void testRestorePartitionedOrdinalMapMismatch_producerEnabledDatasetDisabled() {
516+
InMemoryBlobStore blobStore = new InMemoryBlobStore();
517+
HollowInMemoryBlobStager blobStager = new HollowInMemoryBlobStager();
518+
519+
// Produce dataset with partitionedOrdinalMap disabled (default)
520+
HollowProducer producer1 = HollowProducer.withPublisher(blobStore)
521+
.withBlobStager(blobStager)
522+
.build();
523+
producer1.initializeDataModel(TestPojoV1.class);
524+
long version = producer1.runCycle(ws -> ws.add(new TestPojoV1(1, 1)));
525+
526+
// Restore with partitionedOrdinalMap enabled → should fail
527+
HollowProducer producer2 = HollowProducer.withPublisher(blobStore)
528+
.withBlobStager(blobStager)
529+
.withPartitionedOrdinalMap(true)
530+
.build();
531+
producer2.initializeDataModel(TestPojoV1.class);
532+
producer2.restore(version, blobStore);
533+
}
534+
535+
@Test(expected = IllegalStateException.class)
536+
public void testRestorePartitionedOrdinalMapMismatch_producerDisabledDatasetEnabled() {
537+
InMemoryBlobStore blobStore = new InMemoryBlobStore();
538+
HollowInMemoryBlobStager blobStager = new HollowInMemoryBlobStager();
539+
540+
// Produce dataset with partitionedOrdinalMap enabled
541+
HollowProducer producer1 = HollowProducer.withPublisher(blobStore)
542+
.withBlobStager(blobStager)
543+
.withPartitionedOrdinalMap(true)
544+
.build();
545+
producer1.initializeDataModel(TestPojoV1.class);
546+
long version = producer1.runCycle(ws -> ws.add(new TestPojoV1(1, 1)));
547+
548+
// Restore with partitionedOrdinalMap disabled → should fail
549+
HollowProducer producer2 = HollowProducer.withPublisher(blobStore)
550+
.withBlobStager(blobStager)
551+
.build();
552+
producer2.initializeDataModel(TestPojoV1.class);
553+
producer2.restore(version, blobStore);
554+
}
555+
556+
@Test
557+
public void testRestorePartitionedOrdinalMapMatch_bothEnabled() {
558+
InMemoryBlobStore blobStore = new InMemoryBlobStore();
559+
HollowInMemoryBlobStager blobStager = new HollowInMemoryBlobStager();
560+
561+
// Produce dataset with partitionedOrdinalMap enabled
562+
HollowProducer producer1 = HollowProducer.withPublisher(blobStore)
563+
.withBlobStager(blobStager)
564+
.withPartitionedOrdinalMap(true)
565+
.build();
566+
producer1.initializeDataModel(TestPojoV1.class);
567+
long version = producer1.runCycle(ws -> ws.add(new TestPojoV1(1, 1)));
568+
569+
// Restore with partitionedOrdinalMap enabled → should not throw IllegalStateException
570+
// from our validation check (partitioned ordinal map mismatch)
571+
HollowProducer producer2 = HollowProducer.withPublisher(blobStore)
572+
.withBlobStager(blobStager)
573+
.withPartitionedOrdinalMap(true)
574+
.build();
575+
producer2.initializeDataModel(TestPojoV1.class);
576+
producer2.restore(version, blobStore);
577+
}
578+
579+
@Test
580+
public void testRestorePartitionedOrdinalMapMatch_bothDisabled() {
581+
InMemoryBlobStore blobStore = new InMemoryBlobStore();
582+
HollowInMemoryBlobStager blobStager = new HollowInMemoryBlobStager();
583+
584+
// Produce dataset with partitionedOrdinalMap disabled (default)
585+
HollowProducer producer1 = HollowProducer.withPublisher(blobStore)
586+
.withBlobStager(blobStager)
587+
.build();
588+
producer1.initializeDataModel(TestPojoV1.class);
589+
long version = producer1.runCycle(ws -> ws.add(new TestPojoV1(1, 1)));
590+
591+
// Restore with partitionedOrdinalMap disabled → should succeed
592+
HollowProducer producer2 = HollowProducer.withPublisher(blobStore)
593+
.withBlobStager(blobStager)
594+
.build();
595+
producer2.initializeDataModel(TestPojoV1.class);
596+
HollowProducer.ReadState readState = producer2.restore(version, blobStore);
597+
Assert.assertNotNull(readState);
598+
assertEquals(version, readState.getVersion());
599+
}
600+
514601
@Test
515602
public void testReshardingAllTypes() {
516603
HollowInMemoryBlobStager blobStager = new HollowInMemoryBlobStager();

0 commit comments

Comments
 (0)