Skip to content

Commit c1c8b0e

Browse files
docs(sdk): clarify BSP max_concurrent_exports scope for thread-based processor (#3403)
Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com>
1 parent ac6cc5f commit c1c8b0e

File tree

2 files changed

+77
-15
lines changed

2 files changed

+77
-15
lines changed

opentelemetry-sdk/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
//! | `OTEL_BSP_SCHEDULE_DELAY` | Delay interval (in milliseconds) between two consecutive exports. | `5000` |
110110
//! | `OTEL_BSP_MAX_QUEUE_SIZE` | Maximum queue size. | `2048` |
111111
//! | `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | Maximum batch size. Must be less than or equal to `OTEL_BSP_MAX_QUEUE_SIZE`. | `512` |
112-
//! | `OTEL_BSP_MAX_CONCURRENT_EXPORTS` | Maximum number of concurrent exports. | `1` |
112+
//! | `OTEL_BSP_MAX_CONCURRENT_EXPORTS` | Maximum number of concurrent exports. Honored by `span_processor_with_async_runtime::BatchSpanProcessor`; thread-based `BatchSpanProcessor` exports serially. For concurrent exports, enable `experimental_trace_batch_span_processor_with_async_runtime` and use the async-runtime processor. | `1` |
113113
//!
114114
//! ### Logs: Batch Log Record Processor (BLRP)
115115
//!

opentelemetry-sdk/src/trace/span_processor.rs

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ pub(crate) const OTEL_BSP_MAX_EXPORT_BATCH_SIZE_DEFAULT: usize = 512;
6565
pub(crate) const OTEL_BSP_EXPORT_TIMEOUT: &str = "OTEL_BSP_EXPORT_TIMEOUT";
6666
/// Default maximum allowed time to export data.
6767
pub(crate) const OTEL_BSP_EXPORT_TIMEOUT_DEFAULT: Duration = Duration::from_millis(30_000);
68-
/// Environment variable to configure max concurrent exports for batch span
69-
/// processor.
7068
pub(crate) const OTEL_BSP_MAX_CONCURRENT_EXPORTS: &str = "OTEL_BSP_MAX_CONCURRENT_EXPORTS";
7169
/// Default max concurrent exports for BSP
7270
pub(crate) const OTEL_BSP_MAX_CONCURRENT_EXPORTS_DEFAULT: usize = 1;
@@ -786,11 +784,6 @@ pub struct BatchConfig {
786784
pub(crate) max_export_timeout: Duration,
787785

788786
#[allow(dead_code)]
789-
/// Maximum number of concurrent exports
790-
///
791-
/// Limits the number of spawned tasks for exports and thus memory consumed
792-
/// by an exporter. A value of 1 will cause exports to be performed
793-
/// synchronously on the BatchSpanProcessor task.
794787
pub(crate) max_concurrent_exports: usize,
795788
}
796789

@@ -863,15 +856,20 @@ impl BatchConfigBuilder {
863856

864857
#[cfg(feature = "experimental_trace_batch_span_processor_with_async_runtime")]
865858
/// Set max_concurrent_exports for [`BatchConfigBuilder`].
866-
/// It's the maximum number of concurrent exports.
867-
/// Limits the number of spawned tasks for exports and thus memory consumed by an exporter.
868-
/// The default value is 1.
869-
/// If the max_concurrent_exports value is default value, it will cause exports to be performed
870-
/// synchronously on the BatchSpanProcessor task.
871-
/// The default value is 1.
859+
///
860+
/// This value is honored by
861+
/// `span_processor_with_async_runtime::BatchSpanProcessor`, where it limits
862+
/// the number of concurrent export tasks.
863+
///
864+
/// The thread-based `BatchSpanProcessor` exports serially and ignores this
865+
/// setting.
872866
///
873867
/// Corresponding environment variable: `OTEL_BSP_MAX_CONCURRENT_EXPORTS`.
874868
///
869+
/// For concurrent exports, enable
870+
/// `experimental_trace_batch_span_processor_with_async_runtime` and use the
871+
/// async-runtime processor.
872+
///
875873
/// Note: Programmatically setting this will override any value set via the environment variable.
876874
pub fn with_max_concurrent_exports(mut self, max_concurrent_exports: usize) -> Self {
877875
self.max_concurrent_exports = max_concurrent_exports;
@@ -1181,7 +1179,10 @@ mod tests {
11811179

11821180
use crate::Resource;
11831181
use opentelemetry::{Key, KeyValue, Value};
1184-
use std::sync::{atomic::Ordering, Arc, Mutex};
1182+
use std::sync::{
1183+
atomic::{AtomicUsize, Ordering},
1184+
Arc, Mutex,
1185+
};
11851186

11861187
// Mock exporter to test functionality
11871188
#[derive(Debug)]
@@ -1356,6 +1357,67 @@ mod tests {
13561357
);
13571358
}
13581359

1360+
#[test]
1361+
fn batchspanprocessor_sync_ignores_max_concurrent_exports() {
1362+
#[derive(Debug)]
1363+
struct TrackingExporter {
1364+
active: Arc<AtomicUsize>,
1365+
max_inflight: Arc<AtomicUsize>,
1366+
export_calls: Arc<AtomicUsize>,
1367+
delay: Duration,
1368+
}
1369+
1370+
impl SpanExporter for TrackingExporter {
1371+
async fn export(&self, _batch: Vec<SpanData>) -> OTelSdkResult {
1372+
self.export_calls.fetch_add(1, Ordering::SeqCst);
1373+
let inflight = self.active.fetch_add(1, Ordering::SeqCst) + 1;
1374+
self.max_inflight.fetch_max(inflight, Ordering::SeqCst);
1375+
1376+
std::thread::sleep(self.delay);
1377+
self.active.fetch_sub(1, Ordering::SeqCst);
1378+
Ok(())
1379+
}
1380+
}
1381+
1382+
let active = Arc::new(AtomicUsize::new(0));
1383+
let max_inflight = Arc::new(AtomicUsize::new(0));
1384+
let export_calls = Arc::new(AtomicUsize::new(0));
1385+
let exporter = TrackingExporter {
1386+
active: active.clone(),
1387+
max_inflight: max_inflight.clone(),
1388+
export_calls: export_calls.clone(),
1389+
delay: Duration::from_millis(50),
1390+
};
1391+
1392+
let config = BatchConfig {
1393+
max_export_batch_size: 1,
1394+
max_queue_size: 16,
1395+
scheduled_delay: Duration::from_secs(3600),
1396+
max_export_timeout: Duration::from_secs(5),
1397+
max_concurrent_exports: 4,
1398+
};
1399+
1400+
let processor = BatchSpanProcessor::new(exporter, config);
1401+
1402+
processor.on_end(new_test_export_span_data());
1403+
processor.on_end(new_test_export_span_data());
1404+
processor.on_end(new_test_export_span_data());
1405+
1406+
processor.force_flush().expect("force flush failed");
1407+
processor.shutdown().expect("shutdown failed");
1408+
1409+
assert_eq!(
1410+
export_calls.load(Ordering::SeqCst),
1411+
3,
1412+
"expected three exports for three spans with max_export_batch_size=1"
1413+
);
1414+
assert_eq!(
1415+
max_inflight.load(Ordering::SeqCst),
1416+
1,
1417+
"sync BatchSpanProcessor should export serially regardless of max_concurrent_exports"
1418+
);
1419+
}
1420+
13591421
#[test]
13601422
fn validate_span_attributes_exported_correctly() {
13611423
let exporter = MockSpanExporter::new();

0 commit comments

Comments
 (0)