danhuawang opened a new issue, #11297:
URL: https://github.com/apache/gravitino/issues/11297
### Version
main branch
### Describe what's wrong
After calling `revokePrivilegesFromRole` to remove the last (and only)
privilege from a role's securable object on a schema, `listBindingRoleNames()`
on that schema still returns the revoked role. The role should no longer appear
because the securable object row has been soft-deleted in the database.
This is a **general issue** affecting all schema types (both flat and
hierarchical). It is not specific to hierarchical/nested namespace schemas.
The root cause appears to be a cache invalidation gap:
`RelationalEntityStore.update` invalidates the role entity cache
(`cache.invalidate(roleIdent, ROLE)`) after the revoke, but the
`METADATA_OBJECT_ROLE_REL` relation cache — keyed by the schema's identifier —
is not properly invalidated. As a result, `listBindingRoleNames` reads stale
cached relation data that still includes the revoked role.
### Error message and/or stacktrace
```
org.opentest4j.AssertionFailedError: Flat schema should no longer have role
after revoke,
got: [hs_d2286d6c_role_flat] ==> expected: <false> but was: <true>
at org.junit.jupiter.api.Assertions.assertFalse(Assertions.java:235)
at org.apache.gravitino.integration.test.schema.TestHierarchicalSchemaIT
.testRoleBindingRevokeOnFlatSchema(TestHierarchicalSchemaIT.java:1021)
```
The same failure occurs for hierarchical schemas:
```
org.opentest4j.AssertionFailedError: Level1 schema should no longer have
role1 after revoke
==> expected: <false> but was: <true>
at org.apache.gravitino.integration.test.schema.TestHierarchicalSchemaIT
.testRoleBindingForHierarchicalSchema(TestHierarchicalSchemaIT.java:965)
```
### How to reproduce
1. Use Gravitino main branch with any catalog (Iceberg, Hive, etc.).
2. Create a schema and a role:
```java
icebergCatalog.asSchemas().createSchema("test_schema", "comment",
Map.of());
metalake.createRole("test_role", Map.of(), List.of());
```
3. Grant a privilege to the role on the schema:
```java
MetadataObject schemaObject = MetadataObjects.of(catalogName,
"test_schema", MetadataObject.Type.SCHEMA);
metalake.grantPrivilegesToRole("test_role", schemaObject,
Set.of(Privileges.UseSchema.allow()));
```
4. Verify the role is bound (this succeeds):
```java
Schema schema = catalog.asSchemas().loadSchema("test_schema");
String[] roles = schema.supportsRoles().listBindingRoleNames();
// roles contains "test_role" ✓
```
5. Revoke the privilege:
```java
metalake.revokePrivilegesFromRole("test_role", schemaObject,
Set.of(Privileges.UseSchema.allow()));
```
6. Check binding again:
```java
String[] rolesAfter = schema.supportsRoles().listBindingRoleNames();
// rolesAfter STILL contains "test_role" ✗
```
7. **Actual result**: `listBindingRoleNames` still returns `["test_role"]`.
8. **Expected result**: `listBindingRoleNames` should return `[]` because
the securable object has been removed from the role.
### Additional context
**Affected code path**:
- `RelationalEntityStore.update()` (line ~136) — invalidates
`cache.invalidate(ident, entityType)` where ident is the role, not the schema.
- `RoleMetaService.updateRole()` — correctly soft-deletes the securable
object row via `batchSoftDeleteSecurableObjects`.
- `RoleMetaService.listRolesByMetadataObject()` — queries via SQL JOIN on
`securable_object` table with `se.deleted_at = 0`, which should exclude
soft-deleted rows.
- `CaffeineEntityCache` — the `METADATA_OBJECT_ROLE_REL` relation (keyed by
schema identifier) is not invalidated when the role entity is updated.
**Confirmed via control test**: A flat (non-hierarchical) schema exhibits
the exact same behavior, proving this is not related to hierarchical namespace
support.
**Database layer is correct**: The
`PermissionManager.updateRevokedSecurableObject` correctly removes the
securable object when no privileges remain, and the SQL query in
`listRolesByMetadataObjectIdAndType` filters by `se.deleted_at = 0`. The issue
is that the cached relation result is served without re-querying the database.
**Suggested fix**: When `RelationalEntityStore.update` is called for a
`ROLE` entity type, it should also invalidate the `METADATA_OBJECT_ROLE_REL`
relation cache entries for all metadata objects that were in the role's
securable objects (both old and new). Alternatively, the
`CaffeineEntityCache.invalidate(roleIdent, ROLE)` BFS traversal should follow
the reverse index from role → securable objects → schema relation keys.
--
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]