From 3809b056dc0a5e241f3863777c4c4e5f80776fba Mon Sep 17 00:00:00 2001 From: Andrii Poruchynskyi Date: Mon, 6 Jul 2015 15:10:23 +0300 Subject: [PATCH] Fixed #228: Memcached failover in non-sticky mode --- .../web/msm/RequestTrackingHostValve.java | 9 +++++- .../web/msm/RequestTrackingHostValveTest.java | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/de/javakaffee/web/msm/RequestTrackingHostValve.java b/core/src/main/java/de/javakaffee/web/msm/RequestTrackingHostValve.java index 007a99a4..e4b81fb8 100644 --- a/core/src/main/java/de/javakaffee/web/msm/RequestTrackingHostValve.java +++ b/core/src/main/java/de/javakaffee/web/msm/RequestTrackingHostValve.java @@ -122,7 +122,7 @@ public void invoke( final Request request, final Response response ) throws IOEx final String requestId = getURIWithQueryString( request ); if(!_enabled.get() || !_msmContext.equals(request.getContext())) { getNext().invoke( request, response ); - } else if ( _ignorePattern != null && _ignorePattern.matcher( requestId ).matches() ) { + } else if ( _ignorePattern != null && _ignorePattern.matcher( requestId ).matches() && isPrimaryMemcachedNodeIsAvailable(request)) { if(_log.isDebugEnabled()) { _log.debug( ">>>>>> Ignoring: " + requestId + " (requestedSessionId "+ request.getRequestedSessionId() +") ==================" ); } @@ -280,4 +280,11 @@ private void logDebugResponseCookie( final Response response ) { } } + private boolean isPrimaryMemcachedNodeIsAvailable(Request request) { + MemcachedNodesManager memcachedNodesManager = _sessionBackupService.getMemcachedNodesManager(); + String primaryNodeIdentifier = memcachedNodesManager.getSessionIdFormat().extractMemcachedId(request.getRequestedSessionId()); + /* In case we have no primary node identifier then primary node is definitely not available for the session */ + return primaryNodeIdentifier == null ? false : memcachedNodesManager.isNodeAvailable(primaryNodeIdentifier); + } + } diff --git a/core/src/test/java/de/javakaffee/web/msm/RequestTrackingHostValveTest.java b/core/src/test/java/de/javakaffee/web/msm/RequestTrackingHostValveTest.java index d5827fa1..05d85afe 100644 --- a/core/src/test/java/de/javakaffee/web/msm/RequestTrackingHostValveTest.java +++ b/core/src/test/java/de/javakaffee/web/msm/RequestTrackingHostValveTest.java @@ -50,11 +50,18 @@ */ public abstract class RequestTrackingHostValveTest { + private static final String PRIMARY_NODE_IDENTIFIER = "n2"; + private static final boolean IS_PRIMARY_MEMCACHED_NODE_OPERATIONAL = true; + private static final String SESSION_ID = "9956461BA10903169EB0C4041DCA7BF1-n2"; + private static final String REQUEST_IGNORED = "de.javakaffee.msm.request.ignored"; + protected MemcachedSessionService _service; private RequestTrackingHostValve _sessionTrackerValve; private Valve _nextValve; private Request _request; private Response _response; + private MemcachedNodesManager _memcachedNodesManager; + private SessionIdFormat _sessionIdFormat; @BeforeMethod public void setUp() throws Exception { @@ -75,6 +82,8 @@ public void setUp() throws Exception { _nextValve = mock( Valve.class ); _sessionTrackerValve.setNext( _nextValve ); _sessionTrackerValve.setContainer(_hostContainer); + _memcachedNodesManager = mock( MemcachedNodesManager.class ); + _sessionIdFormat = mock( SessionIdFormat.class ); when(_request.getRequestURI()).thenReturn( "/someRequest"); when(_request.getMethod()).thenReturn("GET"); @@ -83,6 +92,11 @@ public void setUp() throws Exception { when(_request.getNote(eq(RequestTrackingHostValve.REQUEST_PROCESSED))).thenReturn(Boolean.TRUE); when(_request.getNote(eq(RequestTrackingHostValve.SESSION_ID_CHANGED))).thenReturn(Boolean.FALSE); + + when(_sessionIdFormat.extractMemcachedId(anyString())).thenReturn(PRIMARY_NODE_IDENTIFIER); + when(_service.getMemcachedNodesManager()).thenReturn(_memcachedNodesManager); + when(_memcachedNodesManager.isNodeAvailable(PRIMARY_NODE_IDENTIFIER)).thenReturn(IS_PRIMARY_MEMCACHED_NODE_OPERATIONAL); + when(_memcachedNodesManager.getSessionIdFormat()).thenReturn(_sessionIdFormat); } @Nonnull @@ -176,6 +190,24 @@ public final void testRequestFinishedShouldBeInvokedForIgnoredResources() throws verify( _service ).requestFinished( eq( "foo" ), anyString() ); } + @Test + public final void testRequestUrlIgnorePatternIsSkippedIfPrimaryMemcachedNodeIsDown() throws IOException, ServletException { + when(_memcachedNodesManager.isNodeAvailable(PRIMARY_NODE_IDENTIFIER)).thenReturn(false); + when(_request.getRequestedSessionId()).thenReturn(SESSION_ID); + _sessionTrackerValve.invoke( _request, _response ); + + verify( _request ).setNote(RequestTrackingHostValve.REQUEST_PROCESS, Boolean.TRUE); + } + + @Test + public final void testRequestUrlIgnorePatternIsUsedIfPrimaryMemcachedNodeIsOperational() throws IOException, ServletException { + when(_memcachedNodesManager.isNodeAvailable(PRIMARY_NODE_IDENTIFIER)).thenReturn(true); + when(_request.getRequestURI()).thenReturn("/pixel.gif"); + _sessionTrackerValve.invoke( _request, _response ); + + verify(_request).setNote(REQUEST_IGNORED, Boolean.TRUE); + } + protected abstract void setupGetResponseSetCookieHeadersExpectations(Response response, String[] result); @Nonnull