forked from lbovet/vertx-rest-storage
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #196 from swisspost/develop
PR for release
- Loading branch information
Showing
13 changed files
with
321 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
src/main/java/org/swisspush/reststorage/redis/DefaultRedisReadyProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package org.swisspush.reststorage.redis; | ||
|
||
import io.vertx.core.Future; | ||
import io.vertx.core.Vertx; | ||
import io.vertx.redis.client.RedisAPI; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
/** | ||
* Default implementation of the {@link RedisReadyProvider} based on the <code>INFO</code> command in Redis | ||
* | ||
* @author https://github.com/mcweba [Marc-Andre Weber] | ||
*/ | ||
public class DefaultRedisReadyProvider implements RedisReadyProvider { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(DefaultRedisReadyProvider.class); | ||
private static final String DELIMITER = ":"; | ||
private static final String LOADING = "loading"; | ||
final AtomicBoolean redisReady = new AtomicBoolean(true); | ||
final AtomicBoolean updateRedisReady = new AtomicBoolean(true); | ||
|
||
/** | ||
* Constructor defining the "ready-state" update interval | ||
* @param vertx | ||
* @param updateIntervalMs interval in ms how often to update the "ready-state" | ||
*/ | ||
public DefaultRedisReadyProvider(Vertx vertx, int updateIntervalMs) { | ||
vertx.setPeriodic(updateIntervalMs, l -> { | ||
updateRedisReady.set(true); | ||
}); | ||
} | ||
|
||
@Override | ||
public Future<Boolean> ready(RedisAPI redisAPI) { | ||
if(updateRedisReady.compareAndSet(true, false)){ | ||
return updateRedisReadyState(redisAPI); | ||
} | ||
return Future.succeededFuture(redisReady.get()); | ||
} | ||
|
||
/** | ||
* Call the <code>INFO</code> command in Redis with a constraint to persistence related information | ||
* | ||
* @param redisAPI | ||
* @return async boolean true when Redis is ready, otherwise false | ||
*/ | ||
public Future<Boolean> updateRedisReadyState(RedisAPI redisAPI) { | ||
return redisAPI.info(List.of("Persistence")).compose(response -> { | ||
boolean ready = getReadyStateFromResponse(response.toString()); | ||
redisReady.set(ready); | ||
return Future.succeededFuture(ready); | ||
}, throwable -> { | ||
log.error("Error reading redis info", throwable); | ||
redisReady.set(false); | ||
return Future.succeededFuture(false); | ||
}); | ||
} | ||
|
||
/** | ||
* Check the response having a <code>loading:0</code> entry. If so, Redis is ready. When the response contains a | ||
* <code>loading:1</code> entry or not related entry at all, we consider Redis to be not ready | ||
* | ||
* @param persistenceInfo the response from Redis _INFO_ command | ||
* @return boolean true when Redis is ready, otherwise false | ||
*/ | ||
private boolean getReadyStateFromResponse(String persistenceInfo) { | ||
byte loadingValue; | ||
try { | ||
Optional<String> loadingOpt = persistenceInfo | ||
.lines() | ||
.filter(source -> source.startsWith(LOADING + DELIMITER)) | ||
.findAny(); | ||
if (loadingOpt.isEmpty()) { | ||
log.warn("No 'loading' section received from redis. Unable to calculate ready state"); | ||
return false; | ||
} | ||
loadingValue = Byte.parseByte(loadingOpt.get().split(DELIMITER)[1]); | ||
if (loadingValue == 0) { | ||
return true; | ||
} | ||
|
||
} catch (NumberFormatException ex) { | ||
log.warn("Invalid 'loading' section received from redis. Unable to calculate ready state"); | ||
return false; | ||
} | ||
|
||
return false; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/org/swisspush/reststorage/redis/RedisReadyProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package org.swisspush.reststorage.redis; | ||
|
||
import io.vertx.core.Future; | ||
import io.vertx.redis.client.RedisAPI; | ||
|
||
/** | ||
* Provides the "ready state" of the Redis database. The connection to Redis may be already established, but Redis is not | ||
* yet ready to be used | ||
* | ||
* @author https://github.com/mcweba [Marc-Andre Weber] | ||
*/ | ||
public interface RedisReadyProvider { | ||
|
||
/** | ||
* Get the "ready state" of the Redis database. | ||
* | ||
* @param redisAPI API to access redis database | ||
* @return An async boolean true when Redis can be used. Returns async boolean false otherwise or in case of an error | ||
*/ | ||
Future<Boolean> ready(RedisAPI redisAPI); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/test/java/org/swisspush/reststorage/redis/DefaultRedisReadyProviderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package org.swisspush.reststorage.redis; | ||
|
||
import io.vertx.core.AsyncResult; | ||
import io.vertx.core.Future; | ||
import io.vertx.core.Vertx; | ||
import io.vertx.core.buffer.Buffer; | ||
import io.vertx.ext.unit.Async; | ||
import io.vertx.ext.unit.TestContext; | ||
import io.vertx.ext.unit.junit.VertxUnitRunner; | ||
import io.vertx.redis.client.RedisAPI; | ||
import io.vertx.redis.client.impl.types.BulkType; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.mockito.Mockito; | ||
import org.swisspush.reststorage.util.ResourcesUtils; | ||
|
||
import static org.mockito.Mockito.*; | ||
|
||
/** | ||
* Tests for the {@link DefaultRedisReadyProvider} class | ||
* | ||
* @author https://github.com/mcweba [Marc-Andre Weber] | ||
*/ | ||
@RunWith(VertxUnitRunner.class) | ||
public class DefaultRedisReadyProviderTest { | ||
|
||
private final String REDIS_INFO_LOADING = ResourcesUtils.loadResource("redis_info_persistance_loading_1", true); | ||
private final String REDIS_INFO_NOT_LOADING = ResourcesUtils.loadResource("redis_info_persistance_loading_0", true); | ||
|
||
private Vertx vertx; | ||
private RedisAPI redisAPI; | ||
private DefaultRedisReadyProvider readyProvider; | ||
|
||
@Before | ||
public void setUp() { | ||
this.vertx = Vertx.vertx(); | ||
redisAPI = Mockito.mock(RedisAPI.class); | ||
readyProvider = new DefaultRedisReadyProvider(vertx, 1000); | ||
} | ||
|
||
private void assertReadiness(TestContext testContext, AsyncResult<Boolean> event, Boolean expectedReadiness) { | ||
testContext.assertTrue(event.succeeded()); | ||
testContext.assertEquals(expectedReadiness, event.result()); | ||
} | ||
|
||
@Test | ||
public void testRedisReady(TestContext testContext) { | ||
Async async = testContext.async(); | ||
Mockito.when(redisAPI.info(any())).thenReturn(Future.succeededFuture(BulkType.create(Buffer.buffer(REDIS_INFO_NOT_LOADING), false))); | ||
|
||
readyProvider.ready(redisAPI).onComplete(event -> { | ||
assertReadiness(testContext, event, true); | ||
async.complete(); | ||
}); | ||
} | ||
|
||
@Test | ||
public void testRedisReadyMultipleCalls(TestContext testContext) { | ||
Async async = testContext.async(); | ||
Mockito.when(redisAPI.info(any())).thenReturn(Future.succeededFuture(BulkType.create(Buffer.buffer(REDIS_INFO_NOT_LOADING), false))); | ||
|
||
readyProvider.ready(redisAPI).onComplete(event -> { | ||
assertReadiness(testContext, event, true); | ||
readyProvider.ready(redisAPI).onComplete(event2 -> { | ||
assertReadiness(testContext, event2, true); | ||
async.complete(); | ||
}); | ||
}); | ||
|
||
verify(redisAPI, times(1)).info(any()); | ||
} | ||
|
||
@Test | ||
public void testRedisNotReady(TestContext testContext) { | ||
Async async = testContext.async(); | ||
Mockito.when(redisAPI.info(any())).thenReturn(Future.succeededFuture(BulkType.create(Buffer.buffer(REDIS_INFO_LOADING), false))); | ||
|
||
readyProvider.ready(redisAPI).onComplete(event -> { | ||
assertReadiness(testContext, event, false); | ||
async.complete(); | ||
}); | ||
} | ||
|
||
@Test | ||
public void testRedisNotReadyInvalidInfoResponse(TestContext testContext) { | ||
Async async = testContext.async(); | ||
Mockito.when(redisAPI.info(any())).thenReturn(Future.succeededFuture(BulkType.create(Buffer.buffer("some invalid info response"), false))); | ||
|
||
readyProvider.ready(redisAPI).onComplete(event -> { | ||
assertReadiness(testContext, event, false); | ||
async.complete(); | ||
}); | ||
} | ||
|
||
@Test | ||
public void testRedisNotReadyExceptionWhenAccessingRedisAPI(TestContext testContext) { | ||
Async async = testContext.async(); | ||
Mockito.when(redisAPI.info(any())).thenReturn(Future.failedFuture("Boooom")); | ||
|
||
readyProvider.ready(redisAPI).onComplete(event -> { | ||
assertReadiness(testContext, event, false); | ||
async.complete(); | ||
}); | ||
} | ||
} |
Oops, something went wrong.