Skip to content

Commit 26466c3

Browse files
fix: ensure otel context propagated across ffi boundary (#544)
* test: support shared global rust tracing in test_tracing_subscriber.py * test: add quilc and qvm unit tests in test_tracing_subscriber.py * feat: update pyo3 opentelemetry dependencies --------- Co-authored-by: Graham Smith <[email protected]>
1 parent 8db2254 commit 26466c3

File tree

20 files changed

+1363
-897
lines changed

20 files changed

+1363
-897
lines changed

Cargo.lock

Lines changed: 1039 additions & 769 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ members = ["crates/*"]
33
resolver = "2"
44

55
[workspace.dependencies]
6-
qcs-api-client-common = "0.11.9"
7-
qcs-api-client-grpc = "0.11.9"
8-
qcs-api-client-openapi = "0.12.9"
6+
qcs-api-client-common = "0.12.0"
7+
qcs-api-client-grpc = "0.12.0"
8+
qcs-api-client-openapi = "0.13.0"
99
serde_json = "1.0.86"
1010
thiserror = "1.0.57"
1111
tokio = "1.36.0"
@@ -24,11 +24,13 @@ quil-rs = { git = "https://github.com/rigetti/quil-rs", tag = "quil-py/v0.15.2"
2424
# and need to be updated together.
2525
ndarray = { version = "0.15.6", features = ["serde"] }
2626
numpy = "0.20.0"
27+
opentelemetry = { version = "0.27.1" }
28+
opentelemetry_sdk = { version = "0.27.1" }
2729
pyo3 = { version = "0.20.0", default-features = false}
2830
pyo3-asyncio = { version = "0.20", features = ["tokio-runtime"] }
2931
pyo3-log = { version = "0.8.2" }
30-
pyo3-opentelemetry = { version = "0.3" }
31-
pyo3-tracing-subscriber = { version = "0.1.4", default-features = false }
32+
pyo3-opentelemetry = "0.4.0"
33+
pyo3-tracing-subscriber = { version = "0.2.0", default-features = false }
3234

3335
pyo3-build-config = "0.20.0"
3436
rigetti-pyo3 = { version = "0.4.1", default-features = false, features = ["complex", "time"] }

crates/lib/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ readme = "./README.md"
1313
manual-tests = []
1414
tracing = ["dep:tracing", "qcs-api-client-common/tracing", "qcs-api-client-grpc/tracing", "qcs-api-client-openapi/tracing"]
1515
tracing-config = ["tracing", "qcs-api-client-common/tracing-config", "qcs-api-client-grpc/tracing-config", "qcs-api-client-openapi/tracing-config"]
16-
otel-tracing = ["tracing-config", "qcs-api-client-grpc/otel-tracing", "qcs-api-client-openapi/otel-tracing"]
1716
libquil = ["dep:libquil-sys"]
1817
grpc-web = ["qcs-api-client-grpc/grpc-web"]
1918
tracing-opentelemetry = ["tracing-config", "qcs-api-client-grpc/tracing-opentelemetry", "qcs-api-client-openapi/tracing-opentelemetry"]
@@ -27,9 +26,10 @@ indexmap = "2.2.6"
2726
lazy_static = "1.4.0"
2827
ndarray.workspace = true
2928
num = { version = "0.4.0", features = ["serde"] }
30-
opentelemetry = { version = "0.23.0" }
31-
opentelemetry_sdk = { version = "0.23.0" }
29+
opentelemetry = { workspace = true }
30+
opentelemetry_sdk = { workspace = true }
3231
pbjson-types = "0.7.0"
32+
pyo3 = { workspace = true }
3333
qcs-api-client-common.workspace = true
3434
qcs-api-client-openapi.workspace = true
3535
qcs-api-client-grpc.workspace = true

crates/lib/Makefile.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[env]
22
RUST_BACKTRACE = 0
3-
CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = "--features otel-tracing,libquil" # Disable --all-features to avoid manual tests in CI for now
3+
CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = "--features tracing-opentelemetry,libquil" # Disable --all-features to avoid manual tests in CI for now
44

55
[tasks.pre-test]
66
command = "docker"

crates/lib/examples/delayed_job_retrieval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ RX(pi) 0
88
MEASURE 0 ro[0]
99
"#;
1010

11-
const QUANTUM_PROCESSOR_ID: &str = "Aspen-M-3";
11+
const QUANTUM_PROCESSOR_ID: &str = "Ankaa-3";
1212

1313
async fn quilc_client() -> rpcq::Client {
1414
let qcs = Qcs::load();

crates/lib/src/client.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
use std::time::Duration;
66

77
use qcs_api_client_common::configuration::{ClientConfiguration, TokenError};
8+
#[cfg(feature = "tracing")]
9+
use qcs_api_client_grpc::tonic::wrap_channel_with_tracing;
10+
#[cfg(feature = "tracing")]
11+
use qcs_api_client_grpc::tonic::CustomTraceService;
812
#[cfg(feature = "grpc-web")]
913
use qcs_api_client_grpc::tonic::{wrap_channel_with_grpc_web, GrpcWebWrapperLayerService};
1014
use qcs_api_client_grpc::{
@@ -15,6 +19,7 @@ use qcs_api_client_grpc::{
1519
},
1620
};
1721
use qcs_api_client_openapi::apis::configuration::Configuration as OpenApiConfiguration;
22+
#[cfg(not(any(feature = "grpc-web", feature = "tracing")))]
1823
use tonic::transport::Channel;
1924
use tonic::Status;
2025

@@ -29,17 +34,27 @@ const DEFAULT_MAX_MESSAGE_DECODING_SIZE: usize = 50 * 1024 * 1024;
2934
/// It is public so that users can create gRPC clients with different APIs using a "raw" connection
3035
/// initialized by this library. This ensures that the exact Tonic version used for such clients
3136
/// matches what this library uses.
32-
#[cfg(not(feature = "grpc-web"))]
37+
#[cfg(not(any(feature = "grpc-web", feature = "tracing")))]
3338
pub type GrpcConnection = RetryService<RefreshService<Channel, ClientConfiguration>>;
3439

3540
/// A type alias for the underlying gRPC connection used by all gRPC clients within this library.
3641
/// It is public so that users can create gRPC clients with different APIs using a "raw" connection
3742
/// initialized by this library. This ensures that the exact Tonic version used for such clients
3843
/// matches what this library uses.
39-
#[cfg(feature = "grpc-web")]
44+
#[cfg(all(feature = "grpc-web", not(feature = "tracing")))]
4045
pub type GrpcConnection =
4146
GrpcWebWrapperLayerService<RetryService<RefreshService<Channel, ClientConfiguration>>>;
4247

48+
/// A type alias for the underlying gRPC connection used by all gRPC clients within this library.
49+
/// It is public so that users can create gRPC clients with different APIs using a "raw" connection
50+
/// initialized by this library. This ensures that the exact Tonic version used for such clients
51+
/// matches what this library uses.
52+
///
53+
/// The underlying [`tonic::transport::Channel`] is wrapped by [`CustomTraceService`],
54+
/// [`RefreshService`], and [`RetryService`] to provide tracing, token refresh, and retry.
55+
#[cfg(all(not(feature = "grpc-web"), feature = "tracing"))]
56+
pub type GrpcConnection = RetryService<RefreshService<CustomTraceService, ClientConfiguration>>;
57+
4358
/// TODO: make configurable at the client level.
4459
/// <https://github.com/rigetti/qcs-sdk-rust/issues/239>
4560
pub(crate) static DEFAULT_HTTP_API_TIMEOUT: Duration = Duration::from_secs(10);
@@ -103,11 +118,25 @@ impl Qcs {
103118
) -> Result<TranslationClient<GrpcConnection>, GrpcError<TokenError>> {
104119
let uri = parse_uri(translation_grpc_endpoint)?;
105120
let channel = get_channel(uri)?;
106-
let service =
107-
wrap_channel_with_retry(wrap_channel_with(channel, self.get_config().clone()));
121+
122+
// First add tracing if enabled
123+
#[cfg(feature = "tracing")]
124+
let channel = wrap_channel_with_tracing(
125+
channel,
126+
translation_grpc_endpoint.to_string(),
127+
self.get_config()
128+
.tracing_configuration()
129+
.cloned()
130+
.unwrap_or_default(),
131+
);
132+
133+
// Then wrap with refresh and retry
134+
let channel = wrap_channel_with(channel, self.get_config().clone());
135+
let channel = wrap_channel_with_retry(channel);
136+
108137
#[cfg(feature = "grpc-web")]
109-
let service = wrap_channel_with_grpc_web(service);
110-
Ok(TranslationClient::new(service)
138+
let channel = wrap_channel_with_grpc_web(service);
139+
Ok(TranslationClient::new(channel)
111140
.max_encoding_message_size(DEFAULT_MAX_MESSAGE_ENCODING_SIZE)
112141
.max_decoding_message_size(DEFAULT_MAX_MESSAGE_DECODING_SIZE))
113142
}

crates/lib/src/qpu/api.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use derive_builder::Builder;
1111
use qcs_api_client_common::configuration::TokenError;
1212
#[cfg(feature = "grpc-web")]
1313
use qcs_api_client_grpc::tonic::wrap_channel_with_grpc_web;
14+
#[cfg(feature = "tracing")]
15+
use qcs_api_client_grpc::tonic::wrap_channel_with_tracing;
1416
pub use qcs_api_client_grpc::tonic::Error as GrpcError;
1517
use qcs_api_client_grpc::{
1618
get_channel_with_timeout,
@@ -581,10 +583,27 @@ pub trait ExecutionTarget<'a> {
581583
let uri = parse_uri(address).map_err(QpuApiError::GrpcError)?;
582584
let channel = get_channel_with_timeout(uri, self.timeout())
583585
.map_err(|err| QpuApiError::GrpcError(err.into()))?;
584-
let channel =
585-
wrap_channel_with_retry(wrap_channel_with(channel, client.get_config().clone()));
586+
587+
// First add tracing if enabled
588+
#[cfg(feature = "tracing")]
589+
let channel = wrap_channel_with_tracing(
590+
channel,
591+
address.to_string(),
592+
client
593+
.get_config()
594+
.tracing_configuration()
595+
.cloned()
596+
.unwrap_or_default(),
597+
);
598+
599+
// Then wrap with refresh and retry
600+
let channel = wrap_channel_with(channel, client.get_config().clone());
601+
let channel = wrap_channel_with_retry(channel);
602+
603+
// Add grpc-web if enabled
586604
#[cfg(feature = "grpc-web")]
587605
let channel = wrap_channel_with_grpc_web(channel);
606+
588607
Ok(channel)
589608
}
590609

crates/lib/src/qpu/translation.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
use std::{collections::HashMap, time::Duration};
55

6+
use pyo3::{exceptions::PyValueError, PyErr};
67
use qcs_api_client_grpc::{
78
models::controller::EncryptedControllerJob,
89
services::translation::{
@@ -29,6 +30,16 @@ pub enum Error {
2930
ClientTimeout(#[from] Elapsed),
3031
}
3132

33+
impl From<Error> for PyErr {
34+
fn from(value: Error) -> Self {
35+
let message = value.to_string();
36+
match value {
37+
Error::Grpc(_) => PyValueError::new_err(message),
38+
Error::ClientTimeout(_) => PyValueError::new_err(message),
39+
}
40+
}
41+
}
42+
3243
/// An encrypted and translated program, along with `readout_map`
3344
/// to map job `readout_data` back to program-declared variables.
3445
#[derive(Debug)]
@@ -44,7 +55,10 @@ pub struct EncryptedTranslationResult {
4455
}
4556

4657
/// Translate a program, returning an encrypted and translated program.
47-
#[cfg_attr(feature = "tracing", instrument(skip_all))]
58+
#[cfg_attr(
59+
feature = "tracing",
60+
instrument(skip(quil_program, client, translation_options))
61+
)]
4862
pub async fn translate<TO>(
4963
quantum_processor_id: &str,
5064
quil_program: &str,

crates/python/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ crate-type = ["cdylib", "rlib"]
2020

2121
[dependencies]
2222
async-trait = "0.1.73"
23+
futures-util = "0.3.24"
2324
qcs = { path = "../lib", features = ["tracing-opentelemetry", "experimental"] }
2425
qcs-api-client-common = { workspace = true, features = ["python"] }
2526
qcs-api-client-grpc.workspace = true
@@ -37,8 +38,8 @@ rigetti-pyo3.workspace = true
3738
paste = "1.0.11"
3839
pyo3-log.workspace = true
3940
once_cell = "1.18.0"
40-
opentelemetry = { version = "0.23.0" }
41-
opentelemetry_sdk = { version = "0.23.0" }
41+
opentelemetry = { workspace = true }
42+
opentelemetry_sdk = { workspace = true }
4243
tracing = { version = "0.1.37" }
4344
prost = "0.13.3"
4445

crates/python/qcs_sdk/qvm/api.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ class MultishotRequest:
8787

8888
def __new__(
8989
cls,
90-
compiled_quil: str,
91-
trials: int,
90+
program: str,
91+
shots: int,
9292
addresses: Mapping[str, AddressRequest],
9393
measurement_noise: Optional[Tuple[float, float, float]],
9494
gate_noise: Optional[Tuple[float, float, float]],
@@ -142,7 +142,7 @@ def run(
142142
"""
143143
...
144144

145-
def run_async(
145+
async def run_async(
146146
request: MultishotRequest,
147147
client: QVMClient,
148148
options: Optional[QVMOptions] = None,

0 commit comments

Comments
 (0)