private void addCuratedIndexEntriesToLogSegment(long sizeToMakeIndexEntriesFor, int expectedLogSegmentCount,
      boolean addTtlUpdates) throws StoreException {
    // First Index Segment
    // 1 PUT
    Offset firstJournalEntryAddedNow =
        addPutEntries(1, CuratedLogIndexState.PUT_RECORD_SIZE, Utils.Infinite_Time).get(0).getValue().getOffset();
    assertEquals("Incorrect log segment count", expectedLogSegmentCount, index.getLogSegmentCount());
    assertFalse("Expected nonempty index", index.isEmpty());

    // 2 more PUT
    addPutEntries(2, CuratedLogIndexState.PUT_RECORD_SIZE, Utils.Infinite_Time);
    // 2 PUT EXPIRED
    addPutEntries(2, CuratedLogIndexState.PUT_RECORD_SIZE, 0);
    // 5 entries were added - firstJournalEntryAddedNow should still be a part of the journal
    List<JournalEntry> entries = index.journal.getEntriesSince(firstJournalEntryAddedNow, true);
    assertEquals("There should have been exactly 5 entries returned from the journal", 5, entries.size());

    // Second Index Segment
    // 3 PUT
    addPutEntries(3, CuratedLogIndexState.PUT_RECORD_SIZE, Utils.Infinite_Time);
    // 1 DELETE for a PUT in the same index segment
    MockId idToDelete = getIdToDeleteFromIndexSegment(referenceIndex.lastKey(), false);
    addDeleteEntry(idToDelete);
    // Maintain one deleted key whose PUT record is in the same log segment. This is used for getBlobReadInfoTest() in IndexTest.
    // The intention is to ensure that hardDelete invokes getMessageInfo() in getDeletedBlobReadOptions() method and successfully
    // throw I/O exception with curated error message. Thus, we can verify disk I/O error can be correctly captured.
    deletedKeyWithPutInSameSegment =
        deletedKeyWithPutInSameSegment == null ? idToDelete : deletedKeyWithPutInSameSegment;
    // 1 DELETE for a PUT in the first index segment
    Offset firstIndexSegmentStartOffset = referenceIndex.lowerKey(referenceIndex.lastKey());
    idToDelete = getIdToDeleteFromIndexSegment(firstIndexSegmentStartOffset, false);
    addDeleteEntry(idToDelete);
    // 5 more entries (for a total of 10) were added - firstJournalEntryAddedNow should still be a part of the journal
    entries = index.journal.getEntriesSince(firstJournalEntryAddedNow, true);
    assertEquals("There should have been exactly 10 entries returned from the journal", 10, entries.size());

    // Third and Fourth Index Segment
    for (int seg = 0; seg < 2; seg++) {
      // 1 PUT
      addPutEntries(1, CuratedLogIndexState.PUT_RECORD_SIZE, Utils.Infinite_Time);
      // > 10 entries were added - firstJournalEntryAddedNow should no longer be a part of the journal
      assertNull("There should no entries returned from the journal",
          index.journal.getEntriesSince(firstJournalEntryAddedNow, true));
      // 2 PUT
      addPutEntries(2, CuratedLogIndexState.PUT_RECORD_SIZE, Utils.Infinite_Time);
      // 1 PUT for an expired blob
      addPutEntries(1, CuratedLogIndexState.PUT_RECORD_SIZE, 0);
      // 1 DELETE for the expired PUT
      MockId expiredId = logOrder.lastEntry().getValue().getFirst();
      addDeleteEntry(expiredId);
      deletedKeys.add(expiredId);
      expiredKeys.remove(expiredId);
    }

    Offset fourthIndexSegmentStartOffset = referenceIndex.lastKey();
    Offset thirdIndexSegmentStartOffset = referenceIndex.lowerKey(fourthIndexSegmentStartOffset);
    // Fifth Index Segment
    // 1 PUT entry
    addPutEntries(1, CuratedLogIndexState.PUT_RECORD_SIZE, Utils.Infinite_Time);
    // 1 DELETE for a PUT in each of the third and fourth segments
    idToDelete = getIdToDeleteFromIndexSegment(thirdIndexSegmentStartOffset, false);
    addDeleteEntry(idToDelete);
    idToDelete = getIdToDeleteFromIndexSegment(fourthIndexSegmentStartOffset, false);
    addDeleteEntry(idToDelete);
    // 1 DELETE for a PUT entry that does not exist (Make this DELETE carry expiration time to help test expired
    // deletes. Permanent DELETE will be mocked by subsequent DELETE record with TTL update flag.)
    MockId uniqueId = getUniqueId();
    addDeleteEntry(uniqueId, new MessageInfo(uniqueId, Integer.MAX_VALUE, time.milliseconds(), uniqueId.getAccountId(),
        uniqueId.getContainerId(), time.milliseconds()));
    // Temporary code while other classes are upgraded to understand TTL updates. Once other classes do, the
    // variable "addTtlUpdates" will be dropped
    if (addTtlUpdates) {
      long expiresAtMs = time.milliseconds() + TimeUnit.HOURS.toMillis(1);
      // 1 PUT with non zero expire time
      addPutEntries(1, PUT_RECORD_SIZE, expiresAtMs);
      List<MockId> idsToDelete = new ArrayList<>();

      // sixth index segment
      // 3 PUT with non zero expire time
      addPutEntries(3, PUT_RECORD_SIZE, expiresAtMs);
      // 2 TTL updates for a PUTs in the same segment
      MockId idToUpdate = getIdToTtlUpdateFromIndexSegment(referenceIndex.lastKey());
      makePermanent(idToUpdate, false);
      idsToDelete.add(idToUpdate);
      makePermanent(getIdToTtlUpdateFromIndexSegment(referenceIndex.lastKey()), false);

      Offset prevIndSegStartOff = referenceIndex.lastKey();
      // seventh index segment
      // 1 TTL update for a PUT in the previous segment
      idToUpdate = getIdToTtlUpdateFromIndexSegment(prevIndSegStartOff);
      makePermanent(idToUpdate, false);
      idsToDelete.add(idToUpdate);
      // 4 more PUTs with non zero expire time
      addPutEntries(4, PUT_RECORD_SIZE, expiresAtMs);

      prevIndSegStartOff = referenceIndex.lastKey();
      // eighth index segment
      // 1 PUT, TTL update, DELETE in the same segment
      addPutEntries(1, PUT_RECORD_SIZE, expiresAtMs);
      idToUpdate = getIdToTtlUpdateFromIndexSegment(referenceIndex.lastKey());
      makePermanent(idToUpdate, false);
      addDeleteEntry(idToUpdate);
      // 1 TTL update for a PUT in the prev segment
      makePermanent(getIdToTtlUpdateFromIndexSegment(prevIndSegStartOff), false);
      // 1 more PUT with non zero expire time
      addPutEntries(1, PUT_RECORD_SIZE, expiresAtMs);

      prevIndSegStartOff = referenceIndex.lastKey();
      // ninth index segment
      // 1 TTL update for a PUT in prev segment
      idToUpdate = getIdToTtlUpdateFromIndexSegment(prevIndSegStartOff);
      makePermanent(idToUpdate, false);
      idsToDelete.add(idToUpdate);
      // 3 DELETES from idsToDelete
      assertEquals("Number of IDs to delete has changed", 3, idsToDelete.size());
      for (MockId id : idsToDelete) {
        addDeleteEntry(id);
      }
      // 1 DELETE that has the TTL update flag set but has no corresponding TTL update or PUT entries
      uniqueId = getUniqueId();
      permanentDeleteTombstones.add(uniqueId);
      addDeleteEntry(uniqueId,
          new MessageInfo(uniqueId, Integer.MAX_VALUE, true, true, Utils.Infinite_Time, uniqueId.getAccountId(),
              uniqueId.getContainerId(), time.milliseconds()));

      // tenth index segment
      // 1 orphaned TTL update entry (with delete in the same index segment)
      // note that if a TTL update record exists for a key, then a PUT or DELETE record MUST exist
      idToDelete = getUniqueId();
      makePermanent(idToDelete, true);
      // 1 DELETE for the entry above
      addDeleteEntry(idToDelete);
      // 1 orphaned TTL update entry (with delete in another index segment)
      idToDelete = getUniqueId();
      makePermanent(idToDelete, true);
      // 2 PUT
      addPutEntries(2, PUT_RECORD_SIZE, expiresAtMs);

      // eleventh index segment
      // 1 DELETE for an orphaned TTL update entry above
      addDeleteEntry(idToDelete);
      // 2 PUTs with infinite TTL
      addPutEntries(2, PUT_RECORD_SIZE, Utils.Infinite_Time);
      // 2 PUTs with finite TTL
      addPutEntries(2, PUT_RECORD_SIZE, expiresAtMs);

      // twelfth index segment (setting up for cross log segment)
      // 3 PUTs
      addPutEntries(3, PUT_RECORD_SIZE, expiresAtMs);
      // 1 TTL update for a key in this segment
      makePermanent(getIdToTtlUpdateFromIndexSegment(referenceIndex.lastKey()), false);
    }
    // 1 PUT entry that spans the rest of the data in the segment
    long size = sizeToMakeIndexEntriesFor - index.getCurrentEndOffset().getOffset();
    addPutEntries(1, size, Utils.Infinite_Time);
    assertEquals("Incorrect log segment count", expectedLogSegmentCount, index.getLogSegmentCount());
  }
Adds some curated entries into the index in order to ensure a good mix for testing. For understanding the created
index, please read the source code which is annotated with comments.
@param sizeToMakeIndexEntriesFor the size to make index entries for.
@param expectedLogSegmentCount the number of log segments that are expected to assist after the addition of the
                               first entry and at the end of the addition of all entries.
@param addTtlUpdates if {@code true}, adds entries that update TTL.
@throws StoreException
ambry-store/src/test/java/com/github/ambry/store/CuratedLogIndexState.java