You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make the unset, store_put, and store_or_unset methods of aws_smithy_types::config_bag::Layer (Layer) return the previously stored value(s).
These methods currently return a mutable reference to the Layer and never return the previously stored value.
Background
I am currently writing a crate that facilitates X-Ray tracing in AWS Lambda. I came up with an idea to utilize interceptor to start and end a subsegment for an AWS service operation. Here is what in my mind:
implInterceptforXrayIntercept{// starts a subsegment at every attemptfnread_before_attempt(&self,_context:&BeforeTransmitInterceptorContextRef<'_>,_runtime_components:&RuntimeComponents,cfg:&mutConfigBag,) -> Result<(),BoxError>{let session = self.enter_subsegment(self.service,self.operation);
cfg.interceptor_state().store_put(session);Ok(())}// injects the X-Amzn-Trace-Id headerfnmodify_before_transmit(&self,context:&mutBeforeTransmitInterceptorContextMut<'_>,_runtime_components:&RuntimeComponents,cfg:&mutConfigBag,) -> Result<(),BoxError>{let trace_id = cfg.interceptor_state().load::<SubsegmentSession>().and_then(|s| s.x_amzn_trace_id());ifletSome(trace_id) = trace_id {
context
.request_mut().headers_mut().insert("X-Amzn-Trace-Id", trace_id);}Ok(())}// ends the subsegment updated with the request ID and responsefnread_after_attempt(&self,context:&FinalizerInterceptorContextRef<'_>,_runtime_components:&RuntimeComponents,cfg:&mutConfigBag,) -> Result<(),BoxError>{let session = cfg.interceptor_state().unset::<SubsegmentSession>();ifletSome(mut session) = session {ifletSome(response) = context.response(){
session.response_status(response.status().as_u16());ifletSome(request_id) = response.request_id(){
session.request_id(request_id);}}}// session reports the subsegment to the X-Ray daemon when it is droppedOk(())}}
Unfortunately, the above idea is not feasible because the unset method of Layer won't return the replaced value; i.e., I have no chance to update the session information before it is dropped.
What I am currently doing is to make the above XrayIntercept have a session wrapped in Mutex, which I feel awkward:
structXrayIntercept{// wrapped in Mutex because Intercept has to be immutablesession:Arc<Mutex<Option<SubsegmentSession>>>,}implInterceptforXrayIntercept{fnread_before_attempt(&self,_context:&BeforeTransmitInterceptorContextRef<'_>,_runtime_components:&RuntimeComponents,_cfg:&mutConfigBag,) -> Result<(),BoxError>{let session = self.enter_subsegment(self.service,self.operation);*self.session.lock().unwrap() = Some(session);Ok(())}fnmodify_before_transmit(&self,context:&mutBeforeTransmitInterceptorContextMut<'_>,_runtime_components:&RuntimeComponents,_cfg:&mutConfigBag,) -> Result<(),BoxError>{let trace_id = self.session.lock().unwrap().as_ref().and_then(|s| s.x_amzn_trace_id());ifletSome(trace_id) = trace_id {
context
.request_mut().headers_mut().insert("X-Amzn-Trace-Id", trace_id);}Ok(())}fnread_after_attempt(&self,context:&FinalizerInterceptorContextRef<'_>,_runtime_components:&RuntimeComponents,_cfg:&mutConfigBag,) -> Result<(),BoxError>{letmut session = self.session.lock().unwrap();ifletSome(mut session) = session.take(){ifletSome(response) = context.response(){
session.response_status(response.status().as_u16());ifletSome(request_id) = response.request_id(){
session.request_id(request_id);}}}Ok(())}}
/// Remove `T` from this bagpubfnunset<T:Send + Sync + Debug + 'static>(&mutself) -> UnsetResult<'_>{let value = self.put_directly::<StoreReplace<T>>(Value::ExplicitlyUnset(type_name::<T>()));UnsetResult::new(self, value)}/// Stores `item` of type `T` into the config bag, overriding a previous value of the same typepubfnstore_put<T>(&mutself,item:T) -> UnsetResult<'_>whereT:Storable<Storer = StoreReplace<T>>,{let old = self.put_directly::<StoreReplace<T>>(Value::Set(item));UnsetResult::new(self, old)}/// Stores `item` of type `T` into the config bag, overriding a previous value of the same type,/// or unsets it by passing a `None`pubfnstore_or_unset<T>(&mutself,item:Option<T>) -> UnsetResult<'_>whereT:Storable<Storer = StoreReplace<T>>,{let item = match item {Some(item) => Value::Set(item),None => Value::ExplicitlyUnset(type_name::<T>()),};let old = self.put_directly::<StoreReplace<T>>(item);UnsetResult::new(self, old)}
unset, store_put, and store_or_unset no longer return &mut Layer but UnsetResult which can be derefed as &mut Layer for backward compatibility.
/// Unset result.#[derive(Debug)]pubstructUnsetResult<'a>{layer:&'a mutLayer,value:Option<UnsetResultValue>,}impl<'a>UnsetResult<'a>{fnnew(layer:&'a mutLayer,value:Option<TypeErasedBox>) -> Self{Self{
layer,value: value.map(UnsetResultValue),}}/// Decomposes the result into the layer and the value.pubfninto_parts(self) -> (&'a mutLayer,Option<UnsetResultValue>){(self.layer,self.value)}}impl<'a> std::ops::DerefforUnsetResult<'a>{typeTarget = Layer;fnderef(&self) -> &Self::Target{self.layer}}impl<'a> std::ops::DerefMutforUnsetResult<'a>{fnderef_mut(&mutself) -> &mutSelf::Target{self.layer}}
UnsetResultValue is introduced to extract the underlying value that is wrapped in the private enum Value when it is stored in the Layer.
/// Value part of [`UnsetResult`].#[derive(Debug)]pubstructUnsetResultValue(TypeErasedBox);implUnsetResultValue{/// Downcasts the underlying value to a given type `T`.////// This method extracts the mutable reference to the underlying value that/// is wrapped in `Value::Set`.pubfndowncast_mut<T: fmt::Debug + Send + Sync + 'static>(&mutself) -> Option<&mutT>{matchself.0.downcast_mut::<Value<T>>(){Some(Value::Set(t)) => Some(t),
_ => None,}}}
The read_after_attempt method in my initial intended code would become something similar to the following if we incorporated the above suggestion into:
// ends the subsegment updated with the request ID and responsefnread_after_attempt(&self,context:&FinalizerInterceptorContextRef<'_>,_runtime_components:&RuntimeComponents,cfg:&mutConfigBag,) -> Result<(),BoxError>{let result = cfg.interceptor_state().unset::<SubsegmentSession>();let(_,mut session) = result.into_parts();ifletSome(session) = session.downcast_mut::<SubsegmentSession>(){ifletSome(response) = context.response(){
session.response_status(response.status().as_u16());ifletSome(request_id) = response.request_id(){
session.request_id(request_id);}}}// session reports the subsegment to the X-Ray daemon when it is droppedOk(())}
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Summary
Make the
unset
,store_put
, andstore_or_unset
methods ofaws_smithy_types::config_bag::Layer
(Layer
) return the previously stored value(s).These methods currently return a mutable reference to the
Layer
and never return the previously stored value.Background
I am currently writing a crate that facilitates X-Ray tracing in AWS Lambda. I came up with an idea to utilize interceptor to start and end a subsegment for an AWS service operation. Here is what in my mind:
Unfortunately, the above idea is not feasible because the
unset
method ofLayer
won't return the replaced value; i.e., I have no chance to update the session information before it is dropped.What I am currently doing is to make the above
XrayIntercept
have a session wrapped inMutex
, which I feel awkward:You can find the actual code here.
Suggested changes (PoC)
I have a PoC of my suggested changes.
https://github.com/codemonger-io/smithy-rs/blob/daa7e3fda222d6efa155022d908d666307f9d4e9/rust-runtime/aws-smithy-types/src/config_bag.rs#L416-L443
unset
,store_put
, andstore_or_unset
no longer return&mut Layer
butUnsetResult
which can be derefed as&mut Layer
for backward compatibility.https://github.com/codemonger-io/smithy-rs/blob/daa7e3fda222d6efa155022d908d666307f9d4e9/rust-runtime/aws-smithy-types/src/config_bag.rs#L518-L551
UnsetResultValue
is introduced to extract the underlying value that is wrapped in the private enumValue
when it is stored in theLayer
.https://github.com/codemonger-io/smithy-rs/blob/daa7e3fda222d6efa155022d908d666307f9d4e9/rust-runtime/aws-smithy-types/src/config_bag.rs#L553-L568
The
read_after_attempt
method in my initial intended code would become something similar to the following if we incorporated the above suggestion into:Beta Was this translation helpful? Give feedback.
All reactions