From 9a58002b4656c3139369eea6ec258879957705e0 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 27 May 2024 17:35:12 -0300 Subject: [PATCH 1/6] Update PushManager and SyncManager --- .../split/engine/common/PushManagerImp.java | 24 ++++--- .../split/engine/common/SyncManagerImp.java | 1 - .../split/engine/common/PushManagerTest.java | 64 ++++++++++++++++++- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/client/src/main/java/io/split/engine/common/PushManagerImp.java b/client/src/main/java/io/split/engine/common/PushManagerImp.java index b6118efb6..27b535d0d 100644 --- a/client/src/main/java/io/split/engine/common/PushManagerImp.java +++ b/client/src/main/java/io/split/engine/common/PushManagerImp.java @@ -102,7 +102,7 @@ public synchronized void start() { return; } - stop(); + cleanUpResources(); if (response.isRetry()) { _pushStatusTracker.handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR); } else { @@ -113,16 +113,11 @@ public synchronized void start() { @Override public synchronized void stop() { _log.debug("Stopping PushManagerImp"); - _eventSourceClient.stop(); - stopWorkers(); - if (_nextTokenRefreshTask != null) { - _log.debug("Cancel nextTokenRefreshTask"); - _nextTokenRefreshTask.cancel(false); - } + cleanUpResources(); } @Override - public synchronized void scheduleConnectionReset() { + public void scheduleConnectionReset() { _log.debug(String.format("scheduleNextTokenRefresh in %s SECONDS", _expirationTime)); _nextTokenRefreshTask = _scheduledExecutorService.schedule(() -> { _log.debug("Starting scheduleNextTokenRefresh ..."); @@ -142,14 +137,23 @@ private boolean startSse(String token, String channels) { } @Override - public synchronized void startWorkers() { + public void startWorkers() { _featureFlagsWorker.start(); _segmentWorker.start(); } @Override - public synchronized void stopWorkers() { + public void stopWorkers() { _featureFlagsWorker.stop(); _segmentWorker.stop(); } + + private void cleanUpResources() { + _eventSourceClient.stop(); + stopWorkers(); + if (_nextTokenRefreshTask != null) { + _log.debug("Cancel nextTokenRefreshTask"); + _nextTokenRefreshTask.cancel(false); + } + } } \ No newline at end of file diff --git a/client/src/main/java/io/split/engine/common/SyncManagerImp.java b/client/src/main/java/io/split/engine/common/SyncManagerImp.java index 4f5ce3714..691b6def4 100644 --- a/client/src/main/java/io/split/engine/common/SyncManagerImp.java +++ b/client/src/main/java/io/split/engine/common/SyncManagerImp.java @@ -218,7 +218,6 @@ private void startPollingMode() { long howLong = _backoff.interval(); _log.info(String.format("Retryable error in streaming subsystem. Switching to polling and retrying in %d seconds", howLong)); _synchronizer.startPeriodicFetching(); - _pushManager.stopWorkers(); _pushManager.stop(); Thread.sleep(howLong * 1000); _incomingPushStatus.clear(); diff --git a/client/src/test/java/io/split/engine/common/PushManagerTest.java b/client/src/test/java/io/split/engine/common/PushManagerTest.java index 12664ad1d..33ce13416 100644 --- a/client/src/test/java/io/split/engine/common/PushManagerTest.java +++ b/client/src/test/java/io/split/engine/common/PushManagerTest.java @@ -22,9 +22,13 @@ public class PushManagerTest { private PushManager _pushManager; private PushStatusTracker _pushStatusTracker; private TelemetryStorage _telemetryStorage; + private FeatureFlagsWorker _featureFlagsWorker; + private SegmentsWorkerImp _segmentsWorkerImp; @Before public void setUp() { + _featureFlagsWorker = Mockito.mock(FeatureFlagsWorker.class); + _segmentsWorkerImp = Mockito.mock(SegmentsWorkerImp.class); _authApiClient = Mockito.mock(AuthApiClient.class); _eventSourceClient = Mockito.mock(EventSourceClient.class); _backoff = Mockito.mock(Backoff.class); @@ -32,8 +36,8 @@ public void setUp() { _telemetryStorage = new InMemoryTelemetryStorage(); _pushManager = new PushManagerImp(_authApiClient, _eventSourceClient, - Mockito.mock(FeatureFlagsWorker.class), - Mockito.mock(SegmentsWorkerImp.class), + _featureFlagsWorker, + _segmentsWorkerImp, _pushStatusTracker, _telemetryStorage, null); @@ -107,4 +111,60 @@ public void startWithPushDisabledAndRetryTrueShouldConnect() throws InterruptedE Thread.sleep(1500); Mockito.verify(_pushStatusTracker, Mockito.times(1)).handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR); } + + + @Test + public void startAndStop() throws InterruptedException { + AuthenticationResponse response = new AuthenticationResponse(true, "token-test", "channels-test", 1, false); + + Mockito.when(_authApiClient.Authenticate()) + .thenReturn(response); + + Mockito.when(_eventSourceClient.start(response.getChannels(), response.getToken())) + .thenReturn(true); + + _pushManager.start(); + + Mockito.verify(_authApiClient, Mockito.times(1)).Authenticate(); + Mockito.verify(_eventSourceClient, Mockito.times(1)).start(response.getChannels(), response.getToken()); + + Thread.sleep(1500); + + Mockito.verify(_pushStatusTracker, Mockito.times(0)).handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR); + Mockito.verify(_pushStatusTracker, Mockito.times(0)).forcePushDisable(); + Assert.assertEquals(1, _telemetryStorage.popStreamingEvents().size()); + + _pushManager.stop(); + + Mockito.verify(_eventSourceClient, Mockito.times(1)).stop(); + Mockito.verify(_featureFlagsWorker, Mockito.times(1)).stop(); + Mockito.verify(_segmentsWorkerImp, Mockito.times(1)).stop(); + } + + @Test + public void validateStartWorkers() { + _pushManager.startWorkers(); + Mockito.verify(_featureFlagsWorker, Mockito.times(1)).start(); + Mockito.verify(_segmentsWorkerImp, Mockito.times(1)).start(); + } + + @Test + public void validateScheduleConnectionReset() throws InterruptedException { + AuthenticationResponse response = new AuthenticationResponse(false, "token-test", "channels-test", 3, false); + + Mockito.when(_authApiClient.Authenticate()) + .thenReturn(response); + + Mockito.when(_eventSourceClient.start(response.getChannels(), response.getToken())) + .thenReturn(true); + + _pushManager.start(); + + _pushManager.scheduleConnectionReset(); + Thread.sleep(1000); + + Mockito.verify(_eventSourceClient, Mockito.times(3)).stop(); + Mockito.verify(_featureFlagsWorker, Mockito.times(3)).stop(); + Mockito.verify(_segmentsWorkerImp, Mockito.times(3)).stop(); + } } \ No newline at end of file From 68ff6988761c0faacf91d055b65db14ef5248814 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Wed, 5 Jun 2024 14:06:23 -0300 Subject: [PATCH 2/6] Update SSEClient to not use synchronized --- .../java/io/split/engine/sse/client/SSEClient.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/split/engine/sse/client/SSEClient.java b/client/src/main/java/io/split/engine/sse/client/SSEClient.java index 9c2024d99..2a87d8e96 100644 --- a/client/src/main/java/io/split/engine/sse/client/SSEClient.java +++ b/client/src/main/java/io/split/engine/sse/client/SSEClient.java @@ -59,8 +59,8 @@ private enum ConnectionState { private final AtomicReference _ongoingRequest = new AtomicReference<>(); private AtomicBoolean _forcedStop; private final RequestDecorator _requestDecorator; - private final TelemetryRuntimeProducer _telemetryRuntimeProducer; + private final AtomicBoolean openGuard = new AtomicBoolean(false); public SSEClient(Function eventCallback, Function statusCallback, @@ -77,12 +77,17 @@ public SSEClient(Function eventCallback, _requestDecorator = requestDecorator; } - public synchronized boolean open(URI uri) { + public boolean open(URI uri) { if (isOpen()) { _log.info("SSEClient already open."); return false; } + if (!openGuard.compareAndSet(false, true)) { + _log.debug("Open SSEClient already running"); + return false; + } + _statusCallback.apply(StatusMessage.INITIALIZATION_IN_PROGRESS); CountDownLatch signal = new CountDownLatch(1); @@ -99,6 +104,8 @@ public synchronized boolean open(URI uri) { } _log.info(e.getMessage()); return false; + } finally { + openGuard.set(false); } return isOpen(); } From 5fc3a313d823f10314129641f87b2e433d1211e9 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 7 Jun 2024 12:58:25 -0300 Subject: [PATCH 3/6] SDKS-8457 --- .../split/engine/common/PushManagerImp.java | 50 ++++++++----- .../io/split/engine/sse/client/SSEClient.java | 73 ++++++++++--------- 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/client/src/main/java/io/split/engine/common/PushManagerImp.java b/client/src/main/java/io/split/engine/common/PushManagerImp.java index 27b535d0d..f787bf66a 100644 --- a/client/src/main/java/io/split/engine/common/PushManagerImp.java +++ b/client/src/main/java/io/split/engine/common/PushManagerImp.java @@ -30,6 +30,8 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import static com.google.common.base.Preconditions.checkNotNull; import static io.split.client.utils.SplitExecutorFactory.buildSingleThreadScheduledExecutor; @@ -42,6 +44,8 @@ public class PushManagerImp implements PushManager { private final FeatureFlagsWorker _featureFlagsWorker; private final Worker _segmentWorker; private final PushStatusTracker _pushStatusTracker; + private static final Lock startLock = new ReentrantLock(); + private static final Lock stopLock = new ReentrantLock(); private Future _nextTokenRefreshTask; private final ScheduledExecutorService _scheduledExecutorService; @@ -92,28 +96,38 @@ public static PushManagerImp build(Synchronizer synchronizer, } @Override - public synchronized void start() { - AuthenticationResponse response = _authApiClient.Authenticate(); - _log.debug(String.format("Auth service response pushEnabled: %s", response.isPushEnabled())); - if (response.isPushEnabled() && startSse(response.getToken(), response.getChannels())) { - _expirationTime.set(response.getExpiration()); - _telemetryRuntimeProducer.recordStreamingEvents(new StreamingEvent(StreamEventsEnum.TOKEN_REFRESH.getType(), - response.getExpiration(), System.currentTimeMillis())); - return; - } - - cleanUpResources(); - if (response.isRetry()) { - _pushStatusTracker.handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR); - } else { - _pushStatusTracker.forcePushDisable(); + public void start() { + try { + startLock.lock(); + AuthenticationResponse response = _authApiClient.Authenticate(); + _log.debug(String.format("Auth service response pushEnabled: %s", response.isPushEnabled())); + if (response.isPushEnabled() && startSse(response.getToken(), response.getChannels())) { + _expirationTime.set(response.getExpiration()); + _telemetryRuntimeProducer.recordStreamingEvents(new StreamingEvent(StreamEventsEnum.TOKEN_REFRESH.getType(), + response.getExpiration(), System.currentTimeMillis())); + return; + } + + cleanUpResources(); + if (response.isRetry()) { + _pushStatusTracker.handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR); + } else { + _pushStatusTracker.forcePushDisable(); + } + } finally { + startLock.unlock(); } } @Override - public synchronized void stop() { - _log.debug("Stopping PushManagerImp"); - cleanUpResources(); + public void stop() { + try { + stopLock.lock(); + _log.debug("Stopping PushManagerImp"); + cleanUpResources(); + } finally { + stopLock.unlock(); + } } @Override diff --git a/client/src/main/java/io/split/engine/sse/client/SSEClient.java b/client/src/main/java/io/split/engine/sse/client/SSEClient.java index 2a87d8e96..608d7f598 100644 --- a/client/src/main/java/io/split/engine/sse/client/SSEClient.java +++ b/client/src/main/java/io/split/engine/sse/client/SSEClient.java @@ -6,7 +6,6 @@ import io.split.telemetry.domain.enums.StreamEventsEnum; import io.split.telemetry.storage.TelemetryRuntimeProducer; import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.slf4j.Logger; @@ -25,6 +24,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import static com.google.common.base.Preconditions.checkNotNull; @@ -49,6 +50,8 @@ private enum ConnectionState { private final static String SOCKET_CLOSED_MESSAGE = "Socket closed"; private final static String KEEP_ALIVE_PAYLOAD = ":keepalive\n"; private final static long CONNECT_TIMEOUT = 30000; + private static final Lock openLock = new ReentrantLock(); + private static final Lock closeLock = new ReentrantLock(); private static final Logger _log = LoggerFactory.getLogger(SSEClient.class); private final ExecutorService _connectionExecutor; private final CloseableHttpClient _client; @@ -60,7 +63,6 @@ private enum ConnectionState { private AtomicBoolean _forcedStop; private final RequestDecorator _requestDecorator; private final TelemetryRuntimeProducer _telemetryRuntimeProducer; - private final AtomicBoolean openGuard = new AtomicBoolean(false); public SSEClient(Function eventCallback, Function statusCallback, @@ -78,53 +80,56 @@ public SSEClient(Function eventCallback, } public boolean open(URI uri) { - if (isOpen()) { - _log.info("SSEClient already open."); - return false; - } - - if (!openGuard.compareAndSet(false, true)) { - _log.debug("Open SSEClient already running"); - return false; - } - - _statusCallback.apply(StatusMessage.INITIALIZATION_IN_PROGRESS); - - CountDownLatch signal = new CountDownLatch(1); - _connectionExecutor.submit(() -> connectAndLoop(uri, signal)); try { - if (!signal.await(CONNECT_TIMEOUT, TimeUnit.SECONDS)) { + openLock.lock(); + if (isOpen()) { + _log.info("SSEClient already open."); return false; } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - if(e.getMessage() == null){ - _log.info("The thread was interrupted while opening SSEClient"); + + _statusCallback.apply(StatusMessage.INITIALIZATION_IN_PROGRESS); + + CountDownLatch signal = new CountDownLatch(1); + _connectionExecutor.submit(() -> connectAndLoop(uri, signal)); + try { + if (!signal.await(CONNECT_TIMEOUT, TimeUnit.SECONDS)) { + return false; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + if(e.getMessage() == null){ + _log.info("The thread was interrupted while opening SSEClient"); + return false; + } + _log.info(e.getMessage()); return false; } - _log.info(e.getMessage()); - return false; + return isOpen(); } finally { - openGuard.set(false); + openLock.unlock(); } - return isOpen(); } public boolean isOpen() { return (ConnectionState.OPEN.equals(_state.get())); } - public synchronized void close() { - _forcedStop.set(true); - if (_state.compareAndSet(ConnectionState.OPEN, ConnectionState.CLOSED)) { - if (_ongoingResponse.get() != null) { - try { - _ongoingRequest.get().abort(); - _ongoingResponse.get().close(); - } catch (IOException e) { - _log.debug(String.format("SSEClient close forced: %s", e.getMessage())); + public void close() { + try { + closeLock.lock(); + _forcedStop.set(true); + if (_state.compareAndSet(ConnectionState.OPEN, ConnectionState.CLOSED)) { + if (_ongoingResponse.get() != null) { + try { + _ongoingRequest.get().abort(); + _ongoingResponse.get().close(); + } catch (IOException e) { + _log.debug(String.format("SSEClient close forced: %s", e.getMessage())); + } } } + } finally { + closeLock.unlock(); } } From 8fe21b1b49554cba5cca4e4530b41fd52e9cbb7b Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 7 Jun 2024 15:23:13 -0300 Subject: [PATCH 4/6] Update lock in PushManager and SSEClient. Update rc version --- client/pom.xml | 2 +- .../java/io/split/engine/common/PushManagerImp.java | 11 +++++------ .../java/io/split/engine/sse/client/SSEClient.java | 11 +++++------ pluggable-storage/pom.xml | 2 +- pom.xml | 2 +- redis-wrapper/pom.xml | 2 +- testing/pom.xml | 2 +- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index b48aa1100..bfa02a10f 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,7 +5,7 @@ io.split.client java-client-parent - 4.12.0 + 4.12.1-rc java-client jar diff --git a/client/src/main/java/io/split/engine/common/PushManagerImp.java b/client/src/main/java/io/split/engine/common/PushManagerImp.java index f787bf66a..3c15481fd 100644 --- a/client/src/main/java/io/split/engine/common/PushManagerImp.java +++ b/client/src/main/java/io/split/engine/common/PushManagerImp.java @@ -44,8 +44,7 @@ public class PushManagerImp implements PushManager { private final FeatureFlagsWorker _featureFlagsWorker; private final Worker _segmentWorker; private final PushStatusTracker _pushStatusTracker; - private static final Lock startLock = new ReentrantLock(); - private static final Lock stopLock = new ReentrantLock(); + private static final Lock lock = new ReentrantLock(); private Future _nextTokenRefreshTask; private final ScheduledExecutorService _scheduledExecutorService; @@ -98,7 +97,7 @@ public static PushManagerImp build(Synchronizer synchronizer, @Override public void start() { try { - startLock.lock(); + lock.lock(); AuthenticationResponse response = _authApiClient.Authenticate(); _log.debug(String.format("Auth service response pushEnabled: %s", response.isPushEnabled())); if (response.isPushEnabled() && startSse(response.getToken(), response.getChannels())) { @@ -115,18 +114,18 @@ public void start() { _pushStatusTracker.forcePushDisable(); } } finally { - startLock.unlock(); + lock.unlock(); } } @Override public void stop() { try { - stopLock.lock(); + lock.lock(); _log.debug("Stopping PushManagerImp"); cleanUpResources(); } finally { - stopLock.unlock(); + lock.unlock(); } } diff --git a/client/src/main/java/io/split/engine/sse/client/SSEClient.java b/client/src/main/java/io/split/engine/sse/client/SSEClient.java index 608d7f598..37cc6dac9 100644 --- a/client/src/main/java/io/split/engine/sse/client/SSEClient.java +++ b/client/src/main/java/io/split/engine/sse/client/SSEClient.java @@ -50,8 +50,7 @@ private enum ConnectionState { private final static String SOCKET_CLOSED_MESSAGE = "Socket closed"; private final static String KEEP_ALIVE_PAYLOAD = ":keepalive\n"; private final static long CONNECT_TIMEOUT = 30000; - private static final Lock openLock = new ReentrantLock(); - private static final Lock closeLock = new ReentrantLock(); + private static final Lock lock = new ReentrantLock(); private static final Logger _log = LoggerFactory.getLogger(SSEClient.class); private final ExecutorService _connectionExecutor; private final CloseableHttpClient _client; @@ -81,7 +80,7 @@ public SSEClient(Function eventCallback, public boolean open(URI uri) { try { - openLock.lock(); + lock.lock(); if (isOpen()) { _log.info("SSEClient already open."); return false; @@ -106,7 +105,7 @@ public boolean open(URI uri) { } return isOpen(); } finally { - openLock.unlock(); + lock.unlock(); } } @@ -116,7 +115,7 @@ public boolean isOpen() { public void close() { try { - closeLock.lock(); + lock.lock(); _forcedStop.set(true); if (_state.compareAndSet(ConnectionState.OPEN, ConnectionState.CLOSED)) { if (_ongoingResponse.get() != null) { @@ -129,7 +128,7 @@ public void close() { } } } finally { - closeLock.unlock(); + lock.unlock(); } } diff --git a/pluggable-storage/pom.xml b/pluggable-storage/pom.xml index a6f21cd7c..b02db86e3 100644 --- a/pluggable-storage/pom.xml +++ b/pluggable-storage/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.12.0 + 4.12.1-rc 2.1.0 diff --git a/pom.xml b/pom.xml index fc4f01fa5..6f1c36b2f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.split.client java-client-parent - 4.12.0 + 4.12.1-rc diff --git a/redis-wrapper/pom.xml b/redis-wrapper/pom.xml index 6f5231911..6c83e5f19 100644 --- a/redis-wrapper/pom.xml +++ b/redis-wrapper/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.12.0 + 4.12.1-rc redis-wrapper 3.1.0 diff --git a/testing/pom.xml b/testing/pom.xml index 0faeed8c3..d3b6f2562 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -5,7 +5,7 @@ io.split.client java-client-parent - 4.12.0 + 4.12.1-rc java-client-testing jar From d147e48979c1d07243667187050467eb8f632b52 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 7 Jun 2024 17:20:26 -0300 Subject: [PATCH 5/6] Update changelog --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 0d1f612ea..072fab1f9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +4.12.1 (Jun 10, 2024) +- Fixed deadlock for virtual thread in Push Manager and SSE Client. + 4.12.0 (May 15, 2024) - Added support for targeting rules based on semantic versions (https://semver.org/). - Added the logic to handle correctly when the SDK receives an unsupported Matcher type. From 950303b6e2ef9fd8c21508a77ac355e63342456d Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 10 Jun 2024 12:36:29 -0300 Subject: [PATCH 6/6] Update client version --- client/pom.xml | 2 +- pluggable-storage/pom.xml | 2 +- pom.xml | 2 +- redis-wrapper/pom.xml | 2 +- testing/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index bfa02a10f..b8d94bba7 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,7 +5,7 @@ io.split.client java-client-parent - 4.12.1-rc + 4.12.1 java-client jar diff --git a/pluggable-storage/pom.xml b/pluggable-storage/pom.xml index b02db86e3..f643564f9 100644 --- a/pluggable-storage/pom.xml +++ b/pluggable-storage/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.12.1-rc + 4.12.1 2.1.0 diff --git a/pom.xml b/pom.xml index 6f1c36b2f..3dc7d33f9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.split.client java-client-parent - 4.12.1-rc + 4.12.1 diff --git a/redis-wrapper/pom.xml b/redis-wrapper/pom.xml index 6c83e5f19..a8ce195f5 100644 --- a/redis-wrapper/pom.xml +++ b/redis-wrapper/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.12.1-rc + 4.12.1 redis-wrapper 3.1.0 diff --git a/testing/pom.xml b/testing/pom.xml index d3b6f2562..2240c94db 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -5,7 +5,7 @@ io.split.client java-client-parent - 4.12.1-rc + 4.12.1 java-client-testing jar