Skip to content

Commit 5285203

Browse files
authored
[#10770] feat(auth): Add group cache mapper support (#11014)
### What changes were proposed in this pull request? This PR splits the group-related authorization cache mapper work from #10996. It adds: - `group_meta.updated_at` to H2/MySQL/PostgreSQL 1.3.0 schemas and 1.2.0-to-1.3.0 upgrade scripts. - Group updated-at mapper APIs and SQL providers. - `GroupUpdatedAt` PO. - `GroupMetaService.updateGroup` updates `group_meta.updated_at` when group role assignments change. - Mapper and service tests for group updated-at behavior. ### Why are the changes needed? Group role cache validation needs a DB-side version sentinel, matching the existing user/role cache pattern. Fix: #10770 ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? ```bash ./gradlew :core:spotlessApply ./gradlew :core:test --tests org.apache.gravitino.storage.relational.mapper.provider.base.TestAuthMappers --tests org.apache.gravitino.storage.relational.service.TestGroupMetaService ```
1 parent e326022 commit 5285203

14 files changed

Lines changed: 173 additions & 3 deletions

File tree

core/src/main/java/org/apache/gravitino/storage/relational/mapper/GroupMetaMapper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.List;
2323
import org.apache.gravitino.storage.relational.po.ExtendedGroupPO;
2424
import org.apache.gravitino.storage.relational.po.GroupPO;
25+
import org.apache.gravitino.storage.relational.po.auth.GroupUpdatedAt;
2526
import org.apache.ibatis.annotations.DeleteProvider;
2627
import org.apache.ibatis.annotations.InsertProvider;
2728
import org.apache.ibatis.annotations.Param;
@@ -88,4 +89,11 @@ Integer updateGroupMeta(
8889
method = "deleteGroupMetasByLegacyTimeline")
8990
Integer deleteGroupMetasByLegacyTimeline(
9091
@Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit);
92+
93+
@UpdateProvider(type = GroupMetaSQLProviderFactory.class, method = "touchGroupUpdatedAt")
94+
void touchGroupUpdatedAt(@Param("groupId") long groupId);
95+
96+
@SelectProvider(type = GroupMetaSQLProviderFactory.class, method = "getGroupUpdatedAt")
97+
GroupUpdatedAt getGroupUpdatedAt(
98+
@Param("metalakeName") String metalakeName, @Param("groupName") String groupName);
9199
}

core/src/main/java/org/apache/gravitino/storage/relational/mapper/GroupMetaSQLProviderFactory.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,13 @@ public static String deleteGroupMetasByLegacyTimeline(
9595
@Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit) {
9696
return getProvider().deleteGroupMetasByLegacyTimeline(legacyTimeline, limit);
9797
}
98+
99+
public static String touchGroupUpdatedAt(@Param("groupId") long groupId) {
100+
return getProvider().touchGroupUpdatedAt(groupId);
101+
}
102+
103+
public static String getGroupUpdatedAt(
104+
@Param("metalakeName") String metalakeName, @Param("groupName") String groupName) {
105+
return getProvider().getGroupUpdatedAt(metalakeName, groupName);
106+
}
98107
}

core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/GroupMetaBaseSQLProvider.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,25 @@ public String deleteGroupMetasByLegacyTimeline(
182182
+ GROUP_TABLE_NAME
183183
+ " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT #{limit}";
184184
}
185+
186+
public String touchGroupUpdatedAt(@Param("groupId") long groupId) {
187+
return "UPDATE "
188+
+ GROUP_TABLE_NAME
189+
+ " SET updated_at = (UNIX_TIMESTAMP() * 1000.0)"
190+
+ " + EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(3)) / 1000"
191+
+ " WHERE group_id = #{groupId} AND deleted_at = 0";
192+
}
193+
194+
public String getGroupUpdatedAt(
195+
@Param("metalakeName") String metalakeName, @Param("groupName") String groupName) {
196+
return "SELECT gm.group_id as groupId, gm.updated_at as updatedAt"
197+
+ " FROM "
198+
+ GROUP_TABLE_NAME
199+
+ " gm"
200+
+ " JOIN "
201+
+ MetalakeMetaMapper.TABLE_NAME
202+
+ " mm ON gm.metalake_id = mm.metalake_id AND mm.deleted_at = 0"
203+
+ " WHERE mm.metalake_name = #{metalakeName} AND gm.group_name = #{groupName}"
204+
+ " AND gm.deleted_at = 0";
205+
}
185206
}

