public void saveV2(ImageWriter imageWriter) throws IOException {
        try {
            // 1 json for myself,1 json for number of users, 2 json for each user(kv)
            // 1 json for number of roles, 2 json for each role(kv)
            final int cnt = 1 + 1 + userToPrivilegeCollection.size() * 2
                    + 1 + roleIdToPrivilegeCollection.size() * 2;
            SRMetaBlockWriter writer = imageWriter.getBlockWriter(SRMetaBlockID.AUTHORIZATION_MGR, cnt);
            // 1 json for myself
            writer.writeJson(this);
            // 1 json for num user
            writer.writeInt(userToPrivilegeCollection.size());
            for (Map.Entry<UserIdentity, UserPrivilegeCollectionV2> entry : userToPrivilegeCollection.entrySet()) {
                writer.writeJson(entry.getKey());
                writer.writeJson(entry.getValue());
            }
            // 1 json for num roles
            writer.writeInt(roleIdToPrivilegeCollection.size());
            for (Map.Entry<Long, RolePrivilegeCollectionV2> entry : roleIdToPrivilegeCollection.entrySet()) {
                RolePrivilegeCollectionV2 value = entry.getValue();
                // Avoid newly added PEntryObject type corrupt forward compatibility,
                // since built-in roles are always initialized on startup, we don't need to persist them.
                // But to keep the correct relationship with roles inherited from them, we still need to persist
                // an empty role for them, just for the role id.
                if (PrivilegeBuiltinConstants.IMMUTABLE_BUILT_IN_ROLE_IDS.contains(entry.getKey())) {
                    // clone to avoid race condition
                    RolePrivilegeCollectionV2 clone = value.cloneSelf();
                    clone.typeToPrivilegeEntryList = new HashMap<>();
                    value = clone;
                }
                writer.writeLong(entry.getKey());
                writer.writeJson(value);
            }
            writer.close();
        } catch (SRMetaBlockException e) {
            throw new IOException("failed to save AuthenticationManager!", e);
        }
    }
@Test
    public void testWontSavePrivCollForBuiltInRole() throws Exception {
        GlobalStateMgr masterGlobalStateMgr = ctx.getGlobalStateMgr();
        AuthorizationMgr authorizationMgr = masterGlobalStateMgr.getAuthorizationMgr();

        UtFrameUtils.PseudoJournalReplayer.resetFollowerJournalQueue();
        UtFrameUtils.PseudoImage emptyImage = new UtFrameUtils.PseudoImage();
        authorizationMgr.saveV2(emptyImage.getImageWriter());

        SRMetaBlockReader reader = new SRMetaBlockReaderV2(emptyImage.getJsonReader());
        // read the whole first
        reader.readJson(AuthorizationMgr.class);
        // read the number of user
        int numUser = reader.readInt();
        // there should be only 2 users: root and test_user
        Assert.assertEquals(2, numUser);

        // read users and ignore them
        for (int i = 0; i != numUser; ++i) {
            // 2 json for each user(kv)
            reader.readJson(UserIdentity.class);
            reader.readJson(UserPrivilegeCollectionV2.class);
        }

        // read the number of roles
        int numRole = reader.readInt();
        for (int i = 0; i != numRole; ++i) {
            // 2 json for each role(kv)
            Long roleId = reader.readLong();
            RolePrivilegeCollectionV2 collection = reader.readJson(RolePrivilegeCollectionV2.class);
            if (PrivilegeBuiltinConstants.IMMUTABLE_BUILT_IN_ROLE_IDS.contains(roleId)) {
                // built-in role's priv collection should not be saved
                Assert.assertTrue(collection.typeToPrivilegeEntryList.isEmpty());
            }
        }
    }