| Crates.io | drasi-bootstrap-application |
| lib.rs | drasi-bootstrap-application |
| version | 0.1.2 |
| created_at | 2026-01-15 00:15:15.512415+00 |
| updated_at | 2026-01-23 06:16:45.585531+00 |
| description | Application bootstrap plugin for Drasi |
| homepage | |
| repository | https://github.com/drasi-project/drasi-core |
| max_upload_size | |
| id | 2044233 |
| size | 102,561 |
The Application Bootstrap Provider is a bootstrap plugin for Drasi that replays stored insert events during query subscription. It works in conjunction with the Application Source to provide initial data (bootstrap) to queries when they start or subscribe to a data source.
This provider implements in-memory replay of historical insert events, enabling queries to establish their initial state before processing real-time updates. It's designed primarily for testing scenarios, embedded applications, and situations where you need programmatic control over bootstrap data.
Arc<RwLock<Vec<SourceChange>>>Ideal for:
Not suitable for:
The Application Bootstrap Provider uses a simple architecture:
ApplicationSource
|
| Stores Insert Events
v
bootstrap_data: Arc<RwLock<Vec<SourceChange>>>
^
| Shared Reference
|
ApplicationBootstrapProvider
|
| Replays Events on Bootstrap Request
v
Query Subscription
BootstrapProvider traitSourceChange::Insert eventsThe provider can operate in two modes:
bootstrap_data with an Application Source, allowing it to replay actual insert eventsThe Application Bootstrap Provider does not use YAML configuration. It is created programmatically and configured through code.
The provider uses a builder pattern for configuration:
use drasi_bootstrap_application::ApplicationBootstrapProvider;
// Create with isolated storage (for testing)
let provider = ApplicationBootstrapProvider::builder().build();
// Create with shared storage (for production use)
use std::sync::Arc;
use tokio::sync::RwLock;
use drasi_core::models::SourceChange;
let shared_data = Arc::new(RwLock::new(Vec::<SourceChange>::new()));
let provider = ApplicationBootstrapProvider::builder()
.with_shared_data(shared_data)
.build();
The provider offers multiple construction methods:
| Method | Description | Use Case |
|---|---|---|
new() |
Creates provider with isolated storage | Testing provider behavior |
with_shared_data(Arc<RwLock<Vec<SourceChange>>>) |
Creates provider sharing storage with Application Source | Normal usage |
builder() |
Returns builder for fluent configuration | Flexible configuration |
default() |
Same as new() |
Default construction |
The most common usage is connecting the bootstrap provider to an Application Source:
use drasi_bootstrap_application::ApplicationBootstrapProvider;
use drasi_source_application::ApplicationSource;
use drasi_server_core::config::SourceConfig;
use std::collections::HashMap;
use tokio::sync::mpsc;
// Create Application Source
let config = SourceConfig {
id: "my-source".to_string(),
source_type: "application".to_string(),
auto_start: true,
properties: HashMap::new(),
bootstrap_provider: None,
};
let (source_event_tx, _source_event_rx) = mpsc::channel(100);
let (event_tx, _event_rx) = mpsc::channel(100);
let (source, handle) = ApplicationSource::new(config, source_event_tx, event_tx);
// Create Bootstrap Provider sharing the source's bootstrap data
// Note: This requires accessing the source's internal bootstrap_data field
// In practice, ApplicationSource handles this internally
let bootstrap_data = source.get_bootstrap_data();
let provider = ApplicationBootstrapProvider::with_shared_data(bootstrap_data);
use drasi_bootstrap_application::ApplicationBootstrapProvider;
use std::sync::Arc;
use tokio::sync::RwLock;
use drasi_core::models::SourceChange;
// Create shared bootstrap storage
let bootstrap_data = Arc::new(RwLock::new(Vec::<SourceChange>::new()));
// Build provider with shared data
let provider = ApplicationBootstrapProvider::builder()
.with_shared_data(bootstrap_data.clone())
.build();
// The bootstrap_data can now be populated by other components
// (typically by ApplicationSource when it receives insert events)
use drasi_bootstrap_application::ApplicationBootstrapProvider;
use drasi_core::models::{Element, ElementMetadata, SourceChange};
#[tokio::test]
async fn test_bootstrap_provider() {
// Create provider with isolated storage
let provider = ApplicationBootstrapProvider::new();
// Manually populate bootstrap data for testing
let element = Element::Node {
metadata: create_test_metadata("node-1", vec!["Person"]),
properties: create_test_properties(),
};
provider.store_insert_event(SourceChange::Insert { element }).await;
// Verify stored events
let stored = provider.get_stored_events().await;
assert_eq!(stored.len(), 1);
}
The provider offers methods to manage stored events (typically called by Application Source):
use drasi_bootstrap_application::ApplicationBootstrapProvider;
use drasi_core::models::{Element, SourceChange};
let provider = ApplicationBootstrapProvider::new();
// Store an insert event
let element = Element::Node { /* ... */ };
provider.store_insert_event(SourceChange::Insert { element }).await;
// Get all stored events (for inspection or testing)
let events = provider.get_stored_events().await;
println!("Stored {} events", events.len());
// Clear stored events (for testing or reset)
provider.clear_stored_events().await;
The provider automatically filters events based on query requirements:
// When a query requests bootstrap with specific labels:
// BootstrapRequest {
// query_id: "my-query",
// node_labels: vec!["Person", "Employee"],
// relation_labels: vec!["WORKS_FOR"],
// }
// The provider will only replay events that match:
// - Nodes with labels "Person" OR "Employee"
// - Relations with label "WORKS_FOR"
// - All events if both label lists are empty
new() -> SelfCreates a new provider with isolated bootstrap data storage.
Returns: A provider instance with its own independent storage
Example:
let provider = ApplicationBootstrapProvider::new();
with_shared_data(bootstrap_data: Arc<RwLock<Vec<SourceChange>>>) -> SelfCreates a provider sharing bootstrap data with an Application Source.
Parameters:
bootstrap_data: Shared reference to the Application Source's bootstrap dataReturns: A provider instance connected to shared storage
Example:
let shared_data = Arc::new(RwLock::new(Vec::new()));
let provider = ApplicationBootstrapProvider::with_shared_data(shared_data);
builder() -> ApplicationBootstrapProviderBuilderCreates a builder for fluent configuration.
Returns: Builder instance for constructing the provider
Example:
let provider = ApplicationBootstrapProvider::builder()
.with_shared_data(shared_data)
.build();
default() -> SelfCreates a provider using default settings (same as new()).
Returns: A provider instance with isolated storage
store_insert_event(&self, change: SourceChange) -> ()Stores an insert event for future bootstrap replay.
Parameters:
change: A SourceChange::Insert event (other variants are ignored)Behavior:
Insert events; ignores Update, Delete, and Future eventsExample:
let element = Element::Node { /* ... */ };
provider.store_insert_event(SourceChange::Insert { element }).await;
get_stored_events(&self) -> Vec<SourceChange>Returns a copy of all stored insert events.
Returns: Vector of stored SourceChange::Insert events
Use Cases:
Example:
let events = provider.get_stored_events().await;
println!("Bootstrap data contains {} events", events.len());
clear_stored_events(&self) -> ()Removes all stored events from bootstrap storage.
Use Cases:
Example:
provider.clear_stored_events().await;
assert_eq!(provider.get_stored_events().await.len(), 0);
new() -> SelfCreates a new builder instance.
Example:
let builder = ApplicationBootstrapProviderBuilder::new();
with_shared_data(self, data: Arc<RwLock<Vec<SourceChange>>>) -> SelfConfigures the builder to use shared bootstrap data.
Parameters:
data: Shared reference to bootstrap data storageReturns: Builder instance for method chaining
Example:
let builder = ApplicationBootstrapProviderBuilder::new()
.with_shared_data(shared_data);
build(self) -> ApplicationBootstrapProviderConstructs the final ApplicationBootstrapProvider instance.
Returns: Configured provider instance
Example:
let provider = ApplicationBootstrapProviderBuilder::new()
.with_shared_data(shared_data)
.build();
default() -> SelfCreates a builder using default settings.
Example:
let provider = ApplicationBootstrapProviderBuilder::default().build();
bootstrap(&self, request: BootstrapRequest, context: &BootstrapContext, event_tx: BootstrapEventSender) -> Result<usize>Processes a bootstrap request from a query subscription.
Parameters:
request: Bootstrap request containing query ID and required labelscontext: Bootstrap context (currently unused by this provider)event_tx: Channel for sending bootstrap events (currently unused - ApplicationSource handles event sending)Returns:
Result<usize>: Number of matching events found, or error if bootstrap failsBehavior:
Label Matching Logic:
The Application Bootstrap Provider is designed to work seamlessly with the Application Source:
Application Code
|
| handle.send_node_insert(...)
v
ApplicationSource
|
+-- Stores Insert Event in bootstrap_data
|
+-- Forwards Event to Query Engine
v
Query Engine
Query Subscription Request
|
v
ApplicationSource.subscribe()
|
| Reads bootstrap_data
|
+-- Filters by Requested Labels
|
+-- Sends Bootstrap Events
v
Query Initialization
Current Implementation: As of the current version, ApplicationSource handles bootstrap directly in its subscribe() method (lines 337-384 in sources/application/mod.rs). The ApplicationBootstrapProvider exists for testing and potential future integration where bootstrap logic might be delegated to the provider system.
Data Connection: To connect the provider to an Application Source, you must share the same Arc<RwLock<Vec<SourceChange>>> reference between them.
Event Storage: Only Insert events are stored. Update and Delete events are not included in bootstrap data, as bootstrap represents the initial creation state, not the current state.
Events that can be stored and replayed:
pub enum SourceChange {
Insert { element: Element }, // Stored for bootstrap
Update { element: Element }, // NOT stored for bootstrap
Delete { metadata: ElementMetadata }, // NOT stored for bootstrap
Future { future_ref: FutureElementRef }, // NOT supported in bootstrap
}
Request sent by queries when subscribing:
pub struct BootstrapRequest {
pub query_id: String,
pub node_labels: Vec<String>, // Filter nodes by these labels
pub relation_labels: Vec<String>, // Filter relations by these labels
}
Vec<SourceChange>RwLock for concurrent access to bootstrap data| Scenario | Performance Impact |
|---|---|
| Few insert events (< 1000) | Excellent - minimal overhead |
| Medium datasets (1000-10000) | Good - vector operations efficient |
| Large datasets (> 10000) | Poor - consider persistent storage |
| High write rate | Moderate - write lock contention possible |
| Many concurrent queries | Good - read locks don't block each other |
| Feature | Application | PostgreSQL |
|---|---|---|
| Storage | In-memory | Database snapshot |
| Persistence | Lost on restart | Survives restarts |
| Data Source | Programmatic | Database tables |
| Performance | Fastest (in-memory) | Slower (database query) |
| Use Case | Testing, embedded | Production systems |
| Feature | Application | Platform |
|---|---|---|
| Storage | In-memory vector | Remote Query API + Redis Streams |
| Data Source | Direct API calls | External Drasi instance |
| Complexity | Minimal | High (requires external services) |
| Use Case | Single-instance apps | Multi-environment integration |
// Clear bootstrap data between tests
#[tokio::test]
async fn test_scenario() {
let provider = ApplicationBootstrapProvider::new();
provider.clear_stored_events().await;
// Populate test data
// ... send insert events
// Run test
// ... verify behavior
// Clean up
provider.clear_stored_events().await;
}
// Share bootstrap data with Application Source
let bootstrap_data = Arc::new(RwLock::new(Vec::new()));
let provider = ApplicationBootstrapProvider::with_shared_data(bootstrap_data.clone());
let (source, handle) = ApplicationSource::with_bootstrap_data(config, bootstrap_data);
// Bootstrap data is automatically populated as you send events
handle.send_node_insert("node-1", vec!["Person"], props).await?;
// Inspect bootstrap data during debugging
let events = provider.get_stored_events().await;
for (i, event) in events.iter().enumerate() {
println!("Bootstrap Event {}: {:?}", i, event);
}
Problem: Queries don't receive expected bootstrap data
Solutions:
Arc<RwLock<Vec<SourceChange>>> as the Application Sourceget_stored_events())Problem: Application memory usage increases over time
Solutions:
clear_stored_events() if bootstrap data is no longer neededProblem: Bootstrap data reflects old state after updates/deletes
Explanation: This is expected behavior. Bootstrap only stores insert events, not the cumulative effect of updates and deletes.
Solutions:
The Application Bootstrap Provider is fully thread-safe:
get_stored_events() simultaneouslystore_insert_event() safely serializes writesArc<RwLock<>> can be cloned and sharedThe provider requires these crates:
[dependencies]
drasi-lib = { path = "../../../lib" }
drasi-core = { path = "../../../core" }
anyhow = "1.0"
async-trait = "0.1"
log = "0.4"
tokio = { version = "1.0", features = ["sync"] }
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.
/drasi-core/components/sources/application/src/README.md for details on the Application Source/drasi-lib/src/bootstrap/mod.rs for the bootstrap provider trait/drasi-core/core/src/models/ for SourceChange and Element definitions