Skip to content

Commit 874b816

Browse files
committed
nfs4: update state handler to use java.time.Clock
Motivation: The use of System.currentMillis() make it hard to implement time based tests. Thus use of java.time.Clock provides a more flexible time source that can instrumented during testing, if needed. Modification: Update NFSv4StateHandler and NFS4Client to use java.time.Clock. Update Cache and CacheElement to use Duration. Result: non-functional API change that provide self documenting API with better testing capabilities. Acked-by: Lea Morschel Acked-by: Paul Millar Target: master
1 parent 225dcf1 commit 874b816

File tree

12 files changed

+145
-101
lines changed

12 files changed

+145
-101
lines changed

API-changes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changes to NFS4J public API
22

3+
## 0.24
4+
5+
- update org.dcache.nfs.util.Cache and org.dcache.nfs.v4.NFSv4StateHandler to use java.time.Duration instead of a _long in millis_ to describe various amounts of time.
6+
- update org.dcache.nfs.v4.{NFSv4StateHandler,NFS4Client} to use java.time.Clock as a time source
7+
38
## 0.23
49

510
- dropped Stat#get/setFileId methods

benchmarks/src/main/java/org/dcache/nfs/benchmarks/CacheBenchmark.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.dcache.nfs.benchmarks;
22

3+
import java.time.Duration;
4+
import java.time.Instant;
35
import org.dcache.nfs.util.Cache;
46
import org.dcache.nfs.util.NopCacheEventListener;
57
import org.openjdk.jmh.annotations.Benchmark;
@@ -28,8 +30,8 @@ public static class CacheHolder {
2830

2931
@Setup
3032
public void setUp() {
31-
cache = new Cache<>("test cache", 64, Integer.MAX_VALUE,
32-
Integer.MAX_VALUE,
33+
cache = new Cache<>("test cache", 64, Duration.ofSeconds(Long.MAX_VALUE),
34+
Duration.ofSeconds(Long.MAX_VALUE),
3335
new NopCacheEventListener());
3436
cache.put("foo", "bar");
3537
}
@@ -42,7 +44,7 @@ public Cache<String, String> getCache() {
4244
@Benchmark
4345
@Threads(16)
4446
@Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
45-
public long cachePutRemoveBenchmark(CacheHolder cacheHolder) {
47+
public Instant cachePutRemoveBenchmark(CacheHolder cacheHolder) {
4648

4749
final var cache = cacheHolder.getCache();
4850
var key = "key";

core/src/main/java/org/dcache/nfs/util/Cache.java

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@
2020
package org.dcache.nfs.util;
2121

2222
import java.time.Clock;
23+
import java.time.Duration;
24+
import java.time.Instant;
2325
import java.util.ArrayList;
2426
import java.util.HashMap;
2527
import java.util.Iterator;
2628
import java.util.List;
2729
import java.util.Map;
2830
import java.util.MissingResourceException;
29-
import java.util.concurrent.atomic.AtomicLong;
31+
import java.util.concurrent.atomic.AtomicReference;
3032
import java.util.concurrent.locks.StampedLock;
3133

3234
import org.slf4j.Logger;
@@ -40,8 +42,8 @@
4042
*
4143
* Typical usage is:
4244
* <pre>
43-
* Cache&lt;String, String&gt; cache = new Cache&lt;&gt;("test cache", 10, TimeUnit.HOURS.toMillis(1),
44-
* TimeUnit.MINUTES.toMillis(5));
45+
* Cache&lt;String, String&gt; cache = new Cache&lt;&gt;("test cache", 10, Duration.ofHours(1),
46+
* Duration.ofMinutes(2);
4547
*
4648
* cache.put("key", "value");
4749
* String value = cache.get("key");
@@ -65,17 +67,16 @@ public class Cache<K, V> {
6567
private final String _name;
6668

6769
/**
68-
* Maximum allowed time, in milliseconds, that an object is allowed to be cached.
70+
* Maximum amount of time that an object is allowed to be cached.
6971
* After expiration of this time cache entry invalidated.
7072
*/
71-
72-
private final long _defaultEntryMaxLifeTime;
73+
private final Duration _defaultEntryMaxLifeTime;
7374

7475
/**
75-
* Time in milliseconds since last use of the object. After expiration of this
76+
* Time amount since last use of the object. After expiration of this
7677
* time cache entry is invalidated.
7778
*/
78-
private final long _defaultEntryIdleTime;
79+
private final Duration _defaultEntryIdleTime;
7980

8081
/**
8182
* Maximum number of entries in cache.
@@ -105,18 +106,18 @@ public class Cache<K, V> {
105106
/**
106107
* Last cleanup time
107108
*/
108-
private final AtomicLong _lastClean = new AtomicLong(System.currentTimeMillis());
109+
private final AtomicReference<Instant> _lastClean;
109110

110111
/**
111112
* Create new cache instance with default {@link CacheEventListener} and
112113
* default cleanup period.
113114
*
114115
* @param name Unique id for this cache.
115116
* @param size maximal number of elements.
116-
* @param entryLifeTime maximal time in milliseconds.
117-
* @param entryIdleTime maximal idle time in milliseconds.
117+
* @param entryLifeTime maximal time that an entry allowed to stay in the cache after creation.
118+
* @param entryIdleTime maximal time that an entry allowed to stay in the cache after last access.
118119
*/
119-
public Cache(String name, int size, long entryLifeTime, long entryIdleTime) {
120+
public Cache(String name, int size, Duration entryLifeTime, Duration entryIdleTime) {
120121
this(name, size, entryLifeTime, entryIdleTime,
121122
new NopCacheEventListener<K, V>());
122123
}
@@ -126,11 +127,11 @@ public Cache(String name, int size, long entryLifeTime, long entryIdleTime) {
126127
*
127128
* @param name Unique id for this cache.
128129
* @param size maximal number of elements.
129-
* @param entryLifeTime maximal time in milliseconds.
130-
* @param entryIdleTime maximal idle time in milliseconds.
130+
* @param entryLifeTime maximal time that an entry allowed to stay in the cache after creation.
131+
* @param entryIdleTime maximal time that an entry allowed to stay in the cache after last access.
131132
* @param eventListener {@link CacheEventListener}
132133
*/
133-
public Cache(final String name, int size, long entryLifeTime, long entryIdleTime,
134+
public Cache(final String name, int size, Duration entryLifeTime, Duration entryIdleTime,
134135
CacheEventListener<K, V> eventListener) {
135136
this(name, size, entryLifeTime, entryIdleTime, eventListener, Clock.systemDefaultZone());
136137
}
@@ -140,16 +141,16 @@ public Cache(final String name, int size, long entryLifeTime, long entryIdleTime
140141
*
141142
* @param name Unique id for this cache.
142143
* @param size maximal number of elements.
143-
* @param entryLifeTime maximal time in milliseconds.
144-
* @param entryIdleTime maximal idle time in milliseconds.
144+
* @param entryLifeTime maximal time that an entry allowed to stay in the cache after creation.
145+
* @param entryIdleTime maximal time that an entry allowed to stay in the cache after last access.
145146
* @param eventListener {@link CacheEventListener}
146147
* @param clock {@link Clock} to use
147148
* <code>timeValue</code> parameter.
148149
*/
149-
public Cache(final String name, int size, long entryLifeTime, long entryIdleTime,
150+
public Cache(final String name, int size, Duration entryLifeTime, Duration entryIdleTime,
150151
CacheEventListener<K, V> eventListener, Clock clock) {
151152

152-
checkArgument(entryLifeTime >= entryIdleTime, "Entry life time cant be smaller that idle time");
153+
checkArgument(entryLifeTime.compareTo(entryIdleTime) >= 0, "Entry life time cant be smaller that idle time");
153154

154155
_name = name;
155156
_size = size;
@@ -159,6 +160,7 @@ public Cache(final String name, int size, long entryLifeTime, long entryIdleTime
159160
_eventListener = eventListener;
160161
_mxBean = new CacheMXBeanImpl<>(this);
161162
_timeSource = clock;
163+
_lastClean = new AtomicReference<>(_timeSource.instant());
162164
}
163165

164166
/**
@@ -186,12 +188,12 @@ public void put(K k, V v) {
186188
*
187189
* @param k key associated with the value.
188190
* @param v value associated with key.
189-
* @param entryMaxLifeTime maximal life time in milliseconds.
190-
* @param entryIdleTime maximal idle time in milliseconds.
191+
* @param entryMaxLifeTime maximal time that an entry allowed to stay in the cache after creation.
192+
* @param entryIdleTime maximal time that an entry allowed to stay in the cache after last access.
191193
*
192194
* @throws MissingResourceException if Cache limit is reached.
193195
*/
194-
public void put(K k, V v, long entryMaxLifeTime, long entryIdleTime) {
196+
public void put(K k, V v, Duration entryMaxLifeTime, Duration entryIdleTime) {
195197
_log.debug("Adding new cache entry: key = [{}], value = [{}]", k, v);
196198

197199
long stamp = _accessLock.writeLock();
@@ -229,8 +231,7 @@ public V get(K k) {
229231
return null;
230232
}
231233

232-
long now = _timeSource.millis();
233-
valid = element.validAt(now);
234+
valid = element.validAt(_timeSource.instant());
234235
v = element.getObject();
235236

236237
if ( !valid ) {
@@ -278,7 +279,7 @@ public V remove(K k) {
278279
try {
279280
CacheElement<V> element = _storage.remove(k);
280281
if( element == null ) return null;
281-
valid = element.validAt(_timeSource.millis());
282+
valid = element.validAt(_timeSource.instant());
282283
v = element.getObject();
283284
} finally {
284285
_accessLock.unlock(stamp);
@@ -310,18 +311,18 @@ int size() {
310311
/**
311312
* Get maximal idle time until entry become unavailable.
312313
*
313-
* @return time in milliseconds.
314+
* @return default amount of an entry's maximal idle time.
314315
*/
315-
public long getEntryIdleTime() {
316+
public Duration getEntryIdleTime() {
316317
return _defaultEntryIdleTime;
317318
}
318319

319320
/**
320321
* Get maximal total time until entry become unavailable.
321322
*
322-
* @return time in milliseconds.
323+
* @return default amount of an entry's live time.
323324
*/
324-
public long getEntryLiveTime() {
325+
public Duration getEntryLiveTime() {
325326
return _defaultEntryMaxLifeTime;
326327
}
327328

@@ -348,7 +349,7 @@ public void cleanUp() {
348349

349350
long stamp = _accessLock.writeLock();
350351
try {
351-
long now = _timeSource.millis();
352+
Instant now = _timeSource.instant();
352353
Iterator<Map.Entry<K, CacheElement<V>>> entries = _storage.entrySet().iterator();
353354
while (entries.hasNext()) {
354355
Map.Entry<K, CacheElement<V>> entry = entries.next();
@@ -386,7 +387,7 @@ public List<CacheElement<V>> entries() {
386387
return entries;
387388
}
388389

389-
public long lastClean() {
390+
public Instant lastClean() {
390391
return _lastClean.get();
391392
}
392393
}

core/src/main/java/org/dcache/nfs/util/CacheElement.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton,
2+
* Copyright (c) 2009 - 2022 Deutsches Elektronen-Synchroton,
33
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
44
*
55
* This library is free software; you can redistribute it and/or modify
@@ -20,7 +20,8 @@
2020
package org.dcache.nfs.util;
2121

2222
import java.time.Clock;
23-
import java.util.Date;
23+
import java.time.Duration;
24+
import java.time.Instant;
2425

2526
/**
2627
* CacheElement wrapper.
@@ -31,33 +32,33 @@
3132
public class CacheElement<V> {
3233

3334
/**
34-
* Maximum allowed time, in milliseconds, that an object is allowed to be cached.
35+
* Maximum amount of time that the cache entry is allowed to be cached.
3536
* After expiration of this time cache entry invalidated.
3637
*/
37-
private final long _maxLifeTime;
38+
private final Duration _maxLifeTime;
3839
/**
39-
* Time in milliseconds since last use of the object. After expiration of this
40+
* Maximum amount of time that the cache entry is allowed to be cached since last use. After expiration of this
4041
* time cache entry is invalidated.
4142
*/
42-
private final long _idleTime;
43+
private final Duration _idleTime;
4344
/**
4445
* Element creation time.
4546
*/
46-
private final long _creationTime;
47+
private final Instant _creationTime;
4748
/**
4849
* Elements last access time.
4950
*/
50-
private long _lastAccessTime;
51+
private Instant _lastAccessTime;
5152
/**
5253
* internal object.
5354
*/
5455
private final V _inner;
5556

5657
private final Clock _clock;
5758

58-
CacheElement(V inner, Clock clock, long maxLifeTime, long idleTime) {
59+
CacheElement(V inner, Clock clock, Duration maxLifeTime, Duration idleTime) {
5960
_clock = clock;
60-
_creationTime = _clock.millis();
61+
_creationTime = _clock.instant();
6162
_lastAccessTime = _creationTime;
6263
_inner = inner;
6364
_maxLifeTime = maxLifeTime;
@@ -71,7 +72,7 @@ public class CacheElement<V> {
7172
* @return internal object.
7273
*/
7374
public V getObject() {
74-
_lastAccessTime = _clock.millis();
75+
_lastAccessTime = _clock.instant();
7576
return _inner;
7677
}
7778

@@ -86,21 +87,21 @@ public V peekObject() {
8687
}
8788

8889
/**
89-
* Check the entry's validity at the specified time.
90-
*
91-
* @param time in milliseconds since 1 of January 1970.
90+
* Check the entry's validity at the specified point in time.
9291
*
92+
* @param instant point in time at which entry validity is checked.
9393
* @return true if entry still valid and false otherwise.
9494
*/
95-
public boolean validAt(long time) {
96-
return time - _lastAccessTime < _idleTime && time - _creationTime < _maxLifeTime;
95+
public boolean validAt(Instant instant) {
96+
return Duration.between(_lastAccessTime, instant).compareTo(_idleTime) <= 0 &&
97+
Duration.between(_creationTime, instant).compareTo(_maxLifeTime) <= 0;
9798
}
9899

99100
@Override
100101
public String toString() {
101-
long now = _clock.millis();
102-
return String.format("Element: [%s], created: %s, last access: %s, life time %d, idle: %d, max idle: %d",
103-
_inner.toString(), new Date( _creationTime), new Date(_lastAccessTime),
104-
_maxLifeTime, now - _lastAccessTime, _idleTime);
102+
Instant now = _clock.instant();
103+
return String.format("Element: [%s], created: %s, last access: %s, life time %s, idle: %s, max idle: %s",
104+
_inner.toString(), _creationTime, _lastAccessTime,
105+
_maxLifeTime, Duration.between(_lastAccessTime, now), _idleTime);
105106
}
106107
}

core/src/main/java/org/dcache/nfs/util/CacheMXBeanImpl.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton,
2+
* Copyright (c) 2009 - 2022 Deutsches Elektronen-Synchroton,
33
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
44
*
55
* This library is free software; you can redistribute it and/or modify
@@ -19,6 +19,8 @@
1919
*/
2020
package org.dcache.nfs.util;
2121
import java.lang.management.ManagementFactory;
22+
import java.time.Duration;
23+
import java.time.Instant;
2224
import java.util.List;
2325
import javax.management.*;
2426
import org.slf4j.Logger;
@@ -75,17 +77,17 @@ public int getSize() {
7577

7678
@Override
7779
public long getEntryIdleTime() {
78-
return _cache.getEntryIdleTime();
80+
return _cache.getEntryIdleTime().toMillis();
7981
}
8082

8183
@Override
8284
public long getEntryLiveTime() {
83-
return _cache.getEntryLiveTime();
85+
return _cache.getEntryLiveTime().toMillis();
8486
}
8587

8688
@Override
8789
public long getLastClean() {
88-
return System.currentTimeMillis() - _cache.lastClean();
90+
return Duration.between(Instant.now(), _cache.lastClean()).toMillis();
8991
}
9092
}
9193

core/src/main/java/org/dcache/nfs/v4/DefaultClientCache.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 iterate GmbH
2+
* Copyright (c) 2020 - 2022 iterate GmbH
33
*
44
* This library is free software; you can redistribute it and/or modify
55
* it under the terms of the GNU Library General Public License as
@@ -18,18 +18,19 @@
1818
*/
1919
package org.dcache.nfs.v4;
2020

21+
import java.time.Duration;
22+
import java.time.Instant;
2123
import org.dcache.nfs.util.Cache;
2224
import org.dcache.nfs.util.CacheElement;
2325
import org.dcache.nfs.util.CacheEventListener;
2426
import org.dcache.nfs.v4.xdr.clientid4;
2527

26-
import java.util.concurrent.TimeUnit;
2728
import java.util.stream.Stream;
2829

2930
public class DefaultClientCache extends Cache<clientid4, NFS4Client> implements ClientCache {
30-
public DefaultClientCache(int leaseTime, CacheEventListener<clientid4, NFS4Client> eventListener) {
31-
super("NFSv41 clients", 5000, Long.MAX_VALUE,
32-
TimeUnit.SECONDS.toMillis(leaseTime * 2),
31+
public DefaultClientCache(Duration leaseTime, CacheEventListener<clientid4, NFS4Client> eventListener) {
32+
super("NFSv41 clients", 5000, Duration.ofSeconds(Long.MAX_VALUE),
33+
leaseTime.multipliedBy(2),
3334
eventListener);
3435
}
3536

0 commit comments

Comments
 (0)