Bitwarden Server Seeder 数据密度验证:verification.md 的 Q0–Q11 SQL 查询与预设期望值详解
【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server
本文以 Bitwarden 开源仓库中util/Seeder/Seeds/docs/verification.md为主体,讲解如何用手写 SQL 查询(Q0–Q11)验证 Seeder 密度算法(density modeling)的输出是否符合预设(Scale 与 Validation 预设)的期望分布,并完整覆盖文档中 9 个 Scale 预设与 4 个 Validation 预设的逐项期望值。读完本文,你将能够:对任意一个dotnet run -- preset --name {name} --mangle生成的组织,独立跑通整套验证查询、对照期望值做 pass/fail 判定,并理解每条查询背后对应哪一条密度参数(membership.shape、collectionFanOut、directAccessRatio、cipherAssignment.*等)。
文档定位:谁来用、什么时候用
verification.md开头就明确了自己的读者画像:面向开发者(Developer-facing),用于在跑完某个 Scale 或 Validation 预设后,核对数据库中的实际分布与预设 JSON 中声明的密度参数是否一致,"正常 Seeder 用户无需关心"。文档推荐的两种使用方式:
- 手动模式(Manual Usage):先执行 Q0 按组织名查出 Organization ID,再把该 ID 粘贴进其余查询。
- 让 Claude Code 代跑:文档给出的标准三步——
- 播种预设:
dotnet run -- preset --name scale.md-balanced-sterling-cooper --mangle; - 把 Seeder 输出(含 Org ID)贴给 Claude Code,并要求"对 Org ID {id} 执行 Q1–Q8,并与 verification.md 中 Sterling Cooper 的期望值对比";
- Claude Code 逐条执行查询、输出 pass/fail 表格并标记超差项。
- 播种预设:
文档特别指出:密度算法最初就是这样被验证的——Claude Code 跑完所有查询、对照期望值逐项判定,当场暴露出若干分布 bug 并在同一会话中被修复。因此这套查询同时充当了回归检查手册的角色。
验证查询全集(Q0–Q11)
所有查询都带NOLOCK提示、使用@OrgId UNIQUEIDENTIFIER变量,直接面向 SQL Server 的dboschema 表(Organization、Group、GroupUser、Collection、CollectionGroup、CollectionUser、CollectionCipher、Cipher、OrganizationUser)。以下按文档顺序完整列出。
Q0:找到 Organization ID
SELECT Id, [Name] FROM [dbo].[Organization] WITH (NOLOCK) WHERE [Name] = 'PASTE_ORG_NAME_HERE';拿到Id后,把下面所有查询里的PASTE_ORG_ID_HERE替换掉即可。
Q1:Group 成员分布
验证membership.shape与membership.skew。成员计数应体现 Uniform(大致均等)、PowerLaw(递减)或 MegaGroup(一个占绝对多数)三种形态之一:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT G.[Name], COUNT(GU.OrganizationUserId) AS Members FROM [dbo].[Group] G WITH (NOLOCK) LEFT JOIN [dbo].[GroupUser] GU WITH (NOLOCK) ON G.Id = GU.GroupId WHERE G.OrganizationId = @OrgId GROUP BY G.[Name] ORDER BY Members DESC;Q2:CollectionGroup 记录总数
验证collectionFanOut。总数应落在collections * min到collections * max之间:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS CollectionGroupCount FROM [dbo].[CollectionGroup] CG WITH (NOLOCK) INNER JOIN [dbo].[Collection] C WITH (NOLOCK) ON CG.CollectionId = C.Id WHERE C.OrganizationId = @OrgId;Q3:权限分布
验证permissions权重;零权重权限必须产生零条记录。查询用UNION ALL把CollectionUser与CollectionGroup两个来源合并统计,ReadWrite 定义为三个标志位全 0:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT 'CollectionUser' AS [Source], COUNT(*) AS Total, SUM(CASE WHEN CU.ReadOnly = 1 THEN 1 ELSE 0 END) AS ReadOnly, SUM(CASE WHEN CU.Manage = 1 THEN 1 ELSE 0 END) AS Manage, SUM(CASE WHEN CU.HidePasswords = 1 THEN 1 ELSE 0 END) AS HidePasswords, SUM(CASE WHEN CU.ReadOnly = 0 AND CU.Manage = 0 AND CU.HidePasswords = 0 THEN 1 ELSE 0 END) AS ReadWrite FROM [dbo].[CollectionUser] CU WITH (NOLOCK) INNER JOIN [dbo].[OrganizationUser] OU WITH (NOLOCK) ON CU.OrganizationUserId = OU.Id WHERE OU.OrganizationId = @OrgId UNION ALL SELECT 'CollectionGroup', COUNT(*), SUM(CASE WHEN CG.ReadOnly = 1 THEN 1 ELSE 0 END), SUM(CASE WHEN CG.Manage = 1 THEN 1 ELSE 0 END), SUM(CASE WHEN CG.HidePasswords = 1 THEN 1 ELSE 0 END), SUM(CASE WHEN CG.ReadOnly = 0 AND CG.Manage = 0 AND CG.HidePasswords = 0 THEN 1 ELSE 0 END) FROM [dbo].[CollectionGroup] CG WITH (NOLOCK) INNER JOIN [dbo].[Collection] C WITH (NOLOCK) ON CG.CollectionId = C.Id WHERE C.OrganizationId = @OrgId;Q4:孤儿 Cipher(Orphan Ciphers)
验证cipherAssignment.orphanRate。孤儿指没有任何CollectionCipher分配的 cipher:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS TotalCiphers, SUM(CASE WHEN CC.CipherId IS NULL THEN 1 ELSE 0 END) AS Orphans FROM [dbo].[Cipher] CI WITH (NOLOCK) LEFT JOIN (SELECT DISTINCT CipherId FROM [dbo].[CollectionCipher] WITH (NOLOCK)) CC ON CI.Id = CC.CipherId WHERE CI.OrganizationId = @OrgId;Q5:Direct Access 比例
验证directAccessRatio。文档强调计算式为int(userCount * directAccessRatio) / userCount,因此小组织会出现截断误差;注意总用户数只统计[Status] = 2(Confirmed):
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT TotalOrgUsers, UsersWithDirectAccess, CAST(UsersWithDirectAccess AS FLOAT) / NULLIF(TotalOrgUsers, 0) AS DirectAccessRatio FROM ( SELECT (SELECT COUNT(*) FROM [dbo].[OrganizationUser] WITH (NOLOCK) WHERE OrganizationId = @OrgId AND [Status] = 2) AS TotalOrgUsers, (SELECT COUNT(DISTINCT CU.OrganizationUserId) FROM [dbo].[CollectionUser] CU WITH (NOLOCK) INNER JOIN [dbo].[OrganizationUser] OU WITH (NOLOCK) ON CU.OrganizationUserId = OU.Id WHERE OU.OrganizationId = @OrgId) AS UsersWithDirectAccess ) T;Q6:Cipher 分布形态
验证cipherAssignment.skew。变异系数 CV 接近 0 表示 uniform,CV 偏高表示 heavyRight:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS Collections, MIN(CipherCount) AS MinCiphers, MAX(CipherCount) AS MaxCiphers, AVG(CipherCount) AS AvgCiphers, CASE WHEN AVG(CipherCount) > 0 THEN STDEV(CipherCount) / AVG(CipherCount) ELSE 0 END AS CoefficientOfVariation FROM ( SELECT C.Id, COUNT(CC.CipherId) AS CipherCount FROM [dbo].[Collection] C WITH (NOLOCK) LEFT JOIN [dbo].[CollectionCipher] CC WITH (NOLOCK) ON C.Id = CC.CollectionId WHERE C.OrganizationId = @OrgId GROUP BY C.Id ) T;Q7:每用户 Collection 数分布
验证userCollections。CV 接近 0 = uniform,CV > 0.5 = power-law:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS UsersWithDirectAccess, MIN(CollectionCount) AS MinCollections, MAX(CollectionCount) AS MaxCollections, AVG(CollectionCount) AS AvgCollections, CASE WHEN AVG(CollectionCount) > 0 THEN STDEV(CollectionCount) / AVG(CollectionCount) ELSE 0 END AS CoefficientOfVariation FROM ( SELECT CU.OrganizationUserId, COUNT(DISTINCT CU.CollectionId) AS CollectionCount FROM [dbo].[CollectionUser] CU WITH (NOLOCK) INNER JOIN [dbo].[OrganizationUser] OU WITH (NOLOCK) ON CU.OrganizationUserId = OU.Id WHERE OU.OrganizationId = @OrgId GROUP BY CU.OrganizationUserId ) T;Q8:多 Collection Cipher 比例
验证cipherAssignment.multiCollectionRate,比例应近似配置值:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS TotalAssignedCiphers, SUM(CASE WHEN CollectionCount > 1 THEN 1 ELSE 0 END) AS MultiCollectionCiphers, MAX(CollectionCount) AS MaxCollectionsPerCipher FROM ( SELECT CC.CipherId, COUNT(DISTINCT CC.CollectionId) AS CollectionCount FROM [dbo].[CollectionCipher] CC WITH (NOLOCK) INNER JOIN [dbo].[Cipher] CI WITH (NOLOCK) ON CC.CipherId = CI.Id WHERE CI.OrganizationId = @OrgId GROUP BY CC.CipherId ) T;Q9:已删除的组织 Cipher
验证cipherAssignment.deletedRate与maxDeletedCiphers,适用于所有 Scale 预设的组织 cipher:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS TotalOrgCiphers, SUM(CASE WHEN DeletedDate IS NOT NULL THEN 1 ELSE 0 END) AS DeletedCiphers FROM [dbo].[Cipher] WITH (NOLOCK) WHERE OrganizationId = @OrgId;Q10:个人池中的 Archived/Deleted Cipher
验证cipherAssignment.archivedRate、deletedRate、archivedAndDeletedOverlapRate,但对象是个人(非组织)cipher 池——仅适用于配置了density.personalCiphers的预设(Sterling Cooper、Wayne Enterprises、Weyland-Yutani)。由于个人 cipher 不挂OrganizationId,查询通过OrganizationUser限定到被播种组织的用户;归档判定复用dbo.CipherDetails中计算ArchivedDate的同款 JSON 路径模式(Archives列中按大写 UserId 取值):
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS TotalPersonalCiphers, SUM(CASE WHEN JSON_VALUE(C.[Archives], CONCAT('$."', UPPER(CONVERT(VARCHAR(36), C.UserId)), '"')) IS NOT NULL THEN 1 ELSE 0 END) AS ArchivedCiphers, SUM(CASE WHEN C.DeletedDate IS NOT NULL THEN 1 ELSE 0 END) AS DeletedCiphers, SUM(CASE WHEN C.DeletedDate IS NOT NULL AND JSON_VALUE(C.[Archives], CONCAT('$."', UPPER(CONVERT(VARCHAR(36), C.UserId)), '"')) IS NOT NULL THEN 1 ELSE 0 END) AS BothArchivedAndDeleted FROM [dbo].[Cipher] C WITH (NOLOCK) WHERE C.OrganizationId IS NULL AND C.UserId IN ( SELECT OU.UserId FROM [dbo].[OrganizationUser] OU WITH (NOLOCK) WHERE OU.OrganizationId = @OrgId );Q11:组织池中的 Archived/Deleted Cipher
同样验证三个 rate,但对象是组织 cipher,适用于每个Scale 预设。文档解释:组织 cipher 的归档是"独立"于个人池的,且归档对象是轮询(round-robin)选出的某位组织成员(见GenerateCiphersStep),因此只需检查Archives列是否非空,无需像 Q10 那样限定到具体用户:
DECLARE @OrgId UNIQUEIDENTIFIER = 'PASTE_ORG_ID_HERE'; SELECT COUNT(*) AS TotalOrgCiphers, SUM(CASE WHEN Archives IS NOT NULL THEN 1 ELSE 0 END) AS ArchivedCiphers, SUM(CASE WHEN DeletedDate IS NOT NULL THEN 1 ELSE 0 END) AS DeletedCiphers, SUM(CASE WHEN DeletedDate IS NOT NULL AND Archives IS NOT NULL THEN 1 ELSE 0 END) AS BothArchivedAndDeleted FROM [dbo].[Cipher] WITH (NOLOCK) WHERE OrganizationId = @OrgId;源码侧印证:期望值从哪里来
文档中的每个"Check"都对应预设 JSON 里density块的一个参数。以 Sterling Cooper 预设 为例,其density块是:
"density": { "membership": { "shape": "powerLaw", "skew": 0.6 }, "collectionFanOut": { "min": 1, "max": 5, "shape": "powerLaw", "emptyGroupRate": 0.1 }, "directAccessRatio": 0.5, "permissions": { "readOnly": 0.55, "readWrite": 0.20, "manage": 0.15, "hidePasswords": 0.10 }, "cipherAssignment": { "skew": "heavyRight", "orphanRate": 0.08, "multiCollectionRate": 0.20, "maxCollectionsPerCipher": 3, "deletedRate": 0.03, "archivedRate": 0.06, "archivedAndDeletedOverlapRate": 0.02 }, "userCollections": { "min": 1, "max": 10, "shape": "powerLaw", "skew": 0.5 }, "personalCiphers": { "shape": "realistic" } }与 verification.md 中 Sterling Cooper 的期望值表逐行对应:PowerLaw(skew 0.6) 的 50 组 → Q1;fan-out 1–5 × 500 个 collection → Q2 的 500–2,500 区间;directAccessRatio 0.5→ Q5;四档权限权重 → Q3;orphanRate 0.08× 5,000 → Q4 的约 400 条孤儿;archivedRate 0.06(=300)被钳制到上限 50 → Q11 的 50 条归档。再看 Initech 预设:directAccessRatio: 1.0解释了为什么该预设 Q2 期望 0 条 CollectionGroup("DirectAccessRatio 是 1.0,CollectionGroup 生成被整体跳过"),orphanRate: 0.85解释了 Q4 期望 85% 孤儿。
从源码结构看,这些参数由util/Seeder/Steps/下的步骤消费:CreateGroupsStep(成员分布)、CreateCollectionsStep(fan-out)、CreateCipherCollectionsStep与GenerateCiphersStep(cipher 分配、归档/删除,Q11 注释即指向GenerateCiphersStep)。参数与实现的映射关系是文档中每条期望值"可复算"的根本原因——这也是 Q0–Q11 能充当回归检查的前提。
Scale 预设期望值(9 个预设全量)
以下完整继承 verification.md 的期望值表。判定标准:与预设 JSON 的density参数对照,容差内视为 pass。
1. Central Perk(XS)
| Check | Expected |
|---|---|
| Membership shape | Uniform — 2 组,每组约 3 人。 |
| CollectionGroups | 10–20 条记录。每 collection 1–2 组的 uniform fan-out。 |
| Permissions | 约 50% Manage、40% ReadWrite、10% ReadOnly、0% HidePasswords。 |
| Orphan ciphers | 0 / 200(0% 孤儿率)。 |
| Direct access ratio | 0.8 —— 约 80% 的访问路径是直接 CollectionUser。 |
| Collections per user | Uniform 1–3。Min=1,Max=3,Avg=2。 |
| Multi-collection rate | 200 个非孤儿 cipher 中 20% 位于 2 个 collection,约 40 条。 |
| Archived org ciphers | 约 8 / 200(4% 率,低于 50 上限)。 |
| Deleted org ciphers | 约 4 / 200(2% 率,低于 25 上限);其中约 2 条同时归档(1% 重叠),约 2 条仅删除。 |
2. Planet Express(SM)
| Check | Expected |
|---|---|
| Membership shape | PowerLaw(skew 0.4) —— 首组最大,8 组间温和递减。 |
| CollectionGroups | 200–400 条。每 collection 2–4 组的 uniform fan-out。 |
| Permissions | 约 40% ReadOnly、30% ReadWrite、25% Manage、5% HidePasswords。 |
| Orphan ciphers | 约 37 / 750(5%)。 |
| Direct access ratio | 0.7。 |
| Collections per user | PowerLaw 1–5(skew 0.3)。首用户最多 5 个,多数 1–2。CV > 0.3。 |
| Multi-collection rate | 约 713 个非孤儿 cipher 中 15% 位于 2 个 collection,约 107 条。 |
| Archived org ciphers | 约 45 / 750(6%)。 |
| Deleted org ciphers | 约 22 / 750(3%);约 15 条同时归档(2% 重叠),约 7 条仅删除。 |
3. Bluth Company(SM)
| Check | Expected |
|---|---|
| Membership shape | PowerLaw(skew 0.7) —— 4 组间陡降,首组占绝对多数。 |
| CollectionGroups | 25–125 条。每 collection 1–5 组的 PowerLaw fan-out。 |
| Permissions | 约 82% ReadOnly、9% ReadWrite、5% Manage、4% HidePasswords。 |
| Orphan ciphers | 约 75 / 500(15%)。 |
| Direct access ratio | 0.6。 |
| Collections per user | Uniform 1–3。Min=1,Max=3,Avg=2。 |
| Multi-collection rate | 约 425 个非孤儿 cipher 中 10%,约 42 条。 |
| Archived org ciphers | 约 40 / 500(8%)。 |
| Deleted org ciphers | 约 20 / 500(4%);约 15 条同时归档(3% 重叠),约 5 条仅删除。 |
4. Sterling Cooper(MD)
| Check | Expected |
|---|---|
| Membership shape | PowerLaw(skew 0.6) —— 50 组间中度递减。 |
| CollectionGroups | 500–2,500 条。每 collection 1–5 个活跃组的 PowerLaw fan-out。 |
| Permissions | 约 55% ReadOnly、20% ReadWrite、15% Manage、10% HidePasswords。 |
| Orphan ciphers | 约 400 / 5,000(8%)。 |
| Direct access ratio | 0.5 —— 直接访问与组中介访问大致各半。 |
| Empty group rate | 约 26% —— 50 组中约 13 组因幂律尾部截断而 0 成员。 |
| Collections per user | PowerLaw 1–10(skew 0.5)。首用户最多 10 个,多数 1–2。CV > 0.5。 |
| Multi-collection rate | 约 4,600 个非孤儿 cipher 中 20% 位于 2–3 个 collection,单 cipher 最多 3 个。 |
| Archived org ciphers | 50 / 5,000(6% = 300,钳制到 50 上限)。 |
| Deleted org ciphers | 25 / 5,000(3% = 150,钳制到 25 上限)。25 条全部同时归档(2% 重叠 = 100,钳制后)——0 条仅删除。 |
| Archived personal ciphers | 约 3,638 个个人 cipher 中的 50 个(6% = 约 218,钳制到 50)。 |
| Deleted personal ciphers | 约 3,638 中的 25 个(3% 钳制到 25)。25 条全部同时归档(2% 重叠 = 约 73,钳制到min(archivedTarget, maxDeletedCiphers)= 25)——0 条仅删除。该钳制可对照ArchiveAndDeleteRateTests.BothTarget_NeverExceedsMaxDeletedCiphers_EvenWhenArchivedCeilingIsHigher单测。Sterling Cooper 的归档/删除项同时落在组织池与个人池,因为archivedRate/deletedRate/archivedAndDeletedOverlapRate对两个池分别独立生效。 |
5. Umbrella Corp(MD)
| Check | Expected |
|---|---|
| Membership shape | MegaGroup(skew 0.5) —— 组 1 约 72% 成员,其余 7 组平分剩余。 |
| CollectionGroups | 800–2,400 条。每 collection 1–3 组的 FrontLoaded fan-out。 |
| Permissions | 约 50% ReadWrite、20% Manage、20% ReadOnly、10% HidePasswords。 |
| Orphan ciphers | 约 600 / 3,000(20%)。 |
| Direct access ratio | 0.9。 |
| Collections per user | PowerLaw 1–15(skew 0.6)。CV > 0.5。 |
| Multi-collection rate | 约 2,400 个非孤儿 cipher 中 25% 位于 2–3 个 collection,单 cipher 最多 3 个。 |
| Archived org ciphers | 50 / 3,000(8% = 240,钳制到 50)。 |
| Deleted org ciphers | 25 / 3,000(4% = 120,钳制到 25)。25 条全部同时归档(3% 重叠 = 90,钳制后)——0 条仅删除。 |
6. Wayne Enterprises(LG)
| Check | Expected |
|---|---|
| Membership shape | PowerLaw(skew 0.7) —— 100 组间陡降,首几组明显更大。 |
| CollectionGroups | 2,000–10,000 条。每 collection 1–5 个活跃组的 PowerLaw fan-out。 |
| Permissions | 约 82% ReadOnly、9% ReadWrite、5% Manage、4% HidePasswords。 |
| Orphan ciphers | 约 1,000 / 10,000(10%)。 |
| Direct access ratio | 0.5。 |
| Empty group rate | 约 30% —— 100 组中约 30 组 0 成员(幂律尾部截断)。 |
| Collections per user | PowerLaw 1–25(skew 0.6)。CV > 0.5。 |
| Multi-collection rate | 约 9,000 个非孤儿 cipher 中 25% 位于 2–4 个 collection,单 cipher 最多 4 个。 |
| Archived org ciphers | 50 / 10,000(6% = 600,钳制到 50)。 |
| Deleted org ciphers | 25 / 10,000(3% = 300,钳制到 25)。25 条全部同时归档(2% 重叠 = 200,钳制后)——0 条仅删除。 |
| Archived personal ciphers | 约 14,525 个个人 cipher 中的 50 个(6% = 约 872,钳制到 50)。 |
| Deleted personal ciphers | 约 14,525 中的 25 个(3% 钳制到 25)。25 条全部同时归档(2% 重叠,钳制方式同 Sterling Cooper)——0 条仅删除。Wayne Enterprises 的组织池与个人池都播种归档/删除项。 |
7. Tyrell Corp(LG)
| Check | Expected |
|---|---|
| Membership shape | PowerLaw(skew 0.8) —— 75 组间极陡递减,首组非常大。 |
| CollectionGroups | 4,600–18,400 条。每 collection 2–8 个活跃组的 PowerLaw fan-out。 |
| Permissions | 约 82% ReadOnly、9% ReadWrite、5% Manage、4% HidePasswords。 |
| Orphan ciphers | 约 2,550 / 17,000(15%)。 |
| Direct access ratio | 0.6。 |
| Empty group rate | 20% —— 75 组中约 15 组 0 成员。 |
| Collections per user | PowerLaw 1–30(skew 0.7)。CV > 0.5。 |
| Multi-collection rate | 约 14,450 个非孤儿 cipher 中 30% 位于 2–4 个 collection,单 cipher 最多 4 个。 |
| Archived org ciphers | 50 / 17,000(8% = 1,360,钳制到 50)。 |
| Deleted org ciphers | 25 / 17,000(4% = 680,钳制到 25)。25 条全部同时归档(3% 重叠 = 510,钳制后)——0 条仅删除。 |
8. Weyland-Yutani(XL)
| Check | Expected |
|---|---|
| Membership shape | PowerLaw(skew 0.8) —— 500 组间极陡递减,长尾小群体。 |
| CollectionGroups | 1,200–3,600 条。每 collection 1–3 个活跃组的 PowerLaw fan-out。 |
| Permissions | 约 55% ReadWrite、25% ReadOnly、10% Manage、10% HidePasswords。 |
| Orphan ciphers | 约 1,500 / 15,000(10%)。 |
| Direct access ratio | 0.4 —— 多数访问经组中介。 |
| Empty group rate | 约 68% —— 500 组中约 341 组 0 成员(幂律尾部截断)。 |
| Collections per user | PowerLaw 1–50(skew 0.8)。CV > 0.5。 |
| Multi-collection rate | 约 13,500 个非孤儿 cipher 中 30% 位于 2–5 个 collection,单 cipher 最多 5 个。 |
| Archived org ciphers | 50 / 15,000(4% = 600,钳制到 50)。 |
| Deleted org ciphers | 25 / 15,000(3% = 450,钳制到 25)。25 条全部同时归档(1% 重叠 = 150,钳制后)——0 条仅删除。 |
| Archived personal ciphers | 约 11,000 个个人 cipher 中的 50 个(4% = 440,钳制到 50)。 |
| Deleted personal ciphers | 约 11,000 中的 25 个(3% 钳制到 25)。25 条全部同时归档(1% 重叠 = 110,同式钳制)——0 条仅删除。Weyland-Yutani 同样在组织池与个人池双池播种归档/删除项。 |
9. Initech(XL)
| Check | Expected |
|---|---|
| Membership shape | MegaGroup(skew 0.95) —— 组 1 约 93% 成员,其余 4 组平分剩余。 |
| CollectionGroups | 0 条记录。DirectAccessRatio 是 1.0,CollectionGroup 生成被整体跳过。 |
| Permissions | 约 30% Manage、30% ReadWrite、30% ReadOnly、10% HidePasswords。 |
| Orphan ciphers | 约 12,750 / 15,000(85%)。 |
| Direct access ratio | 1.0 —— 100% 直接 CollectionUser 访问。 |
| Collections per user | PowerLaw 1–20(skew 0.5)。首用户最多 20 个,多数 1 个。CV > 0.2。 |
| Multi-collection rate | 约 2,250 个非孤儿 cipher 中 15% 位于 2–3 个 collection,单 cipher 最多 3 个。 |
| Archived org ciphers | 50 / 15,000(8% = 1,200,钳制到 50)。 |
| Deleted org ciphers | 25 / 15,000(5% = 750,钳制到 25)。25 条全部同时归档(3% 重叠 = 450,钳制后)——0 条仅删除。 |
Validation 预设期望值(4 个预设全量)
Validation 预设用于 Seeder 开发阶段的算法验证(非通用用途),逐个跑--mangle后对照下表的"Check/Expected"判定。
1. Power-Law Distribution
dotnet run -- preset --name validation.density-modeling-power-law-test --mangle| Check | Expected |
|---|---|
| Groups | 10 组。首组约 50 人,递减到 1;最后 2 组 0 成员(20% 空组率)。 |
| CollectionGroups | > 0 条。靠前 collection 分到的组更多(PowerLaw fan-out)。 |
| Permissions | 约 50% ReadOnly、30% ReadWrite、15% Manage、5% HidePasswords。 |
| Orphan ciphers | 约 50 / 500(10%)。 |
| DirectAccessRatio | 0.6。 |
2. MegaGroup Distribution
dotnet run -- preset --name validation.density-modeling-mega-group-test --mangle| Check | Expected |
|---|---|
| Groups | 5 组。组 1 约 90 人(90.5%);组 2–5 平分约 10 人。 |
| CollectionUsers | 0 条。DirectAccessRatio 为 0.0 —— 全部访问经组。 |
| CollectionGroups | > 0。前 10 个 collection 各得 3 组(FrontLoaded),其余各 1 组。 |
| Permissions | ReadOnly / ReadWrite / Manage / HidePasswords 各 25%(均匀切分)。 |
3. Empty Groups
dotnet run -- preset --name validation.density-modeling-empty-groups-test --mangle| Check | Expected |
|---|---|
| Groups | 共 10 组:5 组各约 10 人,5 组 0 人(50% 空组)。 |
| CollectionGroups | 只引用那 5 个非空组。 |
| DirectAccessRatio | 0.5 —— 约一半用户获得直接 CollectionUser 记录。 |
4. No Density(Baseline,基线)
dotnet run -- preset --name validation.density-modeling-no-density-test --mangle| Check | Expected |
|---|---|
| Groups | 5 组,每组约 10 人(uniform round-robin)。 |
| CollectionGroups | 0 条。无 density = 不生成 CollectionGroup。 |
| Permissions | 每用户首次分配为 Manage,之后为 ReadOnly(原始轮转模式)。 |
| Orphan ciphers | 0。每个 cipher 至少分配进一个 collection。 |
延伸阅读路径
- 预设目录与各预设组成(org + roster + ciphers 的装配方式):presets.md,其中 Scale 表列出了 9 个预设的 Users/Groups/Collections/Ciphers 规模,与本文期望值表相互印证;
- 密度参数 JSON Schema(字段取值范围的权威定义):
util/Seeder/Seeds/schemas/preset.schema.json(在 presets.md 中被声明为 source of truth); - Fixture 与 Preset 的分层架构:architecture.md;
- Seeder 使用入口与 Quick Start:util/Seeder/Seeds/README.md 与 util/Seeder/README.md。
适用前提:本文所有查询面向 SQL Server 的dboschema 与NOLOCK语义,验证对象为通过dotnet run -- preset --name {name} --mangle播种的 Bitwarden Seeder 数据;--mangle会改写组织名以避免与既有数据冲突,因此 Q0 应按 Seeder 实际打印的组织名检索。
【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考