Skip to content

Commit 2bc487e

Browse files
committed
JCBC-1647 BucketManager doesn't recognize ephemeral bucket ejection values
Motivation ---------- Allow users to create and manage ephemeral and memcached buckets. Modifications ------------- Deprecate EjectionPolicy in favor of EvictionPolicyType, which is the new standard name for this thing across all SDKs. Add the "noEviction" and "nruEviction" policies used by ephemeral buckets. Let server assign the default eviction policy for each bucket type to avoid the client getting it wrong. When posting the settings to the server, omit settings that aren't applicable to the bucket type, so the server doesn't return an error message. Customize Eviction/EjectionPolicy Jackson serialization using @JsonValue instead of @JsonProperty so we don't need to specify the alias redundantly. Add integration tests for creating different bucket types. Centralize the bucket cleanup code in an "AfterEach" method to ensure all the test buckets get dropped. Change-Id: Icaec7beda5bda4d33f6db1728e37c826b7000f77 Reviewed-on: http://review.couchbase.org/c/couchbase-jvm-clients/+/131387 Tested-by: Build Bot <[email protected]> Reviewed-by: Michael Reiche <[email protected]>
1 parent 02eb336 commit 2bc487e

File tree

5 files changed

+260
-30
lines changed

5 files changed

+260
-30
lines changed

java-client/src/integrationTest/java/com/couchbase/client/java/manager/bucket/BucketManagerIntegrationTest.java

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,21 @@
3131
import com.couchbase.client.test.IgnoreWhen;
3232
import com.couchbase.client.test.Util;
3333
import org.junit.jupiter.api.AfterAll;
34+
import org.junit.jupiter.api.AfterEach;
3435
import org.junit.jupiter.api.BeforeAll;
3536
import org.junit.jupiter.api.Test;
3637

3738
import java.time.Duration;
39+
import java.util.HashSet;
3840
import java.util.Map;
41+
import java.util.Set;
3942
import java.util.UUID;
4043

44+
import static com.couchbase.client.java.manager.bucket.BucketType.MEMCACHED;
45+
import static com.couchbase.client.java.manager.bucket.EvictionPolicyType.FULL;
46+
import static com.couchbase.client.java.manager.bucket.EvictionPolicyType.NO_EVICTION;
47+
import static com.couchbase.client.java.manager.bucket.EvictionPolicyType.NOT_RECENTLY_USED;
48+
import static com.couchbase.client.java.manager.bucket.EvictionPolicyType.VALUE_ONLY;
4149
import static com.couchbase.client.test.Util.waitUntilCondition;
4250
import static org.junit.jupiter.api.Assertions.assertEquals;
4351
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -53,6 +61,7 @@ class BucketManagerIntegrationTest extends JavaIntegrationTest {
5361
private static Cluster cluster;
5462
private static ClusterEnvironment environment;
5563
private static BucketManager buckets;
64+
private final Set<String> bucketsToDrop = new HashSet<>();
5665

5766
@BeforeAll
5867
static void setup() {
@@ -63,6 +72,21 @@ static void setup() {
6372
bucket.waitUntilReady(Duration.ofSeconds(5));
6473
}
6574

75+
@AfterEach
76+
void dropBuckets() {
77+
try {
78+
for (String bucketName : bucketsToDrop) {
79+
try {
80+
buckets.dropBucket(bucketName);
81+
} catch (BucketNotFoundException e) {
82+
// that's fine, the test probably dropped the bucket already
83+
}
84+
}
85+
} finally {
86+
bucketsToDrop.clear();
87+
}
88+
}
89+
6690
@AfterAll
6791
static void tearDown() {
6892
cluster.disconnect();
@@ -74,8 +98,7 @@ private void waitUntilHealthy(String bucket) {
7498
try {
7599
BucketSettings bkt = buckets.getBucket(bucket);
76100
return bkt.healthy();
77-
}
78-
catch (BucketNotFoundException err) {
101+
} catch (BucketNotFoundException err) {
79102
return false;
80103
}
81104
});
@@ -86,8 +109,7 @@ private void waitUntilDropped(String bucket) {
86109
try {
87110
buckets.getBucket(bucket);
88111
return false;
89-
}
90-
catch (BucketNotFoundException err) {
112+
} catch (BucketNotFoundException err) {
91113
return true;
92114
}
93115
});
@@ -118,11 +140,57 @@ void getAllBuckets() {
118140
}
119141
}
120142

