void add(final InternalCacheEvent event,
      final ClientUpdateMessageImpl clientMessage,
      final Conflatable conflatable,
      final Set<ClientProxyMembershipID> originalFilterClientIDs,
      final CacheClientNotifier cacheClientNotifier) {
    if (registeringProxyEventQueues.isEmpty()) {
      return;
    }

    if (originalFilterClientIDs.isEmpty()
        && event.getOperation().isEntry()
        && !(clientMessage instanceof ClientTombstoneMessage)) {
      EntryEventImpl entryEvent = (EntryEventImpl) event;
      entryEvent.exportNewValue(clientMessage);
    }

    ClientRegistrationEvent clientRegistrationEvent =
        new ClientRegistrationEvent(event, conflatable);

    for (final ClientRegistrationEventQueue registrationEventQueue : registeringProxyEventQueues) {
      registrationEventQueue.lockForPutting();
      try {
        // If this is an HAEventWrapper we need to increment the PutInProgress counter so
        // that the contents of the contents of the HAEventWrapper are preserved when the
        // event is drained and processed. See incrementPutInProgressCounter() and
        // decrementPutInProgressCounter() for more details.
        if (conflatable instanceof HAEventWrapper) {
          ((HAEventWrapper) conflatable).incrementPutInProgressCounter("client registration");
        }

        ClientProxyMembershipID clientProxyMembershipID =
            registrationEventQueue.getClientProxyMembershipID();

        // After taking out the lock, we need to determine if the client is still actually
        // registering since there is a small race where it may have finished registering
        // after we pulled the queue out of the registeringProxyEventQueues collection
        if (registeringProxyEventQueues.contains(registrationEventQueue)) {
          // If the event value is off-heap, copy it to heap so we are guaranteed the value
          // is available when we drain the registration queue
          copyOffHeapToHeapForRegistrationQueue(event);

          registrationEventQueue.add(clientRegistrationEvent);

          // Because this event will be processed and sent when it is drained out of the temporary
          // client registration queue, we do not need to send it to the client at this time.
          // We can prevent the event from being sent to the registering client at this time
          // by removing its client proxy membership ID from the filter clients collection,
          // if it exists.
          originalFilterClientIDs.remove(clientProxyMembershipID);
        } else {
          // The client is no longer registering, but we want to reprocess the filter info on
          // this event and potentially deliver the conflatable to handle the edge case where
          // the original client filter IDs generated in the "normal" put processing path did
          // not include the registering client because the filter info was not yet available.
          CacheClientProxy cacheClientProxy =
              cacheClientNotifier.getClientProxy(clientProxyMembershipID);

          processEventAndDeliverConflatable(cacheClientProxy, cacheClientNotifier, event,
              conflatable, originalFilterClientIDs);
        }
      } finally {
        registrationEventQueue.unlockForPutting();
      }
    }
  }
This class is responsible for queueing events for clients while they are registering. Once the
client is completely registered (filter info retrieved and GII complete), we will drain the
client's queued events and deliver them to the cache client proxy if necessary.
geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/ClientRegistrationEventQueueManager.java