Mock Source
Overview
The Mock Source is a synthetic data generator for testing, development, and demonstration purposes within Drasi applications. It generates continuous streams of graph node data without requiring external systems, making it ideal for:
- Testing: Validate query logic and reactions without external dependencies
- Prototyping: Rapidly build and iterate on Drasi applications
- Demonstrations: Show Drasi capabilities without infrastructure setup
- Load Testing: Generate high-volume data streams for performance testing
Key Features
- Three data generation modes: Counter, SensorReading, and Generic
- Configurable generation intervals (milliseconds to seconds)
- Realistic sensor behavior (INSERT on first reading, UPDATE thereafter)
- Builder pattern for fluent construction
- Built-in test utilities for unit testing
- Zero external dependencies
Getting Started
Installation
Add to your Cargo.toml:
[]
= { = "path/to/drasi-core/components/sources/mock" }
= { = "path/to/drasi-core/lib" }
= { = "1", = ["full"] }
= "1"
Minimal Example
use ;
use Source;
async
Choosing a Data Type
| Data Type | Use When You Need | Event Behavior |
|---|---|---|
Counter |
Sequential, predictable values for testing ordering | Always INSERT |
SensorReading |
Realistic IoT simulation with updates to existing entities | INSERT then UPDATE |
Generic |
Random data for general testing | Always INSERT |
Usage Examples
Basic: Counter Source
use ;
// Counter generates sequential values: 1, 2, 3, ...
let source = builder
.with_data_type
.with_interval_ms // Generate every second
.build?;
IoT Simulation: Sensor Readings
use ;
// Simulates 10 sensors reporting temperature and humidity
let source = builder
.with_data_type
.with_interval_ms
.build?;
Integration with DrasiLib
use ;
use ;
async
Unit Testing
use ;
async
Data Types
Counter
Generates sequentially numbered nodes starting from 1.
Label: Counter
Element ID: counter_{sequence} (e.g., counter_1, counter_2)
Properties:
- value: Integer (1, 2, 3, ...)
- timestamp: String (RFC3339)
Example output:
SensorReading
Simulates IoT sensors with temperature and humidity readings.
Label: SensorReading
Element ID: sensor_{sensor_id} (e.g., sensor_0, sensor_3)
Properties:
- sensor_id: String
- temperature: Float (20.0 - 30.0)
- humidity: Float (40.0 - 60.0)
- timestamp: String (RFC3339)
Key behavior: First reading for each sensor → INSERT. Subsequent readings → UPDATE.
Example output:
Generic
Generates nodes with random integer values.
Label: Generic
Element ID: generic_{sequence} (e.g., generic_1, generic_2)
Properties:
- value: Integer (random i32)
- message: String ("Generic mock data")
- timestamp: String (RFC3339)
Example output:
Configuration
Builder Pattern (Recommended)
use ;
use DispatchMode;
let source = builder
.with_data_type // 10 sensors
.with_interval_ms // 1 second interval
.with_dispatch_mode // Backpressure support
.with_dispatch_buffer_capacity // Buffer size
.with_auto_start // Start when added to DrasiLib
.build?;
Config Struct
For configuration-file-driven scenarios:
use ;
let config = MockSourceConfig ;
let source = new?;
Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
id |
String |
Required | Unique source identifier |
data_type |
DataType |
Generic |
Data generation mode |
interval_ms |
u64 |
5000 |
Milliseconds between events (min: 1) |
dispatch_mode |
DispatchMode |
Channel |
Channel (backpressure) or Broadcast |
dispatch_buffer_capacity |
usize |
1000 |
Event buffer size |
auto_start |
bool |
true |
Auto-start when added to DrasiLib |
Validation
Configuration is validated on construction. Errors occur if:
interval_msis 0sensor_countis 0 (for SensorReading mode)
Testing Utilities
test_subscribe()
Subscribe directly to source events, bypassing DrasiLib:
let source = builder.build?;
let mut rx = source.test_subscribe;
source.start.await?;
while let Ok = rx.recv.await
inject_event()
Inject custom events for deterministic testing:
use ;
use Arc;
let source = builder.build?;
let mut rx = source.test_subscribe;
// Create custom element
let reference = new;
let metadata = ElementMetadata ;
let mut properties = new;
properties.insert;
let element = Node ;
// Inject and receive
source.inject_event.await?;
let event = rx.recv.await?;
Implementation Details
Lifecycle
Stopped → Starting → Running → Stopping → Stopped
- start(): Spawns a Tokio task that generates events at the configured interval
- stop(): Aborts the generation task and waits for completion
- status(): Returns current
ComponentStatus
Event Generation
The source runs an internal Tokio task that:
- Waits for the configured interval
- Generates a node based on
data_type - Dispatches the event to all subscribers
- Repeats until stopped
Dispatch Modes
| Mode | Behavior | Use Case |
|---|---|---|
Channel |
Isolated channel per subscriber with backpressure | Production, reliable delivery |
Broadcast |
Shared channel, no backpressure, may drop events | High throughput, lossy acceptable |
Performance
Recommended Intervals
| Use Case | Interval | Events/sec |
|---|---|---|
| Load testing | 10-50ms | 20-100 |
| Development | 100-500ms | 2-10 |
| Demos | 1000-3000ms | 0.3-1 |
Warning: Intervals below 10ms may saturate CPU with complex queries.
Memory
- ~200-500 bytes per event
- Single Tokio task per source
- Buffer memory =
dispatch_buffer_capacity× event size
Troubleshooting
No Events Received
- Verify source is started:
source.start().await? - Check status:
source.status().awaitshould beRunning - Ensure subscriber is registered before starting
Wrong Data Type
Check the data_type property:
let props = source.properties;
println!;
Query Returns Empty
Verify label and property names match the data type:
| Data Type | Label | Properties |
|---|---|---|
| Counter | Counter |
value, timestamp |
| SensorReading | SensorReading |
sensor_id, temperature, humidity, timestamp |
| Generic | Generic |
value, message, timestamp |
Validation Errors
Error: interval_ms cannot be 0
→ Use a positive interval (minimum 1)
Error: sensor_count cannot be 0
→ Use DataType::sensor_reading(n) where n ≥ 1
Limitations
- Generates nodes only (no relationships/edges)
- Fixed schemas per data type (not customizable)
- Only SensorReading supports UPDATE events
- Counter resets on restart; seen sensors persist across stop/start cycles
- Randomness is not cryptographically secure
Related Documentation
- Source trait:
lib/src/sources/mod.rs - Test examples:
components/sources/mock/src/tests.rs