protected boolean saveInternal(Jedis jedis, Session session, boolean forceSave)
      throws IOException {
    Boolean error = true;

    try {
      log.trace("Saving session " + session + " into Redis");

      RedisSession redisSession = (RedisSession) session;

      if (log.isTraceEnabled()) {
        log.trace("Session Contents [" + redisSession.getId() + "]:");
        Enumeration en = redisSession.getAttributeNames();
        while (en.hasMoreElements()) {
          log.trace("  " + en.nextElement());
        }
      }

      byte[] binaryId = redisSession.getId().getBytes();

      Boolean isCurrentSessionPersisted;
      SessionSerializationMetadata sessionSerializationMetadata =
          currentSessionSerializationMetadata.get();
      byte[] originalSessionAttributesHash =
          sessionSerializationMetadata.getSessionAttributesHash();
      byte[] sessionAttributesHash = null;
      if (forceSave
          || redisSession.isDirty()
          || null == (isCurrentSessionPersisted = this.currentSessionIsPersisted.get())
          || !isCurrentSessionPersisted
          || !Arrays.equals(
              originalSessionAttributesHash,
              (sessionAttributesHash = serializer.attributesHashFrom(redisSession)))) {

        log.trace("Save was determined to be necessary");

        if (null == sessionAttributesHash) {
          sessionAttributesHash = serializer.attributesHashFrom(redisSession);
        }

        SessionSerializationMetadata updatedSerializationMetadata =
            new SessionSerializationMetadata();
        updatedSerializationMetadata.setSessionAttributesHash(sessionAttributesHash);

        jedis.set(binaryId, serializer.serializeFrom(redisSession, updatedSerializationMetadata));

        redisSession.resetDirtyTracking();
        currentSessionSerializationMetadata.set(updatedSerializationMetadata);
        currentSessionIsPersisted.set(true);
      } else {
        log.trace("Save was determined to be unnecessary");
      }

      log.trace(
          "Setting expire timeout on session ["
              + redisSession.getId()
              + "] to "
              + getMaxInactiveInterval());
      jedis.expire(binaryId, getMaxInactiveInterval());

      error = false;

      return error;
    } catch (IOException e) {
      log.error(e.getMessage());

      throw e;
    } finally {
      return error;
    }
  }