@feltdb/core 0.4.12 → 0.4.13
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/cli/commands.js +5 -2
- package/dist/cli/index.js +1 -1
- package/dist/create/cli.js +16 -1
- package/dist/create/create.js +30 -16
- package/dist/create/docker-compose-generator.js +48 -9
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +2238 -0
- package/dist/create/server-source/Cargo.toml +7 -0
- package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
- package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
- package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
- package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
- package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
- package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
- package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
- package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
- package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
- package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
- package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
- package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
- package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
- package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
- package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
- package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
- package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
- package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
- package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
- package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
- package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
- package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
- package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
- package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
- package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
- package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
- package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
- package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
- package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
- package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
- package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
- package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
- package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
- package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
- package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
- package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
- package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
- package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
- package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
- package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
- package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
- package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
- package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
- package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
- package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
- package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
- package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
- package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
- package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
- package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
- package/dist/flowspec.d.ts +2 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +28 -4
- package/dist/state-contract.d.ts +7 -1
- package/dist/state-contract.d.ts.map +1 -1
- package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
- package/dist/studio/components/index.js +1 -1
- package/dist/studio/{components-Q0Nx6gLE.js → components-bHTARBin.js} +156 -46
- package/dist/studio/index.js +31 -28
- package/dist/studio-app/assets/{index-BHOmLZF-.js → index-BqKquLuU.js} +8 -8
- package/dist/studio-app/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
/// Content addressable storage and acquisition module
|
|
2
|
+
use crate::{
|
|
3
|
+
AcquisitionPolicy, ContentIdentity, ContentTransferError, FlowRef, PeerAdvertisement, PeerId,
|
|
4
|
+
};
|
|
5
|
+
use std::collections::HashMap;
|
|
6
|
+
use std::sync::{Arc, Mutex};
|
|
7
|
+
|
|
8
|
+
/// A content addressable store that tracks where content lives
|
|
9
|
+
#[derive(Clone)]
|
|
10
|
+
pub struct ContentAddressableStore {
|
|
11
|
+
/// Map from content hash to actual content
|
|
12
|
+
content: Arc<Mutex<HashMap<String, Vec<u8>>>>,
|
|
13
|
+
/// Map from content hash to which peers have it
|
|
14
|
+
content_locations: Arc<Mutex<HashMap<String, Vec<PeerId>>>>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl ContentAddressableStore {
|
|
18
|
+
/// Create a new content addressable store
|
|
19
|
+
pub fn new() -> Self {
|
|
20
|
+
Self {
|
|
21
|
+
content: Arc::new(Mutex::new(HashMap::new())),
|
|
22
|
+
content_locations: Arc::new(Mutex::new(HashMap::new())),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Store content and track its hash
|
|
27
|
+
pub fn store(&self, content: Vec<u8>, hash: String) -> Result<(), String> {
|
|
28
|
+
let mut store = self.content.lock().map_err(|e| e.to_string())?;
|
|
29
|
+
store.insert(hash, content);
|
|
30
|
+
Ok(())
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Retrieve content by hash
|
|
34
|
+
pub fn get(&self, hash: &str) -> Result<Option<Vec<u8>>, String> {
|
|
35
|
+
let store = self.content.lock().map_err(|e| e.to_string())?;
|
|
36
|
+
Ok(store.get(hash).cloned())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Record that a peer has a piece of content
|
|
40
|
+
pub fn record_location(&self, hash: String, peer: PeerId) -> Result<(), String> {
|
|
41
|
+
let mut locations = self.content_locations.lock().map_err(|e| e.to_string())?;
|
|
42
|
+
locations.entry(hash).or_insert_with(Vec::new).push(peer);
|
|
43
|
+
Ok(())
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Find peers that have a specific piece of content
|
|
47
|
+
pub fn find_peers_for_content(&self, hash: &str) -> Result<Vec<PeerId>, String> {
|
|
48
|
+
let locations = self.content_locations.lock().map_err(|e| e.to_string())?;
|
|
49
|
+
Ok(locations.get(hash).cloned().unwrap_or_default())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Check if content exists locally
|
|
53
|
+
pub fn has_content(&self, hash: &str) -> Result<bool, String> {
|
|
54
|
+
let store = self.content.lock().map_err(|e| e.to_string())?;
|
|
55
|
+
Ok(store.contains_key(hash))
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
impl Default for ContentAddressableStore {
|
|
60
|
+
fn default() -> Self {
|
|
61
|
+
Self::new()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Reference resolution with fallback to peer acquisition
|
|
66
|
+
pub struct DistributedReferenceResolver {
|
|
67
|
+
/// Local content store
|
|
68
|
+
_local_store: ContentAddressableStore,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
impl DistributedReferenceResolver {
|
|
72
|
+
/// Create a new distributed reference resolver
|
|
73
|
+
pub fn new(local_store: ContentAddressableStore) -> Self {
|
|
74
|
+
Self {
|
|
75
|
+
_local_store: local_store,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Try to resolve a reference, first locally then from peers
|
|
80
|
+
pub fn resolve(
|
|
81
|
+
&self,
|
|
82
|
+
flow_ref: &FlowRef,
|
|
83
|
+
available_peers: &[PeerAdvertisement],
|
|
84
|
+
) -> Result<Option<Vec<u8>>, String> {
|
|
85
|
+
// Step 1: Try local resolution
|
|
86
|
+
// Check if we have this content hash locally
|
|
87
|
+
// For now, just indicate it wasn't found
|
|
88
|
+
// In a real implementation, we'd query from local content store
|
|
89
|
+
|
|
90
|
+
// Step 2: Look for peers that might have this
|
|
91
|
+
for peer_ad in available_peers {
|
|
92
|
+
if peer_ad.provides_namespace(&flow_ref.namespace) {
|
|
93
|
+
// This peer might have the content
|
|
94
|
+
// In a full implementation, we'd make a remote call here
|
|
95
|
+
return Ok(None); // Placeholder
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Ok(None)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// Projection distribution - tracks where projections are hosted
|
|
104
|
+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
105
|
+
pub struct ProjectionDistribution {
|
|
106
|
+
/// Namespace this projection is for
|
|
107
|
+
pub namespace: String,
|
|
108
|
+
/// Projection type (e.g., "search", "vector")
|
|
109
|
+
pub projection_type: String,
|
|
110
|
+
/// Peer hosting this projection
|
|
111
|
+
pub hosted_by: PeerId,
|
|
112
|
+
/// Last update timestamp
|
|
113
|
+
pub updated_at_ms: u128,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
impl ProjectionDistribution {
|
|
117
|
+
/// Create a new projection distribution entry
|
|
118
|
+
pub fn new(
|
|
119
|
+
namespace: impl Into<String>,
|
|
120
|
+
projection_type: impl Into<String>,
|
|
121
|
+
hosted_by: PeerId,
|
|
122
|
+
) -> Self {
|
|
123
|
+
Self {
|
|
124
|
+
namespace: namespace.into(),
|
|
125
|
+
projection_type: projection_type.into(),
|
|
126
|
+
hosted_by,
|
|
127
|
+
updated_at_ms: crate::now_ms(),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Capability location registry - tracks where capabilities are available
|
|
133
|
+
#[derive(Clone)]
|
|
134
|
+
pub struct CapabilityLocationRegistry {
|
|
135
|
+
/// Map from capability name to list of peers providing it
|
|
136
|
+
capability_locations: Arc<Mutex<HashMap<String, Vec<PeerId>>>>,
|
|
137
|
+
/// Map from peer ID to capabilities it provides
|
|
138
|
+
peer_capabilities: Arc<Mutex<HashMap<String, Vec<String>>>>,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
impl CapabilityLocationRegistry {
|
|
142
|
+
/// Create a new capability location registry
|
|
143
|
+
pub fn new() -> Self {
|
|
144
|
+
Self {
|
|
145
|
+
capability_locations: Arc::new(Mutex::new(HashMap::new())),
|
|
146
|
+
peer_capabilities: Arc::new(Mutex::new(HashMap::new())),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Register that a peer provides a capability
|
|
151
|
+
pub fn register_capability(&self, capability: String, peer: PeerId) -> Result<(), String> {
|
|
152
|
+
let mut locations = self
|
|
153
|
+
.capability_locations
|
|
154
|
+
.lock()
|
|
155
|
+
.map_err(|e| e.to_string())?;
|
|
156
|
+
locations
|
|
157
|
+
.entry(capability.clone())
|
|
158
|
+
.or_insert_with(Vec::new)
|
|
159
|
+
.push(peer.clone());
|
|
160
|
+
|
|
161
|
+
let mut peer_caps = self.peer_capabilities.lock().map_err(|e| e.to_string())?;
|
|
162
|
+
peer_caps
|
|
163
|
+
.entry(peer.id.clone())
|
|
164
|
+
.or_insert_with(Vec::new)
|
|
165
|
+
.push(capability);
|
|
166
|
+
|
|
167
|
+
Ok(())
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Find peers that provide a capability
|
|
171
|
+
pub fn peers_for_capability(&self, capability: &str) -> Result<Vec<PeerId>, String> {
|
|
172
|
+
let locations = self
|
|
173
|
+
.capability_locations
|
|
174
|
+
.lock()
|
|
175
|
+
.map_err(|e| e.to_string())?;
|
|
176
|
+
Ok(locations.get(capability).cloned().unwrap_or_default())
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// Get all capabilities provided by a peer
|
|
180
|
+
pub fn capabilities_for_peer(&self, peer_id: &PeerId) -> Result<Vec<String>, String> {
|
|
181
|
+
let peer_caps = self.peer_capabilities.lock().map_err(|e| e.to_string())?;
|
|
182
|
+
Ok(peer_caps.get(&peer_id.id).cloned().unwrap_or_default())
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
impl Default for CapabilityLocationRegistry {
|
|
187
|
+
fn default() -> Self {
|
|
188
|
+
Self::new()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Manager for acquiring content from distributed peers
|
|
193
|
+
#[derive(Clone)]
|
|
194
|
+
pub struct ContentAcquisitionManager {
|
|
195
|
+
/// Local content store
|
|
196
|
+
content_store: ContentAddressableStore,
|
|
197
|
+
/// Peer registry for discovering providers
|
|
198
|
+
peer_registry: Arc<Mutex<HashMap<String, PeerAdvertisement>>>,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
impl ContentAcquisitionManager {
|
|
202
|
+
/// Create a new content acquisition manager
|
|
203
|
+
pub fn new(content_store: ContentAddressableStore) -> Self {
|
|
204
|
+
Self {
|
|
205
|
+
content_store,
|
|
206
|
+
peer_registry: Arc::new(Mutex::new(HashMap::new())),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// Register a peer for content acquisition
|
|
211
|
+
pub fn register_peer(&self, advertisement: PeerAdvertisement) -> Result<(), String> {
|
|
212
|
+
let mut registry = self.peer_registry.lock().map_err(|e| e.to_string())?;
|
|
213
|
+
registry.insert(advertisement.peer_id.id.clone(), advertisement);
|
|
214
|
+
Ok(())
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// Resolve a reference using the acquisition policy
|
|
218
|
+
pub fn resolve(
|
|
219
|
+
&self,
|
|
220
|
+
flow_ref: &FlowRef,
|
|
221
|
+
policy: AcquisitionPolicy,
|
|
222
|
+
) -> Result<Option<Vec<u8>>, String> {
|
|
223
|
+
// Step 1: Always try local first
|
|
224
|
+
let local_key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
|
|
225
|
+
|
|
226
|
+
// Check local content store by namespace:id key
|
|
227
|
+
// First, try to get it directly
|
|
228
|
+
if let Ok(Some(content)) = self.content_store.get(&local_key) {
|
|
229
|
+
return Ok(Some(content));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Step 2: Handle acquisition policies
|
|
233
|
+
match policy {
|
|
234
|
+
AcquisitionPolicy::LocalOnly => {
|
|
235
|
+
// Fail if not available locally
|
|
236
|
+
Ok(None)
|
|
237
|
+
}
|
|
238
|
+
AcquisitionPolicy::PreferLocal
|
|
239
|
+
| AcquisitionPolicy::RemoteAllowed
|
|
240
|
+
| AcquisitionPolicy::Required => {
|
|
241
|
+
// Try to find a peer that has this content
|
|
242
|
+
// In a real implementation, this would be async and perform network I/O
|
|
243
|
+
self.find_and_acquire_from_peer(flow_ref)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// Find a peer that can provide content and attempt acquisition
|
|
249
|
+
fn find_and_acquire_from_peer(&self, flow_ref: &FlowRef) -> Result<Option<Vec<u8>>, String> {
|
|
250
|
+
let registry = self.peer_registry.lock().map_err(|e| e.to_string())?;
|
|
251
|
+
|
|
252
|
+
// Find peers that provide this namespace
|
|
253
|
+
let providers: Vec<_> = registry
|
|
254
|
+
.values()
|
|
255
|
+
.filter(|ad| ad.provides_namespace(&flow_ref.namespace))
|
|
256
|
+
.collect();
|
|
257
|
+
|
|
258
|
+
if providers.is_empty() {
|
|
259
|
+
return Ok(None);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// For now, try the first available provider
|
|
263
|
+
// In a real implementation, this would use load-aware selection
|
|
264
|
+
if providers.first().is_some() {
|
|
265
|
+
// Simulate content acquisition by checking what we already have
|
|
266
|
+
// In reality, this would perform a remote RPC call
|
|
267
|
+
Ok(None)
|
|
268
|
+
} else {
|
|
269
|
+
Ok(None)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/// Verify content against its content identity
|
|
274
|
+
pub fn verify_content(
|
|
275
|
+
content: &[u8],
|
|
276
|
+
expected_identity: &ContentIdentity,
|
|
277
|
+
) -> Result<(), ContentTransferError> {
|
|
278
|
+
let computed_identity = ContentIdentity::from_bytes(content);
|
|
279
|
+
|
|
280
|
+
if computed_identity.hash == expected_identity.hash {
|
|
281
|
+
Ok(())
|
|
282
|
+
} else {
|
|
283
|
+
Err(ContentTransferError::HashMismatch {
|
|
284
|
+
expected: expected_identity.hash.clone(),
|
|
285
|
+
computed: computed_identity.hash,
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/// Materialize acquired content into local storage
|
|
291
|
+
pub fn materialize(
|
|
292
|
+
&self,
|
|
293
|
+
flow_ref: &FlowRef,
|
|
294
|
+
content: Vec<u8>,
|
|
295
|
+
content_identity: &ContentIdentity,
|
|
296
|
+
) -> Result<(), String> {
|
|
297
|
+
// Verify content hash
|
|
298
|
+
Self::verify_content(&content, content_identity).map_err(|e| e.to_string())?;
|
|
299
|
+
|
|
300
|
+
// Store in content store
|
|
301
|
+
let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
|
|
302
|
+
self.content_store.store(content, key)?;
|
|
303
|
+
|
|
304
|
+
Ok(())
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/// Get content from local store
|
|
308
|
+
pub fn get_local(&self, flow_ref: &FlowRef) -> Result<Option<Vec<u8>>, String> {
|
|
309
|
+
let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
|
|
310
|
+
self.content_store.get(&key)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/// Check if content exists locally
|
|
314
|
+
pub fn has_local(&self, flow_ref: &FlowRef) -> Result<bool, String> {
|
|
315
|
+
let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
|
|
316
|
+
self.content_store.has_content(&key)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
impl Default for ContentAcquisitionManager {
|
|
321
|
+
fn default() -> Self {
|
|
322
|
+
Self::new(ContentAddressableStore::new())
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
#[cfg(test)]
|
|
327
|
+
mod tests {
|
|
328
|
+
use super::*;
|
|
329
|
+
|
|
330
|
+
#[test]
|
|
331
|
+
fn test_content_addressable_store() {
|
|
332
|
+
let store = ContentAddressableStore::new();
|
|
333
|
+
let content = b"hello world".to_vec();
|
|
334
|
+
let hash = "abc123".to_string();
|
|
335
|
+
|
|
336
|
+
store.store(content.clone(), hash.clone()).unwrap();
|
|
337
|
+
let retrieved = store.get(&hash).unwrap();
|
|
338
|
+
assert_eq!(retrieved, Some(content));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
#[test]
|
|
342
|
+
fn test_content_addressable_store_locations() {
|
|
343
|
+
let store = ContentAddressableStore::new();
|
|
344
|
+
let hash = "abc123".to_string();
|
|
345
|
+
let peer_a = PeerId::new("peer-a");
|
|
346
|
+
let peer_b = PeerId::new("peer-b");
|
|
347
|
+
|
|
348
|
+
store.record_location(hash.clone(), peer_a).unwrap();
|
|
349
|
+
store.record_location(hash.clone(), peer_b).unwrap();
|
|
350
|
+
|
|
351
|
+
let peers = store.find_peers_for_content(&hash).unwrap();
|
|
352
|
+
assert_eq!(peers.len(), 2);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
#[test]
|
|
356
|
+
fn test_projection_distribution() {
|
|
357
|
+
let peer = PeerId::new("search-node");
|
|
358
|
+
let proj = ProjectionDistribution::new("docs", "search", peer.clone());
|
|
359
|
+
|
|
360
|
+
assert_eq!(proj.namespace, "docs");
|
|
361
|
+
assert_eq!(proj.projection_type, "search");
|
|
362
|
+
assert_eq!(proj.hosted_by.id, "search-node");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
#[test]
|
|
366
|
+
fn test_capability_location_registry() {
|
|
367
|
+
let registry = CapabilityLocationRegistry::new();
|
|
368
|
+
let peer_a = PeerId::new("peer-a");
|
|
369
|
+
let peer_b = PeerId::new("peer-b");
|
|
370
|
+
|
|
371
|
+
registry
|
|
372
|
+
.register_capability("search:docs".to_string(), peer_a.clone())
|
|
373
|
+
.unwrap();
|
|
374
|
+
registry
|
|
375
|
+
.register_capability("search:docs".to_string(), peer_b.clone())
|
|
376
|
+
.unwrap();
|
|
377
|
+
registry
|
|
378
|
+
.register_capability("vector:embeddings".to_string(), peer_b.clone())
|
|
379
|
+
.unwrap();
|
|
380
|
+
|
|
381
|
+
let search_peers = registry.peers_for_capability("search:docs").unwrap();
|
|
382
|
+
assert_eq!(search_peers.len(), 2);
|
|
383
|
+
|
|
384
|
+
let vector_peers = registry.peers_for_capability("vector:embeddings").unwrap();
|
|
385
|
+
assert_eq!(vector_peers.len(), 1);
|
|
386
|
+
|
|
387
|
+
let peer_a_caps = registry.capabilities_for_peer(&peer_a).unwrap();
|
|
388
|
+
assert_eq!(peer_a_caps.len(), 1);
|
|
389
|
+
|
|
390
|
+
let peer_b_caps = registry.capabilities_for_peer(&peer_b).unwrap();
|
|
391
|
+
assert_eq!(peer_b_caps.len(), 2);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
#[test]
|
|
395
|
+
fn test_content_acquisition_manager_local_only() {
|
|
396
|
+
let store = ContentAddressableStore::new();
|
|
397
|
+
let manager = ContentAcquisitionManager::new(store);
|
|
398
|
+
|
|
399
|
+
let flow_ref = FlowRef::new("docs", "123");
|
|
400
|
+
|
|
401
|
+
// Should not find content with LocalOnly policy
|
|
402
|
+
let result = manager
|
|
403
|
+
.resolve(&flow_ref, AcquisitionPolicy::LocalOnly)
|
|
404
|
+
.unwrap();
|
|
405
|
+
assert!(result.is_none());
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
#[test]
|
|
409
|
+
fn test_content_acquisition_manager_materialize() {
|
|
410
|
+
let store = ContentAddressableStore::new();
|
|
411
|
+
let manager = ContentAcquisitionManager::new(store);
|
|
412
|
+
|
|
413
|
+
let flow_ref = FlowRef::new("docs", "123");
|
|
414
|
+
let content = b"test content".to_vec();
|
|
415
|
+
let content_id = ContentIdentity::from_bytes(&content);
|
|
416
|
+
|
|
417
|
+
// Materialize content
|
|
418
|
+
manager
|
|
419
|
+
.materialize(&flow_ref, content.clone(), &content_id)
|
|
420
|
+
.unwrap();
|
|
421
|
+
|
|
422
|
+
// Verify it's stored locally
|
|
423
|
+
let retrieved = manager.get_local(&flow_ref).unwrap();
|
|
424
|
+
assert_eq!(retrieved, Some(content));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
#[test]
|
|
428
|
+
fn test_content_acquisition_manager_has_local() {
|
|
429
|
+
let store = ContentAddressableStore::new();
|
|
430
|
+
let manager = ContentAcquisitionManager::new(store);
|
|
431
|
+
|
|
432
|
+
let flow_ref = FlowRef::new("docs", "123");
|
|
433
|
+
|
|
434
|
+
// Initially should not have content
|
|
435
|
+
assert!(!manager.has_local(&flow_ref).unwrap());
|
|
436
|
+
|
|
437
|
+
// Materialize content
|
|
438
|
+
let content = b"test content".to_vec();
|
|
439
|
+
let content_id = ContentIdentity::from_bytes(&content);
|
|
440
|
+
manager
|
|
441
|
+
.materialize(&flow_ref, content, &content_id)
|
|
442
|
+
.unwrap();
|
|
443
|
+
|
|
444
|
+
// Now should have it
|
|
445
|
+
assert!(manager.has_local(&flow_ref).unwrap());
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
#[test]
|
|
449
|
+
fn test_verify_content_success() {
|
|
450
|
+
let content = b"test content".to_vec();
|
|
451
|
+
let content_id = ContentIdentity::from_bytes(&content);
|
|
452
|
+
|
|
453
|
+
let result = ContentAcquisitionManager::verify_content(&content, &content_id);
|
|
454
|
+
assert!(result.is_ok());
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
#[test]
|
|
458
|
+
fn test_verify_content_hash_mismatch() {
|
|
459
|
+
let content = b"test content".to_vec();
|
|
460
|
+
let wrong_id = ContentIdentity::new("wronghash");
|
|
461
|
+
|
|
462
|
+
let result = ContentAcquisitionManager::verify_content(&content, &wrong_id);
|
|
463
|
+
assert!(result.is_err());
|
|
464
|
+
}
|
|
465
|
+
}
|