11use super :: { Cas , SwapError } ;
22use anyhow:: { Context , Result } ;
3- use spin_core:: { async_trait, wasmtime:: component:: Resource } ;
3+ use spin_core:: {
4+ async_trait,
5+ wasmtime:: component:: { Accessor , FutureReader , Resource , StreamReader } ,
6+ } ;
47use spin_factor_otel:: OtelFactorState ;
58use spin_resource_table:: Table ;
69use spin_telemetry:: traces:: { self , Blame } ;
10+ use spin_world:: spin:: key_value:: key_value as v3;
711use spin_world:: v2:: key_value;
812use spin_world:: wasi:: keyvalue as wasi_keyvalue;
913use spin_world:: MAX_HOST_BUFFERED_BYTES ;
@@ -43,6 +47,13 @@ pub trait Store: Sync + Send {
4347 async fn delete ( & self , key : & str ) -> Result < ( ) , Error > ;
4448 async fn exists ( & self , key : & str ) -> Result < bool , Error > ;
4549 async fn get_keys ( & self , max_result_bytes : usize ) -> Result < Vec < String > , Error > ;
50+ async fn get_keys_async (
51+ & self ,
52+ max_result_bytes : usize ,
53+ ) -> (
54+ tokio:: sync:: mpsc:: Receiver < String > ,
55+ tokio:: sync:: oneshot:: Receiver < Result < ( ) , v3:: Error > > ,
56+ ) ;
4657 async fn get_many (
4758 & self ,
4859 keys : Vec < String > ,
@@ -220,6 +231,140 @@ impl key_value::HostStore for KeyValueDispatch {
220231 }
221232}
222233
234+ impl spin_core:: wasmtime:: component:: HasData for KeyValueDispatch {
235+ type Data < ' a > = & ' a mut KeyValueDispatch ;
236+ }
237+
238+ impl v3:: Host for KeyValueDispatch { }
239+
240+ impl v3:: HostStore for KeyValueDispatch {
241+ async fn drop ( & mut self , store : Resource < v3:: Store > ) -> Result < ( ) > {
242+ self . stores . remove ( store. rep ( ) ) ;
243+ Ok ( ( ) )
244+ }
245+ }
246+
247+ impl v3:: HostStoreWithStore for crate :: KeyValueFactorData {
248+ async fn open < T > (
249+ accessor : & Accessor < T , Self > ,
250+ label : String ,
251+ ) -> Result < Result < Resource < v3:: Store > , v3:: Error > > {
252+ let ( allowed, manager) = accessor. with ( |mut access| {
253+ let host = access. get ( ) ;
254+ host. otel . reparent_tracing_span ( ) ;
255+ ( host. allowed_stores . contains ( & label) , host. manager . clone ( ) )
256+ } ) ;
257+
258+ if !allowed {
259+ return Ok ( Err ( v3:: Error :: AccessDenied ) ) ;
260+ }
261+
262+ let store = manager. get ( & label) . await ?;
263+ store. after_open ( ) . await ?;
264+
265+ let rsrc = accessor. with ( |mut access| {
266+ let host = access. get ( ) ;
267+ host. stores
268+ . push ( store)
269+ . map ( Resource :: new_own)
270+ . map_err ( |( ) | v3:: Error :: StoreTableFull )
271+ } ) ;
272+
273+ Ok ( rsrc)
274+ }
275+
276+ async fn get < T > (
277+ accessor : & Accessor < T , Self > ,
278+ store : Resource < v3:: Store > ,
279+ key : String ,
280+ ) -> Result < Result < Option < Vec < u8 > > , v3:: Error > > {
281+ let store = accessor. with ( |mut access| {
282+ let host = access. get ( ) ;
283+ host. otel . reparent_tracing_span ( ) ;
284+ host. get_store ( store) . cloned ( )
285+ } ) ?;
286+ Ok ( store
287+ . get ( & key, MAX_HOST_BUFFERED_BYTES )
288+ . await
289+ . map_err ( to_v3_err)
290+ . map_err ( track_error_on_span_v3) )
291+ }
292+
293+ async fn set < T > (
294+ accessor : & Accessor < T , Self > ,
295+ store : Resource < v3:: Store > ,
296+ key : String ,
297+ value : Vec < u8 > ,
298+ ) -> Result < Result < ( ) , v3:: Error > > {
299+ let store = accessor. with ( |mut access| {
300+ let host = access. get ( ) ;
301+ host. otel . reparent_tracing_span ( ) ;
302+ host. get_store ( store) . cloned ( )
303+ } ) ?;
304+ Ok ( store
305+ . set ( & key, & value)
306+ . await
307+ . map_err ( to_v3_err)
308+ . map_err ( track_error_on_span_v3) )
309+ }
310+
311+ async fn delete < T > (
312+ accessor : & Accessor < T , Self > ,
313+ store : Resource < v3:: Store > ,
314+ key : String ,
315+ ) -> Result < Result < ( ) , v3:: Error > > {
316+ let store = accessor. with ( |mut access| {
317+ let host = access. get ( ) ;
318+ host. otel . reparent_tracing_span ( ) ;
319+ host. get_store ( store) . cloned ( )
320+ } ) ?;
321+ Ok ( store
322+ . delete ( & key)
323+ . await
324+ . map_err ( to_v3_err)
325+ . map_err ( track_error_on_span_v3) )
326+ }
327+
328+ async fn exists < T > (
329+ accessor : & Accessor < T , Self > ,
330+ store : Resource < v3:: Store > ,
331+ key : String ,
332+ ) -> Result < Result < bool , v3:: Error > > {
333+ let store = accessor. with ( |mut access| {
334+ let host = access. get ( ) ;
335+ host. otel . reparent_tracing_span ( ) ;
336+ host. get_store ( store) . cloned ( )
337+ } ) ?;
338+ Ok ( store
339+ . exists ( & key)
340+ . await
341+ . map_err ( to_v3_err)
342+ . map_err ( track_error_on_span_v3) )
343+ }
344+
345+ async fn get_keys < T > (
346+ accessor : & Accessor < T , Self > ,
347+ store : Resource < v3:: Store > ,
348+ ) -> Result < ( StreamReader < String > , FutureReader < Result < ( ) , v3:: Error > > ) > {
349+ let store = accessor. with ( |mut access| {
350+ let host = access. get ( ) ;
351+ host. otel . reparent_tracing_span ( ) ;
352+ host. get_store ( store) . cloned ( )
353+ } ) ?;
354+
355+ let ( keys_rx, err_rx) = store. get_keys_async ( MAX_HOST_BUFFERED_BYTES ) . await ;
356+
357+ let producer = spin_wasi_async:: stream:: producer ( keys_rx) ;
358+ let ( ksr, efr) = accessor. with ( |mut access| {
359+ let ksr = StreamReader :: new ( & mut access, producer) ;
360+ let efr = FutureReader :: new ( & mut access, err_rx) ;
361+ ( ksr, efr)
362+ } ) ;
363+
364+ Ok ( ( ksr, efr) )
365+ }
366+ }
367+
223368/// Make sure that infrastructure related errors are tracked in the current span.
224369fn track_error_on_span ( err : Error ) -> Error {
225370 let blame = match err {
@@ -230,6 +375,16 @@ fn track_error_on_span(err: Error) -> Error {
230375 err
231376}
232377
378+ /// Make sure that infrastructure related errors are tracked in the current span.
379+ fn track_error_on_span_v3 ( err : v3:: Error ) -> v3:: Error {
380+ let blame = match err {
381+ v3:: Error :: NoSuchStore | v3:: Error :: AccessDenied => Blame :: Guest ,
382+ v3:: Error :: StoreTableFull | v3:: Error :: Other ( _) => Blame :: Host ,
383+ } ;
384+ traces:: mark_as_error ( & err, Some ( blame) ) ;
385+ err
386+ }
387+
233388fn to_wasi_err ( e : Error ) -> wasi_keyvalue:: store:: Error {
234389 match track_error_on_span ( e) {
235390 Error :: AccessDenied => wasi_keyvalue:: store:: Error :: AccessDenied ,
@@ -239,6 +394,15 @@ fn to_wasi_err(e: Error) -> wasi_keyvalue::store::Error {
239394 }
240395}
241396
397+ pub fn to_v3_err ( e : Error ) -> v3:: Error {
398+ match track_error_on_span ( e) {
399+ Error :: AccessDenied => v3:: Error :: AccessDenied ,
400+ Error :: NoSuchStore => v3:: Error :: NoSuchStore ,
401+ Error :: StoreTableFull => v3:: Error :: StoreTableFull ,
402+ Error :: Other ( msg) => v3:: Error :: Other ( msg) ,
403+ }
404+ }
405+
242406impl wasi_keyvalue:: store:: Host for KeyValueDispatch {
243407 #[ instrument( name = "wasi_key_value.open" , skip_all, fields( otel. kind = "client" ) ) ]
244408 async fn open (
@@ -484,6 +648,11 @@ pub fn log_error(err: impl std::fmt::Debug) -> Error {
484648 Error :: Other ( format ! ( "{err:?}" ) )
485649}
486650
651+ pub fn log_error_v3 ( err : impl std:: fmt:: Debug ) -> v3:: Error {
652+ tracing:: warn!( "key-value error: {err:?}" ) ;
653+ v3:: Error :: Other ( format ! ( "{err:?}" ) )
654+ }
655+
487656pub fn log_cas_error ( err : impl std:: fmt:: Debug ) -> SwapError {
488657 tracing:: warn!( "key-value error: {err:?}" ) ;
489658 SwapError :: Other ( format ! ( "{err:?}" ) )
0 commit comments