1919 */
2020package org .neo4j .gds .api ;
2121
22+ import com .github .benmanes .caffeine .cache .Cache ;
23+ import com .github .benmanes .caffeine .cache .Caffeine ;
24+ import com .github .benmanes .caffeine .cache .Scheduler ;
25+ import com .github .benmanes .caffeine .cache .Ticker ;
2226import org .neo4j .gds .api .nodeproperties .ValueType ;
2327import org .neo4j .gds .api .properties .nodes .NodePropertyValues ;
28+ import org .neo4j .gds .core .concurrency .ExecutorServiceUtil ;
29+ import org .neo4j .gds .core .utils .ClockService ;
2430
2531import java .util .Collection ;
26- import java .util .HashMap ;
2732import java .util .List ;
28- import java .util .Map ;
33+ import java .util .concurrent .ScheduledExecutorService ;
34+ import java .util .concurrent .TimeUnit ;
2935import java .util .function .LongUnaryOperator ;
3036import java .util .stream .Stream ;
3137
@@ -34,16 +40,17 @@ public class EphemeralResultStore implements ResultStore {
3440 private static final String NO_PROPERTY_KEY = "" ;
3541 private static final List <String > NO_PROPERTIES_LIST = List .of (NO_PROPERTY_KEY );
3642
37- private final Map <NodeKey , NodePropertyValues > nodeProperties ;
38- private final Map <String , NodeLabelEntry > nodeIdsByLabel ;
39- private final Map <RelationshipKey , RelationshipEntry > relationships ;
40- private final Map <RelationshipKey , RelationshipStreamEntry > relationshipStreams ;
43+ private final Cache <NodeKey , NodePropertyValues > nodeProperties ;
44+ private final Cache <String , NodeLabelEntry > nodeIdsByLabel ;
45+ private final Cache <RelationshipKey , RelationshipEntry > relationships ;
46+ private final Cache <RelationshipKey , RelationshipStreamEntry > relationshipStreams ;
4147
4248 public EphemeralResultStore () {
43- this .nodeProperties = new HashMap <>();
44- this .nodeIdsByLabel = new HashMap <>();
45- this .relationships = new HashMap <>();
46- this .relationshipStreams = new HashMap <>();
49+ var singleThreadScheduler = ExecutorServiceUtil .createSingleThreadScheduler ("GDS-ResultStore" );
50+ this .nodeProperties = createCache (singleThreadScheduler );
51+ this .nodeIdsByLabel = createCache (singleThreadScheduler );
52+ this .relationships = createCache (singleThreadScheduler );
53+ this .relationshipStreams = createCache (singleThreadScheduler );
4754 }
4855
4956 @ Override
@@ -53,12 +60,12 @@ public void addNodePropertyValues(List<String> nodeLabels, String propertyKey, N
5360
5461 @ Override
5562 public NodePropertyValues getNodePropertyValues (List <String > nodeLabels , String propertyKey ) {
56- return this .nodeProperties .get (new NodeKey (nodeLabels , propertyKey ));
63+ return this .nodeProperties .getIfPresent (new NodeKey (nodeLabels , propertyKey ));
5764 }
5865
5966 @ Override
6067 public void removeNodePropertyValues (List <String > nodeLabels , String propertyKey ) {
61- this .nodeProperties .remove (new NodeKey (nodeLabels , propertyKey ));
68+ this .nodeProperties .invalidate (new NodeKey (nodeLabels , propertyKey ));
6269 }
6370
6471 @ Override
@@ -68,17 +75,17 @@ public void addNodeLabel(String nodeLabel, long nodeCount, LongUnaryOperator toO
6875
6976 @ Override
7077 public boolean hasNodeLabel (String nodeLabel ) {
71- return this .nodeIdsByLabel .containsKey (nodeLabel );
78+ return this .nodeIdsByLabel .getIfPresent (nodeLabel ) != null ;
7279 }
7380
7481 @ Override
7582 public NodeLabelEntry getNodeIdsByLabel (String nodeLabel ) {
76- return this .nodeIdsByLabel .get (nodeLabel );
83+ return this .nodeIdsByLabel .getIfPresent (nodeLabel );
7784 }
7885
7986 @ Override
8087 public void removeNodeLabel (String nodeLabel ) {
81- this .nodeIdsByLabel .remove (nodeLabel );
88+ this .nodeIdsByLabel .invalidate (nodeLabel );
8289 }
8390
8491 @ Override
@@ -112,12 +119,12 @@ public void addRelationshipStream(
112119
113120 @ Override
114121 public RelationshipStreamEntry getRelationshipStream (String relationshipType , List <String > propertyKeys ) {
115- return this .relationshipStreams .get (new RelationshipKey (relationshipType , propertyKeys ));
122+ return this .relationshipStreams .getIfPresent (new RelationshipKey (relationshipType , propertyKeys ));
116123 }
117124
118125 @ Override
119126 public void removeRelationshipStream (String relationshipType , List <String > propertyKeys ) {
120- this .relationshipStreams .remove (new RelationshipKey (relationshipType , propertyKeys ));
127+ this .relationshipStreams .invalidate (new RelationshipKey (relationshipType , propertyKeys ));
121128 }
122129
123130 @ Override
@@ -127,7 +134,7 @@ public RelationshipEntry getRelationship(String relationshipType) {
127134
128135 @ Override
129136 public RelationshipEntry getRelationship (String relationshipType , String propertyKey ) {
130- return this .relationships .get (new RelationshipKey (relationshipType , List .of (propertyKey )));
137+ return this .relationships .getIfPresent (new RelationshipKey (relationshipType , List .of (propertyKey )));
131138 }
132139
133140 @ Override
@@ -137,7 +144,7 @@ public void removeRelationship(String relationshipType) {
137144
138145 @ Override
139146 public void removeRelationship (String relationshipType , String propertyKey ) {
140- this .relationships .remove (new RelationshipKey (relationshipType , List .of (propertyKey )));
147+ this .relationships .invalidate (new RelationshipKey (relationshipType , List .of (propertyKey )));
141148 }
142149
143150 @ Override
@@ -147,12 +154,28 @@ public boolean hasRelationship(String relationshipType) {
147154
148155 @ Override
149156 public boolean hasRelationship (String relationshipType , List <String > propertyKeys ) {
150- return this .relationships .containsKey (new RelationshipKey (relationshipType , propertyKeys ));
157+ return this .relationships .getIfPresent (new RelationshipKey (relationshipType , propertyKeys )) != null ;
151158 }
152159
153160 @ Override
154161 public boolean hasRelationshipStream (String relationshipType , List <String > propertyKeys ) {
155- return this .relationshipStreams .containsKey (new RelationshipKey (relationshipType , propertyKeys ));
162+ return this .relationshipStreams .getIfPresent (new RelationshipKey (relationshipType , propertyKeys )) != null ;
163+ }
164+
165+ private static <K , V > Cache <K , V > createCache (ScheduledExecutorService singleThreadScheduler ) {
166+ return Caffeine .newBuilder ()
167+ .expireAfterAccess (10 , TimeUnit .MINUTES )
168+ .ticker (new ClockServiceWrappingTicker ())
169+ .executor (singleThreadScheduler )
170+ .scheduler (Scheduler .forScheduledExecutorService (singleThreadScheduler ))
171+ .build ();
172+ }
173+
174+ private static class ClockServiceWrappingTicker implements Ticker {
175+ @ Override
176+ public long read () {
177+ return ClockService .clock ().millis () * 1000000 ;
178+ }
156179 }
157180
158181 private record NodeKey (List <String > nodeLabels , String propertyKey ) {}
0 commit comments