143+
@Test
144+
void createEphemeralBucketWithDefaultEvictionPolicy() {
145+
String name = UUID.randomUUID().toString();
146+
createBucket(BucketSettings.create(name).bucketType(BucketType.EPHEMERAL));
147+
BucketSettings settings = buckets.getBucket(name);
148+
assertEquals(NO_EVICTION, settings.evictionPolicy());
149+
}
150+
151+
@Test
152+
void createEphemeralBucketWithNruEvictionPolicy() {
153+
String name = UUID.randomUUID().toString();
154+
createBucket(BucketSettings.create(name)
155+
.bucketType(BucketType.EPHEMERAL)
156+
.evictionPolicy(NOT_RECENTLY_USED));
157+
158+
BucketSettings settings = buckets.getBucket(name);
159+
assertEquals(NOT_RECENTLY_USED, settings.evictionPolicy());
160+
}
161+
162+
@Test
163+
void createCouchbaseBucketWithDefaultEvictionPolicy() {
164+
String name = UUID.randomUUID().toString();
165+
createBucket(BucketSettings.create(name)
166+
.bucketType(BucketType.COUCHBASE));
167+
BucketSettings settings = buckets.getBucket(name);
168+
assertEquals(VALUE_ONLY, settings.evictionPolicy());
169+
}
170+
171+
@Test
172+
void createCouchbaseBucketWithFullEvictionPolicy() {
173+
String name = UUID.randomUUID().toString();
174+
createBucket(BucketSettings.create(name)
175+
.bucketType(BucketType.COUCHBASE)
176+
.evictionPolicy(FULL));
177+
BucketSettings settings = buckets.getBucket(name);
178+
assertEquals(FULL, settings.evictionPolicy());
179+
}
180+
181+
@Test
182+
void createMemcachedBucket() {
183+
String name = UUID.randomUUID().toString();
184+
createBucket(BucketSettings.create(name).bucketType(MEMCACHED));
185+
BucketSettings settings = buckets.getBucket(name);
186+
assertEquals(MEMCACHED, settings.bucketType());
187+
}
188+
121189
@Test
122190
void createAndDropBucket() {
123191
String name = UUID.randomUUID().toString();
124192

125-
buckets.createBucket(BucketSettings.create(name));
193+
createBucket(BucketSettings.create(name));
126194
waitUntilHealthy(name);
127195
assertTrue(buckets.getAllBuckets().containsKey(name));
128196

@@ -136,7 +204,7 @@ void flushBucket() {
136204
Bucket bucket = cluster.bucket(config().bucketname());
137205
Collection collection = bucket.defaultCollection();
138206

139-
String id = UUID.randomUUID().toString();
207+
String id = UUID.randomUUID().toString();
140208
collection.upsert(id, "value");
141209
assertTrue(collection.exists(id).exists());
142210

@@ -146,21 +214,15 @@ void flushBucket() {
146214

147215
@Test
148216
void failIfBucketFlushDisabled() {
149-
String bucketName = UUID.randomUUID().toString();
150-
buckets.createBucket(BucketSettings.create(bucketName).flushEnabled(false));
151-
waitUntilCondition(() -> buckets.getAllBuckets().containsKey(bucketName));
217+
String bucketName = UUID.randomUUID().toString();
218+
createBucket(BucketSettings.create(bucketName).flushEnabled(false));
152219
assertThrows(BucketNotFlushableException.class, () -> buckets.flushBucket(bucketName));
153220
}
154221

155222
@Test
156223
void createShouldFailWhenPresent() {
157-
assertThrows(
158-
BucketExistsException.class,
159-
() -> {
160-
buckets.createBucket(BucketSettings.create(config().bucketname()));
161-
waitUntilHealthy(config().bucketname());
162-
}
163-
);
224+
assertThrows(BucketExistsException.class, () ->
225+
buckets.createBucket(BucketSettings.create(config().bucketname())));
164226
}
165227

166228
@Test
@@ -188,15 +250,11 @@ void updateShouldFailIfNotPresent() {
188250
void createWithMoreThanOneReplica() {
189251
String name = UUID.randomUUID().toString();
190252

191-
buckets.createBucket(BucketSettings.create(name).numReplicas(3));
253+
createBucket(BucketSettings.create(name).numReplicas(3));
192254
waitUntilHealthy(name);
193255

194256
BucketSettings bucket = buckets.getBucket(name);
195257
assertEquals(3, bucket.numReplicas());
196-
197-
buckets.dropBucket(name);
198-
waitUntilDropped(name);
199-
assertFalse(buckets.getAllBuckets().containsKey(name));
200258
}
201259

202260
/**
@@ -207,4 +265,9 @@ private void assertCreatedBucket(final BucketSettings settings) {
207265
assertTrue(settings.ramQuotaMB() > 0);
208266
}
209267

268+
private void createBucket(BucketSettings settings) {
269+
buckets.createBucket(settings);
270+
bucketsToDrop.add(settings.name());
271+
waitUntilHealthy(settings.name());
272+
}
210273
}

java-client/src/main/java/com/couchbase/client/java/manager/bucket/AsyncBucketManager.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import static com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpMethod.POST;
3939
import static com.couchbase.client.core.logging.RedactableArgument.redactMeta;
4040
import static com.couchbase.client.core.util.UrlQueryStringBuilder.urlEncode;
41+
import static com.couchbase.client.java.manager.bucket.BucketType.MEMCACHED;
4142
import static com.couchbase.client.java.manager.bucket.CreateBucketOptions.createBucketOptions;
4243
import static com.couchbase.client.java.manager.bucket.DropBucketOptions.dropBucketOptions;
4344
import static com.couchbase.client.java.manager.bucket.FlushBucketOptions.flushBucketOptions;
@@ -115,18 +116,25 @@ private UrlQueryStringBuilder convertSettingsToParams(final BucketSettings setti
115116
UrlQueryStringBuilder params = UrlQueryStringBuilder.createForUrlSafeNames();
116117

117118
params.add("ramQuotaMB", settings.ramQuotaMB());
118-
params.add("replicaNumber", settings.numReplicas());
119+
if (settings.bucketType() != MEMCACHED) {
120+
params.add("replicaNumber", settings.numReplicas());
121+
}
119122
params.add("flushEnabled", settings.flushEnabled() ? 1 : 0);
120123
params.add("maxTTL", settings.maxExpiry().getSeconds());
121-
params.add("evictionPolicy", settings.ejectionPolicy().alias());
124+
if (settings.evictionPolicy() != null) {
125+
// let server assign the default policy for this bucket type
126+
params.add("evictionPolicy", settings.evictionPolicy().alias());
127+
}
122128
params.add("compressionMode", settings.compressionMode().alias());
123129

124130
// The following values must not be changed on update
125131
if (!update) {
126132
params.add("name", settings.name());
127133
params.add("bucketType", settings.bucketType().alias());
128134
params.add("conflictResolutionType", settings.conflictResolutionType().alias());
129-
params.add("replicaIndex", settings.replicaIndexes() ? 1 : 0);
135+
if (settings.bucketType() != BucketType.EPHEMERAL) {
136+
params.add("replicaIndex", settings.replicaIndexes() ? 1 : 0);
137+
}
130138
}
131139

132140
return params;

java-client/src/main/java/com/couchbase/client/java/manager/bucket/BucketSettings.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class BucketSettings {
4242
private CompressionMode compressionMode = CompressionMode.PASSIVE;
4343
private BucketType bucketType = BucketType.COUCHBASE;
4444
private ConflictResolutionType conflictResolutionType = ConflictResolutionType.SEQUENCE_NUMBER;
45-
private EjectionPolicy evictionPolicy = EjectionPolicy.VALUE_ONLY;
45+
private EvictionPolicyType evictionPolicy = null; // null means default for the bucket type
4646
private boolean healthy = true;
4747

4848
public static BucketSettings create(final String name) {
@@ -81,7 +81,7 @@ public BucketSettings(
8181
@JsonProperty("compressionMode") final CompressionMode compressionMode,
8282
@JsonProperty("bucketType") final BucketType bucketType,
8383
@JsonProperty("conflictResolutionType") final ConflictResolutionType conflictResolutionType,
84-
@JsonProperty("evictionPolicy") final EjectionPolicy evictionPolicy
84+
@JsonProperty("evictionPolicy") final EvictionPolicyType evictionPolicy
8585
) {
8686
this.name = name;
8787
this.flushEnabled = controllers.containsKey("flush");
@@ -161,7 +161,15 @@ public ConflictResolutionType conflictResolutionType() {
161161
return conflictResolutionType;
162162
}
163163

164+
/**
165+
* @deprecated Please use {@link #evictionPolicy} instead.
166+
*/
167+
@Deprecated
164168
public EjectionPolicy ejectionPolicy() {
169+
return EjectionPolicy.of(evictionPolicy);
170+
}
171+
172+
public EvictionPolicyType evictionPolicy() {
165173
return evictionPolicy;
166174
}
167175

@@ -223,8 +231,21 @@ public BucketSettings conflictResolutionType(ConflictResolutionType conflictReso
223231
return this;
224232
}
225233

234+
/**
235+
* @param ejectionPolicy (nullable) policy to use, or null for default policy for the bucket type.
236+
* @deprecated Please use {@link #evictionPolicy} instead.
237+
*/
238+
@Deprecated
226239
public BucketSettings ejectionPolicy(EjectionPolicy ejectionPolicy) {
227-
this.evictionPolicy = requireNonNull(ejectionPolicy);
240+
this.evictionPolicy = ejectionPolicy == null ? null : ejectionPolicy.toEvictionPolicy();
241+
return this;
242+
}
243+
244+
/**
245+
* @param evictionPolicy (nullable) policy to use, or null for default policy for the bucket type.
246+
*/
247+
public BucketSettings evictionPolicy(EvictionPolicyType evictionPolicy) {
248+
this.evictionPolicy = evictionPolicy;
228249
return this;
229250
}
230251

java-client/src/main/java/com/couchbase/client/java/manager/bucket/EjectionPolicy.java

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,88 @@
1717
package com.couchbase.client.java.manager.bucket;
1818

1919
import com.couchbase.client.core.annotation.Stability;
20-
import com.couchbase.client.core.deps.com.fasterxml.jackson.annotation.JsonProperty;
20+
import com.couchbase.client.core.deps.com.fasterxml.jackson.annotation.JsonValue;
2121

22+
/**
23+
* @deprecated Please use {@link EvictionPolicyType} instead.
24+
*/
2225
@Stability.Volatile
26+
@Deprecated
2327
public enum EjectionPolicy {
24-
@JsonProperty("valueOnly") VALUE_ONLY("valueOnly"),
25-
@JsonProperty("fullEviction") FULL("fullEviction");
28+
/**
29+
* During ejection, only the value will be ejected (key and metadata will remain in memory).
30+
* <p>
31+
* Value Ejection needs more system memory, but provides better performance than Full Ejection.
32+
* <p>
33+
* This value is only valid for buckets of type {@link BucketType#COUCHBASE}.
34+
*/
35+
VALUE_ONLY("valueOnly"),
36+
37+
/**
38+
* During ejection, everything (including key, metadata, and value) will be ejected.
39+
* <p>
40+
* Full Ejection reduces the memory overhead requirement, at the cost of performance.
41+
* <p>
42+
* This value is only valid for buckets of type {@link BucketType#COUCHBASE}.
43+
*/
44+
FULL("fullEviction"),
45+
46+
/**
47+
* Couchbase Server keeps all data until explicitly deleted, but will reject
48+
* any new data if you reach the quota (dedicated memory) you set for your bucket.
49+
* <p>
50+
* This value is only valid for buckets of type {@link BucketType#EPHEMERAL}.
51+
*/
52+
NO_EVICTION("noEviction"),
53+
54+
/**
55+
* When the memory quota is reached, Couchbase Server ejects data that has
56+
* not been used recently.
57+
* <p>
58+
* This value is only valid for buckets of type {@link BucketType#EPHEMERAL}.
59+
*/
60+
NOT_RECENTLY_USED("nruEviction");
2661

2762
private final String alias;
2863

2964
EjectionPolicy(String alias) {
3065
this.alias = alias;
3166
}
3267

68+
@JsonValue
3369
public String alias() {
3470
return alias;
3571
}
3672

73+
@Stability.Internal
74+
static EjectionPolicy of(EvictionPolicyType evictionPolicy) {
75+
switch (evictionPolicy) {
76+
case FULL:
77+
return FULL;
78+
case VALUE_ONLY:
79+
return VALUE_ONLY;
80+
case NO_EVICTION:
81+
return NO_EVICTION;
82+
case NOT_RECENTLY_USED:
83+
return NOT_RECENTLY_USED;
84+
default:
85+
throw new RuntimeException("Unrecognized EvictionPolicyType alias: " + evictionPolicy.alias());
86+
}
87+
}
88+
89+
@Stability.Internal
90+
EvictionPolicyType toEvictionPolicy() {
91+
switch (this) {
92+
case FULL:
93+
return EvictionPolicyType.FULL;
94+
case VALUE_ONLY:
95+
return EvictionPolicyType.VALUE_ONLY;
96+
case NO_EVICTION:
97+
return EvictionPolicyType.NO_EVICTION;
98+
case NOT_RECENTLY_USED:
99+
return EvictionPolicyType.NOT_RECENTLY_USED;
100+
default:
101+
throw new RuntimeException("Don't know how to convert " + this + " to " + EvictionPolicyType.class.getSimpleName());
102+
}
103+
}
37104
}

0 commit comments

Comments
 (0)