Skip to content

Commit c1611c2

Browse files
committed
fix: sync hstore schema cache clears
Register a JVM-wide schema cache clear listener for CachedSchemaTransactionV2 so HStore server nodes can invalidate local schema caches when another node changes schema. Clear V2 schema-id/name caches and the attached array cache by graph name, and publish schema cache clear events after schema add, update, and remove paths. Add unit coverage for graph-scoped V2 schema cache clearing. Fixes #2617
1 parent 836b348 commit c1611c2

2 files changed

Lines changed: 113 additions & 10 deletions

File tree

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedSchemaTransactionV2.java

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
import java.util.ArrayList;
2121
import java.util.List;
22+
import java.util.Map;
2223
import java.util.Set;
2324
import java.util.concurrent.ConcurrentHashMap;
25+
import java.util.concurrent.atomic.AtomicBoolean;
2426
import java.util.function.Consumer;
2527

2628
import org.apache.hugegraph.HugeGraphParams;
@@ -43,6 +45,13 @@
4345

4446
public class CachedSchemaTransactionV2 extends SchemaTransactionV2 {
4547

48+
private static final String ID_CACHE_PREFIX = "schema-id";
49+
private static final String NAME_CACHE_PREFIX = "schema-name";
50+
51+
// MetaDriver doesn't expose unlisten, register the PD listener once.
52+
private static final AtomicBoolean metaEventListenerRegistered =
53+
new AtomicBoolean(false);
54+
4655
private final Cache<Id, Object> idCache;
4756
private final Cache<Id, Object> nameCache;
4857

@@ -58,8 +67,8 @@ public CachedSchemaTransactionV2(MetaDriver metaDriver,
5867

5968
final long capacity = graphParams.configuration()
6069
.get(CoreOptions.SCHEMA_CACHE_CAPACITY);
61-
this.idCache = this.cache("schema-id", capacity);
62-
this.nameCache = this.cache("schema-name", capacity);
70+
this.idCache = this.cache(ID_CACHE_PREFIX, capacity);
71+
this.nameCache = this.cache(NAME_CACHE_PREFIX, capacity);
6372

6473
SchemaCaches<SchemaElement> attachment = this.idCache.attachment();
6574
if (attachment == null) {
@@ -86,11 +95,36 @@ public void close() {
8695
}
8796

8897
private Cache<Id, Object> cache(String prefix, long capacity) {
89-
final String name = prefix + "-" + this.graph().spaceGraphName();
98+
final String name = cacheName(prefix, this.graph().spaceGraphName());
9099
// NOTE: must disable schema cache-expire due to getAllSchema()
91100
return CacheManager.instance().cache(name, capacity);
92101
}
93102

103+
private static String cacheName(String prefix, String spaceGraphName) {
104+
return prefix + "-" + spaceGraphName;
105+
}
106+
107+
private static void clearSchemaCache(String spaceGraphName) {
108+
Map<String, Cache<Id, Object>> caches = CacheManager.instance().caches();
109+
110+
Cache<Id, Object> idCache = caches.get(cacheName(ID_CACHE_PREFIX,
111+
spaceGraphName));
112+
if (idCache != null) {
113+
idCache.clear();
114+
115+
SchemaCaches<?> arrayCaches = idCache.attachment();
116+
if (arrayCaches != null) {
117+
arrayCaches.clear();
118+
}
119+
}
120+
121+
Cache<Id, Object> nameCache = caches.get(cacheName(NAME_CACHE_PREFIX,
122+
spaceGraphName));
123+
if (nameCache != null) {
124+
nameCache.clear();
125+
}
126+
}
127+
94128
private void listenChanges() {
95129
// Listen store event: "store.init", "store.clear", ...
96130
Set<String> storeEvents = ImmutableSet.of(Events.STORE_INIT,
@@ -142,15 +176,44 @@ private void listenChanges() {
142176
return false;
143177
};
144178
EventHub schemaEventHub = this.graphParams().schemaEventHub();
145-
if (!schemaEventHub.containsListener(Events.CACHE)) {
146-
schemaEventHub.listen(Events.CACHE, this.cacheEventListener);
179+
schemaEventHub.listen(Events.CACHE, this.cacheEventListener);
180+
181+
listenSchemaCacheClear();
182+
}
183+
184+
private static void listenSchemaCacheClear() {
185+
if (!metaEventListenerRegistered.compareAndSet(false, true)) {
186+
return;
187+
}
188+
189+
try {
190+
MetaDriver metaDriver = MetaManager.instance().metaDriver();
191+
MetaManager.instance().listenSchemaCacheClear(response -> {
192+
List<String> graphNames =
193+
metaDriver.extractValuesFromResponse(response);
194+
if (graphNames == null) {
195+
return;
196+
}
197+
for (String graphName : graphNames) {
198+
LOG.debug("Graph {} clear schema cache on meta event",
199+
graphName);
200+
clearSchemaCache(graphName);
201+
}
202+
});
203+
} catch (RuntimeException e) {
204+
metaEventListenerRegistered.set(false);
205+
throw e;
147206
}
148207
}
149208

150209
public void clearCache(boolean notify) {
151210
this.idCache.clear();
152211
this.nameCache.clear();
153212
this.arrayCaches.clear();
213+
214+
if (notify) {
215+
this.notifySchemaCacheClear();
216+
}
154217
}
155218

156219
private void resetCachedAllIfReachedCapacity() {
@@ -202,6 +265,7 @@ protected void updateSchema(SchemaElement schema,
202265
super.updateSchema(schema, updateCallback);
203266

204267
this.updateCache(schema);
268+
this.notifySchemaCacheClear();
205269
}
206270

207271
@Override
@@ -210,11 +274,7 @@ protected void addSchema(SchemaElement schema) {
210274

211275
this.updateCache(schema);
212276

213-
if (!this.graph().option(CoreOptions.TASK_SYNC_DELETION)) {
214-
MetaManager.instance()
215-
.notifySchemaCacheClear(this.graph().graphSpace(),
216-
this.graph().name());
217-
}
277+
this.notifySchemaCacheClear();
218278
}
219279

220280
private void updateCache(SchemaElement schema) {
@@ -238,6 +298,10 @@ public void removeSchema(SchemaElement schema) {
238298

239299
this.invalidateCache(schema.type(), schema.id());
240300

301+
this.notifySchemaCacheClear();
302+
}
303+
304+
private void notifySchemaCacheClear() {
241305
if (!this.graph().option(CoreOptions.TASK_SYNC_DELETION)) {
242306
MetaManager.instance()
243307
.notifySchemaCacheClear(this.graph().graphSpace(),

hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/cache/CachedSchemaTransactionTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
import org.apache.hugegraph.HugeFactory;
2121
import org.apache.hugegraph.HugeGraph;
2222
import org.apache.hugegraph.HugeGraphParams;
23+
import org.apache.hugegraph.backend.cache.Cache;
24+
import org.apache.hugegraph.backend.cache.CacheManager;
2325
import org.apache.hugegraph.backend.cache.CachedSchemaTransaction;
26+
import org.apache.hugegraph.backend.cache.CachedSchemaTransactionV2;
27+
import org.apache.hugegraph.backend.id.Id;
2428
import org.apache.hugegraph.backend.id.IdGenerator;
2529
import org.apache.hugegraph.testutil.Assert;
2630
import org.apache.hugegraph.testutil.Whitebox;
@@ -165,6 +169,41 @@ public void testGetSchema() throws Exception {
165169
cache.getPropertyKey(IdGenerator.of(1)).name());
166170
}
167171

172+
@Test
173+
public void testClearV2SchemaCacheByGraphName() {
174+
String graphName = "DEFAULT-unit-test-v2";
175+
String otherGraphName = "DEFAULT-other-v2";
176+
177+
Cache<Id, Object> idCache = CacheManager.instance()
178+
.cache("schema-id-" +
179+
graphName, 10L);
180+
Cache<Id, Object> nameCache = CacheManager.instance()
181+
.cache("schema-name-" +
182+
graphName, 10L);
183+
Cache<Id, Object> otherIdCache = CacheManager.instance()
184+
.cache("schema-id-" +
185+
otherGraphName,
186+
10L);
187+
188+
idCache.update(IdGenerator.of(1), "fake-pk-by-id");
189+
nameCache.update(IdGenerator.of("fake-pk"), "fake-pk-by-name");
190+
otherIdCache.update(IdGenerator.of(2), "other-pk-by-id");
191+
192+
Assert.assertEquals(1L, idCache.size());
193+
Assert.assertEquals(1L, nameCache.size());
194+
Assert.assertEquals(1L, otherIdCache.size());
195+
196+
Whitebox.invokeStatic(CachedSchemaTransactionV2.class,
197+
new Class<?>[]{String.class},
198+
"clearSchemaCache", graphName);
199+
200+
Assert.assertEquals(0L, idCache.size());
201+
Assert.assertEquals(0L, nameCache.size());
202+
Assert.assertEquals(1L, otherIdCache.size());
203+
204+
otherIdCache.clear();
205+
}
206+
168207
@Test
169208
public void testResetCachedAllIfReachedCapacity() throws Exception {
170209
CachedSchemaTransaction cache = this.cache();

0 commit comments

Comments
 (0)