public void saveValidityBackup() {
   final String backupValidityKey = _sessionIdFormat.createBackupKey(_validityKey);
   final int maxInactiveInterval = _session.getMaxInactiveInterval();
   // fix for #88, along with the change in session.getMemcachedExpirationTimeToSet
   final int expiration = maxInactiveInterval <= 0 ? 0 : maxInactiveInterval;
   _memcached.set(backupValidityKey, toMemcachedExpiration(expiration), _validityData);
 }
  static byte[] serializeSessionFields(final MemcachedBackupSession session) {

    final byte[] idData = serializeId(session.getIdInternal());

    final byte[] principalData =
        session.getPrincipal() != null ? serializePrincipal(session.getPrincipal()) : null;
    final int principalDataLength = principalData != null ? principalData.length : 0;

    final int sessionFieldsDataLength =
        2 // short value for the version
            // the following might change with other versions, refactoring needed then
            + 2 // short value that stores the dataLength
            + NUM_BYTES // bytes that store all session attributes but the id
            + 2 // short value that stores the idData length
            + idData.length // the number of bytes for the id
            + 2 // short value for the authType
            + 2 // short value that stores the principalData length
            + principalDataLength; // the number of bytes for the principal
    final byte[] data = new byte[sessionFieldsDataLength];

    int idx = 0;
    idx = encodeNum(CURRENT_VERSION, data, idx, 2);
    idx = encodeNum(sessionFieldsDataLength, data, idx, 2);
    idx = encodeNum(session.getCreationTimeInternal(), data, idx, 8);
    idx = encodeNum(session.getLastAccessedTimeInternal(), data, idx, 8);
    idx = encodeNum(session.getMaxInactiveInterval(), data, idx, 4);
    idx = encodeBoolean(session.isNewInternal(), data, idx);
    idx = encodeBoolean(session.isValidInternal(), data, idx);
    idx = encodeNum(session.getThisAccessedTimeInternal(), data, idx, 8);
    idx = encodeNum(session.getLastBackupTime(), data, idx, 8);
    idx = encodeNum(idData.length, data, idx, 2);
    idx = copy(idData, data, idx);
    idx = encodeNum(AuthType.valueOfValue(session.getAuthType()).getId(), data, idx, 2);
    idx = encodeNum(principalDataLength, data, idx, 2);
    copy(principalData, data, idx);

    return data;
  }
  /**
   * Is invoked after the backup of the session is initiated, it's represented by the provided
   * backupResult. The requestId is identifying the request.
   */
  protected void onAfterBackupSession(
      @Nonnull final MemcachedBackupSession session,
      final boolean backupWasForced,
      @Nonnull final Future<BackupResult> result,
      @Nonnull final String requestId,
      @Nonnull final BackupSessionService backupSessionService) {

    if (!_sessionIdFormat.isValid(session.getIdInternal())) {
      return;
    }

    try {

      final long start = System.currentTimeMillis();

      final int maxInactiveInterval = session.getMaxInactiveInterval();
      final byte[] validityData =
          encode(
              maxInactiveInterval,
              session.getLastAccessedTimeInternal(),
              session.getThisAccessedTimeInternal());
      final String validityKey =
          _sessionIdFormat.createValidityInfoKeyName(session.getIdInternal());
      // fix for #88, along with the change in session.getMemcachedExpirationTimeToSet
      final int expiration = maxInactiveInterval <= 0 ? 0 : maxInactiveInterval;
      final Future<Boolean> validityResult =
          _memcached.set(validityKey, toMemcachedExpiration(expiration), validityData);
      if (!_manager.isSessionBackupAsync()) {
        // TODO: together with session backup wait not longer than sessionBackupTimeout.
        // Details: Now/here we're waiting the whole session backup timeout, even if (perhaps) some
        // time
        // was spent before when waiting for session backup result.
        // For sync session backup it would be better to set both the session data and
        // validity info and afterwards wait for both results (but in sum no longer than
        // sessionBackupTimeout)
        validityResult.get(_manager.getSessionBackupTimeout(), TimeUnit.MILLISECONDS);
      }
      if (_log.isDebugEnabled()) {
        _log.debug("Stored session validity info for session " + session.getIdInternal());
      }

      /* The following task are performed outside of the request thread (includes waiting for the backup result):
       * - ping session if the backup was skipped (depends on the backup result)
       * - save secondary session backup if session was modified (backup not skipped)
       * - ping secondary session backup if the backup was skipped
       * - save secondary validity backup
       */
      final boolean pingSessionIfBackupWasSkipped = !backupWasForced;
      final boolean performAsyncTasks = pingSessionIfBackupWasSkipped || _storeSecondaryBackup;

      if (performAsyncTasks) {
        final Callable<?> backupSessionTask =
            new OnAfterBackupSessionTask(
                session,
                result,
                pingSessionIfBackupWasSkipped,
                backupSessionService,
                _storeSecondaryBackup,
                validityKey,
                validityData);
        _executor.submit(backupSessionTask);
      }

      _stats.registerSince(NON_STICKY_AFTER_BACKUP, start);

    } catch (final Throwable e) {
      _log.warn("An error occurred during onAfterBackupSession.", e);
    }
  }