chalk-remote-call-python 1.9.0__tar.gz → 1.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chalk_remote_call_python-1.9.0/chalk_remote_call_python.egg-info → chalk_remote_call_python-1.9.1}/PKG-INFO +1 -1
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/Cargo.lock +1 -1
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +1 -1
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +219 -0
- chalk_remote_call_python-1.9.1/chalk_remote_call/_version.py +1 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/tracing.py +34 -7
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1/chalk_remote_call_python.egg-info}/PKG-INFO +1 -1
- chalk_remote_call_python-1.9.0/chalk_remote_call/_version.py +0 -1
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/README.md +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/cli.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/server.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/servicer.py +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/setup.cfg +0 -0
- {chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/setup.py +0 -0
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/Cargo.lock
RENAMED
|
@@ -511,7 +511,7 @@ dependencies = [
|
|
|
511
511
|
[[package]]
|
|
512
512
|
name = "chalk-queue"
|
|
513
513
|
version = "0.0.0"
|
|
514
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
514
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=9c61f6a938d8f885f138217ac64149e29d7a691e#9c61f6a938d8f885f138217ac64149e29d7a691e"
|
|
515
515
|
dependencies = [
|
|
516
516
|
"base64 0.22.1",
|
|
517
517
|
"redis",
|
|
@@ -36,6 +36,6 @@ futures-util = "0.3"
|
|
|
36
36
|
# `[patch.crates-io]` here it builds against vanilla crates.io redis (it only
|
|
37
37
|
# uses standard APIs). Same rev across all three so cargo reuses one checkout.
|
|
38
38
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
39
|
-
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "
|
|
39
|
+
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "9c61f6a938d8f885f138217ac64149e29d7a691e" }
|
|
40
40
|
chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "783e9ce6b2c" }
|
|
41
41
|
chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "783e9ce6b2c" }
|
|
@@ -66,6 +66,7 @@ const ENV_POLL_BACKOFF_INITIAL_MS: &str = "CHALK_FNQ_POLL_BACKOFF_INITIAL_MS";
|
|
|
66
66
|
const ENV_POLL_BACKOFF_MAX_SECS: &str = "CHALK_FNQ_POLL_BACKOFF_MAX_SECS";
|
|
67
67
|
const ENV_FLUSH_TIMEOUT_MS: &str = "CHALK_FNQ_FLUSH_TIMEOUT_MS";
|
|
68
68
|
const ENV_QUEUE_PROTOCOL: &str = "CHALK_FNQ_QUEUE_PROTOCOL";
|
|
69
|
+
const ENV_STREAM_READ_COUNT: &str = "CHALK_FNQ_STREAM_READ_COUNT";
|
|
69
70
|
|
|
70
71
|
/// Lease lifetime (default). Intentionally short relative to how long a call may run: the
|
|
71
72
|
/// per-pod renewer keeps a live call's lease alive (see module docs), so the
|
|
@@ -88,6 +89,14 @@ const DEFAULT_POLL_BACKOFF_MAX_SECS: u64 = 2;
|
|
|
88
89
|
/// chunk append races with the wait setup.
|
|
89
90
|
/// Configurable via `CHALK_FNQ_FLUSH_TIMEOUT_MS`.
|
|
90
91
|
const DEFAULT_FLUSH_TIMEOUT_MS: u64 = 100;
|
|
92
|
+
/// A bounded prefetch window for Redis Streams. This is deliberately much
|
|
93
|
+
/// smaller than a logical handler batch: several reads can feed the shared
|
|
94
|
+
/// coalescer, while one read remains cheap to reserve, decode and recover.
|
|
95
|
+
const DEFAULT_STREAM_READ_COUNT: usize = 64;
|
|
96
|
+
/// Recovery does not belong on the normal dequeue path. A dead consumer is
|
|
97
|
+
/// still recovered promptly, while healthy traffic avoids an XAUTOCLAIM RPC
|
|
98
|
+
/// for every read window.
|
|
99
|
+
const STREAM_RECLAIM_INTERVAL: Duration = Duration::from_secs(10);
|
|
91
100
|
|
|
92
101
|
/// `peer` recorded in the handler context for queue-consumed calls; there is no
|
|
93
102
|
/// inbound network peer on this path.
|
|
@@ -109,6 +118,7 @@ pub struct SelfConsumerConfig {
|
|
|
109
118
|
pub poll_backoff_max: Duration,
|
|
110
119
|
pub flush_timeout: Duration,
|
|
111
120
|
pub queue_protocol: QueueProtocol,
|
|
121
|
+
pub stream_read_count: usize,
|
|
112
122
|
}
|
|
113
123
|
|
|
114
124
|
impl SelfConsumerConfig {
|
|
@@ -180,6 +190,12 @@ impl SelfConsumerConfig {
|
|
|
180
190
|
})
|
|
181
191
|
})
|
|
182
192
|
.unwrap_or_default();
|
|
193
|
+
let stream_read_count = std::env::var(ENV_STREAM_READ_COUNT)
|
|
194
|
+
.ok()
|
|
195
|
+
.and_then(|s| s.parse::<usize>().ok())
|
|
196
|
+
.filter(|&n| n > 0)
|
|
197
|
+
.unwrap_or(DEFAULT_STREAM_READ_COUNT)
|
|
198
|
+
.min(max_concurrent as usize);
|
|
183
199
|
|
|
184
200
|
Some(Self {
|
|
185
201
|
redis_url,
|
|
@@ -191,6 +207,7 @@ impl SelfConsumerConfig {
|
|
|
191
207
|
poll_backoff_max,
|
|
192
208
|
flush_timeout,
|
|
193
209
|
queue_protocol,
|
|
210
|
+
stream_read_count,
|
|
194
211
|
})
|
|
195
212
|
}
|
|
196
213
|
}
|
|
@@ -225,6 +242,10 @@ pub async fn run(
|
|
|
225
242
|
Uuid::new_v4()
|
|
226
243
|
);
|
|
227
244
|
|
|
245
|
+
// XGROUP CREATE is a startup operation. Keeping it out of every XREAD and
|
|
246
|
+
// XAUTOCLAIM avoids two avoidable Redis round trips per dispatch window.
|
|
247
|
+
input_queue.initialize_consumer_group().await?;
|
|
248
|
+
|
|
228
249
|
spawn_renewer(
|
|
229
250
|
input_queue.clone(),
|
|
230
251
|
conn.clone(),
|
|
@@ -238,10 +259,28 @@ pub async fn run(
|
|
|
238
259
|
tenant = %config.tenant,
|
|
239
260
|
max_concurrent = config.max_concurrent,
|
|
240
261
|
queue_protocol = config.queue_protocol.as_str(),
|
|
262
|
+
stream_read_count = config.stream_read_count,
|
|
241
263
|
consumer = %consumer_name,
|
|
242
264
|
"fnq self-consumer started"
|
|
243
265
|
);
|
|
244
266
|
|
|
267
|
+
if config.queue_protocol == QueueProtocol::StreamV1 {
|
|
268
|
+
return run_stream_dispatch_loop(
|
|
269
|
+
&config,
|
|
270
|
+
queue,
|
|
271
|
+
input_queue,
|
|
272
|
+
legacy_queue,
|
|
273
|
+
sem,
|
|
274
|
+
conn,
|
|
275
|
+
consumer_name,
|
|
276
|
+
live_calls,
|
|
277
|
+
handler,
|
|
278
|
+
metrics_pipeline,
|
|
279
|
+
coalescing_queue,
|
|
280
|
+
)
|
|
281
|
+
.await;
|
|
282
|
+
}
|
|
283
|
+
|
|
245
284
|
let mut backoff = config.poll_backoff_initial;
|
|
246
285
|
loop {
|
|
247
286
|
// Idle-gate: if there is no work, don't take (then immediately release)
|
|
@@ -345,6 +384,165 @@ pub async fn run(
|
|
|
345
384
|
}
|
|
346
385
|
}
|
|
347
386
|
|
|
387
|
+
#[allow(clippy::too_many_arguments)]
|
|
388
|
+
async fn run_stream_dispatch_loop(
|
|
389
|
+
config: &SelfConsumerConfig,
|
|
390
|
+
queue: FunctionQueue,
|
|
391
|
+
input_queue: BoundFunctionQueue,
|
|
392
|
+
legacy_queue: Option<BoundFunctionQueue>,
|
|
393
|
+
sem: chalk_queue::DistributedSemaphore,
|
|
394
|
+
conn: ConnectionManager,
|
|
395
|
+
consumer_name: String,
|
|
396
|
+
live_calls: LiveCalls,
|
|
397
|
+
handler: Arc<PythonHandler>,
|
|
398
|
+
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
399
|
+
coalescing_queue: Option<Arc<CoalescingQueue>>,
|
|
400
|
+
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
401
|
+
let mut backoff = config.poll_backoff_initial;
|
|
402
|
+
// Reclaim once immediately to recover work from a prior crashed pod, then
|
|
403
|
+
// periodically. Healthy messages are kept alive by `spawn_renewer`.
|
|
404
|
+
let mut last_reclaim = Instant::now() - STREAM_RECLAIM_INTERVAL;
|
|
405
|
+
|
|
406
|
+
loop {
|
|
407
|
+
// Capacity must be reserved before XREADGROUP. Reading first would put
|
|
408
|
+
// a potentially huge number of messages into this consumer's PEL while
|
|
409
|
+
// they wait for semaphore slots.
|
|
410
|
+
let mut leases = match sem.try_acquire_many(config.stream_read_count).await {
|
|
411
|
+
Ok(leases) if !leases.is_empty() => leases,
|
|
412
|
+
Ok(_) => {
|
|
413
|
+
tokio::time::sleep(backoff).await;
|
|
414
|
+
backoff = (backoff * 2).min(config.poll_backoff_max);
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
Err(error) => {
|
|
418
|
+
warn!(%error, "batch semaphore acquire failed; backing off");
|
|
419
|
+
tokio::time::sleep(backoff).await;
|
|
420
|
+
backoff = (backoff * 2).min(config.poll_backoff_max);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
let mut deliveries = Vec::with_capacity(leases.len());
|
|
426
|
+
if last_reclaim.elapsed() >= STREAM_RECLAIM_INTERVAL {
|
|
427
|
+
last_reclaim = Instant::now();
|
|
428
|
+
match input_queue
|
|
429
|
+
.reclaim_stale(&consumer_name, config.sem_ttl, leases.len())
|
|
430
|
+
.await
|
|
431
|
+
{
|
|
432
|
+
Ok(reclaimed) => deliveries.extend(reclaimed),
|
|
433
|
+
Err(error) => {
|
|
434
|
+
warn!(%error, "Stream recovery scan failed; continuing with new work")
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// During the one-way List → Stream migration, drain old List entries
|
|
440
|
+
// before reading new Stream traffic. This is one bounded operation per
|
|
441
|
+
// read window, not an LLEN on every Stream message.
|
|
442
|
+
if deliveries.len() < leases.len() {
|
|
443
|
+
if let Some(legacy_queue) = &legacy_queue {
|
|
444
|
+
match legacy_queue
|
|
445
|
+
.dequeue_many(&consumer_name, leases.len() - deliveries.len())
|
|
446
|
+
.await
|
|
447
|
+
{
|
|
448
|
+
Ok(legacy) if !legacy.is_empty() => {
|
|
449
|
+
info!(function = %input_queue.function_name(), count = legacy.len(), "draining legacy List items during Stream migration");
|
|
450
|
+
deliveries.extend(legacy);
|
|
451
|
+
}
|
|
452
|
+
Ok(_) => {}
|
|
453
|
+
Err(error) => {
|
|
454
|
+
warn!(%error, "legacy List dequeue failed; continuing with Stream work")
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if deliveries.len() < leases.len() {
|
|
460
|
+
match input_queue
|
|
461
|
+
.dequeue_many(&consumer_name, leases.len() - deliveries.len())
|
|
462
|
+
.await
|
|
463
|
+
{
|
|
464
|
+
Ok(new_deliveries) => deliveries.extend(new_deliveries),
|
|
465
|
+
Err(error) => warn!(%error, "batched Stream dequeue failed"),
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if deliveries.is_empty() {
|
|
470
|
+
for lease in leases {
|
|
471
|
+
let _ = lease.release().await;
|
|
472
|
+
}
|
|
473
|
+
tokio::time::sleep(backoff).await;
|
|
474
|
+
backoff = (backoff * 2).min(config.poll_backoff_max);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
backoff = config.poll_backoff_initial;
|
|
479
|
+
// `dequeue_many` can return fewer records than requested. Only retain
|
|
480
|
+
// one lease per record and immediately return the unneeded capacity.
|
|
481
|
+
let used = deliveries.len().min(leases.len());
|
|
482
|
+
let unused = leases.split_off(used);
|
|
483
|
+
for lease in unused {
|
|
484
|
+
let _ = lease.release().await;
|
|
485
|
+
}
|
|
486
|
+
for (delivery, lease) in deliveries.into_iter().zip(leases) {
|
|
487
|
+
dispatch_delivery(
|
|
488
|
+
queue.clone(),
|
|
489
|
+
input_queue.clone(),
|
|
490
|
+
coalescing_queue.clone(),
|
|
491
|
+
handler.clone(),
|
|
492
|
+
conn.clone(),
|
|
493
|
+
delivery,
|
|
494
|
+
lease,
|
|
495
|
+
live_calls.clone(),
|
|
496
|
+
config.flush_timeout,
|
|
497
|
+
metrics_pipeline.clone(),
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
#[allow(clippy::too_many_arguments)]
|
|
504
|
+
fn dispatch_delivery(
|
|
505
|
+
queue: FunctionQueue,
|
|
506
|
+
input_queue: BoundFunctionQueue,
|
|
507
|
+
coalescing_queue: Option<Arc<CoalescingQueue>>,
|
|
508
|
+
handler: Arc<PythonHandler>,
|
|
509
|
+
conn: ConnectionManager,
|
|
510
|
+
delivery: DequeuedCall,
|
|
511
|
+
lease: SemaphoreLease,
|
|
512
|
+
live_calls: LiveCalls,
|
|
513
|
+
flush_timeout: Duration,
|
|
514
|
+
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
515
|
+
) {
|
|
516
|
+
live_calls.insert(lease.token().to_string(), delivery.receipt.clone());
|
|
517
|
+
match coalescing_queue {
|
|
518
|
+
Some(cq) => {
|
|
519
|
+
tokio::spawn(run_one_coalesced_call(
|
|
520
|
+
queue,
|
|
521
|
+
input_queue,
|
|
522
|
+
cq,
|
|
523
|
+
conn,
|
|
524
|
+
delivery,
|
|
525
|
+
lease,
|
|
526
|
+
live_calls,
|
|
527
|
+
metrics_pipeline,
|
|
528
|
+
));
|
|
529
|
+
}
|
|
530
|
+
None => {
|
|
531
|
+
tokio::spawn(run_one_call(
|
|
532
|
+
handler,
|
|
533
|
+
queue,
|
|
534
|
+
input_queue,
|
|
535
|
+
conn,
|
|
536
|
+
delivery,
|
|
537
|
+
lease,
|
|
538
|
+
live_calls,
|
|
539
|
+
flush_timeout,
|
|
540
|
+
metrics_pipeline,
|
|
541
|
+
));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
348
546
|
async fn pending_depth(
|
|
349
547
|
queue: &BoundFunctionQueue,
|
|
350
548
|
legacy_queue: Option<&BoundFunctionQueue>,
|
|
@@ -882,6 +1080,7 @@ mod tests {
|
|
|
882
1080
|
std::env::remove_var(ENV_POLL_BACKOFF_MAX_SECS);
|
|
883
1081
|
std::env::remove_var(ENV_FLUSH_TIMEOUT_MS);
|
|
884
1082
|
std::env::remove_var(ENV_QUEUE_PROTOCOL);
|
|
1083
|
+
std::env::remove_var(ENV_STREAM_READ_COUNT);
|
|
885
1084
|
|
|
886
1085
|
let config = SelfConsumerConfig::from_env().expect("config should parse");
|
|
887
1086
|
assert_eq!(config.sem_ttl, Duration::from_secs(DEFAULT_SEM_TTL_SECS));
|
|
@@ -898,6 +1097,7 @@ mod tests {
|
|
|
898
1097
|
Duration::from_millis(DEFAULT_FLUSH_TIMEOUT_MS)
|
|
899
1098
|
);
|
|
900
1099
|
assert_eq!(config.queue_protocol, QueueProtocol::ListV1);
|
|
1100
|
+
assert_eq!(config.stream_read_count, 1);
|
|
901
1101
|
}
|
|
902
1102
|
|
|
903
1103
|
#[test]
|
|
@@ -940,10 +1140,29 @@ mod tests {
|
|
|
940
1140
|
std::env::set_var(ENV_TENANT, "test_tenant");
|
|
941
1141
|
std::env::set_var(ENV_FUNCTION_NAME, "test_fn");
|
|
942
1142
|
std::env::set_var(ENV_QUEUE_PROTOCOL, "stream_v1");
|
|
1143
|
+
std::env::set_var(ENV_MAX_CONCURRENT, "128");
|
|
1144
|
+
std::env::remove_var(ENV_STREAM_READ_COUNT);
|
|
943
1145
|
|
|
944
1146
|
let config = SelfConsumerConfig::from_env().expect("config should parse");
|
|
945
1147
|
assert_eq!(config.queue_protocol, QueueProtocol::StreamV1);
|
|
1148
|
+
assert_eq!(config.stream_read_count, DEFAULT_STREAM_READ_COUNT);
|
|
946
1149
|
std::env::remove_var(ENV_QUEUE_PROTOCOL);
|
|
1150
|
+
std::env::remove_var(ENV_STREAM_READ_COUNT);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
#[test]
|
|
1154
|
+
fn test_stream_read_count_is_configurable_and_capped_by_concurrency() {
|
|
1155
|
+
let _env = lock_env();
|
|
1156
|
+
|
|
1157
|
+
std::env::set_var(ENV_REDIS_URL, "redis://localhost");
|
|
1158
|
+
std::env::set_var(ENV_TENANT, "test_tenant");
|
|
1159
|
+
std::env::set_var(ENV_FUNCTION_NAME, "test_fn");
|
|
1160
|
+
std::env::set_var(ENV_MAX_CONCURRENT, "32");
|
|
1161
|
+
std::env::set_var(ENV_STREAM_READ_COUNT, "128");
|
|
1162
|
+
|
|
1163
|
+
let config = SelfConsumerConfig::from_env().expect("config should parse");
|
|
1164
|
+
assert_eq!(config.stream_read_count, 32);
|
|
1165
|
+
std::env::remove_var(ENV_STREAM_READ_COUNT);
|
|
947
1166
|
}
|
|
948
1167
|
|
|
949
1168
|
#[test]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.9.1"
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/tracing.py
RENAMED
|
@@ -11,6 +11,9 @@ _TRACER_NAME = "chalk_remote_call"
|
|
|
11
11
|
_TRACE_CONTEXT_METADATA_KEYS = ("traceparent", "tracestate", "baggage")
|
|
12
12
|
_REMOTE_FUNCTION_TRACE_POLICY_ENV_VAR = "CHALK_REMOTE_FUNCTION_TRACE_POLICY"
|
|
13
13
|
_REMOTE_FUNCTION_TRACE_SAMPLE_RATE_ENV_VAR = "CHALK_REMOTE_FUNCTION_TRACE_SAMPLE_RATE"
|
|
14
|
+
_REMOTE_FUNCTION_TRACE_OVERRIDE_KEY = "x-chalk-remote-function-tracing"
|
|
15
|
+
_TRACE_OVERRIDE_ON = "on"
|
|
16
|
+
_TRACE_OVERRIDE_OFF = "off"
|
|
14
17
|
_TRACE_POLICY_PARENT_BASED_ALWAYS_OFF = "parentbased_always_off"
|
|
15
18
|
_TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO = "parentbased_traceidratio"
|
|
16
19
|
_TRACE_POLICY_ALWAYS_OFF = "always_off"
|
|
@@ -74,7 +77,9 @@ def _trace_policy() -> str | None:
|
|
|
74
77
|
return None
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
def _trace_sample_rate() -> float | None:
|
|
80
|
+
def _trace_sample_rate(force_trace: bool) -> float | None:
|
|
81
|
+
if force_trace:
|
|
82
|
+
return 1.0
|
|
78
83
|
raw_rate = os.environ.get(_REMOTE_FUNCTION_TRACE_SAMPLE_RATE_ENV_VAR)
|
|
79
84
|
if raw_rate is None:
|
|
80
85
|
return None
|
|
@@ -136,11 +141,11 @@ def _configure_runtime_tracing(sample_rate: float) -> None:
|
|
|
136
141
|
_runtime_tracing_configured = True
|
|
137
142
|
|
|
138
143
|
|
|
139
|
-
def _sample_rate_for_inherited_parent(policy: str) -> float | None:
|
|
144
|
+
def _sample_rate_for_inherited_parent(policy: str, force_trace: bool) -> float | None:
|
|
140
145
|
if policy == _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF:
|
|
141
146
|
return 0.0
|
|
142
147
|
if policy == _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO:
|
|
143
|
-
return _trace_sample_rate()
|
|
148
|
+
return _trace_sample_rate(force_trace)
|
|
144
149
|
return None
|
|
145
150
|
|
|
146
151
|
|
|
@@ -148,6 +153,7 @@ def _span_metadata(
|
|
|
148
153
|
first_metadata: Mapping[str, Any] | None,
|
|
149
154
|
traceparent_metadata: Mapping[str, Any] | None,
|
|
150
155
|
policy: str,
|
|
156
|
+
force_trace: bool,
|
|
151
157
|
) -> tuple[Mapping[str, Any] | None, Any | None, Any]:
|
|
152
158
|
if traceparent_metadata is not None:
|
|
153
159
|
carrier = _trace_carrier(traceparent_metadata)
|
|
@@ -164,7 +170,7 @@ def _span_metadata(
|
|
|
164
170
|
if not parent_span_context.is_valid:
|
|
165
171
|
return None, None, None
|
|
166
172
|
|
|
167
|
-
sample_rate = _sample_rate_for_inherited_parent(policy)
|
|
173
|
+
sample_rate = _sample_rate_for_inherited_parent(policy, force_trace)
|
|
168
174
|
if sample_rate is None:
|
|
169
175
|
return None, None, None
|
|
170
176
|
_configure_runtime_tracing(sample_rate)
|
|
@@ -173,7 +179,7 @@ def _span_metadata(
|
|
|
173
179
|
if policy != _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO:
|
|
174
180
|
return None, None, None
|
|
175
181
|
|
|
176
|
-
sample_rate = _trace_sample_rate()
|
|
182
|
+
sample_rate = _trace_sample_rate(force_trace)
|
|
177
183
|
if sample_rate is None:
|
|
178
184
|
return None, None, None
|
|
179
185
|
|
|
@@ -184,6 +190,17 @@ def _span_metadata(
|
|
|
184
190
|
return first_metadata, None, otel_modules
|
|
185
191
|
|
|
186
192
|
|
|
193
|
+
def _force_sampled_traceparent(metadata: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
194
|
+
parts = str(metadata.get("traceparent") or "").split("-")
|
|
195
|
+
if len(parts) != 4 or len(parts[3]) != 2:
|
|
196
|
+
return metadata
|
|
197
|
+
try:
|
|
198
|
+
parts[3] = f"{int(parts[3], 16) | 1:02x}"
|
|
199
|
+
except ValueError:
|
|
200
|
+
return metadata
|
|
201
|
+
return {**metadata, "traceparent": "-".join(parts)}
|
|
202
|
+
|
|
203
|
+
|
|
187
204
|
def _always_off_span_metadata(
|
|
188
205
|
first_metadata: Mapping[str, Any] | None,
|
|
189
206
|
traceparent_metadata: Mapping[str, Any] | None,
|
|
@@ -275,12 +292,21 @@ def remote_function_invocation_span(
|
|
|
275
292
|
context_metadata: Any,
|
|
276
293
|
coalesced_count: int | None = None,
|
|
277
294
|
) -> Iterator[None]:
|
|
278
|
-
|
|
295
|
+
first_metadata, traceparent_metadata = _collect_trace_metadata(context_metadata)
|
|
296
|
+
override = str((first_metadata or {}).get(_REMOTE_FUNCTION_TRACE_OVERRIDE_KEY) or "").strip().lower()
|
|
297
|
+
if override == _TRACE_OVERRIDE_ON:
|
|
298
|
+
policy = _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO
|
|
299
|
+
elif override == _TRACE_OVERRIDE_OFF:
|
|
300
|
+
policy = _TRACE_POLICY_ALWAYS_OFF
|
|
301
|
+
else:
|
|
302
|
+
policy = _trace_policy()
|
|
279
303
|
if policy is None:
|
|
280
304
|
yield
|
|
281
305
|
return
|
|
282
306
|
|
|
283
|
-
|
|
307
|
+
force_trace = override == _TRACE_OVERRIDE_ON
|
|
308
|
+
if force_trace and traceparent_metadata is not None:
|
|
309
|
+
traceparent_metadata = _force_sampled_traceparent(traceparent_metadata)
|
|
284
310
|
if policy == _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF and traceparent_metadata is None:
|
|
285
311
|
yield
|
|
286
312
|
return
|
|
@@ -295,6 +321,7 @@ def remote_function_invocation_span(
|
|
|
295
321
|
first_metadata,
|
|
296
322
|
traceparent_metadata,
|
|
297
323
|
policy,
|
|
324
|
+
force_trace,
|
|
298
325
|
)
|
|
299
326
|
tracer = _get_tracer(otel_modules[2]) if otel_modules is not None else None
|
|
300
327
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.9.0"
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk-remote-call-rs/Cargo.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/__main__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_gen/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/arrow_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/server.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.0 → chalk_remote_call_python-1.9.1}/chalk_remote_call/servicer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|