pub struct Store { /* private fields */ }Expand description
A BIRD store for reading and writing records.
Implementations§
Source§impl Store
impl Store
Sourcepub fn write_attempt(&self, record: &AttemptRecord) -> Result<()>
pub fn write_attempt(&self, record: &AttemptRecord) -> Result<()>
Write an attempt record to the store (v5 schema).
Call this at invocation start. The outcome should be written when
the invocation completes using write_outcome().
Behavior depends on storage mode:
- Parquet: Creates a new Parquet file in the appropriate date partition
- DuckDB: Inserts directly into the local.attempts table
Sourcepub fn start_invocation(&self, record: &AttemptRecord) -> Result<AttemptRecord>
pub fn start_invocation(&self, record: &AttemptRecord) -> Result<AttemptRecord>
Start an invocation by writing an attempt (v5 schema).
This is the v5 equivalent of start_pending_invocation().
Returns the attempt record for later use with complete_invocation().
Sourcepub fn attempt_count(&self) -> Result<i64>
pub fn attempt_count(&self) -> Result<i64>
Get the count of attempts in the store.
Sourcepub fn get_pending_attempts(&self) -> Result<Vec<AttemptRecord>>
pub fn get_pending_attempts(&self) -> Result<Vec<AttemptRecord>>
Get pending attempts (attempts without outcomes).
In v5 schema, this replaces the pending file mechanism:
SELECT * FROM attempts WHERE id NOT IN (SELECT attempt_id FROM outcomes)
Source§impl Store
impl Store
Sourcepub fn compact_partition_with_opts(
&self,
partition_dir: &Path,
opts: &CompactOptions,
) -> Result<CompactStats>
pub fn compact_partition_with_opts( &self, partition_dir: &Path, opts: &CompactOptions, ) -> Result<CompactStats>
Compact files in a partition directory, grouped by session.
Behavior depends on options:
consolidate: Merge ALL files into single file per sessionfile_threshold: Compact when > N non-compacted filesrecompact_threshold: Re-compact when > N compacted files exist
Sourcepub fn compact_partition(
&self,
partition_dir: &Path,
file_threshold: usize,
session_filter: Option<&str>,
dry_run: bool,
) -> Result<CompactStats>
pub fn compact_partition( &self, partition_dir: &Path, file_threshold: usize, session_filter: Option<&str>, dry_run: bool, ) -> Result<CompactStats>
Compact files in a partition directory (legacy API).
Sourcepub fn compact_data_type(
&self,
data_dir: &Path,
file_threshold: usize,
session_filter: Option<&str>,
dry_run: bool,
) -> Result<CompactStats>
pub fn compact_data_type( &self, data_dir: &Path, file_threshold: usize, session_filter: Option<&str>, dry_run: bool, ) -> Result<CompactStats>
Compact all partitions in a data type directory (attempts, outcomes, outputs, sessions).
Sourcepub fn compact_for_session(
&self,
session_id: &str,
file_threshold: usize,
dry_run: bool,
) -> Result<CompactStats>
pub fn compact_for_session( &self, session_id: &str, file_threshold: usize, dry_run: bool, ) -> Result<CompactStats>
Compact recent data for a specific session (used by shell hooks).
Checks all date partitions in recent data.
Sourcepub fn compact_session_today(
&self,
session_id: &str,
file_threshold: usize,
dry_run: bool,
) -> Result<CompactStats>
pub fn compact_session_today( &self, session_id: &str, file_threshold: usize, dry_run: bool, ) -> Result<CompactStats>
Fast compaction check for today’s partition only (used by shell hooks).
This is the most lightweight check - only looks at today’s date partition.
Sourcepub fn compact_recent(
&self,
file_threshold: usize,
dry_run: bool,
) -> Result<CompactStats>
pub fn compact_recent( &self, file_threshold: usize, dry_run: bool, ) -> Result<CompactStats>
Compact all recent data that exceeds the file threshold (global mode).
Sourcepub fn compact_archive(
&self,
file_threshold: usize,
dry_run: bool,
) -> Result<CompactStats>
pub fn compact_archive( &self, file_threshold: usize, dry_run: bool, ) -> Result<CompactStats>
Compact all archive data that exceeds the file threshold.
Sourcepub fn archive_old_data(
&self,
older_than_days: u32,
dry_run: bool,
) -> Result<ArchiveStats>
pub fn archive_old_data( &self, older_than_days: u32, dry_run: bool, ) -> Result<ArchiveStats>
Migrate old data from recent to archive with consolidation.
Consolidates all files in each date partition into a single parquet file in the archive, then removes the source files.
Sourcepub fn auto_compact(
&self,
opts: &AutoCompactOptions,
) -> Result<(CompactStats, ArchiveStats)>
pub fn auto_compact( &self, opts: &AutoCompactOptions, ) -> Result<(CompactStats, ArchiveStats)>
Run auto-compaction based on options.
Sourcepub fn compact_recent_with_opts(
&self,
opts: &CompactOptions,
) -> Result<CompactStats>
pub fn compact_recent_with_opts( &self, opts: &CompactOptions, ) -> Result<CompactStats>
Compact recent data with full options.
Sourcepub fn compact_archive_with_opts(
&self,
opts: &CompactOptions,
) -> Result<CompactStats>
pub fn compact_archive_with_opts( &self, opts: &CompactOptions, ) -> Result<CompactStats>
Compact archive data with full options.
Sourcepub fn compact_data_type_with_opts(
&self,
data_dir: &Path,
opts: &CompactOptions,
) -> Result<CompactStats>
pub fn compact_data_type_with_opts( &self, data_dir: &Path, opts: &CompactOptions, ) -> Result<CompactStats>
Compact data type directory with full options.
Sourcepub fn compact_for_session_with_opts(
&self,
session_id: &str,
opts: &CompactOptions,
) -> Result<CompactStats>
pub fn compact_for_session_with_opts( &self, session_id: &str, opts: &CompactOptions, ) -> Result<CompactStats>
Compact files for a specific session with full options.
Sourcepub fn clean(&self, opts: &CleanOptions) -> Result<CleanStats>
pub fn clean(&self, opts: &CleanOptions) -> Result<CleanStats>
Clean operation: recover orphaned invocations and optionally prune archive.
This:
- Scans for pending invocations (attempts without matching outcomes)
- Creates outcome records for orphaned invocations
- Optionally prunes old archive data
Sourcepub fn prune_archive(
&self,
older_than_days: u32,
dry_run: bool,
) -> Result<PruneStats>
pub fn prune_archive( &self, older_than_days: u32, dry_run: bool, ) -> Result<PruneStats>
Prune old archive data.
Deletes data from the archive tier older than the specified number of days.
Source§impl Store
impl Store
Sourcepub fn load_format_config(&self) -> Result<FormatConfig>
pub fn load_format_config(&self) -> Result<FormatConfig>
Load format config from BIRD_ROOT/event-formats.toml.
Sourcepub fn detect_format(&self, cmd: &str) -> Result<String>
pub fn detect_format(&self, cmd: &str) -> Result<String>
Detect format for a command using DuckDB SQL matching.
Uses SQL LIKE patterns for matching, which prepares for future integration with duck_hunt_match_command_patterns().
Sourcepub fn extract_events(
&self,
invocation_id: &str,
format_override: Option<&str>,
) -> Result<usize>
pub fn extract_events( &self, invocation_id: &str, format_override: Option<&str>, ) -> Result<usize>
Extract events from an invocation’s output using duck_hunt.
Parses the stdout/stderr of an invocation and stores the extracted events. Uses read_duck_hunt_log() directly on storage refs for efficiency - no content is loaded into Rust memory.
Returns the number of events extracted.
Sourcepub fn write_events(&self, records: &[EventRecord]) -> Result<()>
pub fn write_events(&self, records: &[EventRecord]) -> Result<()>
Write event records to the store.
Behavior depends on storage mode:
- Parquet: Creates Parquet files partitioned by date
- DuckDB: Inserts directly into the local.events
Sourcepub fn query_events(&self, filters: &EventFilters) -> Result<Vec<EventSummary>>
pub fn query_events(&self, filters: &EventFilters) -> Result<Vec<EventSummary>>
Query events with optional filters.
Sourcepub fn event_count(&self, filters: &EventFilters) -> Result<i64>
pub fn event_count(&self, filters: &EventFilters) -> Result<i64>
Count events matching the given filters.
Sourcepub fn delete_events_for_invocation(&self, invocation_id: &str) -> Result<usize>
pub fn delete_events_for_invocation(&self, invocation_id: &str) -> Result<usize>
Delete events for an invocation (for re-extraction).
Behavior depends on storage mode:
- Parquet: Deletes parquet files containing the events
- DuckDB: Deletes rows from local.events
Sourcepub fn invocations_without_events(
&self,
since: Option<NaiveDate>,
limit: Option<usize>,
) -> Result<Vec<InvocationSummary>>
pub fn invocations_without_events( &self, since: Option<NaiveDate>, limit: Option<usize>, ) -> Result<Vec<InvocationSummary>>
Get invocations that have outputs but no events extracted yet.
Useful for backfilling events from existing invocations.
Source§impl Store
impl Store
Sourcepub fn write_invocation(&self, record: &InvocationRecord) -> Result<()>
pub fn write_invocation(&self, record: &InvocationRecord) -> Result<()>
Write an invocation record to the store (v5 schema).
This writes both an attempt and an outcome record, since the invocation is already complete. For long-running commands, use start_invocation() followed by complete_invocation().
Sourcepub fn recent_invocations(&self, limit: usize) -> Result<Vec<InvocationSummary>>
pub fn recent_invocations(&self, limit: usize) -> Result<Vec<InvocationSummary>>
Get recent invocations (last 7 days).
Sourcepub fn last_invocation(&self) -> Result<Option<InvocationSummary>>
pub fn last_invocation(&self) -> Result<Option<InvocationSummary>>
Get the last invocation (most recent).
Sourcepub fn query_invocations_with_limit(
&self,
query: &Query,
default_limit: usize,
) -> Result<Vec<InvocationSummary>>
pub fn query_invocations_with_limit( &self, query: &Query, default_limit: usize, ) -> Result<Vec<InvocationSummary>>
Query invocations with filters from the query micro-language.
Supports:
~Nrange selector (limit to N results)%exit<>0field filters (exit code, duration, etc.)%/pattern/command regex
Use default_limit to specify the limit when no range is provided:
- 20 for listing commands (shq i)
- 1 for single-item commands (shq o, shq I, shq R)
Sourcepub fn query_invocations(&self, query: &Query) -> Result<Vec<InvocationSummary>>
pub fn query_invocations(&self, query: &Query) -> Result<Vec<InvocationSummary>>
Query invocations with default limit of 20 (for listing).
Sourcepub fn invocation_count(&self) -> Result<i64>
pub fn invocation_count(&self) -> Result<i64>
Count total invocations in the store.
Sourcepub fn find_by_tag(&self, tag: &str) -> Result<Option<String>>
pub fn find_by_tag(&self, tag: &str) -> Result<Option<String>>
Find an invocation by its tag. Returns the full invocation ID if found.
Sourcepub fn set_tag(&self, invocation_id: &str, tag: Option<&str>) -> Result<()>
pub fn set_tag(&self, invocation_id: &str, tag: Option<&str>) -> Result<()>
Set or update the tag on an invocation.
V5 schema: Updates the tag on the attempts table.
Sourcepub fn recover_orphaned_invocations(
&self,
max_age_hours: u32,
dry_run: bool,
) -> Result<RecoveryStats>
pub fn recover_orphaned_invocations( &self, max_age_hours: u32, dry_run: bool, ) -> Result<RecoveryStats>
Recover orphaned invocations (v5 schema).
V5: Scans attempts without outcomes and checks if the runner is still alive. If not alive, writes an orphaned outcome record.
Note: This now looks at machine_id field which stores the runner_id for local invocations.
Source§impl Store
impl Store
Sourcepub fn write_outcome(&self, record: &OutcomeRecord) -> Result<()>
pub fn write_outcome(&self, record: &OutcomeRecord) -> Result<()>
Write an outcome record to the store (v5 schema).
Call this when an invocation completes (success, failure, or crash).
The attempt should have been written at invocation start using write_attempt().
Behavior depends on storage mode:
- Parquet: Creates a new Parquet file in the appropriate date partition
- DuckDB: Inserts directly into the local.outcomes table
Sourcepub fn complete_invocation(
&self,
attempt_id: Uuid,
exit_code: i32,
duration_ms: Option<i64>,
date: NaiveDate,
) -> Result<()>
pub fn complete_invocation( &self, attempt_id: Uuid, exit_code: i32, duration_ms: Option<i64>, date: NaiveDate, ) -> Result<()>
Complete an invocation by writing an outcome (v5 schema).
This is the v5 equivalent of complete_pending_invocation().
Sourcepub fn orphan_invocation(&self, attempt_id: Uuid, date: NaiveDate) -> Result<()>
pub fn orphan_invocation(&self, attempt_id: Uuid, date: NaiveDate) -> Result<()>
Mark an invocation as orphaned (crashed without cleanup).
Sourcepub fn kill_invocation(
&self,
attempt_id: Uuid,
signal: i32,
duration_ms: Option<i64>,
date: NaiveDate,
) -> Result<()>
pub fn kill_invocation( &self, attempt_id: Uuid, signal: i32, duration_ms: Option<i64>, date: NaiveDate, ) -> Result<()>
Mark an invocation as killed by signal.
Sourcepub fn timeout_invocation(
&self,
attempt_id: Uuid,
duration_ms: i64,
date: NaiveDate,
) -> Result<()>
pub fn timeout_invocation( &self, attempt_id: Uuid, duration_ms: i64, date: NaiveDate, ) -> Result<()>
Mark an invocation as timed out.
Sourcepub fn outcome_count(&self) -> Result<i64>
pub fn outcome_count(&self) -> Result<i64>
Get the count of outcomes in the store.
Sourcepub fn recover_orphans(&self) -> Result<RecoveryStats>
pub fn recover_orphans(&self) -> Result<RecoveryStats>
Recover orphaned invocations (v5 schema).
Finds attempts without outcomes where the runner is no longer alive, and marks them as orphaned. Returns statistics about the operation.
This is safe to run periodically (e.g., during compaction) and is idempotent - it won’t create duplicate outcomes.
Source§impl Store
impl Store
Sourcepub fn store_output(
&self,
invocation_id: Uuid,
stream: &str,
content: &[u8],
date: NaiveDate,
cmd_hint: Option<&str>,
) -> Result<()>
pub fn store_output( &self, invocation_id: Uuid, stream: &str, content: &[u8], date: NaiveDate, cmd_hint: Option<&str>, ) -> Result<()>
Store output content, routing to inline or blob based on size.
This is the high-level method for storing invocation output. It:
- Computes the BLAKE3 hash
- Routes small content to inline (data: URL) or large to blob (file: URL)
- Handles deduplication for blobs
- Writes the output record to Parquet
Sourcepub fn write_output(&self, record: &OutputRecord) -> Result<()>
pub fn write_output(&self, record: &OutputRecord) -> Result<()>
Write an output record to the store (low-level).
Behavior depends on storage mode:
- Parquet: Creates a new Parquet file in the appropriate date partition
- DuckDB: Inserts directly into the local.outputs
Sourcepub fn get_outputs(
&self,
invocation_id: &str,
stream_filter: Option<&str>,
) -> Result<Vec<OutputInfo>>
pub fn get_outputs( &self, invocation_id: &str, stream_filter: Option<&str>, ) -> Result<Vec<OutputInfo>>
Get outputs for an invocation by ID, optionally filtered by stream.
Sourcepub fn get_output(&self, invocation_id: &str) -> Result<Option<OutputInfo>>
pub fn get_output(&self, invocation_id: &str) -> Result<Option<OutputInfo>>
Get output for an invocation by ID (first match, for backwards compat).
Sourcepub fn read_output_content(&self, output: &OutputInfo) -> Result<Vec<u8>>
pub fn read_output_content(&self, output: &OutputInfo) -> Result<Vec<u8>>
Read content from storage using DuckDB’s read_blob (handles both data: and file:// URLs).
Source§impl Store
impl Store
Sourcepub fn push(
&self,
remote: &RemoteConfig,
opts: PushOptions,
) -> Result<PushStats>
pub fn push( &self, remote: &RemoteConfig, opts: PushOptions, ) -> Result<PushStats>
Push local data to a remote.
Reads from local schema, writes to remote’s tables.
Only pushes records that don’t already exist on the remote (by id).
When sync_blobs is enabled, also syncs blob files for file remotes.
Sourcepub fn pull(
&self,
remote: &RemoteConfig,
opts: PullOptions,
) -> Result<PullStats>
pub fn pull( &self, remote: &RemoteConfig, opts: PullOptions, ) -> Result<PullStats>
Pull data from a remote into local cached_
Reads from remote’s tables, writes to cached_<name> schema.
Only pulls records that don’t already exist in the cached schema (by id).
After pulling, rebuilds the caches union views.
When sync_blobs is enabled, also syncs blob files for file remotes.
Sourcepub fn rebuild_caches_schema(&self, conn: &Connection) -> Result<()>
pub fn rebuild_caches_schema(&self, conn: &Connection) -> Result<()>
Rebuild the caches schema views to union all cached_* schemas.
Uses explicit transaction for DDL safety. The caches.* views reference local cached_* schemas (not attached databases), so they should be safe to persist. V5 schema: unions attempts/outcomes tables and creates invocations view.
Source§impl Store
impl Store
Sourcepub fn write_session(&self, record: &SessionRecord) -> Result<()>
pub fn write_session(&self, record: &SessionRecord) -> Result<()>
Write a session record to the store.
Behavior depends on storage mode:
- Parquet: Creates a new Parquet file in the appropriate date partition
- DuckDB: Inserts directly into the local.sessions
Sessions are written lazily on first invocation from that session.
Sourcepub fn session_exists(&self, session_id: &str) -> Result<bool>
pub fn session_exists(&self, session_id: &str) -> Result<bool>
Check if a session exists in the store.
Sourcepub fn ensure_session(&self, record: &SessionRecord) -> Result<()>
pub fn ensure_session(&self, record: &SessionRecord) -> Result<()>
Ensure a session is registered, creating it if needed.
This is called lazily when an invocation is recorded. If the session doesn’t exist, it creates a new session record.
Sourcepub fn session_count(&self) -> Result<i64>
pub fn session_count(&self) -> Result<i64>
Count total sessions in the store.
Source§impl Store
impl Store
Sourcepub fn connection(&self) -> Result<Connection>
pub fn connection(&self) -> Result<Connection>
Get a DuckDB connection with full features (attachments, ephemeral views).
Sourcepub fn connection_with_options(
&self,
attach_remotes: bool,
) -> Result<Connection>
pub fn connection_with_options( &self, attach_remotes: bool, ) -> Result<Connection>
Get a DuckDB connection with optional remote attachment (legacy API).
Sourcepub fn connect(&self, opts: ConnectionOptions) -> Result<Connection>
pub fn connect(&self, opts: ConnectionOptions) -> Result<Connection>
Get a DuckDB connection with explicit options.
This is the main connection method. Use ConnectionOptions to control:
- Whether remotes are attached
- Whether project database is attached
- Whether ephemeral views are created
- Whether migration should run
Uses retry with exponential backoff to handle concurrent access.
Sourcepub fn attach_remote(
&self,
conn: &Connection,
remote: &RemoteConfig,
) -> Result<()>
pub fn attach_remote( &self, conn: &Connection, remote: &RemoteConfig, ) -> Result<()>
Manually attach a specific remote.
Sourcepub fn detach_remote(&self, conn: &Connection, name: &str) -> Result<()>
pub fn detach_remote(&self, conn: &Connection, name: &str) -> Result<()>
Detach a remote.
Sourcepub fn test_remote(&self, remote: &RemoteConfig) -> Result<()>
pub fn test_remote(&self, remote: &RemoteConfig) -> Result<()>
Test connection to a remote. Returns Ok if successful.
Sourcepub fn query(&self, sql: &str) -> Result<QueryResult>
pub fn query(&self, sql: &str) -> Result<QueryResult>
Query the store using SQL.
Returns results as a Vec of rows, where each row is a Vec of string values.
Sourcepub fn last_invocation_with_output(
&self,
) -> Result<Option<(InvocationSummary, Option<OutputInfo>)>>
pub fn last_invocation_with_output( &self, ) -> Result<Option<(InvocationSummary, Option<OutputInfo>)>>
Get the last invocation with its output (if any).
Sourcepub fn write_batch(&self, batch: &InvocationBatch) -> Result<()>
pub fn write_batch(&self, batch: &InvocationBatch) -> Result<()>
Write a batch of related records atomically.
This is the preferred way to write an invocation with its outputs, session, and events together. In DuckDB mode, all writes are wrapped in a transaction. In Parquet mode, files are written atomically.
Sourcepub fn load_format_hints(&self) -> Result<FormatHints>
pub fn load_format_hints(&self) -> Result<FormatHints>
Load format hints from the config file.
Sourcepub fn save_format_hints(&self, hints: &FormatHints) -> Result<()>
pub fn save_format_hints(&self, hints: &FormatHints) -> Result<()>
Save format hints to the config file.
Sourcepub fn detect_format_for_command(&self, cmd: &str) -> Result<String>
pub fn detect_format_for_command(&self, cmd: &str) -> Result<String>
Detect format for a command using format hints.
Priority:
- User-defined format hints (by priority)
- Default format from config (or “auto”)
Note: duck_hunt detects formats from content analysis, not command names. Use format hints to map commands to formats, then duck_hunt parses the output.
Sourcepub fn list_builtin_formats(&self) -> Result<Vec<BuiltinFormat>>
pub fn list_builtin_formats(&self) -> Result<Vec<BuiltinFormat>>
Get list of duck_hunt built-in formats.
Note: duck_hunt detects formats from content analysis, not command patterns. This lists available format names that can be used with duck_hunt parsing.
Sourcepub fn check_format(&self, cmd: &str) -> Result<FormatMatch>
pub fn check_format(&self, cmd: &str) -> Result<FormatMatch>
Check which format would be detected for a command. Returns the format name and source (user-defined or default).
Note: duck_hunt detects formats from content, not command names. This only checks user-defined format hints.