@feltdb/core 0.9.0 → 0.10.0
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/dist/authority-scope.d.ts +2 -0
- package/dist/authority-scope.d.ts.map +1 -1
- package/dist/authority-scope.js +7 -2
- package/dist/cli/commands.js +19 -21
- package/dist/cli/development-session.js +16 -0
- package/dist/cli/studio-artifact.js +33 -0
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +8 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +16 -0
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +21 -22
- package/dist/create/server-source/crates/feltdb/src/lib.rs +451 -33
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +48 -3
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +569 -4
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +708 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +480 -44
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +123 -0
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +281 -0
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +15 -3
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +28 -18
- package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +328 -0
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +20 -3
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +14 -4
- package/dist/create/server-source/crates/feltdb-server/src/backup.rs +0 -4
- package/dist/create/server-source/crates/feltdb-server/src/canonical_principals.rs +1093 -0
- package/dist/create/server-source/crates/feltdb-server/src/causal.rs +905 -144
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +9 -6
- package/dist/create/server-source/crates/feltdb-server/src/control_plane.rs +90 -0
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +1 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +443 -77
- package/dist/create/server-source/crates/feltdb-server/src/principals.rs +9 -501
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +22 -0
- package/dist/create/server-source/crates/feltdb-server/tests/principal_tenant_migration.rs +247 -0
- package/dist/db.d.ts +14 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +50 -26
- package/dist/deployment.d.ts +2 -0
- package/dist/deployment.d.ts.map +1 -1
- package/dist/deployment.js +4 -2
- package/dist/embedded-transaction.d.ts.map +1 -1
- package/dist/embedded-transaction.js +4 -0
- package/dist/error-codes.d.ts +12 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +20 -2
- package/dist/feltdb.d.ts +4 -0
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.d.ts +10 -0
- package/dist/file-db.d.ts.map +1 -1
- package/dist/file-db.js +18 -0
- package/dist/http-db.d.ts +7 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +83 -15
- package/dist/index-core.d.ts +1 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +1 -0
- package/dist/operation-admission.d.ts +2 -0
- package/dist/operation-admission.d.ts.map +1 -1
- package/dist/public-contract.d.ts +112 -0
- package/dist/public-contract.d.ts.map +1 -0
- package/dist/public-contract.js +125 -0
- package/dist/studio/app.d.ts +23 -0
- package/dist/studio/app.d.ts.map +1 -1
- package/dist/studio/components/ContractInspector.d.ts +5 -0
- package/dist/studio/components/ContractInspector.d.ts.map +1 -1
- package/dist/studio/components/SettingsPanel.d.ts +4 -1
- package/dist/studio/components/SettingsPanel.d.ts.map +1 -1
- package/dist/studio/components/index.js +4 -4
- package/dist/studio/{components-CehkcX_k.js → components-CJFgdKG6.js} +322 -303
- package/dist/studio/control-plane/Data.d.ts.map +1 -1
- package/dist/studio/control-plane/Instance.d.ts +10 -0
- package/dist/studio/control-plane/Instance.d.ts.map +1 -1
- package/dist/studio/control-plane/Operations.d.ts +3 -0
- package/dist/studio/control-plane/Operations.d.ts.map +1 -1
- package/dist/studio/control-plane/State.d.ts.map +1 -1
- package/dist/studio/control-plane/index.d.ts +4 -2
- package/dist/studio/control-plane/index.d.ts.map +1 -1
- package/dist/studio/control-plane/index.js +2 -2
- package/dist/studio/control-plane-DP8ljTDO.js +3207 -0
- package/dist/studio/index.js +96 -75
- package/dist/studio/utils/api.d.ts +1 -1
- package/dist/studio/utils/api.d.ts.map +1 -1
- package/dist/studio/utils/control-plane-api.d.ts +12 -1
- package/dist/studio/utils/control-plane-api.d.ts.map +1 -1
- package/dist/studio/utils/control-plane-api.js +1 -1
- package/dist/studio/utils/index.js +16 -16
- package/dist/studio/utils/service-api.d.ts +8 -0
- package/dist/studio/utils/service-api.d.ts.map +1 -1
- package/dist/studio/utils/service-api.js +15 -12
- package/dist/studio-app/assets/{feltdb_wasm-DksXDnLa.js → feltdb_wasm-CfNb3IoF.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-D9d3gY-E.wasm +0 -0
- package/dist/studio-app/assets/index-SDXdROi1.js +30 -0
- package/dist/studio-app/feltdb-studio.json +6 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/transaction.d.ts +9 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +6 -2
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workspace/local-development-authority.d.ts +8 -0
- package/dist/workspace/local-development-authority.d.ts.map +1 -1
- package/dist/workspace/local-development-authority.js +136 -1
- package/package.json +2 -2
- package/dist/studio/control-plane-D6TT2fCQ.js +0 -3064
- package/dist/studio-app/assets/feltdb_wasm_bg-Yy4QRHJT.wasm +0 -0
- package/dist/studio-app/assets/index-CjELc-ul.js +0 -30
|
@@ -5,6 +5,8 @@ export interface TenantAuthorityScope {
|
|
|
5
5
|
}
|
|
6
6
|
export interface PlatformAuthorityScope {
|
|
7
7
|
kind: 'platform';
|
|
8
|
+
/** Tenants this platform session may explicitly select. Empty means none. */
|
|
9
|
+
tenantIds?: readonly string[];
|
|
8
10
|
}
|
|
9
11
|
export type AuthorityScope = TenantAuthorityScope | PlatformAuthorityScope;
|
|
10
12
|
export declare function normalizeAuthorityScope(scope: AuthorityScope | undefined): AuthorityScope;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authority-scope.d.ts","sourceRoot":"","sources":["../src/authority-scope.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"authority-scope.d.ts","sourceRoot":"","sources":["../src/authority-scope.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,sBAAsB,CAAC;AAE3E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,GAAG,cAAc,CA+BzF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAK/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAGvF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAI3F"}
|
package/dist/authority-scope.js
CHANGED
|
@@ -3,8 +3,13 @@ export function normalizeAuthorityScope(scope) {
|
|
|
3
3
|
if (!scope) {
|
|
4
4
|
throw new FeltDBServiceError('An explicit tenant or platform authority scope is required', FeltDBErrorCode.AUTHORITY_SCOPE_REQUIRED);
|
|
5
5
|
}
|
|
6
|
-
if (scope.kind === 'platform')
|
|
7
|
-
|
|
6
|
+
if (scope.kind === 'platform') {
|
|
7
|
+
const tenantIds = [...new Set((scope.tenantIds ?? []).map(value => value.trim()))];
|
|
8
|
+
if (tenantIds.some(value => !value)) {
|
|
9
|
+
throw new FeltDBServiceError('Platform tenantIds must be non-empty strings', FeltDBErrorCode.AUTHORITY_SCOPE_INVALID);
|
|
10
|
+
}
|
|
11
|
+
return { kind: 'platform', tenantIds };
|
|
12
|
+
}
|
|
8
13
|
if (scope.kind !== 'tenant' || typeof scope.tenantId !== 'string' || !scope.tenantId.trim()) {
|
|
9
14
|
throw new FeltDBServiceError('Tenant authority requires a non-empty tenantId', FeltDBErrorCode.AUTHORITY_SCOPE_REQUIRED);
|
|
10
15
|
}
|
package/dist/cli/commands.js
CHANGED
|
@@ -5,7 +5,7 @@ import fs from 'fs';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import http from 'http';
|
|
7
7
|
import net from 'net';
|
|
8
|
-
import { createRequire } from 'module';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
9
|
import { spawn, spawnSync } from 'child_process';
|
|
10
10
|
import { randomBytes } from 'crypto';
|
|
11
11
|
import { createInterface } from 'node:readline/promises';
|
|
@@ -15,7 +15,7 @@ import { collectRepositoryEvidence } from './repository-evidence.js';
|
|
|
15
15
|
import { discoverWorkspace, ensureWorkspaceGitIgnored, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
|
|
16
16
|
import { applicationUrlCandidate, detectApplication, discoverApplicationUrl } from './application.js';
|
|
17
17
|
import { resolveApplicationLifecycle, startManagedApplication, waitForManagedApplication } from './application-lifecycle.js';
|
|
18
|
-
import { createDevelopmentSession, developmentSessionEnvironment, developmentSessionSummary, transitionDevelopmentSession } from './development-session.js';
|
|
18
|
+
import { createDevelopmentSession, developmentSessionEnvironment, developmentSessionSummary, developmentStudioArguments, transitionDevelopmentSession } from './development-session.js';
|
|
19
19
|
import { openApplication, openStudio } from './browser-opening.js';
|
|
20
20
|
import { publishApplication } from './publish-engine.js';
|
|
21
21
|
import { applyFlowProposalAtomic, assertProposalSourcePlan, describeProposal, loadLocalDevelopmentContract, repositoryContext, reviewFlowProposal, syncGeneratedSource } from './source-sync.js';
|
|
@@ -23,6 +23,7 @@ import { configureAiModel, generateConfiguredFlowProposal, publicAiModelConfigur
|
|
|
23
23
|
import { proposalService } from './proposal-client.js';
|
|
24
24
|
import { resolveManagedAuthorityEnvironment } from './managed-environment.js';
|
|
25
25
|
import { readManagedProjectState } from './managed-project.js';
|
|
26
|
+
import { resolveCanonicalStudioArtifact } from './studio-artifact.js';
|
|
26
27
|
function loadProjectEnvironment(file = path.resolve('.env.local')) {
|
|
27
28
|
if (!fs.existsSync(file))
|
|
28
29
|
return;
|
|
@@ -1127,6 +1128,14 @@ async function handleDev(args) {
|
|
|
1127
1128
|
port: authorityPort,
|
|
1128
1129
|
proposalContext: () => loadLocalDevelopmentContract(projectDir),
|
|
1129
1130
|
intentObservations: () => collectRepositoryEvidence(projectDir),
|
|
1131
|
+
runtimeContext: {
|
|
1132
|
+
namespace: runtimeNamespace || config.namespace,
|
|
1133
|
+
runtime: config.runtime,
|
|
1134
|
+
storage: config.storage,
|
|
1135
|
+
distributed: Boolean(config.distributed),
|
|
1136
|
+
applicationLifecycle,
|
|
1137
|
+
applicationUrl: appUrl,
|
|
1138
|
+
},
|
|
1130
1139
|
});
|
|
1131
1140
|
process.env.VITE_FELTDB_URL = localAuthority.endpoint;
|
|
1132
1141
|
const connectionsPath = path.join(projectDir, '.feltdb', 'connections.json');
|
|
@@ -1277,20 +1286,7 @@ async function handleDev(args) {
|
|
|
1277
1286
|
}
|
|
1278
1287
|
const open = !args.includes('--no-open');
|
|
1279
1288
|
try {
|
|
1280
|
-
const studio = handleStudio(
|
|
1281
|
-
'--port', studioPort,
|
|
1282
|
-
'--namespace', developmentSession.namespace,
|
|
1283
|
-
'--runtime', developmentSession.runtime,
|
|
1284
|
-
'--workspace-id', developmentSession.workspaceId,
|
|
1285
|
-
'--session-id', developmentSession.sessionId,
|
|
1286
|
-
'--project-id', developmentSession.projectId,
|
|
1287
|
-
'--authority-url', developmentSession.authorityUrl,
|
|
1288
|
-
'--pairing-url', developmentSession.pairingUrl,
|
|
1289
|
-
'--lifecycle', developmentSession.lifecycle,
|
|
1290
|
-
...(developmentSession.applicationUrl ? ['--app-url', developmentSession.applicationUrl] : []),
|
|
1291
|
-
...((developmentSession.runtime === 'self-hosted' || developmentSession.runtime === 'managed') ? ['--connect', developmentSession.authorityUrl] : []),
|
|
1292
|
-
'--no-open',
|
|
1293
|
-
], studioUrl => {
|
|
1289
|
+
const studio = handleStudio(developmentStudioArguments(developmentSession, studioPort), studioUrl => {
|
|
1294
1290
|
developmentSession.studioUrl = studioUrl;
|
|
1295
1291
|
transitionDevelopmentSession(developmentSession, 'READY');
|
|
1296
1292
|
console.log(`\n${developmentSessionSummary(developmentSession)}\n`);
|
|
@@ -1640,13 +1636,15 @@ async function handleStudio(args, onReady) {
|
|
|
1640
1636
|
else {
|
|
1641
1637
|
console.log(`Studio Server: http://localhost:${port}\n`);
|
|
1642
1638
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1639
|
+
let studioArtifact;
|
|
1640
|
+
try {
|
|
1641
|
+
studioArtifact = resolveCanonicalStudioArtifact();
|
|
1642
|
+
}
|
|
1643
|
+
catch (error) {
|
|
1644
|
+
console.log(`⚠️ ${error instanceof Error ? error.message : String(error)}`);
|
|
1648
1645
|
return;
|
|
1649
1646
|
}
|
|
1647
|
+
const root = studioArtifact.root;
|
|
1650
1648
|
const canonicalFlow = path.resolve(process.cwd(), 'feltdb.flow');
|
|
1651
1649
|
const projectFlow = fs.existsSync(canonicalFlow) ? canonicalFlow : undefined;
|
|
1652
1650
|
const studioMutationToken = randomBytes(24).toString('hex');
|
|
@@ -41,6 +41,22 @@ export function developmentSessionEnvironment(session) {
|
|
|
41
41
|
VITE_FELTDB_NAMESPACE: session.namespace,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
export function developmentStudioArguments(session, port) {
|
|
45
|
+
return [
|
|
46
|
+
'--port', port,
|
|
47
|
+
'--namespace', session.namespace,
|
|
48
|
+
'--runtime', session.runtime,
|
|
49
|
+
'--workspace-id', session.workspaceId,
|
|
50
|
+
'--session-id', session.sessionId,
|
|
51
|
+
'--project-id', session.projectId,
|
|
52
|
+
'--authority-url', session.authorityUrl,
|
|
53
|
+
'--pairing-url', session.pairingUrl,
|
|
54
|
+
'--lifecycle', session.lifecycle,
|
|
55
|
+
...(session.applicationUrl ? ['--app-url', session.applicationUrl] : []),
|
|
56
|
+
'--connect', session.authorityUrl,
|
|
57
|
+
'--no-open',
|
|
58
|
+
];
|
|
59
|
+
}
|
|
44
60
|
export function developmentSessionSummary(session) {
|
|
45
61
|
const rows = [
|
|
46
62
|
['Session', session.sessionId], ['Workspace', session.workspaceId], ['Project', session.projectId], ['Namespace', session.namespace],
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
export const CANONICAL_STUDIO_ARTIFACT_CONTRACT = 'feltdb.studio.app.v1';
|
|
5
|
+
// Kept non-contiguous so Core assembly's package-import rewrite cannot mutate
|
|
6
|
+
// this artifact identity from its source package into an import subpath.
|
|
7
|
+
export const CANONICAL_STUDIO_ARTIFACT_SOURCE = '@feltdb/' + 'studio';
|
|
8
|
+
/** Resolve the one Studio application shipped by the installed Core package. */
|
|
9
|
+
export function resolveCanonicalStudioArtifact(options = {}) {
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const resolvePackage = options.resolvePackage || (specifier => require.resolve(specifier));
|
|
12
|
+
const packageFile = resolvePackage('@feltdb/core/package.json');
|
|
13
|
+
const root = path.join(path.dirname(packageFile), 'dist', 'studio-app');
|
|
14
|
+
const index = path.join(root, 'index.html');
|
|
15
|
+
const identityFile = path.join(root, 'feltdb-studio.json');
|
|
16
|
+
if (!fs.existsSync(index) || !fs.existsSync(identityFile)) {
|
|
17
|
+
throw new Error(`The installed @feltdb/core package does not contain the canonical FeltDB Studio artifact (${root})`);
|
|
18
|
+
}
|
|
19
|
+
let identity;
|
|
20
|
+
try {
|
|
21
|
+
identity = JSON.parse(fs.readFileSync(identityFile, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error(`The canonical FeltDB Studio artifact identity is unreadable (${identityFile}): ${error instanceof Error ? error.message : String(error)}`);
|
|
25
|
+
}
|
|
26
|
+
if (identity.contract !== CANONICAL_STUDIO_ARTIFACT_CONTRACT
|
|
27
|
+
|| identity.source !== CANONICAL_STUDIO_ARTIFACT_SOURCE
|
|
28
|
+
|| typeof identity.version !== 'string' || !identity.version
|
|
29
|
+
|| identity.entrypoint !== 'src/main.tsx') {
|
|
30
|
+
throw new Error(`The installed Studio artifact is not the canonical @feltdb/core/studio application (${identityFile})`);
|
|
31
|
+
}
|
|
32
|
+
return { root, index, identity };
|
|
33
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// One release train keeps generated applications installable. The repository
|
|
2
2
|
// validation script checks these values against every workspace manifest.
|
|
3
|
-
export const FELTDB_PACKAGE_VERSION = '0.
|
|
3
|
+
export const FELTDB_PACKAGE_VERSION = '0.10.0';
|
|
4
4
|
export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
|
|
@@ -1460,9 +1460,12 @@ mod convergence_acceptance_tests {
|
|
|
1460
1460
|
use crate::FeltDb;
|
|
1461
1461
|
use serde::{Deserialize, Serialize};
|
|
1462
1462
|
use std::path::PathBuf;
|
|
1463
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
1463
1464
|
use std::time::SystemTime;
|
|
1464
1465
|
use std::time::UNIX_EPOCH;
|
|
1465
1466
|
|
|
1467
|
+
static TEMP_FILE_SEQUENCE: AtomicU64 = AtomicU64::new(0);
|
|
1468
|
+
|
|
1466
1469
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
1467
1470
|
struct Task {
|
|
1468
1471
|
title: String,
|
|
@@ -1475,7 +1478,11 @@ mod convergence_acceptance_tests {
|
|
|
1475
1478
|
.duration_since(UNIX_EPOCH)
|
|
1476
1479
|
.expect("time")
|
|
1477
1480
|
.as_nanos();
|
|
1478
|
-
|
|
1481
|
+
let sequence = TEMP_FILE_SEQUENCE.fetch_add(1, Ordering::Relaxed);
|
|
1482
|
+
std::env::temp_dir().join(format!(
|
|
1483
|
+
"feltdb-convergence-test-{}-{nanos}-{sequence}.data",
|
|
1484
|
+
std::process::id()
|
|
1485
|
+
))
|
|
1479
1486
|
}
|
|
1480
1487
|
|
|
1481
1488
|
/// Test convergence: Two instances make concurrent mutations on same key
|
|
@@ -1203,6 +1203,22 @@ impl ApplicationStore {
|
|
|
1203
1203
|
records: Arc::new(RwLock::new(records)),
|
|
1204
1204
|
})
|
|
1205
1205
|
}
|
|
1206
|
+
|
|
1207
|
+
/// Return every tenant scope asserted by canonical application definition
|
|
1208
|
+
/// records for one stable application identity. Callers must reject zero
|
|
1209
|
+
/// or multiple distinct tenants; ordering is never ownership evidence.
|
|
1210
|
+
pub fn tenant_ownership_candidates(&self, application_id: &str) -> Vec<String> {
|
|
1211
|
+
let Ok(records) = self.records.read() else {
|
|
1212
|
+
return Vec::new();
|
|
1213
|
+
};
|
|
1214
|
+
records
|
|
1215
|
+
.revisions
|
|
1216
|
+
.iter()
|
|
1217
|
+
.map(|value| (&value.application_id, &value.tenant_id))
|
|
1218
|
+
.filter(|(candidate, _)| candidate.as_str() == application_id)
|
|
1219
|
+
.map(|(_, tenant)| tenant.clone())
|
|
1220
|
+
.collect()
|
|
1221
|
+
}
|
|
1206
1222
|
fn persist(&self, r: &RevisionRecords) -> Result<(), String> {
|
|
1207
1223
|
if let Some(parent) = self.path.parent() {
|
|
1208
1224
|
fs::create_dir_all(parent).map_err(|e| e.to_string())?;
|
|
@@ -744,11 +744,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
744
744
|
link.peer.clone(),
|
|
745
745
|
envelope.envelope_id.sequence,
|
|
746
746
|
);
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
747
|
+
// Forwarding, like re-sending, is an explicit
|
|
748
|
+
// recovery operation. Prefer a fresh connection so
|
|
749
|
+
// a peer restart cannot leave this write accepted
|
|
750
|
+
// by a stale local socket and delivered nowhere.
|
|
751
|
+
let _ = connect_with_retry(link, 3).await;
|
|
752
|
+
if link.io.send(message).await.is_ok() {
|
|
752
753
|
delivered.push(link.peer.clone());
|
|
753
754
|
}
|
|
754
755
|
}
|
|
@@ -793,19 +794,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
793
794
|
link.peer.clone(),
|
|
794
795
|
envelope.envelope_id.sequence,
|
|
795
796
|
);
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
delivered.push(link.peer.clone());
|
|
807
|
-
}
|
|
808
|
-
}
|
|
797
|
+
// Re-sending is the recovery primitive used after a
|
|
798
|
+
// peer restart. A write to the old TCP socket can
|
|
799
|
+
// still return success after the peer has exited,
|
|
800
|
+
// because that only confirms acceptance by the
|
|
801
|
+
// local kernel. Try to establish a fresh connection
|
|
802
|
+
// first. If the peer never restarted, the connect
|
|
803
|
+
// attempt leaves the established stream untouched.
|
|
804
|
+
let _ = connect_with_retry(link, 3).await;
|
|
805
|
+
if link.io.send(message).await.is_ok() {
|
|
806
|
+
delivered.push(link.peer.clone());
|
|
809
807
|
}
|
|
810
808
|
}
|
|
811
809
|
println!(
|
|
@@ -961,12 +959,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
961
959
|
|
|
962
960
|
let mut delivered = Vec::new();
|
|
963
961
|
for link in &send_links {
|
|
962
|
+
// Acknowledgements are durable recovery evidence and are
|
|
963
|
+
// commonly rebroadcast after a peer restart. Refresh the
|
|
964
|
+
// connection first for the same reason as explicit
|
|
965
|
+
// envelope replay.
|
|
966
|
+
let _ = connect_with_retry(link, 3).await;
|
|
964
967
|
if link.io.send_acknowledgement(ack.clone()).await.is_ok() {
|
|
965
968
|
delivered.push(link.peer.clone());
|
|
966
|
-
} else if connect_with_retry(link, 3).await
|
|
967
|
-
&& link.io.send_acknowledgement(ack.clone()).await.is_ok()
|
|
968
|
-
{
|
|
969
|
-
delivered.push(link.peer.clone());
|
|
970
969
|
}
|
|
971
970
|
}
|
|
972
971
|
println!(
|