code: @GuardedBy("readers")
      private String updateGroupStateIfNeeded() throws ReaderNotInReaderGroupException {
          if (atCheckpoint != null) {
              groupState.checkpoint(atCheckpoint, getPosition());
              releaseSegmentsIfNeeded();
          }
          String checkpoint = groupState.getCheckpoint();
          if (checkpoint != null) {
              log.info("{} at checkpoint {}", this, checkpoint);
              if (groupState.isCheckpointSilent(checkpoint)) {
                  // Checkpoint the reader immediately with the current position. Checkpoint Event is not generated.
                  groupState.checkpoint(checkpoint, getPosition());
                  if (atCheckpoint != null) {
                      //In case the silent checkpoint held up releasing segments
                      releaseSegmentsIfNeeded();
                      atCheckpoint = null;
                  }
                  return null;
              } else {
                  atCheckpoint = checkpoint;
                  return atCheckpoint;
              }
          } else {
              atCheckpoint = null;
              acquireSegmentsIfNeeded();
              return null;
          }
      }
docs: If the last call was a checkpoint updates the reader group state to indicate it has completed
  and releases segments.
  
  If a checkpoint is pending its identifier is returned. (The checkpoint will be considered
  complete when this is invoked again.)
  
  Otherwise it checks for any segments that need to be acquired.
  
  Segments can only be released on the next read call following a checkpoint because this is
  the only point we can be sure the caller has persisted their position, which is needed to be
  sure the segment is located in the position of one of the readers and not left out because it
  was moved while the checkpoint was occurring, while at the same time guaranteeing that
  another reader will not see events following the ones read by this reader until after they
  have been persisted.