core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/GroupMetaPostgreSQLProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,12 @@ public String deleteGroupMetasByLegacyTimeline(
104104
+ GROUP_TABLE_NAME
105105
+ " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT #{limit})";
106106
}
107+
108+
@Override
109+
public String touchGroupUpdatedAt(@Param("groupId") long groupId) {
110+
return "UPDATE "
111+
+ GROUP_TABLE_NAME
112+
+ " SET updated_at = CAST(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000 AS BIGINT)"
113+
+ " WHERE group_id = #{groupId} AND deleted_at = 0";
114+
}
107115
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.gravitino.storage.relational.po.auth;
20+
21+
import lombok.AllArgsConstructor;
22+
import lombok.Getter;
23+
import lombok.NoArgsConstructor;
24+
import lombok.Setter;
25+
26+
/** Group identity + role-list staleness sentinel. */
27+
@Getter
28+
@Setter
29+
@NoArgsConstructor
30+
@AllArgsConstructor
31+
public class GroupUpdatedAt {
32+
private long groupId;
33+
private long updatedAt;
34+
}

core/src/main/java/org/apache/gravitino/storage/relational/service/GroupMetaService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,11 @@ public <E extends Entity & HasIdentifier> GroupEntity updateGroup(
259259
mapper ->
260260
mapper.softDeleteGroupRoleRelByGroupAndRoles(
261261
newEntity.id(), Lists.newArrayList(deleteRoleIds)));
262-
});
262+
},
263+
() ->
264+
SessionUtils.doWithoutCommit(
265+
GroupMetaMapper.class,
266+
mapper -> mapper.touchGroupUpdatedAt(oldGroupPO.getGroupId())));
263267
} catch (RuntimeException re) {
264268
ExceptionUtils.checkSQLException(
265269
re, Entity.EntityType.GROUP, newEntity.nameIdentifier().toString());

core/src/test/java/org/apache/gravitino/storage/relational/mapper/provider/base/TestAuthMappers.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.apache.gravitino.storage.relational.po.RolePO;
4848
import org.apache.gravitino.storage.relational.po.UserPO;
4949
import org.apache.gravitino.storage.relational.po.auth.ChangedOwnerInfo;
50+
import org.apache.gravitino.storage.relational.po.auth.GroupUpdatedAt;
5051
import org.apache.gravitino.storage.relational.po.auth.OwnerInfo;
5152
import org.apache.gravitino.storage.relational.po.auth.RoleUpdatedAt;
5253
import org.apache.gravitino.storage.relational.po.auth.UserUpdatedAt;
@@ -241,6 +242,51 @@ void testUserMetaGetUserUpdatedAt() {
241242
Assertions.assertEquals(expected, info.getUpdatedAt());
242243
}
243244

245+
@Test
246+
void testGroupMetaTouchUpdatedAt() {
247+
insertMetalake(1L, "metalake1");
248+
insertGroup(30L, "group30", 1L);
249+
250+
long before = queryUpdatedAt("group_meta", "group_id", 30L);
251+
Assertions.assertEquals(0L, before);
252+
253+
long jvmBefore = System.currentTimeMillis();
254+
groupMetaMapper.touchGroupUpdatedAt(30L);
255+
long jvmAfter = System.currentTimeMillis();
256+
257+
long after = queryUpdatedAt("group_meta", "group_id", 30L);
258+
Assertions.assertTrue(
259+
after >= jvmBefore - 1000L && after <= jvmAfter + 1000L,
260+
"expected DB-time updated_at within 1s of JVM clock, got " + after);
261+
}
262+
263+
@Test
264+
void testGroupMetaTouchUpdatedAtSkipsSoftDeleted() {
265+
insertMetalake(1L, "metalake1");
266+
insertGroup(31L, "group31", 1L);
267+
groupMetaMapper.softDeleteGroupMetaByGroupId(31L);
268+
269+
long beforeUpdatedAt = queryUpdatedAt("group_meta", "group_id", 31L);
270+
groupMetaMapper.touchGroupUpdatedAt(31L);
271+
long afterUpdatedAt = queryUpdatedAt("group_meta", "group_id", 31L);
272+
273+
Assertions.assertEquals(beforeUpdatedAt, afterUpdatedAt);
274+
}
275+
276+
@Test
277+
void testGroupMetaGetGroupUpdatedAt() {
278+
insertMetalake(1L, "metalake1");
279+
insertGroup(32L, "group32", 1L);
280+
281+
groupMetaMapper.touchGroupUpdatedAt(32L);
282+
long expected = queryUpdatedAt("group_meta", "group_id", 32L);
283+
284+
GroupUpdatedAt info = groupMetaMapper.getGroupUpdatedAt("metalake1", "group32");
285+
Assertions.assertNotNull(info);
286+
Assertions.assertEquals(32L, info.getGroupId());
287+
Assertions.assertEquals(expected, info.getUpdatedAt());
288+
}
289+
244290
@Test
245291
void testOwnerMetaSelectOwnerByMetadataObjectIdAndType() {
246292
insertMetalake(1L, "metalake1");

core/src/test/java/org/apache/gravitino/storage/relational/service/TestGroupMetaService.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.apache.gravitino.storage.relational.mapper.GroupMetaMapper;
5151
import org.apache.gravitino.storage.relational.mapper.RoleMetaMapper;
5252
import org.apache.gravitino.storage.relational.po.RolePO;
53+
import org.apache.gravitino.storage.relational.po.auth.GroupUpdatedAt;
5354
import org.apache.gravitino.storage.relational.session.SqlSessionFactoryHelper;
5455
import org.apache.gravitino.storage.relational.utils.SessionUtils;
5556
import org.apache.gravitino.utils.NameIdentifierUtil;
@@ -552,6 +553,7 @@ void testUpdateGroup() throws IOException {
552553
Lists.newArrayList(role1.name(), role2.name()),
553554
Lists.newArrayList(role1.id(), role2.id()));
554555
groupMetaService.insertGroup(group1, false);
556+
Assertions.assertEquals(0L, getGroupUpdatedAt(group1.name()).getUpdatedAt());
555557
GroupEntity actualGroup = groupMetaService.getGroupByIdentifier(group1.nameIdentifier());
556558
Assertions.assertEquals(group1.name(), actualGroup.name());
557559
Assertions.assertEquals(
@@ -592,7 +594,13 @@ void testUpdateGroup() throws IOException {
592594
.build();
593595
};
594596

597+
long beforeGrant = System.currentTimeMillis();
595598
Assertions.assertNotNull(groupMetaService.updateGroup(group1.nameIdentifier(), grantUpdater));
599+
long afterGrant = System.currentTimeMillis();
600+
long grantUpdatedAt = getGroupUpdatedAt(group1.name()).getUpdatedAt();
601+
Assertions.assertTrue(
602+
grantUpdatedAt >= beforeGrant - 1000L && grantUpdatedAt <= afterGrant + 1000L,
603+
"expected DB-time group updated_at within 1s of JVM clock, got " + grantUpdatedAt);
596604
GroupEntity grantGroup =
597605
GroupMetaService.getInstance().getGroupByIdentifier(group1.nameIdentifier());
598606
Assertions.assertEquals(group1.id(), grantGroup.id());
@@ -630,7 +638,13 @@ void testUpdateGroup() throws IOException {
630638
.build();
631639
};
632640

641+
long beforeRevoke = System.currentTimeMillis();
633642
Assertions.assertNotNull(groupMetaService.updateGroup(group1.nameIdentifier(), revokeUpdater));
643+
long afterRevoke = System.currentTimeMillis();
644+
long revokeUpdatedAt = getGroupUpdatedAt(group1.name()).getUpdatedAt();
645+
Assertions.assertTrue(
646+
revokeUpdatedAt >= beforeRevoke - 1000L && revokeUpdatedAt <= afterRevoke + 1000L,
647+
"expected DB-time group updated_at within 1s of JVM clock, got " + revokeUpdatedAt);
634648
GroupEntity revokeGroup =
635649
GroupMetaService.getInstance().getGroupByIdentifier(group1.nameIdentifier());
636650
Assertions.assertEquals(group1.id(), revokeGroup.id());
@@ -714,7 +728,9 @@ void testUpdateGroup() throws IOException {
714728
.withAuditInfo(updateAuditInfo)
715729
.build();
716730
};
731+
long beforeNoUpdate = getGroupUpdatedAt(group1.name()).getUpdatedAt();
717732
Assertions.assertNotNull(groupMetaService.updateGroup(group1.nameIdentifier(), noUpdater));
733+
Assertions.assertEquals(beforeNoUpdate, getGroupUpdatedAt(group1.name()).getUpdatedAt());
718734
GroupEntity noUpdaterGroup =
719735
GroupMetaService.getInstance().getGroupByIdentifier(group1.nameIdentifier());
720736
Assertions.assertEquals(group1.id(), noUpdaterGroup.id());
@@ -1042,6 +1058,11 @@ private Integer countGroupRoleRels() {
10421058
return count;
10431059
}
10441060

1061+
private GroupUpdatedAt getGroupUpdatedAt(String groupName) {
1062+
return SessionUtils.doWithCommitAndFetchResult(
1063+
GroupMetaMapper.class, mapper -> mapper.getGroupUpdatedAt(metalakeName, groupName));
1064+
}
1065+
10451066
private GroupEntity createGroupEntity(
10461067
Long id, Namespace namespace, String name, AuditInfo auditInfo) {
10471068
return GroupEntity.builder()

scripts/h2/schema-1.3.0-h2.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,10 @@ CREATE TABLE IF NOT EXISTS `group_meta` (
231231
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'group current version',
232232
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'group last version',
233233
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'group deleted at',
234+
`updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'updated at',
234235
PRIMARY KEY (`group_id`),
235-
CONSTRAINT `uk_mid_gr_del` UNIQUE (`metalake_id`, `group_name`, `deleted_at`)
236+
CONSTRAINT `uk_mid_gr_del` UNIQUE (`metalake_id`, `group_name`, `deleted_at`),
237+
KEY `idx_group_meta_name_del_upd` (`metalake_id`, `group_name`, `deleted_at`, `updated_at`)
236238
) ENGINE=InnoDB;
237239

238240
CREATE TABLE IF NOT EXISTS `group_role_rel` (

scripts/h2/upgrade-1.2.0-to-1.3.0-h2.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
ALTER TABLE `user_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'updated at';
2121
ALTER TABLE `role_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'updated at';
2222
ALTER TABLE `owner_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'updated at';
23+
ALTER TABLE `group_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'updated at';
2324

2425
CREATE INDEX IF NOT EXISTS `idx_user_meta_name_del_upd` ON `user_meta`(`metalake_id`, `user_name`, `deleted_at`, `updated_at`);
2526
CREATE INDEX IF NOT EXISTS `idx_owner_meta_del_upd_obj` ON `owner_meta`(`deleted_at`, `updated_at`, `metadata_object_id`);
27+
CREATE INDEX IF NOT EXISTS `idx_group_meta_name_del_upd` ON `group_meta`(`metalake_id`, `group_name`, `deleted_at`, `updated_at`);
2628

2729
CREATE TABLE IF NOT EXISTS `entity_change_log` (
2830
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',

0 commit comments

Comments
 (0)