diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/JDBCBackend.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/JDBCBackend.java index 219ef6c27ea..8c261d4edb2 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/JDBCBackend.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/JDBCBackend.java @@ -224,12 +224,20 @@ public int hardDeleteLegacyData(Entity.EntityType entityType, long legacyTimeLin return TopicMetaService.getInstance() .deleteTopicMetasByLegacyTimeLine( legacyTimeLine, GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT); - - case COLUMN: case USER: + return UserMetaService.getInstance() + .deleteUserMetasByLegacyTimeLine( + legacyTimeLine, GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT); case GROUP: - case AUDIT: + return GroupMetaService.getInstance() + .deleteGroupMetasByLegacyTimeLine( + legacyTimeLine, GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT); case ROLE: + return RoleMetaService.getInstance() + .deleteRoleMetasByLegacyTimeLine( + legacyTimeLine, GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT); + case COLUMN: + case AUDIT: return 0; // TODO: Implement hard delete logic for these entity types. diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupMetaMapper.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupMetaMapper.java index bd7f00e6ab5..2ee109f51f8 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupMetaMapper.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupMetaMapper.java @@ -7,6 +7,7 @@ import com.datastrato.gravitino.storage.relational.po.GroupPO; import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; @@ -132,4 +133,11 @@ Integer updateGroupMeta( + " WHERE re.role_id = #{roleId}" + " AND gr.deleted_at = 0 AND re.deleted_at = 0") List listGroupsByRoleId(@Param("roleId") Long roleId); + + @Delete( + "DELETE FROM " + + GROUP_TABLE_NAME + + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeLine} LIMIT #{limit}") + Integer deleteGroupMetasByLegacyTimeLine( + @Param("legacyTimeLine") Long legacyTimeLine, @Param("limit") int limit); } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupRoleRelMapper.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupRoleRelMapper.java index 819ead7771b..4297a937813 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupRoleRelMapper.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/GroupRoleRelMapper.java @@ -7,6 +7,7 @@ import com.datastrato.gravitino.storage.relational.po.GroupRoleRelPO; import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; @@ -109,4 +110,11 @@ void softDeleteGroupRoleRelByGroupAndRoles( + " SET deleted_at = UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000.0" + " WHERE role_id = #{roleId} AND deleted_at = 0") void softDeleteGroupRoleRelByRoleId(Long roleId); + + @Delete( + "DELETE FROM " + + GROUP_ROLE_RELATION_TABLE_NAME + + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeLine} LIMIT #{limit}") + Integer deleteGroupRoleRelMetasByLegacyTimeLine( + @Param("legacyTimeLine") Long legacyTimeLine, @Param("limit") int limit); } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/RoleMetaMapper.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/RoleMetaMapper.java index 04067a814e5..805d5bd5a0b 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/RoleMetaMapper.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/RoleMetaMapper.java @@ -7,6 +7,7 @@ import com.datastrato.gravitino.storage.relational.po.RolePO; import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; @@ -161,4 +162,11 @@ Long selectRoleIdByMetalakeIdAndName( + " SET deleted_at = UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000.0" + " WHERE metalake_id = #{metalakeId} AND deleted_at = 0") void softDeleteRoleMetasByMetalakeId(@Param("metalakeId") Long metalakeId); + + @Delete( + "DELETE FROM " + + ROLE_TABLE_NAME + + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeLine} LIMIT #{limit}") + Integer deleteRoleMetasByLegacyTimeLine( + @Param("legacyTimeLine") Long legacyTimeLine, @Param("limit") int limit); } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserMetaMapper.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserMetaMapper.java index 8ac7219a1cd..69df436a481 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserMetaMapper.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserMetaMapper.java @@ -7,6 +7,7 @@ import com.datastrato.gravitino.storage.relational.po.UserPO; import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; @@ -132,4 +133,11 @@ Integer updateUserMeta( + " WHERE re.role_id = #{roleId}" + " AND us.deleted_at = 0 AND re.deleted_at = 0") List listUsersByRoleId(@Param("roleId") Long roleId); + + @Delete( + "DELETE FROM " + + USER_TABLE_NAME + + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeLine} LIMIT #{limit}") + Integer deleteUserMetasByLegacyTimeLine( + @Param("legacyTimeLine") Long legacyTimeLine, @Param("limit") int limit); } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserRoleRelMapper.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserRoleRelMapper.java index 735e5f7d435..3135476f55a 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserRoleRelMapper.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/mapper/UserRoleRelMapper.java @@ -7,6 +7,7 @@ import com.datastrato.gravitino.storage.relational.po.UserRoleRelPO; import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; @@ -109,4 +110,11 @@ void softDeleteUserRoleRelByUserAndRoles( + " SET deleted_at = UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000.0" + " WHERE role_id = #{roleId} AND deleted_at = 0") void softDeleteUserRoleRelByRoleId(@Param("roleId") Long roleId); + + @Delete( + "DELETE FROM " + + USER_ROLE_RELATION_TABLE_NAME + + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeLine} LIMIT #{limit}") + Integer deleteUserRoleRelMetasByLegacyTimeLine( + @Param("legacyTimeLine") Long legacyTimeLine, @Param("limit") int limit); } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/service/GroupMetaService.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/service/GroupMetaService.java index 966c20655fd..2da4a0e106d 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/service/GroupMetaService.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/service/GroupMetaService.java @@ -210,4 +210,24 @@ public GroupEntity updateGroup( } return newEntity; } + + public int deleteGroupMetasByLegacyTimeLine(long legacyTimeLine, int limit) { + int[] groupDeletedCount = new int[] {0}; + int[] groupRoleRelDeletedCount = new int[] {0}; + + SessionUtils.doMultipleWithCommit( + () -> + groupDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + GroupMetaMapper.class, + mapper -> mapper.deleteGroupMetasByLegacyTimeLine(legacyTimeLine, limit)), + () -> + groupRoleRelDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + GroupRoleRelMapper.class, + mapper -> + mapper.deleteGroupRoleRelMetasByLegacyTimeLine(legacyTimeLine, limit))); + + return groupDeletedCount[0] + groupRoleRelDeletedCount[0]; + } } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/service/RoleMetaService.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/service/RoleMetaService.java index 88f34f0cd40..394b3b8082b 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/service/RoleMetaService.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/service/RoleMetaService.java @@ -123,4 +123,30 @@ public boolean deleteRole(NameIdentifier identifier) { GroupRoleRelMapper.class, mapper -> mapper.softDeleteGroupRoleRelByRoleId(roleId))); return true; } + + public int deleteRoleMetasByLegacyTimeLine(long legacyTimeLine, int limit) { + int[] roleDeletedCount = new int[] {0}; + int[] userRoleRelDeletedCount = new int[] {0}; + int[] groupRoleRelDeletedCount = new int[] {0}; + + SessionUtils.doMultipleWithCommit( + () -> + roleDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + RoleMetaMapper.class, + mapper -> mapper.deleteRoleMetasByLegacyTimeLine(legacyTimeLine, limit)), + () -> + userRoleRelDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + UserRoleRelMapper.class, + mapper -> mapper.deleteUserRoleRelMetasByLegacyTimeLine(legacyTimeLine, limit)), + () -> + groupRoleRelDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + GroupRoleRelMapper.class, + mapper -> + mapper.deleteGroupRoleRelMetasByLegacyTimeLine(legacyTimeLine, limit))); + + return roleDeletedCount[0] + userRoleRelDeletedCount[0] + groupRoleRelDeletedCount[0]; + } } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/service/UserMetaService.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/service/UserMetaService.java index 13da552d9ef..3caf8af7257 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/service/UserMetaService.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/service/UserMetaService.java @@ -207,4 +207,24 @@ public UserEntity updateUser( } return newEntity; } + + public int deleteUserMetasByLegacyTimeLine(long legacyTimeLine, int limit) { + int[] userDeletedCount = new int[] {0}; + int[] userRoleRelDeletedCount = new int[] {0}; + + SessionUtils.doMultipleWithCommit( + () -> + userDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + UserMetaMapper.class, + mapper -> mapper.deleteUserMetasByLegacyTimeLine(legacyTimeLine, limit)), + () -> + userRoleRelDeletedCount[0] = + SessionUtils.doWithoutCommitAndFetchResult( + UserRoleRelMapper.class, + mapper -> + mapper.deleteUserRoleRelMetasByLegacyTimeLine(legacyTimeLine, limit))); + + return userDeletedCount[0] + userRoleRelDeletedCount[0]; + } } diff --git a/core/src/main/java/com/datastrato/gravitino/storage/relational/utils/SessionUtils.java b/core/src/main/java/com/datastrato/gravitino/storage/relational/utils/SessionUtils.java index fb092eb14cd..ca73df86b99 100644 --- a/core/src/main/java/com/datastrato/gravitino/storage/relational/utils/SessionUtils.java +++ b/core/src/main/java/com/datastrato/gravitino/storage/relational/utils/SessionUtils.java @@ -63,6 +63,21 @@ public static R doWithCommitAndFetchResult(Class mapperClazz, Function } } + /** + * This method is used to perform a database operation without a commit and fetch the result. If + * the operation fails, will throw the RuntimeException. + * + * @param mapperClazz mapper class to be used for the operation + * @param func the operation to be performed with the mapper + * @return the result of the operation + * @param the type of the mapper + * @param the type of the result + */ + public static R doWithoutCommitAndFetchResult(Class mapperClazz, Function func) { + T mapper = SqlSessions.getMapper(mapperClazz); + return func.apply(mapper); + } + /** * This method is used to perform a database operation without a commit. If the operation fails, * will throw the RuntimeException. @@ -72,12 +87,8 @@ public static R doWithCommitAndFetchResult(Class mapperClazz, Function * @param the type of the mapper */ public static void doWithoutCommit(Class mapperClazz, Consumer consumer) { - try { - T mapper = SqlSessions.getMapper(mapperClazz); - consumer.accept(mapper); - } catch (Throwable t) { - throw t; - } + T mapper = SqlSessions.getMapper(mapperClazz); + consumer.accept(mapper); } /** diff --git a/core/src/test/java/com/datastrato/gravitino/storage/relational/TestJDBCBackend.java b/core/src/test/java/com/datastrato/gravitino/storage/relational/TestJDBCBackend.java index ff7fc638a3c..a928782a730 100644 --- a/core/src/test/java/com/datastrato/gravitino/storage/relational/TestJDBCBackend.java +++ b/core/src/test/java/com/datastrato/gravitino/storage/relational/TestJDBCBackend.java @@ -22,6 +22,7 @@ import com.datastrato.gravitino.Configs; import com.datastrato.gravitino.Entity; import com.datastrato.gravitino.Namespace; +import com.datastrato.gravitino.authorization.AuthorizationUtils; import com.datastrato.gravitino.authorization.Privileges; import com.datastrato.gravitino.authorization.SecurableObject; import com.datastrato.gravitino.authorization.SecurableObjects; @@ -39,7 +40,11 @@ import com.datastrato.gravitino.meta.TopicEntity; import com.datastrato.gravitino.meta.UserEntity; import com.datastrato.gravitino.storage.RandomIdGenerator; +import com.datastrato.gravitino.storage.relational.mapper.GroupMetaMapper; +import com.datastrato.gravitino.storage.relational.mapper.UserMetaMapper; +import com.datastrato.gravitino.storage.relational.service.RoleMetaService; import com.datastrato.gravitino.storage.relational.session.SqlSessionFactoryHelper; +import com.datastrato.gravitino.storage.relational.utils.SessionUtils; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import java.io.File; @@ -466,6 +471,34 @@ public void testMetaLifeCycleFromCreationToDeletion() throws IOException { filesetV2.properties().put("version", "2"); backend.update(fileset.nameIdentifier(), Entity.EntityType.FILESET, e -> filesetV2); + RoleEntity role = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace("metalake"), + "role", + auditInfo); + backend.insert(role, false); + + UserEntity user = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace("metalake"), + "user", + auditInfo, + Lists.newArrayList(role.name()), + Lists.newArrayList(role.id())); + backend.insert(user, false); + + GroupEntity group = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofGroupNamespace("metalake"), + "group", + auditInfo, + Lists.newArrayList(role.name()), + Lists.newArrayList(role.id())); + backend.insert(group, false); + // another meta data creation BaseMetalake anotherMetaLake = createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), "another-metalake", auditInfo); @@ -515,6 +548,34 @@ public void testMetaLifeCycleFromCreationToDeletion() throws IOException { backend.update( anotherFileset.nameIdentifier(), Entity.EntityType.FILESET, e -> anotherFilesetV3); + RoleEntity anotherRole = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace("another-metalake"), + "another-role", + auditInfo); + backend.insert(anotherRole, false); + + UserEntity anotherUser = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace("another-metalake"), + "another-user", + auditInfo, + Lists.newArrayList(anotherRole.name()), + Lists.newArrayList(anotherRole.id())); + backend.insert(anotherUser, false); + + GroupEntity anotherGroup = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofGroupNamespace("another-metalake"), + "another-group", + auditInfo, + Lists.newArrayList(anotherRole.name()), + Lists.newArrayList(anotherRole.id())); + backend.insert(anotherGroup, false); + // meta data list List metaLakes = backend.list(metalake.namespace(), Entity.EntityType.METALAKE); assertTrue(metaLakes.contains(metalake)); @@ -536,6 +597,27 @@ public void testMetaLifeCycleFromCreationToDeletion() throws IOException { List topics = backend.list(topic.namespace(), Entity.EntityType.TOPIC); assertTrue(topics.contains(topic)); + RoleEntity roleEntity = backend.get(role.nameIdentifier(), Entity.EntityType.ROLE); + assertEquals(role, roleEntity); + assertEquals(1, RoleMetaService.getInstance().listRolesByUserId(user.id()).size()); + assertEquals(1, RoleMetaService.getInstance().listRolesByGroupId(group.id()).size()); + + UserEntity userEntity = backend.get(user.nameIdentifier(), Entity.EntityType.USER); + assertEquals(user, userEntity); + assertEquals( + 1, + SessionUtils.doWithCommitAndFetchResult( + UserMetaMapper.class, mapper -> mapper.listUsersByRoleId(role.id())) + .size()); + + GroupEntity groupEntity = backend.get(group.nameIdentifier(), Entity.EntityType.GROUP); + assertEquals(group, groupEntity); + assertEquals( + 1, + SessionUtils.doWithCommitAndFetchResult( + GroupMetaMapper.class, mapper -> mapper.listGroupsByRoleId(role.id())) + .size()); + // meta data soft delete backend.delete(metalake.nameIdentifier(), Entity.EntityType.METALAKE, true); @@ -555,6 +637,27 @@ public void testMetaLifeCycleFromCreationToDeletion() throws IOException { assertFalse(backend.exists(table.nameIdentifier(), Entity.EntityType.TABLE)); assertFalse(backend.exists(topic.nameIdentifier(), Entity.EntityType.TOPIC)); + assertFalse(backend.exists(role.nameIdentifier(), Entity.EntityType.ROLE)); + assertEquals(0, RoleMetaService.getInstance().listRolesByUserId(user.id()).size()); + assertEquals(0, RoleMetaService.getInstance().listRolesByGroupId(group.id()).size()); + assertTrue(backend.exists(anotherRole.nameIdentifier(), Entity.EntityType.ROLE)); + + assertFalse(backend.exists(user.nameIdentifier(), Entity.EntityType.USER)); + assertEquals( + 0, + SessionUtils.doWithCommitAndFetchResult( + UserMetaMapper.class, mapper -> mapper.listUsersByRoleId(role.id())) + .size()); + assertTrue(backend.exists(anotherUser.nameIdentifier(), Entity.EntityType.USER)); + + assertFalse(backend.exists(group.nameIdentifier(), Entity.EntityType.GROUP)); + assertEquals( + 0, + SessionUtils.doWithCommitAndFetchResult( + GroupMetaMapper.class, mapper -> mapper.listGroupsByRoleId(role.id())) + .size()); + assertTrue(backend.exists(anotherGroup.nameIdentifier(), Entity.EntityType.GROUP)); + // check legacy record after soft delete assertTrue(legacyRecordExistsInDB(metalake.id(), Entity.EntityType.METALAKE)); assertTrue(legacyRecordExistsInDB(catalog.id(), Entity.EntityType.CATALOG)); @@ -562,6 +665,11 @@ public void testMetaLifeCycleFromCreationToDeletion() throws IOException { assertTrue(legacyRecordExistsInDB(table.id(), Entity.EntityType.TABLE)); assertTrue(legacyRecordExistsInDB(topic.id(), Entity.EntityType.TOPIC)); assertTrue(legacyRecordExistsInDB(fileset.id(), Entity.EntityType.FILESET)); + assertTrue(legacyRecordExistsInDB(role.id(), Entity.EntityType.ROLE)); + assertTrue(legacyRecordExistsInDB(user.id(), Entity.EntityType.USER)); + assertTrue(legacyRecordExistsInDB(group.id(), Entity.EntityType.GROUP)); + assertEquals(2, countRoleRels(role.id())); + assertEquals(2, countRoleRels(anotherRole.id())); assertEquals(2, listFilesetVersions(fileset.id()).size()); assertEquals(3, listFilesetVersions(anotherFileset.id()).size()); @@ -575,6 +683,11 @@ public void testMetaLifeCycleFromCreationToDeletion() throws IOException { assertFalse(legacyRecordExistsInDB(table.id(), Entity.EntityType.TABLE)); assertFalse(legacyRecordExistsInDB(fileset.id(), Entity.EntityType.FILESET)); assertFalse(legacyRecordExistsInDB(topic.id(), Entity.EntityType.TOPIC)); + assertFalse(legacyRecordExistsInDB(role.id(), Entity.EntityType.ROLE)); + assertFalse(legacyRecordExistsInDB(user.id(), Entity.EntityType.USER)); + assertFalse(legacyRecordExistsInDB(group.id(), Entity.EntityType.GROUP)); + assertEquals(0, countRoleRels(role.id())); + assertEquals(2, countRoleRels(anotherRole.id())); assertEquals(0, listFilesetVersions(fileset.id()).size()); // soft delete for old version fileset @@ -621,6 +734,18 @@ private boolean legacyRecordExistsInDB(Long id, Entity.EntityType entityType) { tableName = "topic_meta"; idColumnName = "topic_id"; break; + case ROLE: + tableName = "role_meta"; + idColumnName = "role_id"; + break; + case USER: + tableName = "user_meta"; + idColumnName = "user_id"; + break; + case GROUP: + tableName = "group_meta"; + idColumnName = "group_id"; + break; default: throw new IllegalArgumentException("Unsupported entity type: " + entityType); } @@ -660,6 +785,31 @@ private Map listFilesetVersions(Long filesetId) { return versionDeletedTime; } + private Integer countRoleRels(Long roleId) { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement1 = connection.createStatement(); + ResultSet rs1 = + statement1.executeQuery( + String.format("SELECT count(*) FROM user_role_rel WHERE role_id = %d", roleId)); + Statement statement2 = connection.createStatement(); + ResultSet rs2 = + statement2.executeQuery( + String.format("SELECT count(*) FROM group_role_rel WHERE role_id = %d", roleId))) { + while (rs1.next()) { + count += rs1.getInt(1); + } + while (rs2.next()) { + count += rs2.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } + public static BaseMetalake createBaseMakeLake(Long id, String name, AuditInfo auditInfo) { return BaseMetalake.builder() .withId(id) diff --git a/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestGroupMetaService.java b/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestGroupMetaService.java index 4adef8db3e3..9d93ff24bc7 100644 --- a/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestGroupMetaService.java +++ b/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestGroupMetaService.java @@ -16,12 +16,18 @@ import com.datastrato.gravitino.storage.relational.TestJDBCBackend; import com.datastrato.gravitino.storage.relational.mapper.RoleMetaMapper; import com.datastrato.gravitino.storage.relational.po.RolePO; +import com.datastrato.gravitino.storage.relational.session.SqlSessionFactoryHelper; import com.datastrato.gravitino.storage.relational.utils.SessionUtils; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.time.Instant; import java.util.List; import java.util.function.Function; +import org.apache.ibatis.session.SqlSession; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -611,4 +617,167 @@ void deleteMetalakeCascade() { Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group1.id()).size()); Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group2.id()).size()); } + + @Test + void deleteGroupMetasByLegacyTimeLine() { + AuditInfo auditInfo = + AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build(); + BaseMetalake metalake = + createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), metalakeName, auditInfo); + backend.insert(metalake, false); + + GroupMetaService groupMetaService = GroupMetaService.getInstance(); + RoleMetaService roleMetaService = RoleMetaService.getInstance(); + + RoleEntity role1 = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace(metalakeName), + "role1", + auditInfo); + RoleEntity role2 = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace(metalakeName), + "role2", + auditInfo); + roleMetaService.insertRole(role1, false); + roleMetaService.insertRole(role2, false); + + GroupEntity group1 = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "group1", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + GroupEntity group2 = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "group2", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + GroupEntity group3 = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "group3", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + GroupEntity group4 = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "group4", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + groupMetaService.insertGroup(group1, false); + groupMetaService.insertGroup(group2, false); + groupMetaService.insertGroup(group3, false); + groupMetaService.insertGroup(group4, false); + + // hard delete before soft delete + int deletedCount = + groupMetaService.deleteGroupMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 4); + Assertions.assertEquals(0, deletedCount); + Assertions.assertEquals( + group1.name(), groupMetaService.getGroupByIdentifier(group1.nameIdentifier()).name()); + Assertions.assertEquals( + group2.name(), groupMetaService.getGroupByIdentifier(group2.nameIdentifier()).name()); + Assertions.assertEquals( + group3.name(), groupMetaService.getGroupByIdentifier(group3.nameIdentifier()).name()); + Assertions.assertEquals( + group4.name(), groupMetaService.getGroupByIdentifier(group4.nameIdentifier()).name()); + Assertions.assertEquals(2, roleMetaService.listRolesByGroupId(group1.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByGroupId(group2.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByGroupId(group3.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByGroupId(group4.id()).size()); + Assertions.assertEquals(4, countGroups(metalake.id())); + Assertions.assertEquals(8, countGroupRoleRels()); + + // delete metalake + Assertions.assertTrue( + MetalakeMetaService.getInstance().deleteMetalake(metalake.nameIdentifier(), true)); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> groupMetaService.getGroupByIdentifier(group1.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> groupMetaService.getGroupByIdentifier(group2.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> groupMetaService.getGroupByIdentifier(group3.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> groupMetaService.getGroupByIdentifier(group4.nameIdentifier())); + Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group1.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group2.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group3.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group4.id()).size()); + Assertions.assertEquals(4, countGroups(metalake.id())); + Assertions.assertEquals(8, countGroupRoleRels()); + + // hard delete after soft delete + deletedCount = + groupMetaService.deleteGroupMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(6, deletedCount); // delete 3 group + 3 groupRoleRel + Assertions.assertEquals(1, countGroups(metalake.id())); // 4 - 3 + Assertions.assertEquals(5, countGroupRoleRels()); // 8 - 3 + + deletedCount = + groupMetaService.deleteGroupMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(4, deletedCount); // delete 1 group + 3 groupRoleRel + Assertions.assertEquals(0, countGroups(metalake.id())); + Assertions.assertEquals(2, countGroupRoleRels()); // 5 - 3 + + deletedCount = + groupMetaService.deleteGroupMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(2, deletedCount); + Assertions.assertEquals(0, countGroups(metalake.id())); + Assertions.assertEquals(0, countGroupRoleRels()); + + deletedCount = + groupMetaService.deleteGroupMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(0, deletedCount); // no more to delete + } + + private Integer countGroups(Long metalakeId) { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = + statement.executeQuery( + String.format( + "SELECT count(*) FROM group_meta WHERE metalake_id = %d", metalakeId))) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } + + private Integer countGroupRoleRels() { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery("SELECT count(*) FROM group_role_rel")) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } } diff --git a/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestRoleMetaService.java b/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestRoleMetaService.java index b2759ef2d47..7e9a826793e 100644 --- a/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestRoleMetaService.java +++ b/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestRoleMetaService.java @@ -21,11 +21,17 @@ import com.datastrato.gravitino.storage.relational.mapper.UserMetaMapper; import com.datastrato.gravitino.storage.relational.po.GroupPO; import com.datastrato.gravitino.storage.relational.po.UserPO; +import com.datastrato.gravitino.storage.relational.session.SqlSessionFactoryHelper; import com.datastrato.gravitino.storage.relational.utils.SessionUtils; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.time.Instant; import java.util.List; +import org.apache.ibatis.session.SqlSession; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -437,4 +443,161 @@ void deleteMetalakeCascade() { GroupMetaMapper.class, mapper -> mapper.listGroupsByRoleId(role2.id())) .isEmpty()); } + + @Test + void deleteRoleMetasByLegacyTimeLine() { + AuditInfo auditInfo = + AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build(); + BaseMetalake metalake = + createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), metalakeName, auditInfo); + backend.insert(metalake, false); + + UserMetaService userMetaService = UserMetaService.getInstance(); + GroupMetaService groupMetaService = GroupMetaService.getInstance(); + RoleMetaService roleMetaService = RoleMetaService.getInstance(); + + RoleEntity role1 = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace(metalakeName), + "role1", + auditInfo); + RoleEntity role2 = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace(metalakeName), + "role2", + auditInfo); + roleMetaService.insertRole(role1, false); + roleMetaService.insertRole(role2, false); + + UserEntity user1 = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "user1", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + userMetaService.insertUser(user1, false); + + GroupEntity group1 = + createGroupEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "group1", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + groupMetaService.insertGroup(group1, false); + + // hard delete before soft delete + int deletedCount = + roleMetaService.deleteRoleMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 4); + Assertions.assertEquals(0, deletedCount); + + Assertions.assertEquals(role1, roleMetaService.getRoleByIdentifier(role1.nameIdentifier())); + Assertions.assertEquals(role2, roleMetaService.getRoleByIdentifier(role2.nameIdentifier())); + Assertions.assertEquals( + group1.name(), groupMetaService.getGroupByIdentifier(group1.nameIdentifier()).name()); + Assertions.assertEquals( + user1.name(), userMetaService.getUserByIdentifier(user1.nameIdentifier()).name()); + Assertions.assertEquals(2, roleMetaService.listRolesByUserId(user1.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByGroupId(group1.id()).size()); + Assertions.assertEquals(2, countRoles(metalake.id())); + Assertions.assertEquals(2, countUserRoleRels()); + Assertions.assertEquals(2, countGroupRoleRels()); + + // delete metalake + Assertions.assertTrue( + MetalakeMetaService.getInstance().deleteMetalake(metalake.nameIdentifier(), true)); + + Assertions.assertThrows( + NoSuchEntityException.class, + () -> roleMetaService.getRoleByIdentifier(role1.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> roleMetaService.getRoleByIdentifier(role2.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> groupMetaService.getGroupByIdentifier(group1.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> userMetaService.getUserByIdentifier(user1.nameIdentifier())); + Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(user1.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByGroupId(group1.id()).size()); + Assertions.assertEquals(2, countRoles(metalake.id())); + Assertions.assertEquals(2, countUserRoleRels()); + Assertions.assertEquals(2, countGroupRoleRels()); + + // hard delete after soft delete + deletedCount = + roleMetaService.deleteRoleMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 1); + Assertions.assertEquals(3, deletedCount); // delete 1 role + 1 userRoleRel + 1 groupRoleRel + Assertions.assertEquals(1, countRoles(metalake.id())); // 2 - 1 + Assertions.assertEquals(1, countUserRoleRels()); // 2 - 1 + Assertions.assertEquals(1, countGroupRoleRels()); // 2 - 1 + + deletedCount = + roleMetaService.deleteRoleMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 1); + Assertions.assertEquals(3, deletedCount); + Assertions.assertEquals(0, countRoles(metalake.id())); + Assertions.assertEquals(0, countUserRoleRels()); + Assertions.assertEquals(0, countGroupRoleRels()); + + deletedCount = + roleMetaService.deleteRoleMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 1); + Assertions.assertEquals(0, deletedCount); // no more to delete + } + + private Integer countRoles(Long metalakeId) { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = + statement.executeQuery( + String.format( + "SELECT count(*) FROM role_meta WHERE metalake_id = %d", metalakeId))) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } + + private Integer countUserRoleRels() { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery("SELECT count(*) FROM user_role_rel")) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } + + private Integer countGroupRoleRels() { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery("SELECT count(*) FROM group_role_rel")) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } } diff --git a/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestUserMetaService.java b/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestUserMetaService.java index dabd372c12f..5cd36b328d2 100644 --- a/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestUserMetaService.java +++ b/core/src/test/java/com/datastrato/gravitino/storage/relational/service/TestUserMetaService.java @@ -16,12 +16,18 @@ import com.datastrato.gravitino.storage.relational.TestJDBCBackend; import com.datastrato.gravitino.storage.relational.mapper.RoleMetaMapper; import com.datastrato.gravitino.storage.relational.po.RolePO; +import com.datastrato.gravitino.storage.relational.session.SqlSessionFactoryHelper; import com.datastrato.gravitino.storage.relational.utils.SessionUtils; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.time.Instant; import java.util.List; import java.util.function.Function; +import org.apache.ibatis.session.SqlSession; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -609,4 +615,167 @@ void deleteMetalakeCascade() { Assertions.assertEquals(0, roleMetaService.listRolesByUserId(user1.id()).size()); Assertions.assertEquals(0, roleMetaService.listRolesByUserId(user2.id()).size()); } + + @Test + void deleteUserMetasByLegacyTimeLine() { + AuditInfo auditInfo = + AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build(); + BaseMetalake metalake = + createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), metalakeName, auditInfo); + backend.insert(metalake, false); + + UserMetaService userMetaService = UserMetaService.getInstance(); + RoleMetaService roleMetaService = RoleMetaService.getInstance(); + + RoleEntity role1 = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace(metalakeName), + "role1", + auditInfo); + RoleEntity role2 = + createRoleEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofRoleNamespace(metalakeName), + "role2", + auditInfo); + roleMetaService.insertRole(role1, false); + roleMetaService.insertRole(role2, false); + + UserEntity user1 = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "user1", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + UserEntity user2 = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "user2", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + UserEntity user3 = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "user3", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + UserEntity user4 = + createUserEntity( + RandomIdGenerator.INSTANCE.nextId(), + AuthorizationUtils.ofUserNamespace(metalakeName), + "user4", + auditInfo, + Lists.newArrayList(role1.name(), role2.name()), + Lists.newArrayList(role1.id(), role2.id())); + userMetaService.insertUser(user1, false); + userMetaService.insertUser(user2, false); + userMetaService.insertUser(user3, false); + userMetaService.insertUser(user4, false); + + // hard delete before soft delete + int deletedCount = + userMetaService.deleteUserMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 4); + Assertions.assertEquals(0, deletedCount); + Assertions.assertEquals( + user1.name(), userMetaService.getUserByIdentifier(user1.nameIdentifier()).name()); + Assertions.assertEquals( + user2.name(), userMetaService.getUserByIdentifier(user2.nameIdentifier()).name()); + Assertions.assertEquals( + user3.name(), userMetaService.getUserByIdentifier(user3.nameIdentifier()).name()); + Assertions.assertEquals( + user4.name(), userMetaService.getUserByIdentifier(user4.nameIdentifier()).name()); + Assertions.assertEquals(2, roleMetaService.listRolesByUserId(user1.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByUserId(user2.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByUserId(user3.id()).size()); + Assertions.assertEquals(2, roleMetaService.listRolesByUserId(user4.id()).size()); + Assertions.assertEquals(4, countUsers(metalake.id())); + Assertions.assertEquals(8, countUserRoleRels()); + + // delete metalake + Assertions.assertTrue( + MetalakeMetaService.getInstance().deleteMetalake(metalake.nameIdentifier(), true)); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> userMetaService.getUserByIdentifier(user1.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> userMetaService.getUserByIdentifier(user2.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> userMetaService.getUserByIdentifier(user3.nameIdentifier())); + Assertions.assertThrows( + NoSuchEntityException.class, + () -> userMetaService.getUserByIdentifier(user4.nameIdentifier())); + Assertions.assertEquals(0, roleMetaService.listRolesByUserId(user1.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByUserId(user2.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByUserId(user3.id()).size()); + Assertions.assertEquals(0, roleMetaService.listRolesByUserId(user4.id()).size()); + Assertions.assertEquals(4, countUsers(metalake.id())); + Assertions.assertEquals(8, countUserRoleRels()); + + // hard delete after soft delete + deletedCount = + userMetaService.deleteUserMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(6, deletedCount); // delete 3 user + 3 userRoleRel + Assertions.assertEquals(1, countUsers(metalake.id())); // 4 - 3 + Assertions.assertEquals(5, countUserRoleRels()); // 8 - 3 + + deletedCount = + userMetaService.deleteUserMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(4, deletedCount); // delete 1 user + 3 userRoleRel + Assertions.assertEquals(0, countUsers(metalake.id())); + Assertions.assertEquals(2, countUserRoleRels()); // 5 - 3 + + deletedCount = + userMetaService.deleteUserMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(2, deletedCount); + Assertions.assertEquals(0, countUsers(metalake.id())); + Assertions.assertEquals(0, countUserRoleRels()); + + deletedCount = + userMetaService.deleteUserMetasByLegacyTimeLine(Instant.now().toEpochMilli() + 1000, 3); + Assertions.assertEquals(0, deletedCount); // no more to delete + } + + private Integer countUsers(Long metalakeId) { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = + statement.executeQuery( + String.format( + "SELECT count(*) FROM user_meta WHERE metalake_id = %d", metalakeId))) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } + + private Integer countUserRoleRels() { + int count = 0; + try (SqlSession sqlSession = + SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true); + Connection connection = sqlSession.getConnection(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery("SELECT count(*) FROM user_role_rel")) { + while (rs.next()) { + count = rs.getInt(1); + } + } catch (SQLException e) { + throw new RuntimeException("SQL execution failed", e); + } + return count; + } }