Skip to content

Commit a63a91a

Browse files
atrakhConvex, Inc.
authored and
Convex, Inc.
committed
Add UsageEvent::InsightReadLimit (#36166)
GitOrigin-RevId: e8298af1611c880497659c2dc534532246b9d089
1 parent ee60e7e commit a63a91a

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

crates/events/src/testing.rs

+8
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ impl UsageCounterState {
181181
tables: _,
182182
system_tables: _,
183183
} => todo!(),
184+
UsageEvent::InsightReadLimit {
185+
id: _,
186+
request_id: _,
187+
udf_id: _,
188+
component_path: _,
189+
calls: _,
190+
success: _,
191+
} => todo!(),
184192
}
185193
}
186194
}

crates/events/src/usage.rs

+16
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ pub struct FunctionCallUsageFields {
6060
pub occ_retry_count: Option<u64>,
6161
}
6262

63+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64+
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
65+
pub struct InsightReadLimitCall {
66+
pub table_name: String,
67+
pub bytes_read: u64,
68+
pub documents_read: u64,
69+
}
70+
6371
// TODO(CX-5845): Use proper serializable types for constants rather than
6472
// Strings.
6573
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -116,6 +124,14 @@ pub enum UsageEvent {
116124
egress: u64,
117125
egress_rows: u64,
118126
},
127+
InsightReadLimit {
128+
id: String,
129+
request_id: String,
130+
udf_id: String,
131+
component_path: Option<String>,
132+
calls: Vec<InsightReadLimitCall>,
133+
success: bool,
134+
},
119135
VectorBandwidth {
120136
id: String,
121137
component_path: Option<String>,

crates/usage_tracking/src/lib.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ use anyhow::Context;
1212
use common::{
1313
components::ComponentPath,
1414
execution_context::ExecutionId,
15+
knobs::{
16+
FUNCTION_LIMIT_WARNING_RATIO,
17+
TRANSACTION_MAX_READ_SIZE_BYTES,
18+
TRANSACTION_MAX_READ_SIZE_ROWS,
19+
},
1520
types::{
1621
ModuleEnvironment,
1722
StorageUuid,
@@ -21,6 +26,7 @@ use common::{
2126
};
2227
use events::usage::{
2328
FunctionCallUsageFields,
29+
InsightReadLimitCall,
2430
UsageEvent,
2531
UsageEventLogger,
2632
};
@@ -253,6 +259,7 @@ impl UsageCounter {
253259
stats,
254260
execution_id,
255261
request_id,
262+
success,
256263
&mut usage_metrics,
257264
);
258265
self.usage_logger.record(usage_metrics);
@@ -276,6 +283,7 @@ impl UsageCounter {
276283
stats,
277284
execution_id,
278285
request_id,
286+
true,
279287
&mut usage_metrics,
280288
);
281289
self.usage_logger.record(usage_metrics);
@@ -287,6 +295,7 @@ impl UsageCounter {
287295
stats: FunctionUsageStats,
288296
execution_id: ExecutionId,
289297
request_id: RequestId,
298+
success: bool,
290299
usage_metrics: &mut Vec<UsageEvent>,
291300
) {
292301
// Merge the storage stats.
@@ -332,7 +341,7 @@ impl UsageCounter {
332341
egress_rows: 0,
333342
});
334343
}
335-
for ((component_path, table_name), egress_size) in stats.database_egress_size {
344+
for ((component_path, table_name), egress_size) in stats.database_egress_size.clone() {
336345
let rows = stats
337346
.database_egress_rows
338347
.get(&(component_path.clone(), table_name.clone()))
@@ -348,6 +357,53 @@ impl UsageCounter {
348357
egress_rows: *rows,
349358
});
350359
}
360+
361+
// Check read limits and add InsightReadLimit event if thresholds are exceeded
362+
let total_rows: u64 = stats.database_egress_rows.values().sum();
363+
let total_bytes: u64 = stats.database_egress_size.values().sum();
364+
365+
let row_threshold =
366+
(*TRANSACTION_MAX_READ_SIZE_ROWS as f64 * *FUNCTION_LIMIT_WARNING_RATIO) as u64;
367+
let byte_threshold =
368+
(*TRANSACTION_MAX_READ_SIZE_BYTES as f64 * *FUNCTION_LIMIT_WARNING_RATIO) as u64;
369+
370+
let did_exceed_document_threshold = total_rows >= row_threshold;
371+
let did_exceed_byte_threshold = total_bytes >= byte_threshold;
372+
373+
if did_exceed_document_threshold || did_exceed_byte_threshold {
374+
let mut calls = Vec::new();
375+
let component_path: ComponentPath = stats
376+
.database_egress_rows
377+
.clone()
378+
.into_iter()
379+
.next()
380+
.map(|((cp, _), _)| cp)
381+
.expect("Expected at least one database egress row since thresholds were exceeded");
382+
383+
for ((cp, table_name), egress_rows) in stats.database_egress_rows.into_iter() {
384+
let egress = stats
385+
.database_egress_size
386+
.get(&(cp, table_name.clone()))
387+
.copied()
388+
.unwrap_or(0);
389+
390+
calls.push(InsightReadLimitCall {
391+
table_name,
392+
bytes_read: egress,
393+
documents_read: egress_rows,
394+
});
395+
}
396+
397+
usage_metrics.push(UsageEvent::InsightReadLimit {
398+
id: execution_id.to_string(),
399+
request_id: request_id.to_string(),
400+
udf_id: udf_id.clone(),
401+
component_path: component_path.serialize(),
402+
calls,
403+
success,
404+
});
405+
}
406+
351407
for ((component_path, table_name), ingress_size) in stats.vector_ingress_size {
352408
usage_metrics.push(UsageEvent::VectorBandwidth {
353409
id: execution_id.to_string(),

0 commit comments

Comments
 (0)