Inner dispatch logic for ILP batch (separated for clean quota bookkeeping).
(
state: &SharedState,
tenant_id: TenantId,
batch: &str,
)
| 86 | |
| 87 | /// Inner dispatch logic for ILP batch (separated for clean quota bookkeeping). |
| 88 | async fn flush_ilp_batch_inner( |
| 89 | state: &SharedState, |
| 90 | tenant_id: TenantId, |
| 91 | batch: &str, |
| 92 | ) -> crate::Result<u64> { |
| 93 | // Fast path: extract collection from first line. |
| 94 | let collection = batch |
| 95 | .lines() |
| 96 | .find(|l| !l.is_empty() && !l.starts_with('#')) |
| 97 | .and_then(|l| l.split([',', ' ']).next()) |
| 98 | .unwrap_or("default_metrics") |
| 99 | .to_string(); |
| 100 | |
| 101 | // Route all ILP lines for a collection to the same vShard as the |
| 102 | // collection-based scan uses. This ensures timeseries scans find |
| 103 | // the memtable data on the correct Data Plane core. |
| 104 | // Per-series sharding is deferred until the scan path supports |
| 105 | // fan-out across multiple cores. |
| 106 | let collection_vshard = VShardId::from_collection_in_database(DatabaseId::DEFAULT, &collection); |
| 107 | let mut shard_batches: std::collections::HashMap<u32, String> = |
| 108 | std::collections::HashMap::new(); |
| 109 | |
| 110 | for line in batch.lines() { |
| 111 | if line.is_empty() || line.starts_with('#') { |
| 112 | continue; |
| 113 | } |
| 114 | |
| 115 | let entry = shard_batches.entry(collection_vshard.as_u32()).or_default(); |
| 116 | entry.push_str(line); |
| 117 | entry.push('\n'); |
| 118 | } |
| 119 | |
| 120 | let mut total_accepted = 0u64; |
| 121 | |
| 122 | for (shard_id, shard_batch) in &shard_batches { |
| 123 | let vshard_id = VShardId::new(*shard_id); |
| 124 | let payload_bytes = shard_batch.as_bytes().to_vec(); |
| 125 | |
| 126 | // Append to WAL first — returns the assigned LSN for dedup tracking. |
| 127 | let wal_lsn = crate::control::server::wal_dispatch::wal_append_timeseries( |
| 128 | &state.wal, |
| 129 | tenant_id, |
| 130 | vshard_id, |
| 131 | crate::types::DatabaseId::DEFAULT, |
| 132 | &collection, |
| 133 | &payload_bytes, |
| 134 | Some(&state.credentials), |
| 135 | )? |
| 136 | .map(|lsn| lsn.as_u64()); |
| 137 | |
| 138 | let plan = PhysicalPlan::Timeseries(TimeseriesOp::Ingest { |
| 139 | collection: collection.clone(), |
| 140 | payload: payload_bytes, |
| 141 | format: "ilp".to_string(), |
| 142 | wal_lsn, |
| 143 | surrogates: Vec::new(), |
| 144 | }); |
| 145 |
no test coverage detected