ealeonraz commented on PR #13106:
URL: https://github.com/apache/gravitino/pull/13106#issuecomment-5747818130

   Thanks for the detailed review and for actually reproducing these, that made 
them a lot easier to chase down. Pushed a commit that addresses each point. 
Summary by finding:
   
   **[P1] Missing fence snapshots must not authorize unconditional writes.** 
Fills now fail closed. `doPut` only writes when a miss on the same thread 
recorded the fences that bound the load; with no record (insert-time warming, a 
record evicted from the per-thread bound, a read that failed or hit an 
undecodable entry) nothing is written and the next read loads under a fresh 
record. A failed read also drops any earlier record for that key so it cannot 
vouch for a load it did not bound. Insert no longer warms the cache; the first 
read does. Regression coverage: the insert/delete race, 1,025 misses before 
fill, read failure followed by recovery (pausing the container), and decode 
failure followed by a concurrent invalidation, plus unit tests on the 
bookkeeping.
   
   **[P1] Expiring counters can reuse a fence version.** Fence values are now 
generations from a per-metalake counter (`<ns>:{metalake}:G`) that never 
expires, so a recreated fence can never carry a value an older read observed. 
Fence keys can still expire, and to make that safe a fill whose record is older 
than `fenceTtlMs` is discarded on the client, which closes the absent, set, 
expired, absent sequence too: the only way to see "absent" twice across an 
expiry is to be older than the fence lifetime. So the fence TTL is now the 
explicit safe lifetime of a fill record rather than a hope that fences outlive 
readers. Both ABA sequences are tests now, at value TTL 100 ms and fence TTL 
200 ms.
   
   **[P1] Clearing values and the index separately.** `clear()` is one script 
per metalake: it moves the metalake's own fence to a fresh generation (every 
fill in flight for that metalake is rejected, since every fill checks the 
metalake fence) and deletes the values and the index in the same script. 
`discardQuietly` is also a script now and only removes the entry if it still 
holds the bytes that were read, so a concurrent fill is never unindexed. Test: 
clear between a miss and its fill, then a metalake invalidation, then a scan 
for orphaned value keys.
   
   **[P2] Expired values leave index members indefinitely.** Added a bounded 
reaper script: `ZRANGEBYLEX` a batch from a cursor, `EXISTS` each value, `ZREM` 
the missing ones, all inside one script so a refill that landed first is never 
removed. It runs one batch per 64 writes into a metalake and one batch per 
index visited by `size()`, resuming where it left off. Test: expired members 
reclaimed, and a refill survives the reaper.
   
   **[P2] Wire Redis client closure into the entity-store lifecycle.** Added 
`EntityCache.close()` with a default of `clear()`, so Caffeine keeps 
clear-on-close, and `RelationalEntityStore.close()` now calls it. The Redis 
implementation closes only its own client, and every operation after `close()` 
fails with an `IllegalStateException`: the cluster client otherwise reconnects 
on demand after being closed, which is the "still usable" behavior you saw. 
Store-level test: closing one store leaves the other store's entries, plus unit 
and IT coverage that close never touches data and that a closed cache rejects 
further use.
   
   **[P2] Iterate cluster primaries rather than every discovered node.** 
Node-wide scans now check `INFO replication` and skip replicas; every per-key 
command that follows a scan (`ZCARD`, the clear script) goes through the 
cluster client so a redirection is handled rather than thrown. The cluster IT 
now starts three primaries with one replica each.
   
   **[P2] Namespace scanning can delete another deployment's cache.** The scan 
pattern is anchored on the `:{` that always follows the namespace, ownership of 
every scanned key is checked exactly before it is touched, glob characters are 
escaped, and the namespace config is restricted to letters, digits and `._-:` 
so a metacharacter cannot get in. Tests for `ns` vs `ns:other` on clear and for 
rejected namespaces.
   
   **Tests.** The concurrency test now commits distinct versions and asserts no 
reader ever observes a version older than an invalidation that completed before 
its read began. Added `TestRelationalEntityStoreRedisCache`, which runs two 
real `RelationalEntityStore`s over one H2 and one Redis through insert, get, 
batchGet, update and close, with a latch pausing one store between its backend 
load and its cache fill while the other commits and invalidates. Also 
`TestRedisEntityCacheScripts` for the Lua scripts at interleavings the API 
cannot produce on demand.
   
   **CI.** The cluster IT failed to initialize because it ran the cluster 
container on the host network, which is not reachable the same way everywhere. 
It now runs on the default bridge network and connects to the container's own 
address, which a Linux Docker host (the CI runners) routes to; on a host that 
cannot (Docker Desktop) the suite skips with the reason instead of failing, and 
`GRAVITINO_REDIS_CLUSTER_ADDRESS` still points it at an external cluster. If 
the cluster never becomes ready the failure now carries the container logs.
   
   On `CACHEABLE_TYPES`: I would keep that out of this PR. The reason USER, 
GROUP and ROLE are excluded is not the per-node copies, it is that their 
materialized form embeds relation data that no write path invalidates (renaming 
a securable object never calls `invalidate` on the roles that reference it). A 
shared copy does not change that, so making them cacheable needs those write 
paths to invalidate the principals they affect, which is its own change. Happy 
to open a follow-up issue for it.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to