Skip to content

Commit a879d0b

Browse files
authored
Use expiry(duration) with duration. (#1223)
Also adds test for exceptions thrown during events - with validator. Closes #1204.
1 parent 7c1167f commit a879d0b

File tree

7 files changed

+107
-23
lines changed

7 files changed

+107
-23
lines changed

src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ protected void writeInternal(final Object source, final CouchbaseDocument target
503503
final TreeMap<Integer, String> suffixes = new TreeMap<>();
504504
final TreeMap<Integer, String> idAttributes = new TreeMap<>();
505505

506-
target.setExpiration(entity.getExpiry());
506+
target.setExpiration((int)(entity.getExpiryDuration().getSeconds()));
507507

508508
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
509509
@Override

src/main/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntity.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.data.couchbase.core.mapping;
1818

19+
import java.time.Duration;
20+
import java.time.Instant;
1921
import java.util.Calendar;
2022
import java.util.TimeZone;
2123
import java.util.concurrent.TimeUnit;
@@ -101,8 +103,9 @@ public int getExpiry() {
101103
}
102104

103105
public static int getExpiry(Expiry annotation, Environment environment) {
104-
if (annotation == null)
106+
if (annotation == null) {
105107
return 0;
108+
}
106109

107110
int expiryValue = getExpiryValue(annotation, environment);
108111

@@ -123,6 +126,47 @@ public static int getExpiry(Expiry annotation, Environment environment) {
123126
}
124127
}
125128

129+
@Override
130+
public Duration getExpiryDuration() {
131+
return getExpiryDuration(AnnotatedElementUtils.findMergedAnnotation(getType(), Expiry.class), environment);
132+
}
133+
134+
private static Duration getExpiryDuration(Expiry annotation, Environment environment) {
135+
if (annotation == null) {
136+
return Duration.ofSeconds(0);
137+
}
138+
int expiryValue = getExpiryValue(annotation, environment);
139+
long secondsShift = annotation.expiryUnit().toSeconds(expiryValue);
140+
return Duration.ofSeconds(secondsShift);
141+
}
142+
143+
@Override
144+
public Instant getExpiryInstant() {
145+
return getExpiryInstant(AnnotatedElementUtils.findMergedAnnotation(getType(), Expiry.class), environment);
146+
}
147+
148+
private static Instant getExpiryInstant(Expiry annotation, Environment environment) {
149+
if (annotation == null) {
150+
return Instant.ofEpochSecond(0);
151+
}
152+
int expiryValue = getExpiryValue(annotation, environment);
153+
long secondsShift = annotation.expiryUnit().toSeconds(expiryValue);
154+
if(secondsShift == 0 ){
155+
return Instant.ofEpochSecond(0);
156+
}
157+
// we want it to be represented as a UNIX timestamp style, seconds since Epoch in UTC
158+
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
159+
if (annotation.expiryUnit() == TimeUnit.DAYS) {
160+
// makes sure we won't lose resolution
161+
cal.add(Calendar.DAY_OF_MONTH, expiryValue);
162+
} else {
163+
// use the shift in seconds since resolution should be smaller
164+
cal.add(Calendar.SECOND, (int) secondsShift);
165+
}
166+
return Instant.ofEpochSecond(cal.getTimeInMillis() / 1000); // note: Unix UTC time representation in int is okay
167+
// until year 2038
168+
}
169+
126170
private static int getExpiryValue(Expiry annotation, Environment environment) {
127171
int expiryValue = annotation.expiry();
128172
String expiryExpressionString = annotation.expiryExpression();

src/main/java/org/springframework/data/couchbase/core/mapping/CouchbasePersistentEntity.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import org.springframework.data.mapping.PersistentEntity;
2020

21+
import java.time.Duration;
22+
import java.time.Instant;
23+
2124
/**
2225
* Represents an entity that can be persisted which contains 0 or more properties.
2326
*
@@ -40,6 +43,26 @@ public interface CouchbasePersistentEntity<T> extends PersistentEntity<T, Couchb
4043
*/
4144
int getExpiry();
4245

46+
/**
47+
* Returns the expiration time of the entity.
48+
* <p/>
49+
* The Couchbase format for expiration time is: - for TTL < 31 days (<= 30 * 24 * 60 * 60): expressed as a TTL in
50+
* seconds - for TTL > 30 days: expressed as Unix UTC time of expiry (number of SECONDS since the Epoch)
51+
*
52+
* @return the expiration time Duration
53+
*/
54+
Duration getExpiryDuration();
55+
56+
/**
57+
* Returns the expiration time of the entity.
58+
* <p/>
59+
* The Couchbase format for expiration time is: - for TTL < 31 days (<= 30 * 24 * 60 * 60): expressed as a TTL in
60+
* seconds - for TTL > 30 days: expressed as Unix UTC time of expiry (number of SECONDS since the Epoch)
61+
*
62+
* @return the expiration time Instant
63+
*/
64+
Instant getExpiryInstant();
65+
4366
/**
4467
* Flag for using getAndTouch operations for reads, resetting the expiration (if one was set) when the entity is
4568
* directly read (eg. findOne, findById).

src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@
2727
import java.lang.reflect.Constructor;
2828
import java.lang.reflect.InvocationTargetException;
2929
import java.time.Duration;
30+
import java.time.Instant;
3031
import java.util.Arrays;
3132
import java.util.Collection;
3233
import java.util.HashSet;
34+
import java.util.LinkedList;
3335
import java.util.List;
3436
import java.util.Set;
3537
import java.util.UUID;
3638

37-
import com.couchbase.client.core.error.CouchbaseException;
3839
import org.junit.jupiter.api.BeforeEach;
3940
import org.junit.jupiter.api.Test;
4041
import org.springframework.dao.DataIntegrityViolationException;
@@ -47,7 +48,6 @@
4748
import org.springframework.data.couchbase.core.support.WithDurability;
4849
import org.springframework.data.couchbase.core.support.WithExpiry;
4950
import org.springframework.data.couchbase.domain.Address;
50-
import org.springframework.data.couchbase.domain.Course;
5151
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
5252
import org.springframework.data.couchbase.domain.PersonValue;
5353
import org.springframework.data.couchbase.domain.Submission;
@@ -60,6 +60,7 @@
6060
import org.springframework.data.couchbase.util.IgnoreWhen;
6161
import org.springframework.data.couchbase.util.JavaIntegrationTests;
6262

63+
import com.couchbase.client.core.error.CouchbaseException;
6364
import com.couchbase.client.java.kv.PersistTo;
6465
import com.couchbase.client.java.kv.ReplicateTo;
6566
import com.couchbase.client.java.query.QueryOptions;
@@ -84,6 +85,7 @@ public void beforeEach() {
8485
couchbaseTemplate.removeByQuery(UserAnnotated.class).all();
8586
couchbaseTemplate.removeByQuery(UserAnnotated2.class).all();
8687
couchbaseTemplate.removeByQuery(UserAnnotated3.class).all();
88+
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
8789
}
8890

8991
@Test
@@ -179,8 +181,8 @@ void findProjectingPath() {
179181
user.setSubmissions(
180182
Arrays.asList(new Submission(UUID.randomUUID().toString(), user.getId(), "tid", "status", 123)));
181183
couchbaseTemplate.upsertById(UserSubmission.class).one(user);
182-
assertThrows(CouchbaseException.class, () -> couchbaseTemplate.findByQuery(UserSubmission.class).project(new String[] { "address.street" })
183-
.withConsistency(QueryScanConsistency.REQUEST_PLUS).all());
184+
assertThrows(CouchbaseException.class, () -> couchbaseTemplate.findByQuery(UserSubmission.class)
185+
.project(new String[] { "address.street" }).withConsistency(QueryScanConsistency.REQUEST_PLUS).all());
184186

185187
List<UserSubmission> found = couchbaseTemplate.findByQuery(UserSubmission.class).project(new String[] { "address" })
186188
.withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
@@ -282,15 +284,23 @@ void withExpiryAndExpiryAnnotation()
282284
}
283285
// check that they are gone after a few seconds.
284286
sleepSecs(4);
287+
List<String> errorList = new LinkedList();
285288
for (User user : users) {
286289
User found = couchbaseTemplate.findById(user.getClass()).one(user.getId());
287-
if (found instanceof UserAnnotated3) {
288-
assertNotNull(found, "found should be non null as it was set to have no expiry");
290+
if (user.getId().endsWith(UserAnnotated3.class.getSimpleName())) {
291+
if (found == null) {
292+
errorList.add("\nfound should be non null as it was set to have no expiry " + user.getId() );
293+
}
289294
} else {
290-
assertNull(found, "found should have been null as document should be expired");
295+
if (found != null) {
296+
errorList.add("\nfound should have been null as document should be expired " + user.getId());
297+
}
291298
}
292299
}
293300

301+
if (!errorList.isEmpty()) {
302+
throw new RuntimeException(errorList.toString());
303+
}
294304
}
295305

296306
@Test

src/test/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplateKeyValueIntegrationTests.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.Arrays;
3131
import java.util.Collection;
3232
import java.util.HashSet;
33+
import java.util.LinkedList;
3334
import java.util.List;
3435
import java.util.Set;
3536
import java.util.UUID;
@@ -72,7 +73,7 @@ class ReactiveCouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationT
7273
@Override
7374
public void beforeEach() {
7475
super.beforeEach();
75-
List<RemoveResult> r1 = reactiveCouchbaseTemplate.removeByQuery(User.class).all().collectList().block();
76+
List<RemoveResult> r1 = reactiveCouchbaseTemplate.removeByQuery(User.class).all().collectList().block();
7677
List<RemoveResult> r2 = reactiveCouchbaseTemplate.removeByQuery(UserAnnotated.class).all().collectList().block();
7778
List<RemoveResult> r3 = reactiveCouchbaseTemplate.removeByQuery(UserAnnotated2.class).all().collectList().block();
7879
}
@@ -219,15 +220,23 @@ void withExpiryAndExpiryAnnotation()
219220
}
220221
// check that they are gone after a few seconds.
221222
sleepSecs(4);
223+
List<String> errorList = new LinkedList();
222224
for (User user : users) {
223225
User found = reactiveCouchbaseTemplate.findById(user.getClass()).one(user.getId()).block();
224-
if (found instanceof UserAnnotated3) {
225-
assertNotNull(found, "found should be non null as it was set to have no expiry");
226+
if (user.getId().endsWith(UserAnnotated3.class.getSimpleName())) {
227+
if (found == null) {
228+
errorList.add("\nfound should be non null as it was set to have no expiry " + user.getId());
229+
}
226230
} else {
227-
assertNull(found, "found should have been null as document should be expired");
231+
if (found != null) {
232+
errorList.add("\nfound should have been null as document should be expired " + user.getId());
233+
}
228234
}
229235
}
230236

237+
if (!errorList.isEmpty()) {
238+
throw new RuntimeException(errorList.toString());
239+
}
231240
}
232241

233242
@Test

src/test/java/org/springframework/data/couchbase/core/mapping/BasicCouchbasePersistentEntityTests.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,30 @@ void testNoExpiryByDefault() {
4343
CouchbasePersistentEntity<DefaultExpiry> entity = new BasicCouchbasePersistentEntity<>(
4444
ClassTypeInformation.from(DefaultExpiry.class));
4545

46-
assertThat(entity.getExpiry()).isEqualTo(0);
46+
assertThat(entity.getExpiryDuration().getSeconds()).isEqualTo(0);
4747
}
4848

4949
@Test
5050
void testDefaultExpiryUnitIsSeconds() {
5151
CouchbasePersistentEntity<DefaultExpiryUnit> entity = new BasicCouchbasePersistentEntity<>(
5252
ClassTypeInformation.from(DefaultExpiryUnit.class));
5353

54-
assertThat(entity.getExpiry()).isEqualTo(78);
54+
assertThat(entity.getExpiryDuration().getSeconds()).isEqualTo(78);
5555
}
5656

5757
@Test
5858
void testLargeExpiry30DaysStillInSeconds() {
5959
CouchbasePersistentEntity<LimitDaysExpiry> entityUnder = new BasicCouchbasePersistentEntity<>(
6060
ClassTypeInformation.from(LimitDaysExpiry.class));
61-
assertThat(entityUnder.getExpiry()).isEqualTo(30 * 24 * 60 * 60);
61+
assertThat(entityUnder.getExpiryDuration().getSeconds()).isEqualTo(30 * 24 * 60 * 60);
6262
}
6363

6464
@Test
6565
void testLargeExpiry31DaysIsConvertedToUnixUtcTime() {
6666
CouchbasePersistentEntity<OverLimitDaysExpiry> entityOver = new BasicCouchbasePersistentEntity<>(
6767
ClassTypeInformation.from(OverLimitDaysExpiry.class));
6868

69-
int expiryOver = entityOver.getExpiry();
69+
int expiryOver = (int)entityOver.getExpiryInstant().getEpochSecond();
7070
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
7171
expected.add(Calendar.DAY_OF_YEAR, 31);
7272

@@ -87,7 +87,7 @@ void testLargeExpiryExpression31DaysIsConvertedToUnixUtcTime() {
8787
ClassTypeInformation.from(OverLimitDaysExpiryExpression.class));
8888
entityOver.setEnvironment(environment);
8989

90-
int expiryOver = entityOver.getExpiry();
90+
int expiryOver = (int)entityOver.getExpiryInstant().getEpochSecond();
9191
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
9292
expected.add(Calendar.DAY_OF_YEAR, 31);
9393

@@ -107,7 +107,7 @@ void testLargeExpiry31DaysInSecondsIsConvertedToUnixUtcTime() {
107107
CouchbasePersistentEntity<OverLimitSecondsExpiry> entityOver = new BasicCouchbasePersistentEntity<>(
108108
ClassTypeInformation.from(OverLimitSecondsExpiry.class));
109109

110-
int expiryOver = entityOver.getExpiry();
110+
int expiryOver = (int)entityOver.getExpiryInstant().getEpochSecond();
111111
Calendar expected = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
112112
expected.add(Calendar.DAY_OF_YEAR, 31);
113113

src/test/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreatorTests.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Properties;
2424
import java.util.UUID;
2525

26+
import com.couchbase.client.java.query.QueryScanConsistency;
2627
import org.junit.jupiter.api.BeforeEach;
2728
import org.junit.jupiter.api.Test;
2829
import org.springframework.context.ApplicationContext;
@@ -98,11 +99,8 @@ queryMethod, converter, config().bucketname(), new SpelExpressionParser(),
9899

99100
Query query = creator.createQuery();
100101

101-
try {
102-
Thread.sleep(3000);
103-
} catch (Exception e) {}
104102
ExecutableFindByQuery q = (ExecutableFindByQuery) couchbaseTemplate
105-
.findByQuery(Airline.class).matching(query);
103+
.findByQuery(Airline.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).matching(query);
106104

107105
Optional<Airline> al = q.one();
108106
assertEquals(airline.toString(), al.get().toString());

0 commit comments

Comments
 (0)