pluresdb 1.0.1
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/LICENSE +72 -0
- package/README.md +322 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +253 -0
- package/dist/cli.js.map +1 -0
- package/dist/node-index.d.ts +52 -0
- package/dist/node-index.d.ts.map +1 -0
- package/dist/node-index.js +359 -0
- package/dist/node-index.js.map +1 -0
- package/dist/node-wrapper.d.ts +44 -0
- package/dist/node-wrapper.d.ts.map +1 -0
- package/dist/node-wrapper.js +294 -0
- package/dist/node-wrapper.js.map +1 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/node-types.d.ts +59 -0
- package/dist/types/node-types.d.ts.map +1 -0
- package/dist/types/node-types.js +6 -0
- package/dist/types/node-types.js.map +1 -0
- package/dist/vscode/extension.d.ts +81 -0
- package/dist/vscode/extension.d.ts.map +1 -0
- package/dist/vscode/extension.js +309 -0
- package/dist/vscode/extension.js.map +1 -0
- package/examples/basic-usage.d.ts +2 -0
- package/examples/basic-usage.d.ts.map +1 -0
- package/examples/basic-usage.js +26 -0
- package/examples/basic-usage.js.map +1 -0
- package/examples/basic-usage.ts +29 -0
- package/examples/vscode-extension-example/README.md +95 -0
- package/examples/vscode-extension-example/package.json +49 -0
- package/examples/vscode-extension-example/src/extension.ts +163 -0
- package/examples/vscode-extension-example/tsconfig.json +12 -0
- package/examples/vscode-extension-integration.d.ts +24 -0
- package/examples/vscode-extension-integration.d.ts.map +1 -0
- package/examples/vscode-extension-integration.js +285 -0
- package/examples/vscode-extension-integration.js.map +1 -0
- package/examples/vscode-extension-integration.ts +41 -0
- package/package.json +115 -0
- package/scripts/compiled-crud-verify.ts +28 -0
- package/scripts/dogfood.ts +258 -0
- package/scripts/postinstall.js +155 -0
- package/scripts/run-tests.ts +175 -0
- package/scripts/setup-libclang.ps1 +209 -0
- package/src/benchmarks/memory-benchmarks.ts +316 -0
- package/src/benchmarks/run-benchmarks.ts +293 -0
- package/src/cli.ts +231 -0
- package/src/config.ts +49 -0
- package/src/core/crdt.ts +104 -0
- package/src/core/database.ts +494 -0
- package/src/healthcheck.ts +156 -0
- package/src/http/api-server.ts +334 -0
- package/src/index.ts +28 -0
- package/src/logic/rules.ts +44 -0
- package/src/main.rs +3 -0
- package/src/main.ts +190 -0
- package/src/network/websocket-server.ts +115 -0
- package/src/node-index.ts +385 -0
- package/src/node-wrapper.ts +320 -0
- package/src/sqlite-compat.ts +586 -0
- package/src/sqlite3-compat.ts +55 -0
- package/src/storage/kv-storage.ts +71 -0
- package/src/tests/core.test.ts +281 -0
- package/src/tests/fixtures/performance-data.json +71 -0
- package/src/tests/fixtures/test-data.json +124 -0
- package/src/tests/integration/api-server.test.ts +232 -0
- package/src/tests/integration/mesh-network.test.ts +297 -0
- package/src/tests/logic.test.ts +30 -0
- package/src/tests/performance/load.test.ts +288 -0
- package/src/tests/security/input-validation.test.ts +282 -0
- package/src/tests/unit/core.test.ts +216 -0
- package/src/tests/unit/subscriptions.test.ts +135 -0
- package/src/tests/unit/vector-search.test.ts +173 -0
- package/src/tests/vscode_extension_test.ts +253 -0
- package/src/types/index.ts +32 -0
- package/src/types/node-types.ts +66 -0
- package/src/util/debug.ts +14 -0
- package/src/vector/index.ts +59 -0
- package/src/vscode/extension.ts +364 -0
- package/web/README.md +27 -0
- package/web/svelte/package.json +31 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Ensures libclang (LLVM) is installed and configures the LIBCLANG_PATH required by bindgen-based crates.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
This script automates the detection or installation of libclang on Windows machines.
|
|
7
|
+
It will:
|
|
8
|
+
1. Check existing environment variables and well-known locations for libclang.dll
|
|
9
|
+
2. Optionally install LLVM via winget or Chocolatey if libclang is missing
|
|
10
|
+
3. Configure the LIBCLANG_PATH environment variable (user scope) once libclang is found
|
|
11
|
+
4. Optionally append the path to the current session PATH so cargo build/test immediately picks it up
|
|
12
|
+
|
|
13
|
+
Run it from the repository root before building Rust components that depend on bindgen (e.g., zstd-sys).
|
|
14
|
+
|
|
15
|
+
.PARAMETER ForceInstall
|
|
16
|
+
Always attempt to (re)install LLVM even if libclang is already detected.
|
|
17
|
+
|
|
18
|
+
.PARAMETER ConfigureCurrentProcess
|
|
19
|
+
Also updates $env:LIBCLANG_PATH and $env:PATH for the current PowerShell session
|
|
20
|
+
in addition to setting the persisted user-level environment variable.
|
|
21
|
+
|
|
22
|
+
.PARAMETER SkipInstall
|
|
23
|
+
Do not attempt to install LLVM automatically. The script will fail if libclang is not found.
|
|
24
|
+
|
|
25
|
+
.EXAMPLE
|
|
26
|
+
pwsh ./scripts/setup-libclang.ps1
|
|
27
|
+
|
|
28
|
+
.EXAMPLE
|
|
29
|
+
pwsh ./scripts/setup-libclang.ps1 -ConfigureCurrentProcess
|
|
30
|
+
|
|
31
|
+
.NOTES
|
|
32
|
+
- Requires either winget or Chocolatey for automated installation.
|
|
33
|
+
- When installing via winget, administrative consent may be required for the first run.
|
|
34
|
+
- After the user-level environment variable is written you should restart your shell (unless ConfigureCurrentProcess was supplied).
|
|
35
|
+
#>
|
|
36
|
+
[CmdletBinding()]
|
|
37
|
+
param(
|
|
38
|
+
[switch]$ForceInstall,
|
|
39
|
+
[switch]$ConfigureCurrentProcess,
|
|
40
|
+
[switch]$SkipInstall
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
function Write-Info {
|
|
44
|
+
param([string]$Message)
|
|
45
|
+
Write-Host "[INFO] $Message" -ForegroundColor Cyan
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Write-Success {
|
|
49
|
+
param([string]$Message)
|
|
50
|
+
Write-Host "[ OK ] $Message" -ForegroundColor Green
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function Write-WarningMessage {
|
|
54
|
+
param([string]$Message)
|
|
55
|
+
Write-Host "[WARN] $Message" -ForegroundColor Yellow
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function Write-ErrorMessage {
|
|
59
|
+
param([string]$Message)
|
|
60
|
+
Write-Host "[FAIL] $Message" -ForegroundColor Red
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function Test-CommandExists {
|
|
64
|
+
param([string]$Name)
|
|
65
|
+
return [bool](Get-Command $Name -ErrorAction SilentlyContinue)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function Get-EnvLibClangPath {
|
|
69
|
+
$env:LIBCLANG_PATH
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function Test-LibClangInPath {
|
|
73
|
+
param([string]$Directory)
|
|
74
|
+
if ([string]::IsNullOrWhiteSpace($Directory)) {
|
|
75
|
+
return $null
|
|
76
|
+
}
|
|
77
|
+
$candidate = Join-Path -Path $Directory -ChildPath "libclang.dll"
|
|
78
|
+
if (Test-Path $candidate) {
|
|
79
|
+
return (Get-Item $candidate).Directory.FullName
|
|
80
|
+
}
|
|
81
|
+
return $null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function Get-KnownLibClangLocations {
|
|
85
|
+
$paths = @()
|
|
86
|
+
|
|
87
|
+
# Explicit environment variable first
|
|
88
|
+
$envPath = Get-EnvLibClangPath
|
|
89
|
+
if (-not [string]::IsNullOrWhiteSpace($envPath)) {
|
|
90
|
+
$paths += $envPath
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# LLVM default installation
|
|
94
|
+
$paths += @(
|
|
95
|
+
"$env:ProgramFiles\LLVM\bin",
|
|
96
|
+
"$env:ProgramFiles\LLVM\lib",
|
|
97
|
+
"$env:ProgramFiles\LLVM\lib64",
|
|
98
|
+
"$env:ProgramFiles(x86)\LLVM\bin",
|
|
99
|
+
"$env:ProgramFiles(x86)\LLVM\lib"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Visual Studio (MSVC) bundled LLVM/Clang
|
|
103
|
+
$vsRoot = Join-Path $env:ProgramFiles "Microsoft Visual Studio"
|
|
104
|
+
if (Test-Path $vsRoot) {
|
|
105
|
+
$paths += Get-ChildItem -Path $vsRoot -Directory -Recurse -Filter "libclang.dll" -ErrorAction SilentlyContinue |
|
|
106
|
+
ForEach-Object { $_.Directory.FullName } |
|
|
107
|
+
Select-Object -Unique
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Generic entries that appear in PATH (e.g., if user installed LLVM elsewhere)
|
|
111
|
+
$paths += [Environment]::GetEnvironmentVariable("PATH", "Machine").Split(';')
|
|
112
|
+
$paths += [Environment]::GetEnvironmentVariable("PATH", "User").Split(';')
|
|
113
|
+
|
|
114
|
+
# Remove duplicates/empty
|
|
115
|
+
return $paths | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function Find-LibClang {
|
|
119
|
+
$locations = Get-KnownLibClangLocations
|
|
120
|
+
foreach ($location in $locations) {
|
|
121
|
+
$resolved = Test-LibClangInPath -Directory $location
|
|
122
|
+
if ($resolved) {
|
|
123
|
+
return $resolved
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return $null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function Install-LLVM {
|
|
130
|
+
Write-Info "Attempting to install LLVM (includes libclang)..."
|
|
131
|
+
|
|
132
|
+
if (Test-CommandExists -Name "winget") {
|
|
133
|
+
try {
|
|
134
|
+
winget install LLVM.LLVM --silent --accept-package-agreements --accept-source-agreements | Out-Null
|
|
135
|
+
Write-Success "LLVM installed via winget"
|
|
136
|
+
return $true
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
Write-WarningMessage "winget install failed: $($_.Exception.Message)"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
elseif (Test-CommandExists -Name "choco") {
|
|
143
|
+
try {
|
|
144
|
+
choco install llvm -y --no-progress | Out-Null
|
|
145
|
+
Write-Success "LLVM installed via Chocolatey"
|
|
146
|
+
return $true
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
Write-WarningMessage "Chocolatey install failed: $($_.Exception.Message)"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
Write-WarningMessage "Neither winget nor Chocolatey is available for automated installation."
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return $false
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function Set-LibClangEnvironment {
|
|
160
|
+
param(
|
|
161
|
+
[Parameter(Mandatory = $true)][string]$Directory,
|
|
162
|
+
[switch]$ConfigureProcess
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
Write-Info "Setting LIBCLANG_PATH user environment variable to '$Directory'"
|
|
166
|
+
[Environment]::SetEnvironmentVariable("LIBCLANG_PATH", $Directory, "User")
|
|
167
|
+
|
|
168
|
+
if ($ConfigureProcess) {
|
|
169
|
+
$env:LIBCLANG_PATH = $Directory
|
|
170
|
+
if ($env:PATH -notlike "*$Directory*") {
|
|
171
|
+
$env:PATH = "$Directory;$env:PATH"
|
|
172
|
+
}
|
|
173
|
+
Write-Info "Updated current session PATH for immediate use"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
Write-Success "LIBCLANG_PATH configured. Restart PowerShell or open a new terminal to pick up user-level changes."
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
if ($ForceInstall -and $SkipInstall) {
|
|
181
|
+
throw "Cannot use -ForceInstall together with -SkipInstall."
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
$libclangDir = Find-LibClang
|
|
185
|
+
|
|
186
|
+
if ($ForceInstall -or (-not $libclangDir)) {
|
|
187
|
+
if ($SkipInstall) {
|
|
188
|
+
throw "libclang was not found and installation is disabled (SkipInstall)."
|
|
189
|
+
}
|
|
190
|
+
$installed = Install-LLVM
|
|
191
|
+
if (-not $installed) {
|
|
192
|
+
throw "Automatic LLVM installation failed. Please install LLVM manually and rerun this script."
|
|
193
|
+
}
|
|
194
|
+
$libclangDir = Find-LibClang
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (-not $libclangDir) {
|
|
198
|
+
throw "libclang.dll could not be located even after installation. Please install LLVM manually and rerun."
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
Write-Success "Found libclang at '$libclangDir'"
|
|
202
|
+
Set-LibClangEnvironment -Directory $libclangDir -ConfigureProcess:$ConfigureCurrentProcess
|
|
203
|
+
|
|
204
|
+
Write-Info "You can now run 'cargo build' / 'cargo test' successfully."
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
Write-ErrorMessage $_
|
|
208
|
+
exit 1
|
|
209
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env -S deno run -A
|
|
2
|
+
|
|
3
|
+
import { GunDB } from "../core/database.ts";
|
|
4
|
+
|
|
5
|
+
interface MemoryMetrics {
|
|
6
|
+
operation: string;
|
|
7
|
+
initialMemory: number;
|
|
8
|
+
finalMemory: number;
|
|
9
|
+
memoryIncrease: number;
|
|
10
|
+
recordCount: number;
|
|
11
|
+
memoryPerRecord: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class MemoryBenchmark {
|
|
15
|
+
private results: MemoryMetrics[] = [];
|
|
16
|
+
|
|
17
|
+
private getMemoryUsage(): number {
|
|
18
|
+
return (performance as any).memory?.usedJSHeapSize || 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async measureMemoryUsage(
|
|
22
|
+
operation: string,
|
|
23
|
+
recordCount: number,
|
|
24
|
+
operationFn: () => Promise<void>,
|
|
25
|
+
): Promise<MemoryMetrics> {
|
|
26
|
+
console.log(`Measuring memory usage for: ${operation}`);
|
|
27
|
+
|
|
28
|
+
// Force garbage collection if available
|
|
29
|
+
if ((globalThis as any).gc) {
|
|
30
|
+
(globalThis as any).gc();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const initialMemory = this.getMemoryUsage();
|
|
34
|
+
|
|
35
|
+
await operationFn();
|
|
36
|
+
|
|
37
|
+
// Force garbage collection if available
|
|
38
|
+
if ((globalThis as any).gc) {
|
|
39
|
+
(globalThis as any).gc();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const finalMemory = this.getMemoryUsage();
|
|
43
|
+
const memoryIncrease = finalMemory - initialMemory;
|
|
44
|
+
const memoryPerRecord = memoryIncrease / recordCount;
|
|
45
|
+
|
|
46
|
+
const metrics: MemoryMetrics = {
|
|
47
|
+
operation,
|
|
48
|
+
initialMemory,
|
|
49
|
+
finalMemory,
|
|
50
|
+
memoryIncrease,
|
|
51
|
+
recordCount,
|
|
52
|
+
memoryPerRecord,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
this.results.push(metrics);
|
|
56
|
+
|
|
57
|
+
console.log(` Initial Memory: ${(initialMemory / 1024 / 1024).toFixed(2)}MB`);
|
|
58
|
+
console.log(` Final Memory: ${(finalMemory / 1024 / 1024).toFixed(2)}MB`);
|
|
59
|
+
console.log(` Memory Increase: ${(memoryIncrease / 1024 / 1024).toFixed(2)}MB`);
|
|
60
|
+
console.log(` Memory per Record: ${memoryPerRecord.toFixed(2)} bytes`);
|
|
61
|
+
console.log();
|
|
62
|
+
|
|
63
|
+
return metrics;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
printSummary() {
|
|
67
|
+
console.log("\n" + "=".repeat(80));
|
|
68
|
+
console.log("MEMORY USAGE SUMMARY");
|
|
69
|
+
console.log("=".repeat(80));
|
|
70
|
+
|
|
71
|
+
this.results.forEach((result) => {
|
|
72
|
+
console.log(`${result.operation}:`);
|
|
73
|
+
console.log(` Records: ${result.recordCount.toLocaleString()}`);
|
|
74
|
+
console.log(` Memory Increase: ${(result.memoryIncrease / 1024 / 1024).toFixed(2)}MB`);
|
|
75
|
+
console.log(` Memory per Record: ${result.memoryPerRecord.toFixed(2)} bytes`);
|
|
76
|
+
console.log();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function runMemoryBenchmarks() {
|
|
82
|
+
const benchmark = new MemoryBenchmark();
|
|
83
|
+
const db = new GunDB();
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const kvPath = await Deno.makeTempFile({
|
|
87
|
+
prefix: "kv_",
|
|
88
|
+
suffix: ".sqlite",
|
|
89
|
+
});
|
|
90
|
+
await db.ready(kvPath);
|
|
91
|
+
|
|
92
|
+
console.log("Starting Memory Benchmarks...\n");
|
|
93
|
+
|
|
94
|
+
// Benchmark 1: Small Records
|
|
95
|
+
await benchmark.measureMemoryUsage("Small Records (100 bytes)", 1000, async () => {
|
|
96
|
+
for (let i = 0; i < 1000; i++) {
|
|
97
|
+
await db.put(`small:${i}`, {
|
|
98
|
+
id: i,
|
|
99
|
+
data: "x".repeat(100),
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Benchmark 2: Medium Records
|
|
106
|
+
await benchmark.measureMemoryUsage("Medium Records (1KB)", 1000, async () => {
|
|
107
|
+
for (let i = 0; i < 1000; i++) {
|
|
108
|
+
await db.put(`medium:${i}`, {
|
|
109
|
+
id: i,
|
|
110
|
+
data: "x".repeat(1024),
|
|
111
|
+
timestamp: Date.now(),
|
|
112
|
+
metadata: { size: "medium", type: "test" },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Benchmark 3: Large Records
|
|
118
|
+
await benchmark.measureMemoryUsage("Large Records (10KB)", 100, async () => {
|
|
119
|
+
for (let i = 0; i < 100; i++) {
|
|
120
|
+
await db.put(`large:${i}`, {
|
|
121
|
+
id: i,
|
|
122
|
+
data: "x".repeat(10 * 1024),
|
|
123
|
+
timestamp: Date.now(),
|
|
124
|
+
metadata: { size: "large", type: "test" },
|
|
125
|
+
additional: "y".repeat(1024),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Benchmark 4: Vector Data
|
|
131
|
+
await benchmark.measureMemoryUsage("Vector Data (100 dimensions)", 500, async () => {
|
|
132
|
+
for (let i = 0; i < 500; i++) {
|
|
133
|
+
const vector = Array.from({ length: 100 }, () => Math.random());
|
|
134
|
+
await db.put(`vector:${i}`, {
|
|
135
|
+
id: i,
|
|
136
|
+
text: `Document ${i} with vector data`,
|
|
137
|
+
vector: vector,
|
|
138
|
+
timestamp: Date.now(),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Benchmark 5: Nested Objects
|
|
144
|
+
await benchmark.measureMemoryUsage("Nested Objects", 500, async () => {
|
|
145
|
+
for (let i = 0; i < 500; i++) {
|
|
146
|
+
await db.put(`nested:${i}`, {
|
|
147
|
+
id: i,
|
|
148
|
+
user: {
|
|
149
|
+
name: `User ${i}`,
|
|
150
|
+
email: `user${i}@example.com`,
|
|
151
|
+
profile: {
|
|
152
|
+
age: 20 + (i % 50),
|
|
153
|
+
location: `City ${i}`,
|
|
154
|
+
preferences: {
|
|
155
|
+
theme: i % 2 === 0 ? "dark" : "light",
|
|
156
|
+
notifications: true,
|
|
157
|
+
privacy: "public",
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
metadata: {
|
|
162
|
+
created: Date.now(),
|
|
163
|
+
updated: Date.now(),
|
|
164
|
+
version: 1,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Benchmark 6: Subscriptions Memory Usage
|
|
171
|
+
await benchmark.measureMemoryUsage("Subscriptions", 1000, async () => {
|
|
172
|
+
const subscriptions: Array<() => void> = [];
|
|
173
|
+
for (let i = 0; i < 1000; i++) {
|
|
174
|
+
const unsubscribe = db.on(`sub:${i}`, () => {});
|
|
175
|
+
subscriptions.push(unsubscribe);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Store subscriptions for cleanup
|
|
179
|
+
(globalThis as any).subscriptions = subscriptions;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Clean up subscriptions
|
|
183
|
+
if ((globalThis as any).subscriptions) {
|
|
184
|
+
((globalThis as any).subscriptions as Array<() => void>).forEach((unsubscribe) =>
|
|
185
|
+
unsubscribe(),
|
|
186
|
+
);
|
|
187
|
+
delete (globalThis as any).subscriptions;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Benchmark 7: Type System Memory Usage
|
|
191
|
+
await benchmark.measureMemoryUsage("Type System", 1000, async () => {
|
|
192
|
+
for (let i = 0; i < 1000; i++) {
|
|
193
|
+
await db.put(`type:${i}`, { name: `Item ${i}` });
|
|
194
|
+
await db.setType(`type:${i}`, "TestItem");
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Benchmark 8: CRDT Operations Memory Usage
|
|
199
|
+
await benchmark.measureMemoryUsage("CRDT Operations", 1000, async () => {
|
|
200
|
+
for (let i = 0; i < 1000; i++) {
|
|
201
|
+
await db.put(`crdt:${i}`, {
|
|
202
|
+
value: i,
|
|
203
|
+
timestamp: Date.now(),
|
|
204
|
+
vectorClock: { peer1: i, peer2: i * 2 },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
benchmark.printSummary();
|
|
210
|
+
} finally {
|
|
211
|
+
await db.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function runMemoryLeakTests() {
|
|
216
|
+
console.log("\n" + "=".repeat(80));
|
|
217
|
+
console.log("MEMORY LEAK TESTS");
|
|
218
|
+
console.log("=".repeat(80));
|
|
219
|
+
|
|
220
|
+
const db = new GunDB();
|
|
221
|
+
try {
|
|
222
|
+
const kvPath = await Deno.makeTempFile({
|
|
223
|
+
prefix: "kv_",
|
|
224
|
+
suffix: ".sqlite",
|
|
225
|
+
});
|
|
226
|
+
await db.ready(kvPath);
|
|
227
|
+
|
|
228
|
+
// Test 1: Subscription Memory Leaks
|
|
229
|
+
console.log("Testing subscription memory leaks...");
|
|
230
|
+
|
|
231
|
+
const initialMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
232
|
+
|
|
233
|
+
// Create and destroy many subscriptions
|
|
234
|
+
for (let cycle = 0; cycle < 10; cycle++) {
|
|
235
|
+
const subscriptions: Array<() => void> = [];
|
|
236
|
+
|
|
237
|
+
for (let i = 0; i < 100; i++) {
|
|
238
|
+
subscriptions.push(db.on(`leak:${cycle}:${i}`, () => {}));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Destroy all subscriptions
|
|
242
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
243
|
+
|
|
244
|
+
// Force garbage collection if available
|
|
245
|
+
if ((globalThis as any).gc) {
|
|
246
|
+
(globalThis as any).gc();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const finalMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
251
|
+
const memoryIncrease = finalMemory - initialMemory;
|
|
252
|
+
|
|
253
|
+
console.log(
|
|
254
|
+
`Memory increase after subscription cycles: ${(memoryIncrease / 1024).toFixed(2)}KB`,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (memoryIncrease > 1024 * 1024) {
|
|
258
|
+
// More than 1MB
|
|
259
|
+
console.log("⚠️ Potential memory leak detected in subscriptions");
|
|
260
|
+
} else {
|
|
261
|
+
console.log("✓ No significant memory leak in subscriptions");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Test 2: CRUD Operations Memory Leaks
|
|
265
|
+
console.log("\nTesting CRUD operations memory leaks...");
|
|
266
|
+
|
|
267
|
+
const crudInitialMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
268
|
+
|
|
269
|
+
// Perform many CRUD operations
|
|
270
|
+
for (let cycle = 0; cycle < 100; cycle++) {
|
|
271
|
+
for (let i = 0; i < 100; i++) {
|
|
272
|
+
await db.put(`leak:${cycle}:${i}`, { data: `Cycle ${cycle} Item ${i}` });
|
|
273
|
+
await db.get(`leak:${cycle}:${i}`);
|
|
274
|
+
await db.delete(`leak:${cycle}:${i}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Force garbage collection if available
|
|
278
|
+
if ((globalThis as any).gc) {
|
|
279
|
+
(globalThis as any).gc();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const crudFinalMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
|
284
|
+
const crudMemoryIncrease = crudFinalMemory - crudInitialMemory;
|
|
285
|
+
|
|
286
|
+
console.log(`Memory increase after CRUD cycles: ${(crudMemoryIncrease / 1024).toFixed(2)}KB`);
|
|
287
|
+
|
|
288
|
+
if (crudMemoryIncrease > 1024 * 1024) {
|
|
289
|
+
// More than 1MB
|
|
290
|
+
console.log("⚠️ Potential memory leak detected in CRUD operations");
|
|
291
|
+
} else {
|
|
292
|
+
console.log("✓ No significant memory leak in CRUD operations");
|
|
293
|
+
}
|
|
294
|
+
} finally {
|
|
295
|
+
await db.close();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function main() {
|
|
300
|
+
console.log("PluresDB Memory Benchmarks");
|
|
301
|
+
console.log("===========================\n");
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
await runMemoryBenchmarks();
|
|
305
|
+
await runMemoryLeakTests();
|
|
306
|
+
|
|
307
|
+
console.log("\nMemory benchmarks completed successfully!");
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error("Memory benchmark failed:", error);
|
|
310
|
+
Deno.exit(1);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (import.meta.main) {
|
|
315
|
+
await main();
|
|
316
|
+
}
|