Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Primitive objectmapper nulls #712

Merged
merged 4 commits into from
Dec 16, 2024
Merged

Primitive objectmapper nulls #712

merged 4 commits into from
Dec 16, 2024

Conversation

vvstej
Copy link
Contributor

@vvstej vvstej commented Dec 12, 2024

No description provided.

@vvstej vvstej self-assigned this Dec 12, 2024
}

@Test
public void testWritePrimitivesPersistedWithSentinalValues() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this test empty on purpose?

@@ -116,6 +116,34 @@ public void testSimpleTypes() {
Assert.assertEquals(typeWithAllSimpleTypes, result);
}

@Test
public void testReadPrimitivesPersistedWithSentinalValues() {
Copy link
Contributor

@eduardoramirez eduardoramirez Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is good. in addition, we should add a test case for the specific case that raised this concern, where primitive fields are added in a new cycle but only partially populated. the simplest way i can think of doing this is to use a producer/consumer pair. here is an example of that (where in only show the int field but the rest are the same). it also tests both the HollowReadStateEngine and FlatRecord path

class Test {
  @Test
  public void testPrimitiveNullFieldsReceiveTheirSentinelValues() {
    HollowProducer producer =
        new HollowProducer.Builder<>()
            .withPublisher(new HollowFilesystemPublisher(Paths.get("/tmp/blob-cache/")))
            .build();

    producer.runCycle(
      state -> {
        state.add(new PojoV1(1, "The Matrix"));
      });

    // A new producer with a different schema
    HollowProducer producerWithSchemaChange =
      new HollowProducer.Builder<>()
        .withPublisher(new HollowFilesystemPublisher(Paths.get("/tmp/blob-cache/")))
        .build();
    producerWithSchemaChange.initializeDataModel(PojoV2.class);
    producerWithSchemaChange.restore(
      Long.MAX_VALUE, new HollowFilesystemBlobRetriever(Paths.get("/tmp/blob-cache/")));

    producerWithSchemaChange.runCycle(
      state -> {
        state.getStateEngine().addAllObjectsFromPreviousCycle();
        state.add(new PojoV2(2, "Speed Racer", 2008));
      });

    HollowConsumer consumer =
      HollowConsumer.withBlobRetriever(
          new HollowFilesystemBlobRetriever(Paths.get("/tmp/blob-cache/")))
        .build();
    consumer.triggerRefresh();

    HollowObjectMapper typeMapper = new HollowObjectMapper(new HollowWriteStateEngine());
    typeMapper.initializeTypeState(PojoV2.class);

    //////////////////////////
    // Test ReadState parsing 
    GenericHollowObject theMatrixObj = new GenericHollowObject(consumer.getStateEngine(), "Pojo", 0);
    PojoV2 theMatrixObjPojo = typeMapper.readHollowRecord(theMatrixObj);
    assertThat(theMatrixObjPojo.intField).isEqualTo(Integer.MIN_VALUE);
    // check other primitive fields...

    GenericHollowObject speedRacerObj = new GenericHollowObject(consumer.getStateEngine(), "Pojo", 1);
    PojoV2 speedRacerObjPojo = typeMapper.readHollowRecord(speedRacerObj);
    assertThat(speedRacerObjPojo.intField).isEqualTo(2008);
    // check other primitive fields...

    //////////////////////////
    // Test FlatRecord parsing 
    FlatRecordExtractor extractor = new FlatRecordExtractor(consumer.getStateEngine(), new FakeHollowSchemaIdentifierMapper(consumer.getStateEngine()));
    
    FlatRecord theMatrixFr = extractor.extract("Pojo", 0);
    PojoV2 theMatrixFrPojo = typeMapper.readFlat(theMatrixFr);
    assertThat(theMatrixFrPojo.intField).isEqualTo(Integer.MIN_VALUE);
    // check other primitive fields...
    
    FlatRecord speedRacerFr = extractor.extract("Pojo", 1);
    PojoV2 speedRacerFrPojo = typeMapper.readFlat(speedRacerFr);
    assertThat(speedRacerFrPojo.intField).isEqualTo(2008);
    // check other primitive fields...
  }

  @HollowTypeName(name = "Pojo")
  @HollowPrimaryKey(fields = {"id"})
  static class PojoV1 {
    final int id;
    final String strField;

    public PojoV1(int id, String strField) {
      this.id = id;
      this.strField = strField;
    }
  }

  @HollowTypeName(name = "Pojo")
  @HollowPrimaryKey(fields = {"id"})
  static class PojoV2 {
    final int id;
    final String strField;
    final int intField;
    // all other primitives

    public PojoV2(int id, String strField, int intField) {
      this.id = id;
      this.strField = strField;
      this.intField = intField;
    }
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this test, the underlying issue though seems to be covered by the serialization/deserialization tests though isnt it i.e interpretation of "empty" records generally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the test anyway

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as in, the sentinel values you're manually setting on the POJOs get converted into just the "null" value by ser? in that case, it's probably covered, not 100% sure for schema change-based scenarios. generally, i like adding these "reproduce"-like tests that are very close to the prod use case. it's not always possible but nice when it is. thanks for adding it!

@vvstej vvstej merged commit 3f11f46 into master Dec 16, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants