sunyuhan1998 opened a new issue, #11701:
URL: https://github.com/apache/gravitino/issues/11701
### Version
main branch
### Describe what's wrong
After `grantPrivilegesToRole` (or `overridePrivilegesInRole` adding a new
object, or `createRole` with securable objects) grants a role access to a
metadata object that was already queried before, `listBindingRoleNames()` on
that object does **not** return the newly granted role until the
`METADATA_OBJECT_ROLE_REL` relation cache entry expires (default
`expireAfterAccess` 1h). Continuously polling the object keeps the stale entry
alive even longer.
This is the symmetric **"add"** side of #11297 (which fixed the
**"remove"/revoke** side via the reverse-index patch in
`ROLE_SECURABLE_OBJECT_REVERSE_RULE`). That patch only establishes the
`roleIdent:ROLE -> objectKey` reverse mapping when a role is *already cached as
a binding role* — so a role that is newly granted access (and was therefore
never cached for that object) is unreachable by the role-side invalidation.
Root cause: `RelationalEntityStore.update()` / `put()` only call
`cache.invalidate(roleIdent, ROLE)` after the write. That is a role-side BFS
invalidation; reaching the object-keyed `METADATA_OBJECT_ROLE_REL` entry
requires the reverse index to already map `roleIdent -> objectKey`, which does
not exist for a not-yet-cached role.
### Error message and/or stacktrace
```
org.opentest4j.AssertionFailedError: grant must be immediately visible via
listBindingRoleNames
==> expected: <[roleA, roleB]> but was: <[roleA]>
at org.apache.gravitino.storage.TestEntityStorageRelationCache
.testGrantPrivilegeInvalidatesMetadataObjectRoleRelCache
```
### How to reproduce
1. Use Gravitino main branch.
2. Create metalake/catalog/schema; create `roleA` bound to the schema and
`roleB` with no securable objects.
3. `listBindingRoleNames(schema)` → `[roleA]`. This warms the
`METADATA_OBJECT_ROLE_REL` cache; the reverse index maps `roleA -> schemaKey`
but **not** `roleB`.
4. Grant `roleB` a privilege on the schema:
`metalake.grantPrivilegesToRole("roleB", schemaObject,
Set.of(Privileges.UseSchema.allow()))`.
5. `listBindingRoleNames(schema)` again:
- **Actual**: still `[roleA]` (missing `roleB`).
- **Expected**: `[roleA, roleB]`.
6. The new role only appears after the cache entry's TTL (~1h; longer if
polled).
### Additional context
**Affected code path**:
- `RelationalEntityStore.update()` / `put()` — invalidates only
`cache.invalidate(roleIdent, ROLE)`.
- `CaffeineEntityCache.invalidate(ident, type)` BFS — relies on the reverse
index; `ROLE_SECURABLE_OBJECT_REVERSE_RULE` (`ReverseIndexRules.java:121`) only
adds `roleIdent -> objectKey` when the role is already cached as a binding role.
- The DB layer is correct (`SecurableObjectMapper` inserts the new row;
`listRolesByMetadataObject` SQL would return `roleB` on a fresh query) — the
stale read comes from the cached relation result.
**Why revoke/delete (#11297) is not affected**: when removing a role, that
role was previously cached as a binding role of the object, so the reverse
index already holds `role -> objectKey` and the role-side BFS reaches it.
Invariant: `objectKey contains role ⟺ reverse index has role->objectKey`.
**Suggested fix**: in `RelationalEntityStore.update()` and `put()`, after
the write, explicitly invalidate the `METADATA_OBJECT_ROLE_REL` cache entry
keyed by each securable object of the (new) `RoleEntity`.
--
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]