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]

Reply via email to