@harperfast/harper 5.2.8 → 5.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config-root.schema.json +10 -0
- package/dist/resources/DatabaseTransaction.d.ts +3 -0
- package/dist/resources/DatabaseTransaction.js +90 -2
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.js +2 -1
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RocksIndexStore.js +2 -1
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/Table.js +13 -5
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.js +142 -53
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/databases.d.ts +9 -0
- package/dist/resources/databases.js +315 -76
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/search.d.ts +1 -0
- package/dist/resources/search.js +41 -13
- package/dist/resources/search.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +7 -0
- package/dist/server/storageReclamation.js +10 -0
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +1 -0
- package/dist/server/threads/manageThreads.js +7 -0
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/utility/hdbTerms.d.ts +2 -0
- package/dist/utility/hdbTerms.js +2 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/rocksMemoryConfig.js +7 -4
- package/dist/utility/rocksMemoryConfig.js.map +1 -1
- package/dist/validation/configValidator.js +2 -0
- package/dist/validation/configValidator.js.map +1 -1
- package/npm-shrinkwrap.json +41 -41
- package/package.json +2 -2
- package/resources/DatabaseTransaction.ts +88 -2
- package/resources/PrimaryRocksDatabase.ts +2 -1
- package/resources/RocksIndexStore.ts +2 -1
- package/resources/Table.ts +13 -5
- package/resources/auditStore.ts +144 -52
- package/resources/databases.ts +296 -70
- package/resources/search.ts +43 -14
- package/server/storageReclamation.ts +10 -0
- package/server/threads/manageThreads.js +7 -0
- package/studio/web/assets/Chat-Br06zdMA.js +2267 -0
- package/studio/web/assets/FloatingChat-BWImX5fA.js +23 -0
- package/studio/web/assets/{abnfDiagram-VCTEODGH-C0_BAZyO.js → abnfDiagram-VCTEODGH-B0BebmD2.js} +1 -1
- package/studio/web/assets/{alertDialog-DIHt7Z0r.js → alertDialog-CQyAJJhl.js} +1 -1
- package/studio/web/assets/{apiToken-CrPb_98N.js → apiToken-DN0nmDsq.js} +1 -1
- package/studio/web/assets/applications-kSxVoyeU.js +296 -0
- package/studio/web/assets/architecture-7GRP2DOG-LB-MLAAb.js +1 -0
- package/studio/web/assets/{architectureDiagram-5GKGNRK7-BWzrASgm.js → architectureDiagram-5GKGNRK7-7SW3GD-K.js} +1 -1
- package/studio/web/assets/authStore-C3Nfubqr.js +3 -0
- package/studio/web/assets/{blockDiagram-NRAW4CY4-BdJX9Khj.js → blockDiagram-I7D4REHJ-BqguiadH.js} +2 -2
- package/studio/web/assets/{button-DhiX-njv.js → button-BIsUKRZq.js} +2 -2
- package/studio/web/assets/{c4Diagram-UCG6FXSJ-CI6MzGmQ.js → c4Diagram-7LVT6UL2-LBNf8t_X.js} +1 -1
- package/studio/web/assets/channel-yictG-U-.js +1 -0
- package/studio/web/assets/{chevron-up-Df2c8uoU.js → chevron-up-DtKGqDn3.js} +1 -1
- package/studio/web/assets/{chunk-TEH6E4GO-P87k5mNi.js → chunk-4HAMMTFA-DWtTut21.js} +1 -1
- package/studio/web/assets/{chunk-75Z2AOVW-BT8tVmks.js → chunk-75Z2AOVW-QGQD6th2.js} +1 -1
- package/studio/web/assets/{chunk-DU6HZSFF-9kAOOmI6.js → chunk-DU6HZSFF-Chq20Ba5.js} +1 -1
- package/studio/web/assets/{chunk-F27PBJKO-BW7ao8AY.js → chunk-F27PBJKO-BVA5EPhV.js} +1 -1
- package/studio/web/assets/{chunk-GMAD6QVW-BNyXpoQO.js → chunk-GMAD6QVW-BeS7S07A.js} +1 -1
- package/studio/web/assets/{chunk-OBVCFTLP-D4wWOqDQ.js → chunk-GVQU2GXP-sbwVIQ8i.js} +1 -1
- package/studio/web/assets/{chunk-G27WJ6UU-COyLMcgK.js → chunk-IMKFNOWR-Bnh3tAVd.js} +1 -1
- package/studio/web/assets/{chunk-JQ64N6SF-Cyz1IeLf.js → chunk-L3NEJ4N5-COfUyKII.js} +1 -1
- package/studio/web/assets/chunk-OSK3NFVY-ByciRftO.js +10 -0
- package/studio/web/assets/{chunk-P2QGCYS3-DmIFY4d7.js → chunk-P2QGCYS3-CP1VhG_c.js} +1 -1
- package/studio/web/assets/{chunk-POPQ4Y6H-BPrvMyKz.js → chunk-POPQ4Y6H-ClWhhkwW.js} +1 -1
- package/studio/web/assets/{chunk-PWAF6VOD-2zB6IW9i.js → chunk-PWAF6VOD-1z1THyS5.js} +1 -1
- package/studio/web/assets/{chunk-RHFEMEQ7-2FgyI8YU.js → chunk-SHT3W25Y-LpQkMsah.js} +2 -2
- package/studio/web/assets/{chunk-SVP7TREG-FwtbH2QC.js → chunk-SVP7TREG-jtdAHw0S.js} +1 -1
- package/studio/web/assets/{chunk-LCL6LL3I-HOzK_ppE.js → chunk-TICWLB2K-VOwzetX-.js} +1 -1
- package/studio/web/assets/classDiagram-ZZMXUADV-VaEwSy_g.js +1 -0
- package/studio/web/assets/classDiagram-v2-VYDZK3BY-VaEwSy_g.js +1 -0
- package/studio/web/assets/{createLucideIcon-BKGPfjm2.js → createLucideIcon-CzW9508A.js} +1 -1
- package/studio/web/assets/{cssMode-D8ceA4av.js → cssMode-C1JeufH5.js} +1 -1
- package/studio/web/assets/{cynefin-OW5HDTMX-BRkpLFQV.js → cynefin-OW5HDTMX-BbdbCvub.js} +1 -1
- package/studio/web/assets/{cynefinDiagram-5FMLGOSQ-CHT1DaX6.js → cynefinDiagram-5FMLGOSQ-TP-aIqbt.js} +1 -1
- package/studio/web/assets/{dagre-3AP2YEHR-DpUXBh63.js → dagre-GXQ25YYZ-DShnGpGo.js} +1 -1
- package/studio/web/assets/{diagram-S7CK7UJ4-BuymVFZT.js → diagram-S7CK7UJ4-aoCVTtcy.js} +1 -1
- package/studio/web/assets/{diagram-UQ7AKVKN-CyP148RM.js → diagram-UQ7AKVKN-DglXtQ6x.js} +1 -1
- package/studio/web/assets/{diagram-VSXAHHWV-CoCAg3M9.js → diagram-VSXAHHWV-fhEdmkwM.js} +1 -1
- package/studio/web/assets/{diagram-VX7I27RA-BpOqCFca.js → diagram-VX7I27RA-DccVJet6.js} +1 -1
- package/studio/web/assets/{diagram-Z3DM3KII-Bfpw7Vbj.js → diagram-Z3DM3KII-D-RyJJb7.js} +1 -1
- package/studio/web/assets/{dialog-CBf0Mr1d.js → dialog-Cn2uWgD4.js} +1 -1
- package/studio/web/assets/{dist-lkA3O3eM.js → dist-DP8UjMB_.js} +1 -1
- package/studio/web/assets/{download-BtTOBem-.js → download-B5T5r7ss.js} +1 -1
- package/studio/web/assets/{ebnfDiagram-PWID7BFC-DS_6aWqL.js → ebnfDiagram-PWID7BFC-DJGpIpz_.js} +1 -1
- package/studio/web/assets/{editor-C-jlLd1G.js → editor-qoo9CrGO.js} +1 -1
- package/studio/web/assets/{erDiagram-SSCWMZ5O-DJNk6Fgw.js → erDiagram-RLTQ6QDP-CIfNlgkC.js} +1 -1
- package/studio/web/assets/eventmodeling-NTZA5JFV-CLxnp2CR.js +1 -0
- package/studio/web/assets/flowDiagram-HODETNUW-BIbhmz9f.js +1 -0
- package/studio/web/assets/{ganttDiagram-EL5Y4UJY-2pOExxMY.js → ganttDiagram-EL5Y4UJY-BxToTzzD.js} +1 -1
- package/studio/web/assets/{getAnalytics-D4LKGeVy.js → getAnalytics-GHK8ORfM.js} +1 -1
- package/studio/web/assets/{gitGraph-4MIJSDKK-CH5ZxwzF.js → gitGraph-4MIJSDKK-D2s2w8lE.js} +1 -1
- package/studio/web/assets/{gitGraphDiagram-WWUBYQGX-DVIsIhbO.js → gitGraphDiagram-WWUBYQGX-Dwntd4-x.js} +1 -1
- package/studio/web/assets/{html-BnuKFPjv.js → html-Dt4IIy04.js} +1 -1
- package/studio/web/assets/{htmlMode-DXM6YYUw.js → htmlMode-DXgKKr4C.js} +1 -1
- package/studio/web/assets/index-6onkYFOG.js +824 -0
- package/studio/web/assets/{index-Cxj2_wsl.css → index-7RMEgVG1.css} +1 -1
- package/studio/web/assets/index.lazy-BrCFnpNJ.js +2 -0
- package/studio/web/assets/{info-A6RAGUB7-CPQfTnaG.js → info-A6RAGUB7-DYjkvb0C.js} +1 -1
- package/studio/web/assets/{infoDiagram-RXCK75RN-DlwLYlwm.js → infoDiagram-27XIBGKW-Bnp1FJE5.js} +1 -1
- package/studio/web/assets/{ishikawaDiagram-5VMMS53U-BRXRp29U.js → ishikawaDiagram-5VMMS53U-D9Xh2r6X.js} +1 -1
- package/studio/web/assets/{javascript-CAuAkYxc.js → javascript-DNCQGUBc.js} +1 -1
- package/studio/web/assets/{journeyDiagram-EYS64GPL-B0ou8k0n.js → journeyDiagram-3NMN7TZE-CokIi6ll.js} +2 -2
- package/studio/web/assets/{jsonMode-Dny8hrU7.js → jsonMode-CR6HWruP.js} +1 -1
- package/studio/web/assets/{kanban-definition-3QL26DDD-uYg7iYzp.js → kanban-definition-UXKFOSKX-CukSFJfX.js} +1 -1
- package/studio/web/assets/{languageServices-D1uNqC6s.js → languageServices-BM4fI4rS.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-Bh-bvkyK.js → lspLanguageFeatures-DSa1ttcD.js} +1 -1
- package/studio/web/assets/{mermaid-parser.core-Ck-fC8b7.js → mermaid-parser.core-BlEsOWNO.js} +3 -3
- package/studio/web/assets/{mermaid.core-CP8aNNYm.js → mermaid.core-BlkGaMIH.js} +5 -5
- package/studio/web/assets/{mindmap-definition-FBJOCRG2-CgTZ-rit.js → mindmap-definition-YA3MSWOX-IprMc_0j.js} +1 -1
- package/studio/web/assets/{notifications-DsE0S3zT.js → notifications-BHXLnh6x.js} +1 -1
- package/studio/web/assets/notifications-CUmtIA6z.js +1 -0
- package/studio/web/assets/{packet-AYTQ26CC-DEyoPtPb.js → packet-AYTQ26CC-Bi3V04Zi.js} +1 -1
- package/studio/web/assets/{pegDiagram-XKGWAZYB-DrD-7sD9.js → pegDiagram-XKGWAZYB-BNuPDLZY.js} +1 -1
- package/studio/web/assets/{pie-WAS4IAKB-wjj-EI1d.js → pie-WAS4IAKB-_6DoDbng.js} +1 -1
- package/studio/web/assets/{pieDiagram-E7YTZNPT-GntqDCzv.js → pieDiagram-E7YTZNPT-DqNb6Ht2.js} +1 -1
- package/studio/web/assets/{profile-BIeCZr-w.js → profile-8BeFSF3j.js} +1 -1
- package/studio/web/assets/{quadrantDiagram-AXDQQJYC-0UeqXQGd.js → quadrantDiagram-AXDQQJYC-BGH9E2YR.js} +1 -1
- package/studio/web/assets/{radar-RG4KPBEZ-DFSA5h7k.js → radar-RG4KPBEZ-DDdVczcL.js} +1 -1
- package/studio/web/assets/{railroad-74A4TZTK-CaOUG9wR.js → railroad-74A4TZTK-BJUP4Jds.js} +1 -1
- package/studio/web/assets/railroad-abnf-HS5TGJTU-Bm3L1L0h.js +1 -0
- package/studio/web/assets/railroad-ebnf-LZEXJU2U-CKzLGlkw.js +1 -0
- package/studio/web/assets/railroad-peg-WCYAUIDC-S8xLjslx.js +1 -0
- package/studio/web/assets/{railroadDiagram-O6MQD6OU-yHUELZaV.js → railroadDiagram-O6MQD6OU-DGTPh2KZ.js} +1 -1
- package/studio/web/assets/{regions-B5lGduv5.js → regions-C8qR0HhD.js} +1 -1
- package/studio/web/assets/{register-Cvc1TpmQ.js → register-B4n5i0SD.js} +3 -3
- package/studio/web/assets/{requirementDiagram-EFPCY7ZU-DNEGFjuW.js → requirementDiagram-BXWQKSXE-BJnO6uLz.js} +1 -1
- package/studio/web/assets/{sankeyDiagram-P5KCCOFB-DpyAmSVR.js → sankeyDiagram-P5KCCOFB-0vSOdymH.js} +1 -1
- package/studio/web/assets/{sequenceDiagram-WJ2MYXX4-TyaT7xNk.js → sequenceDiagram-WJ2MYXX4-hETizDWE.js} +1 -1
- package/studio/web/assets/{setComponentFile-D8ZLDWhU.js → setComponentFile-g0_B0lgX.js} +1 -1
- package/studio/web/assets/{setup-D2az-Ue_.js → setup-B0CTj_Q5.js} +2 -2
- package/studio/web/assets/{stateDiagram-HBIQ2CUA-CeEdTArZ.js → stateDiagram-D77RDMKH-CdYQ_KtC.js} +1 -1
- package/studio/web/assets/stateDiagram-v2-MP3YSRHH-CdKuzQMT.js +1 -0
- package/studio/web/assets/status-C6Yib7-K.js +61 -0
- package/studio/web/assets/{swimlanes-XN3QIQJK-B54FmF46.js → swimlanes-42K2YHIH-B8cHIpU4.js} +1 -1
- package/studio/web/assets/swimlanesDiagram-VR7AAH4N-DmOSJwaH.js +8 -0
- package/studio/web/assets/{tabs-B_G5zscN.js → tabs-BrHu7gJi.js} +1 -1
- package/studio/web/assets/{timeline-definition-24CTP7MA-D-a9ujbo.js → timeline-definition-24CTP7MA-BJWYSXqF.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-BdIEw2rK.js → toggleHighContrast-Dgta7bVi.js} +1 -1
- package/studio/web/assets/{treeView-Q6P3EWNA-CrW_6JnS.js → treeView-Q6P3EWNA-qxe_v6CQ.js} +1 -1
- package/studio/web/assets/{treemap-WGGIJYW6-BxyYLdP_.js → treemap-WGGIJYW6-dDo97XXF.js} +1 -1
- package/studio/web/assets/{tsMode-CulNMwI9.js → tsMode-CH_jHvU-.js} +1 -1
- package/studio/web/assets/{typescript-BpRqjmvQ.js → typescript-Co9LCXd5.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-C7x-Yz9r.js → useEntityRestURL-wKC8NsC_.js} +1 -1
- package/studio/web/assets/{useLocalStorage-Dtj1QS8_.js → useLocalStorage-BqMR3D8_.js} +1 -1
- package/studio/web/assets/vendor-core-c2JRRJpV.js +58 -0
- package/studio/web/assets/vendor-datadog-CLUcJXOo.js +6 -0
- package/studio/web/assets/{vendor-react-Dyj4O3HE.js → vendor-react-CJV_K1u4.js} +1 -1
- package/studio/web/assets/vendor-tanstack-DxzraizX.js +1 -0
- package/studio/web/assets/{vendor-ui-vhu-UHhF.js → vendor-ui-BUjK0h8a.js} +2 -2
- package/studio/web/assets/{vennDiagram-4TSXK5OY-Cy7s7Mpy.js → vennDiagram-4TSXK5OY-A3i-lCdl.js} +1 -1
- package/studio/web/assets/{wardley-WFR3VGLG-BeBL35g2.js → wardley-WFR3VGLG-B0ik-_6g.js} +1 -1
- package/studio/web/assets/{wardleyDiagram-VM6X3IG4-BylmIGSg.js → wardleyDiagram-VM6X3IG4-CjrkKWUR.js} +1 -1
- package/studio/web/assets/{workers-B2WpJDK9.js → workers-CWeLxCXA.js} +1 -1
- package/studio/web/assets/x-DIzaLEdK.js +1 -0
- package/studio/web/assets/{xml-CZnimVSX.js → xml-BadC-0Rk.js} +1 -1
- package/studio/web/assets/{xychartDiagram-S5SC5T6Z-CoKALMXr.js → xychartDiagram-S5SC5T6Z-Biok4GYV.js} +1 -1
- package/studio/web/assets/{yaml-BwNuyZS-.js → yaml-BiUfxPbC.js} +1 -1
- package/studio/web/index.html +14 -14
- package/utility/hdbTerms.ts +2 -0
- package/utility/rocksMemoryConfig.ts +7 -4
- package/validation/configValidator.ts +2 -0
- package/studio/web/assets/Chat-DDlysEi_.js +0 -2067
- package/studio/web/assets/FloatingChat-DdJ3DZJX.js +0 -23
- package/studio/web/assets/applications-C6Y4nL_X.js +0 -296
- package/studio/web/assets/architecture-7GRP2DOG-DNdx5tEU.js +0 -1
- package/studio/web/assets/authStore-qKmCZcaf.js +0 -3
- package/studio/web/assets/channel-DtCV8PTL.js +0 -1
- package/studio/web/assets/chunk-R7TYR2AO-Irip67yr.js +0 -10
- package/studio/web/assets/classDiagram-DTDB5LWJ-DbO_dCNE.js +0 -1
- package/studio/web/assets/classDiagram-v2-JRS7N3AN-DbO_dCNE.js +0 -1
- package/studio/web/assets/eventmodeling-NTZA5JFV-5jbe4A5P.js +0 -1
- package/studio/web/assets/flowDiagram-A5DVABFB-Dp9Ezlow.js +0 -1
- package/studio/web/assets/index-BtTvoLf8.js +0 -824
- package/studio/web/assets/index.lazy-PVbC6xQh.js +0 -2
- package/studio/web/assets/notifications-DPTOF-SX.js +0 -1
- package/studio/web/assets/railroad-abnf-HS5TGJTU-Bc0Qi0WH.js +0 -1
- package/studio/web/assets/railroad-ebnf-LZEXJU2U-G8rVVZ2C.js +0 -1
- package/studio/web/assets/railroad-peg-WCYAUIDC-CrehKBhC.js +0 -1
- package/studio/web/assets/stateDiagram-v2-4QOOHH4V-D4tuw9Su.js +0 -1
- package/studio/web/assets/status-BJegPrWN.js +0 -61
- package/studio/web/assets/swimlanesDiagram-VK2B7HYN-XOhmNEvq.js +0 -8
- package/studio/web/assets/vendor-core-RCcadM3e.js +0 -73
- package/studio/web/assets/vendor-datadog-BRv-mOv1.js +0 -6
- package/studio/web/assets/vendor-tanstack-BiFWSB3W.js +0 -1
- package/studio/web/assets/x-B9o9hsep.js +0 -1
- /package/studio/web/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
package/resources/Table.ts
CHANGED
|
@@ -86,7 +86,7 @@ import { rebuildUpdateBefore } from './crdt.ts';
|
|
|
86
86
|
import { appendHeader } from '../server/serverHelpers/Headers.ts';
|
|
87
87
|
import fs from 'node:fs';
|
|
88
88
|
import { Blob, deleteBlobsInObject, findBlobsInObject, startPreCommitBlobsForRecord } from './blob.ts';
|
|
89
|
-
import { onStorageReclamation, getStorageSpaceStats } from '../server/storageReclamation.ts';
|
|
89
|
+
import { onStorageReclamation, removeStorageReclamation, getStorageSpaceStats } from '../server/storageReclamation.ts';
|
|
90
90
|
import { RequestTarget } from './RequestTarget.ts';
|
|
91
91
|
import harperLogger from '../utility/logging/harper_logger.ts';
|
|
92
92
|
import { throttle } from '../server/throttle.ts';
|
|
@@ -1564,7 +1564,12 @@ export function makeTable(options) {
|
|
|
1564
1564
|
if (removeTombstonedCatalog()) await dbisDb.committed;
|
|
1565
1565
|
}
|
|
1566
1566
|
} else {
|
|
1567
|
-
// legacy table per database
|
|
1567
|
+
// legacy table per database. The store to retire is this table's own audit store: nothing
|
|
1568
|
+
// assigns `primaryStore.auditStore` — openAuditStore() assigns `rootStore.auditStore`, and
|
|
1569
|
+
// this is the reference makeTable() was handed. Awaited so a pass suspended mid-removal has
|
|
1570
|
+
// released the primary DBI before it is closed and unlinked.
|
|
1571
|
+
await auditStore?.stopAuditCleanup?.();
|
|
1572
|
+
removeStorageReclamation(primaryStore.path);
|
|
1568
1573
|
await primaryStore.close();
|
|
1569
1574
|
fs.unlinkSync(primaryStore.path);
|
|
1570
1575
|
}
|
|
@@ -3335,6 +3340,7 @@ export function makeTable(options) {
|
|
|
3335
3340
|
// objects. Entries are small and shallow; the clone is cheap next to the query.
|
|
3336
3341
|
conditions = cloneConditions(conditions);
|
|
3337
3342
|
let orderAlignedCondition;
|
|
3343
|
+
let syntheticOrderCondition;
|
|
3338
3344
|
const filtered = {};
|
|
3339
3345
|
|
|
3340
3346
|
function prepareConditions(conditions: any[], operator: string) {
|
|
@@ -3473,7 +3479,7 @@ export function makeTable(options) {
|
|
|
3473
3479
|
// if it is indexed, we add a pseudo-condition to align with the natural sort order of the index.
|
|
3474
3480
|
// the primary key has no secondary index, but the primary store is itself keyed in
|
|
3475
3481
|
// primary-key order, so scanning it is already aligned with the sort
|
|
3476
|
-
orderAlignedCondition = { ...sort, comparator: 'sort' };
|
|
3482
|
+
orderAlignedCondition = syntheticOrderCondition = { ...sort, comparator: 'sort' };
|
|
3477
3483
|
conditions.push(orderAlignedCondition);
|
|
3478
3484
|
} else if (conditions.length === 0 && !target.allowFullScan)
|
|
3479
3485
|
throw handleHDBError(
|
|
@@ -3503,8 +3509,10 @@ export function makeTable(options) {
|
|
|
3503
3509
|
};
|
|
3504
3510
|
}
|
|
3505
3511
|
} else {
|
|
3506
|
-
// if we had to add an aligned condition that isn't first, we remove it and do ordering later
|
|
3507
|
-
|
|
3512
|
+
// if we had to add an aligned condition that isn't first, we remove it and do ordering later —
|
|
3513
|
+
// only the one we added; a caller's own condition on the sort attribute is still a filter
|
|
3514
|
+
const syntheticIndex = syntheticOrderCondition ? conditions.indexOf(syntheticOrderCondition) : -1;
|
|
3515
|
+
if (syntheticIndex >= 0) conditions.splice(syntheticIndex, 1);
|
|
3508
3516
|
postOrdering = sort;
|
|
3509
3517
|
}
|
|
3510
3518
|
}
|
package/resources/auditStore.ts
CHANGED
|
@@ -118,6 +118,13 @@ const warnedBodylessMints = new Set<string>();
|
|
|
118
118
|
const warnedBodylessTables = new Set<number>();
|
|
119
119
|
const FLOAT_TARGET = new Float64Array(1);
|
|
120
120
|
const FLOAT_BUFFER = new Uint8Array(FLOAT_TARGET.buffer);
|
|
121
|
+
|
|
122
|
+
/** Last resort on a detached path: a failing log sink must not itself become an unhandled rejection. */
|
|
123
|
+
function warnContained(message: string, error: unknown) {
|
|
124
|
+
try {
|
|
125
|
+
harperLogger.warn(message, error);
|
|
126
|
+
} catch {}
|
|
127
|
+
}
|
|
121
128
|
let DEFAULT_AUDIT_CLEANUP_DELAY = 10000; // default delay of 10 seconds
|
|
122
129
|
let timestampErrored = false;
|
|
123
130
|
export function openAuditStore(rootStore) {
|
|
@@ -133,7 +140,10 @@ export function openAuditStore(rootStore) {
|
|
|
133
140
|
if (!auditStore) {
|
|
134
141
|
// this means we are creating a new audit store. Initialize with the last removed timestamp (we don't want to put this in legacy audit logs since we don't know if they have had deletions or not).
|
|
135
142
|
auditStore = rootStore.openDB(AUDIT_STORE_NAME, AUDIT_STORE_OPTIONS);
|
|
136
|
-
|
|
143
|
+
// this open path is synchronous, so nothing downstream can own the write's rejection
|
|
144
|
+
updateLastRemoved(auditStore, 1)?.catch?.((error) =>
|
|
145
|
+
warnContained('Error initializing the audit log last-removed marker', error)
|
|
146
|
+
);
|
|
137
147
|
}
|
|
138
148
|
const superGetRange = auditStore.getRange.bind(auditStore);
|
|
139
149
|
auditStore.getRange = function (options) {
|
|
@@ -164,7 +174,15 @@ export function openAuditStore(rootStore) {
|
|
|
164
174
|
let pendingCleanupResolve: (() => void) | null = null;
|
|
165
175
|
let lastCleanupResolution: Promise<void>;
|
|
166
176
|
let cleanupPriority = 0;
|
|
167
|
-
|
|
177
|
+
auditStore.auditCleanupDelay = DEFAULT_AUDIT_CLEANUP_DELAY;
|
|
178
|
+
let cleanupStopped = false;
|
|
179
|
+
// a last-removed marker whose write failed, retried on later passes: dropping it would leave
|
|
180
|
+
// getLastRemoved() reporting a boundary the entries behind it have already been deleted past
|
|
181
|
+
let pendingLastRemoved: number | undefined;
|
|
182
|
+
const isRocksAuditStore = auditStore instanceof RocksTransactionLogStore;
|
|
183
|
+
// A pass yields, so the store can be closed underneath it. Every touch of the environment after a
|
|
184
|
+
// resume — cursor advance, cursor release, marker write, re-arm — has to re-check.
|
|
185
|
+
const storeClosing = () => auditStore.rootStore.status === 'closed' || auditStore.rootStore.status === 'closing';
|
|
168
186
|
onStorageReclamation(rootStore.path, (priority) => {
|
|
169
187
|
cleanupPriority = priority; // update the priority
|
|
170
188
|
if (priority) {
|
|
@@ -175,28 +193,23 @@ export function openAuditStore(rootStore) {
|
|
|
175
193
|
/**
|
|
176
194
|
* Schedules a pass of the audit cleanup loop. The returned promise fulfills once the pass that
|
|
177
195
|
* serves this call has finished, so callers (notably tests) can await completion instead of
|
|
178
|
-
* guessing a delay. Two things it does not promise:
|
|
196
|
+
* guessing a delay. Two things it does not promise: an LMDB pass removes at most
|
|
179
197
|
* MAX_DELETES_PER_CLEANUP entries before rescheduling itself, so fulfillment means "that pass
|
|
180
198
|
* finished", not "the audit log is fully pruned"; and a pass that failed logs and fulfills rather
|
|
181
199
|
* than rejecting, since this promise doubles as the loop's serialization barrier.
|
|
182
200
|
*/
|
|
183
201
|
function scheduleAuditCleanup(newCleanupDelay?: number): Promise<void> {
|
|
184
202
|
// Skip audit cleanup/purge in read-only mode
|
|
185
|
-
if (isReadOnlyMode()) return Promise.resolve();
|
|
186
|
-
if (auditStore instanceof RocksTransactionLogStore) {
|
|
187
|
-
auditStore.rootStore.purgeLogs({
|
|
188
|
-
before: Date.now() - auditRetention / (1 + cleanupPriority * cleanupPriority),
|
|
189
|
-
});
|
|
190
|
-
return Promise.resolve();
|
|
191
|
-
}
|
|
203
|
+
if (cleanupStopped || isReadOnlyMode()) return Promise.resolve();
|
|
192
204
|
|
|
193
|
-
if (newCleanupDelay) auditCleanupDelay = newCleanupDelay;
|
|
205
|
+
if (newCleanupDelay) auditStore.auditCleanupDelay = newCleanupDelay;
|
|
194
206
|
// the pass we are about to cancel has not started, so its callers are handed over to this one
|
|
195
207
|
const supersededResolve = pendingCleanupResolve;
|
|
196
208
|
clearTimeout(pendingCleanup);
|
|
197
209
|
const resolution = new Promise<void>((resolve) => {
|
|
198
210
|
pendingCleanupResolve = resolve;
|
|
199
|
-
|
|
211
|
+
const runCleanupPass = async () => {
|
|
212
|
+
pendingCleanup = null;
|
|
200
213
|
pendingCleanupResolve = null; // started, so a later schedule can no longer cancel this pass
|
|
201
214
|
// claim the serialization slot before yielding: assigning it after the await lets every
|
|
202
215
|
// pass released by the same resolution run concurrently over the same range
|
|
@@ -204,67 +217,146 @@ export function openAuditStore(rootStore) {
|
|
|
204
217
|
lastCleanupResolution = resolution;
|
|
205
218
|
await previousCleanup;
|
|
206
219
|
// query for audit entries that are old
|
|
207
|
-
if (
|
|
220
|
+
if (cleanupStopped || storeClosing()) {
|
|
208
221
|
// nothing to clean up and nothing to reschedule, but leaving `resolution` pending would
|
|
209
222
|
// wedge the loop permanently: it is now the resolution every later pass awaits
|
|
210
223
|
resolve();
|
|
211
224
|
return;
|
|
212
225
|
}
|
|
226
|
+
const passCleanupPriority = cleanupPriority;
|
|
213
227
|
let deleted = 0;
|
|
214
228
|
let lastKey: any;
|
|
215
229
|
try {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
230
|
+
if (isRocksAuditStore) {
|
|
231
|
+
auditStore.rootStore.purgeLogs({
|
|
232
|
+
before: Date.now() - auditRetention / (1 + passCleanupPriority * passCleanupPriority),
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
// Driven explicitly rather than with for-of: this loop suspends on the awaits below, and a
|
|
236
|
+
// close landing mid-pass closes the env under it. for-of calls next() before the body, so a
|
|
237
|
+
// check inside the body advances the cursor first — the guard has to precede every next().
|
|
238
|
+
const entries = auditStore
|
|
239
|
+
.getRange({
|
|
240
|
+
start: 1, // must not be zero or it will be interpreted as null and overlap with symbols in search
|
|
241
|
+
snapshot: false,
|
|
242
|
+
end: Date.now() - auditRetention / (1 + passCleanupPriority * passCleanupPriority), // remove up until the audit retention time, reducing audit retention time if cleanup is higher priority
|
|
243
|
+
})
|
|
244
|
+
[Symbol.iterator]();
|
|
221
245
|
try {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
while (!cleanupStopped && !storeClosing()) {
|
|
247
|
+
const entry = entries.next();
|
|
248
|
+
if (entry.done) break;
|
|
249
|
+
const auditRecord = entry.value;
|
|
250
|
+
try {
|
|
251
|
+
// awaited so a rejection (not just a synchronous throw) is caught here instead of
|
|
252
|
+
// escaping as an unhandled rejection once a later iteration's promise replaces this one
|
|
253
|
+
await removeAuditEntry(auditStore, auditRecord);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
harperLogger.warn('Error removing audit entry', error);
|
|
256
|
+
}
|
|
257
|
+
lastKey = auditRecord.key;
|
|
258
|
+
await new Promise(setImmediate);
|
|
259
|
+
if (++deleted >= MAX_DELETES_PER_CLEANUP) {
|
|
260
|
+
// limit the amount we cleanup per event turn so we don't use too much memory/CPU
|
|
261
|
+
auditStore.auditCleanupDelay = 10; // and keep trying very soon
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} finally {
|
|
266
|
+
// for-of released the underlying cursor on break; an explicit loop owes that itself.
|
|
267
|
+
// Skipped only once the root is closing: releasing a cursor into a closed env reaches
|
|
268
|
+
// native code, while a retirement that leaves the env open still owes the release.
|
|
269
|
+
if (!storeClosing()) entries.return?.();
|
|
234
270
|
}
|
|
235
271
|
}
|
|
236
272
|
} catch (error) {
|
|
237
|
-
//
|
|
238
|
-
//
|
|
273
|
+
// a failed scan is a log line, not a retired loop: the bookkeeping and reschedule below
|
|
274
|
+
// still run
|
|
239
275
|
harperLogger.warn('Error during audit log cleanup', error);
|
|
240
276
|
} finally {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
277
|
+
try {
|
|
278
|
+
if (isRocksAuditStore) {
|
|
279
|
+
// eligibility only changes on rotation/flush, so the LMDB backoff — keyed on a per-entry
|
|
280
|
+
// delete count — would only rescan the same segments
|
|
281
|
+
auditStore.auditCleanupDelay = Math.max(
|
|
282
|
+
DEFAULT_AUDIT_CLEANUP_DELAY,
|
|
283
|
+
Math.min(auditRetention / (1 + cleanupPriority * cleanupPriority) / 10, MAX_CLEANUP_DELAY)
|
|
284
|
+
);
|
|
285
|
+
} else {
|
|
286
|
+
if (deleted === 0) {
|
|
287
|
+
// if we didn't delete anything, we can increase the delay (double until we get to one tenth of
|
|
288
|
+
// the retention time). Plain arithmetic, not `<<`/`>>`: those coerce to int32, so a
|
|
289
|
+
// sub-millisecond retention collapsed the delay to 0 permanently (0 << 1 is 0), and a
|
|
290
|
+
// retention over ~248 days grows it past 2^31 where halving it wraps negative.
|
|
291
|
+
auditStore.auditCleanupDelay = Math.max(
|
|
292
|
+
1,
|
|
293
|
+
Math.min(auditStore.auditCleanupDelay * 2, auditRetention / 10, MAX_CLEANUP_DELAY)
|
|
294
|
+
);
|
|
295
|
+
} else {
|
|
296
|
+
pendingLastRemoved = lastKey;
|
|
297
|
+
// and do updates faster
|
|
298
|
+
if (auditStore.auditCleanupDelay > 100) auditStore.auditCleanupDelay = auditStore.auditCleanupDelay / 2;
|
|
299
|
+
}
|
|
300
|
+
// skipped when the store was retired or closed mid-pass — this writes to the audit
|
|
301
|
+
// store — and carried to the next pass instead, so a failed write is not lost
|
|
302
|
+
if (pendingLastRemoved !== undefined && !cleanupStopped && !storeClosing()) {
|
|
303
|
+
const marker = pendingLastRemoved;
|
|
304
|
+
try {
|
|
305
|
+
// awaited so the barrier below covers the write, and so a rejection is logged
|
|
306
|
+
// here rather than escaping the detached timer callback
|
|
307
|
+
await updateLastRemoved(auditStore, marker);
|
|
308
|
+
if (pendingLastRemoved === marker) pendingLastRemoved = undefined;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
harperLogger.warn('Error recording the last removed audit entry', error);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
} finally {
|
|
315
|
+
// settled and re-armed whatever the bookkeeping above threw: this promise is both the
|
|
316
|
+
// serialization barrier every later pass awaits and the drain barrier
|
|
317
|
+
// stopAuditCleanup() hands its callers, so never settling it wedges both
|
|
318
|
+
resolve();
|
|
319
|
+
// both conjuncts are backstops, not the ownership rule — see DESIGN.md: the arming
|
|
320
|
+
// sites already restrict Rocks to the last worker
|
|
321
|
+
if (
|
|
322
|
+
!cleanupStopped &&
|
|
323
|
+
!storeClosing() &&
|
|
324
|
+
(!isRocksAuditStore || (getWorkerIndex() === getWorkerCount() - 1 && !pendingCleanupResolve))
|
|
325
|
+
) {
|
|
326
|
+
scheduleAuditCleanup();
|
|
327
|
+
}
|
|
258
328
|
}
|
|
259
|
-
scheduleAuditCleanup();
|
|
260
329
|
}
|
|
261
330
|
// we can run this pretty frequently since there is very little overhead to these queries
|
|
262
|
-
}
|
|
331
|
+
};
|
|
332
|
+
pendingCleanup = setTimeout(() => {
|
|
333
|
+
// nothing owns the timer callback's promise, so anything the pass lets escape — including a
|
|
334
|
+
// throw from the logging inside its own containment — would land as an unhandled rejection
|
|
335
|
+
runCleanupPass().catch((error) => {
|
|
336
|
+
warnContained('Error during audit log cleanup', error);
|
|
337
|
+
resolve();
|
|
338
|
+
});
|
|
339
|
+
}, auditStore.auditCleanupDelay).unref();
|
|
263
340
|
});
|
|
264
341
|
if (supersededResolve) resolution.then(supersededResolve);
|
|
265
342
|
return resolution;
|
|
266
343
|
}
|
|
267
344
|
auditStore.scheduleAuditCleanup = scheduleAuditCleanup;
|
|
345
|
+
/**
|
|
346
|
+
* Retires the cleanup loop for good, and returns a drain barrier: the promise settles once the pass
|
|
347
|
+
* that was already running has finished. Retirement alone only stops the loop admitting more work —
|
|
348
|
+
* a pass suspended inside `await removeAuditEntry()` still has a write queued whose DBI the native
|
|
349
|
+
* writer consumes later, so a caller that closes or unlinks stores must await this first. The
|
|
350
|
+
* synchronous teardown paths cannot; the in-pass status checks are what covers them.
|
|
351
|
+
*/
|
|
352
|
+
auditStore.stopAuditCleanup = function (): Promise<void> {
|
|
353
|
+
cleanupStopped = true;
|
|
354
|
+
clearTimeout(pendingCleanup);
|
|
355
|
+
pendingCleanup = null;
|
|
356
|
+
pendingCleanupResolve?.();
|
|
357
|
+
pendingCleanupResolve = null;
|
|
358
|
+
return lastCleanupResolution ?? Promise.resolve();
|
|
359
|
+
};
|
|
268
360
|
if (getWorkerIndex() === getWorkerCount() - 1) {
|
|
269
361
|
scheduleAuditCleanup();
|
|
270
362
|
}
|
|
@@ -296,7 +388,7 @@ export function removeAuditEntry(auditStore: any, auditRecord: AuditRecord): Pro
|
|
|
296
388
|
|
|
297
389
|
function updateLastRemoved(auditStore, lastKey) {
|
|
298
390
|
FLOAT_TARGET[0] = lastKey;
|
|
299
|
-
auditStore.put(Symbol.for('last-removed'), FLOAT_BUFFER);
|
|
391
|
+
return auditStore.put(Symbol.for('last-removed'), FLOAT_BUFFER);
|
|
300
392
|
}
|
|
301
393
|
|
|
302
394
|
export function getLastRemoved(auditStore) {
|