@Test
  public void testSendToWhileConcurrentlyModifyingMapContentsAndVerifyProperSerialization()
      throws IOException, ClassNotFoundException, InterruptedException, ExecutionException,
      TimeoutException {
    final int numEntries = 1000000;

    ClientUpdateMessageImpl.CqNameToOpHashMap originalCqNameToOpHashMap =
        new ClientUpdateMessageImpl.CqNameToOpHashMap(numEntries);
    ClientUpdateMessageImpl.CqNameToOpHashMap modifiedCqNameToOpHashMap =
        new ClientUpdateMessageImpl.CqNameToOpHashMap(numEntries);

    for (int i = 0; i < numEntries; ++i) {
      originalCqNameToOpHashMap.add(String.valueOf(i), MessageType.REQUEST);
      modifiedCqNameToOpHashMap.add(String.valueOf(i), MessageType.REQUEST);
    }

    CompletableFuture<Void> removeFromHashMapTask = CompletableFuture.runAsync(() -> {
      for (int i = 0; i < numEntries; ++i) {
        modifiedCqNameToOpHashMap.remove(String.valueOf(i), MessageType.REQUEST);
      }
    });

    CompletableFuture<Void> serializeReconstructHashMapTask = CompletableFuture.runAsync(() -> {
      try {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DataOutputStream outputStream = new DataOutputStream(byteArrayOutputStream);

        modifiedCqNameToOpHashMap.sendTo(outputStream);

        ByteArrayInputStream byteArrayInputStream =
            new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

        DataInputStream inputStream = new DataInputStream(byteArrayInputStream);

        byte typeByte = inputStream.readByte();
        int cqNamesSize = InternalDataSerializer.readArrayLength(inputStream);

        ClientUpdateMessageImpl.CqNameToOpHashMap reconstructedCqNameToOpHashMap =
            new ClientUpdateMessageImpl.CqNameToOpHashMap(cqNamesSize);

        for (int j = 0; j < cqNamesSize; j++) {
          String cqNamesKey = DataSerializer.readObject(inputStream);
          Integer cqNamesValue = DataSerializer.readObject(inputStream);
          reconstructedCqNameToOpHashMap.add(cqNamesKey, MessageType.valueOf(cqNamesValue));
        }

        // The reconstructed map should have some subset of the entries in the cqNameToOpHashMap,
        // but the specific contents will depend on timing with the removeFromHashMap task.
        MapDifference<String, MessageType> reconstructedVersusOriginalHashMapDifference =
            Maps.difference(reconstructedCqNameToOpHashMap, originalCqNameToOpHashMap);
        assertThat(reconstructedVersusOriginalHashMapDifference.entriesDiffering()).hasSize(0);
      } catch (Exception ex) {
        throw new RuntimeException("Failed to serialize/deserialize the CqNameToOpHashMap", ex);
      }
    });

    CompletableFuture.allOf(removeFromHashMapTask, serializeReconstructHashMapTask).get(1,
        TimeUnit.MINUTES);
  }
This test ensures that we can safely mutate the CqNameToOpHashMap while it is being
serialized in another thread. The use case for this is that we repopulate this map
during client registration, which can happen concurrently with a GII which causes
serialization to occur if this map is referenced by any of the client subscription
queues. This integration test does not exercise this full system level interaction,
but rather does the minimum necessary to prove that map mutation and serialization
can occur concurrently without any issues such as size mismatches or
ConcurrentModificationExceptions.
geode-core/src/integrationTest/java/org/apache/geode/internal/cache/tier/sockets/CqNameToOpHashMapIntegrationTest.java