private void handleException(final MemcachedBackupSession session, final Exception e) { //if ( _log.isWarnEnabled() ) { String msg = "Could not store session " + session.getId() + " in memcached."; if ( _force ) { msg += "\nNote that this session was relocated to this node because the" + " original node was not available."; } _log.warn(msg, e); //} _memcachedNodesManager.setNodeAvailableForSessionId(session.getId(), false); }
private void pingSessionBackup( @Nonnull final MemcachedBackupSession session ) throws InterruptedException { final String key = _sessionIdFormat.createBackupKey( session.getId() ); final Future<Boolean> touchResultFuture = _storage.add( key, 5, BYTE_1 ); try { final boolean touchResult = touchResultFuture.get(_manager.getOperationTimeout(), TimeUnit.MILLISECONDS); if ( touchResult ) { _log.warn( "The secondary backup for session " + session.getIdInternal() + " should be touched in memcached, but it seemed to be" + " not existing. Will store in memcached again." ); saveSessionBackup( session, key ); } else _log.debug( "The secondary session backup was ping'ed successfully." ); } catch ( final TimeoutException e ) { _log.warn( "The secondary backup for session " + session.getIdInternal() + " could not be completed within " + _manager.getOperationTimeout() + " millis, was cancelled now." ); } catch ( final ExecutionException e ) { _log.warn( "An exception occurred when trying to ping session " + session.getIdInternal(), e ); } }
public void saveSessionBackupFromResult( final BackupResult backupResult ) { final byte[] data = backupResult.getData(); if ( data != null ) { final String key = _sessionIdFormat.createBackupKey( _session.getId() ); _storage.set( key, toMemcachedExpiration(_session.getMemcachedExpirationTimeToSet()), data ); } else { _log.warn( "No data set for backupResultStatus " + backupResult.getStatus() + " for sessionId " + _session.getIdInternal() + ", skipping backup" + " of non-sticky session in secondary memcached." ); } }
private void addValidLoadedSession(final MemcachedBackupSession result) { // When the sessionId will be changed later in changeSessionIdOnTomcatFailover/handleSessionTakeOver // (due to a tomcat failover) we don't want to notify listeners via session.activate for the // old sessionId but do that later (in handleSessionTakeOver) // See also http://code.google.com/p/memcached-session-manager/issues/detail?id=92 String jvmRoute; final boolean sessionIdWillBeChanged = _sticky && ( jvmRoute = _manager.getJvmRoute() ) != null && !jvmRoute.equals( getSessionIdFormat().extractJvmRoute( result.getId() ) ); final boolean activate = !sessionIdWillBeChanged; addValidLoadedSession( result, activate ); }
@Override public Void call() throws Exception { final BackupResult backupResult = _result.get(); if ( _pingSessionIfBackupWasSkipped ) { if ( backupResult.getStatus() == BackupResultStatus.SKIPPED ) { pingSession( _session, _backupSessionService ); } } /* * For non-sticky sessions we store a backup of the session in a secondary memcached node (under a special key * that's resolved by the SuffixBasedNodeLocator), but only when we have more than 1 memcached node configured... */ if ( _storeSecondaryBackup ) { try { if ( _log.isDebugEnabled() ) { _log.debug( "Storing backup in secondary memcached for non-sticky session " + _session.getId() ); } if ( backupResult.getStatus() == BackupResultStatus.SKIPPED ) { pingSessionBackup( _session ); } else { saveSessionBackupFromResult( backupResult ); } saveValidityBackup(); } catch( final RuntimeException e ) { _log.info( "Could not store secondary backup of session " + _session.getIdInternal(), e ); } } return null; }
/** * Test for issue #105: Make memcached node optional for single-node setup * http://code.google.com/p/memcached-session-manager/issues/detail?id=105 */ @Test public void testBackupSessionFailureWithoutMemcachedNodeIdConfigured105() throws Exception { _service.setMemcachedNodes( "127.0.0.1:11211" ); _service.setSessionBackupAsync(false); _service.startInternal(new MemcachedStorageClient(_memcachedMock)); final MemcachedBackupSession session = createSession( _service ); session.access(); session.endAccess(); session.setAttribute( "foo", "bar" ); @SuppressWarnings( "unchecked" ) final OperationFuture<Boolean> futureMock = mock( OperationFuture.class ); when( futureMock.get( ) ).thenThrow(new ExecutionException(new RuntimeException("Simulated exception."))); when( futureMock.get( anyInt(), any( TimeUnit.class ) ) ).thenThrow(new ExecutionException(new RuntimeException("Simulated exception."))); when( _memcachedMock.set( eq( session.getId() ), anyInt(), any(), any( Transcoder.class ) ) ).thenReturn( futureMock ); final BackupResult backupResult = _service.backupSession( session.getIdInternal(), false, null ).get(); assertEquals(backupResult.getStatus(), BackupResultStatus.FAILURE); verify( _memcachedMock, times( 1 ) ).set( eq( session.getId() ), anyInt(), any(), any( Transcoder.class ) ); }
session.setAttribute( "foo", "bar" ); _service.backupSession( session.getIdInternal(), false, null ).get(); verify( _memcachedMock, times( 1 ) ).set( eq( session.getId() ), anyInt(), any(), any( Transcoder.class ) ); session.setAttribute( "bar", "baz" ); _service.backupSession( session.getIdInternal(), false, null ).get(); verify( _memcachedMock, times( 2 ) ).set( eq( session.getId() ), anyInt(), any(), any( Transcoder.class ) ); verify( _memcachedMock, times( 2 ) ).set( eq( session.getId() ), anyInt(), any(), any( Transcoder.class ) );
private void storeSessionInMemcached( final MemcachedBackupSession session, final byte[] data) throws InterruptedException, ExecutionException, TimeoutException { /* calculate the expiration time (instead of using just maxInactiveInterval), as * this is relevant for the update of the expiration time: if we would just use * maxInactiveInterval, the session would exist longer in memcached than it would * be valid in tomcat */ final int expirationTime = session.getMemcachedExpirationTimeToSet(); final long start = System.currentTimeMillis(); try { final Future<Boolean> future = _storage.set( _memcachedNodesManager.getStorageKeyFormat().format(session.getId()), toMemcachedExpiration(expirationTime), data ); if ( !_sessionBackupAsync ) { future.get( _sessionBackupTimeout, TimeUnit.MILLISECONDS ); session.setLastMemcachedExpirationTime( expirationTime ); session.setLastBackupTime( System.currentTimeMillis() ); } else { /* in async mode, we asume the session was stored successfully */ session.setLastMemcachedExpirationTime( expirationTime ); session.setLastBackupTime( System.currentTimeMillis() ); } } finally { _statistics.registerSince( MEMCACHED_UPDATE, start ); } }
_service.backupSession( session.getIdInternal(), false, "foo" ).get(); final String oldSessionId = session.getId(); _service.getManager().changeSessionId( session ); verify( _memcachedMock, times( 1 ) ).set( eq( session.getId() ), anyInt(), any(), any( Transcoder.class ) ); verify( _memcachedMock, times( 1 ) ).set( eq( new SessionIdFormat().createValidityInfoKeyName( session.getId() ) ), anyInt(), any(), any( Transcoder.class ) );
@Override public BackupResult call() throws Exception { final MemcachedBackupSession session3 = _service.findSession(session.getId()); assertSame(session3, session2); assertEquals(session3.getRefCount(), 2); // let the other thread proceed (or wait) barrier.await(); // and wait again so that the other thread can do some work barrier.await(); final Future<BackupResult> result = _service.backupSession(session.getId(), false, "unused"); _service.getTrackingHostValve().resetRequestThreadLocal(); assertEquals(result.get().getStatus(), BackupResultStatus.SUCCESS); // The session should be released now and no longer stored assertFalse(_service.getManager().getSessionsInternal().containsKey(session.getId())); // just some double checking on expectations... assertEquals(session2.getRefCount(), 0); return result.get(); }
final String newSessionId = _memcachedNodesManager.getNewSessionIdIfNodeFromSessionIdUnavailable( session.getId() ); if ( newSessionId != null ) { _log.debug( "Session needs to be relocated, setting new id on session..." ); addValidLoadedSession( backupSession, true ); _statistics.requestWithMemcachedFailover(); return backupSession.getId();
assertNotNull(session.getId()); Future<BackupResult> result = _service.backupSession(session.getId(), false, "unused"); assertFalse(_service.getManager().getSessionsInternal().containsKey(session.getId())); _service.getTrackingHostValve().storeRequestThreadLocal(requestMock); when(_memcachedMock.get(eq(session.getId()), any(Transcoder.class))).thenReturn(transcoderService.serialize(session)); final MemcachedBackupSession session2 = _service.findSession(session.getId()); assertTrue(session2.isLocked()); assertEquals(session2.getRefCount(), 1); result = _service.backupSession(session.getId(), false, null); _service.getTrackingHostValve().resetRequestThreadLocal(); assertEquals(result.get().getStatus(), BackupResultStatus.SKIPPED); assertTrue(_service.getManager().getSessionsInternal().containsKey(session.getId()));
_log.debug( "Created new session with id " + session.getId() ); if( _invalidSessionsCache.containsKey(session.getId()) ){ if ( _log.isDebugEnabled() ) { _log.debug( "Remove session id " + session.getId() + " from _invalidSessionsCache, marking new session valid" ); _invalidSessionsCache.remove(session.getId());
session.endAccess(); session.setAttribute( "foo", "bar" ); final String sessionId = session.getId();
/** * Store the provided session in memcached. * @param session the session to backup * @param data the serialized session data (session fields and session attributes). * @param attributesData just the serialized session attributes. * * @return the {@link BackupResultStatus} */ BackupResult doBackupSession( final MemcachedBackupSession session, final byte[] data, final byte[] attributesData ) throws InterruptedException { if ( _log.isDebugEnabled() ) { _log.debug( "Trying to store session in memcached: " + session.getId() ); } try { storeSessionInMemcached( session, data ); return new BackupResult( BackupResultStatus.SUCCESS, data, attributesData ); } catch (final ExecutionException e) { handleException(session, e); return new BackupResult(BackupResultStatus.FAILURE, data, null); } catch (final TimeoutException e) { handleException(session, e); return new BackupResult(BackupResultStatus.FAILURE, data, null); } }
public BackupResult call() throws Exception { if ( _log.isDebugEnabled() ) { _log.debug( "Starting for session id " + _session.getId() ); _log.debug( "Finished for session id " + _session.getId() + ", returning status " + result.getStatus() ); _log.warn("FAILED for session id " + _session.getId(), e); throw e;
final MemcachedBackupSession session = (MemcachedBackupSession) s; if ( _log.isDebugEnabled() ) { _log.debug( "Checking session " + session.getId() + ": " + "\n- isValid: " + session.isValidInternal() + "\n- isExpiring: " + session.isExpiring() + _backupSessionService.updateExpiration( session ); } catch ( final Throwable e ) { _log.info( "Could not update expiration in memcached for session " + session.getId(), e );