Class RedisTypeCache
Stores per-type JSON blobs and a structural index of direct-children (first-level subtypes only) so that polymorphic read responses can be assembled entirely from Redis without a database round-trip.
Key schema (see also RedisKeySpace):
type:uuid:{uuid}— STRING — canonical single-type JSON blobtype:name:{encoded}— STRING — URL-encoded type name → UUID stringtype:children:{uuid}— SET — direct (first-level) subtype UUIDstype:parent:{uuid}— SET — supertype UUIDs (multiple inheritance supported)type:linearization:{uuid}— STRING — precomputed C3-linearized ancestor name list, internal-only (never part of a type's own returned JSON representation)type:list— ZSET — all type UUIDs, used forinvalidateAll()
All operations degrade gracefully: if Redis is unavailable every method
returns null / an empty collection and the caller falls through to
the database.
A miss is just a miss. This cache never rebuilds itself in response to a
lookup that failed to find something: it reports the miss and lets the caller fall through to
its own database path, which every caller already has. Reacting to a single miss by reloading
every type — as this class used to — made an ordinary miss cost O(all types) instead of O(1),
turned a single PUT /types/... (which wipes the cache) into a full rebuild per
concurrent request thread, and let a lookup for a type that does not exist be repeated to
amplify load at will. Repopulating is a background responsibility, driven by the schema
projection worker, never by a request.
Invalidation strategy:
- create — granular: add the new type's own keys and register it as a child of each of its supertypes.
- delete — granular (only possible on leaf types): remove own keys and remove this type from each parent's children SET.
- update — full: wipe all
type:*keys viainvalidateAll()because properties / version change.
- Author:
- Luca Frosini (ISTI - CNR)
-
Nested Class Summary
Nested Classes -
Method Summary
Modifier and TypeMethodDescriptionvoidaddNewTypeToCache(UUID uuid, String typeName, String json, Collection<String> supertypeNames, long version) Granular cache update for a newly created type.getAllDescendantsRaw(UUID rootUuid, long currentVersion) Performs a BFS traversal of thetype:childrengraph rooted atrootUuidand returns the JSON blob of every encountered type (root included) in BFS order.getDirectChildrenUuids(UUID uuid) Returns the set of direct children UUID strings for the given parent UUID, ornullif not cached or Redis is unavailable.static RedisTypeCacheReturns the singleton instance ofRedisTypeCache.getLinearization(UUID uuid, long currentVersion) Returns the precomputed, C3-linearized ancestor name list for a type, ornullif not cached or Redis is unavailable.getRawByName(String typeName, long currentVersion) Returns the cached single-type JSON blob for the given type name, ornullon cache miss or Redis unavailability.getRawByUUID(UUID uuid, long currentVersion) Returns the cached single-type JSON blob for the given UUID, ornullon cache miss or Redis unavailability.getUUIDByName(String typeName) Returns the cached UUID for the given type name, ornullon cache miss or Redis unavailability.voidinvalidate(UUID uuid, String typeName) Granular cache invalidation for a deleted type.voidDeletes alltype:*keys from Redis using a SCAN loop.booleanReturnstruewhen Redis is reachable and the connection is open.booleanisKnownMissing(String typeName, long currentVersion) Returns whether a tombstone recorded bymarkMissing(String, long)is still in force.voidmarkMissing(String typeName, long version) Records, for a short while, that no Type with this name exists.booleansaveAll(List<RedisTypeCache.TypeCacheEntry> entries, long version) Writes an entire set of types in one pipelined batch: their blobs and name index, their parent/child edges, and their precomputed linearizations.voidsaveLinearization(UUID uuid, List<String> supertypes, long version) Stores the precomputed, C3-linearized ancestor name list for a type, computed once at creation / warm-up time so thatCachedType.getSupertypes()never has to recompute or re-derive it on an ordinary read.voidWrites the UUID blob and name→UUID lookup for a single type.
-
Method Details
-
getInstance
Returns the singleton instance ofRedisTypeCache.- Returns:
- the singleton instance
-
isAvailable
public boolean isAvailable()Returnstruewhen Redis is reachable and the connection is open.- Returns:
trueif Redis is available,falseotherwise
-
getUUIDByName
Returns the cached UUID for the given type name, ornullon cache miss or Redis unavailability.- Parameters:
typeName- canonical type name (e.g."Resource")- Returns:
- UUID, or
null
-
getRawByUUID
Returns the cached single-type JSON blob for the given UUID, ornullon cache miss or Redis unavailability.- Parameters:
uuid- the type's UUIDcurrentVersion- the schema version the caller requires the entry to match- Returns:
- single-type JSON, or
null
-
getRawByName
Returns the cached single-type JSON blob for the given type name, ornullon cache miss or Redis unavailability.- Parameters:
typeName- canonical type namecurrentVersion- the schema version the caller requires the entry to match- Returns:
- single-type JSON, or
null
-
getDirectChildrenUuids
Returns the set of direct children UUID strings for the given parent UUID, ornullif not cached or Redis is unavailable.- Parameters:
uuid- the parent type's UUID- Returns:
- set of child UUID strings, or
null
-
getAllDescendantsRaw
Performs a BFS traversal of thetype:childrengraph rooted atrootUuidand returns the JSON blob of every encountered type (root included) in BFS order.Returns
nullif the root type is not in the cache or Redis is unavailable, so that the caller can fall through to the database.- Parameters:
rootUuid- the UUID of the root (requested) typecurrentVersion- the schema version the caller requires every entry to match- Returns:
- list of single-type JSON blobs in BFS order, or
nullon cache miss / unavailability
-
saveType
Writes the UUID blob and name→UUID lookup for a single type. Does not touch children or parent keys.- Parameters:
uuid- the type's UUIDtypeName- canonical type namejson- serialized single-type JSONversion- the schema version this payload reflects
-
saveLinearization
Stores the precomputed, C3-linearized ancestor name list for a type, computed once at creation / warm-up time so thatCachedType.getSupertypes()never has to recompute or re-derive it on an ordinary read. Internal-only: kept alongside the type's canonical JSON blob, the same waytype:parent/type:childrenare, never inside it -- this is never part of a type's own returned representation.- Parameters:
uuid- the type's UUIDsupertypes- the linearized ancestor names, most-derived-firstversion- the schema version this linearization reflects
-
getLinearization
Returns the precomputed, C3-linearized ancestor name list for a type, ornullif not cached or Redis is unavailable.- Parameters:
uuid- the type's UUIDcurrentVersion- the schema version the caller requires the entry to match- Returns:
- the linearized ancestor names, most-derived-first, or
null
-
addNewTypeToCache
public void addNewTypeToCache(UUID uuid, String typeName, String json, Collection<String> supertypeNames, long version) Granular cache update for a newly created type.In addition to writing the UUID blob and name lookup (via
saveType(java.util.UUID, java.lang.String, java.lang.String, long)), this method:- Looks up the UUID of each supertype by name from the cache.
- Adds this type's UUID to the
type:children:{parentUuid}SET of every supertype whose UUID is found in Redis. - Adds each found parent UUID to the
type:parent:{uuid}SET of this type (supports multiple inheritance).
If any supertype UUID is not found in the cache the parent/children association for that supertype is simply skipped; the cache remains consistent for the supertypes that are cached.
- Parameters:
uuid- the new type's UUIDtypeName- canonical type namejson- serialized single-type JSONsupertypeNames- the direct supertype names fromtype.getExtendedTypes()version- the schema version this payload reflects
-
saveAll
Writes an entire set of types in one pipelined batch: their blobs and name index, their parent/child edges, and their precomputed linearizations.Issued through the asynchronous API with auto-flushing disabled, so the whole batch costs one network round trip instead of roughly half a dozen per type. Repopulating ~100 types the one-command-at-a-time way meant several hundred sequential round trips, each paying the full latency of the connection; that cost is what made a full rebuild expensive enough to matter in the first place.
The batch goes through a connection of its own (
RedisConnectionManager.openBulkConnection()), closed before returning: the shared connection keeps serving the other callers meanwhile.- Parameters:
entries- the types to writeversion- the schema version these payloads reflect- Returns:
trueif the batch was written,falseif Redis was unavailable or the write failed
-
markMissing
Records, for a short while, that no Type with this name exists.Without this, a lookup for a name that does not exist misses the cache every single time and reaches the database every single time — so repeating it is an inexpensive way to generate unbounded database load. A tombstone makes the second and subsequent lookups cost one Redis read.
Stamped with the schema version, like every other entry: creating the type advances the counter, which retires the tombstone immediately. Without that, a type registered right after a failed lookup would keep answering
404until the tombstone expired — and the explicit removal performed on creation runs inside a best-effort block that swallows cache failures, so it cannot be relied upon on its own.- Parameters:
typeName- the canonical type name that was not foundversion- the schema version at which the absence was established
-
isKnownMissing
Returns whether a tombstone recorded bymarkMissing(String, long)is still in force.- Parameters:
typeName- the canonical type namecurrentVersion- the current schema version; a tombstone from an earlier version is treated as expired, since the schema has changed since it was recorded- Returns:
trueif this name is known not to exist,falseif it exists or is simply unknown
-
invalidate
Granular cache invalidation for a deleted type.Removes:
type:uuid:{uuid}type:name:{encodedName}, but only if it still points to this UUID - a subsequent create under the same name may already have claimed it, and that mapping must not be clobbered.type:children:{uuid}(should be empty for a leaf type)- This UUID from the
type:children:{parentUuid}SET of every parent (resolved viatype:parent:{uuid}). type:parent:{uuid}type:linearization:{uuid}- This UUID from the global
type:listZSET.
- Parameters:
uuid- the deleted type's UUIDtypeName- canonical type name of the deleted type
-
invalidateAll
public void invalidateAll()Deletes alltype:*keys from Redis using a SCAN loop. Called after every type update (properties / version change) to ensure subsequent reads reflect the latest database state.
-