harmos_operations/storage/
query.rs1use super::IO_POLICY;
25use harmos_domain::MemorySize;
26use harmos_domain::model::storage::StorageCursor;
27use harmos_domain::model::storage::codec::{Nested, Repeated, product};
28use harmos_domain::model::storage::query::transform::Transform;
29use harmos_domain::model::storage::query::{QueryCompletion, QueryOutput, RowSlice, Source};
30use harmos_domain::operations::ReplaySafety;
31#[cfg(feature = "runtime")]
32use harmos_sdk::operations::access::GrantAccess;
33#[cfg(feature = "runtime")]
34use harmos_storage::{ErasedCatalog, StorageManager};
35
36product!(Query {
41 at: Nested<StorageCursor> => 1,
42 source: Nested<Source> => 2,
43 transforms: Repeated<Transform> => 3,
44 slice: Nested<RowSlice> => 4,
45});
46
47#[harmos::operation(
52 harmos_path = crate,
53 resources = [StorageManager<ErasedCatalog>, GrantAccess],
54 codec = manual,
55 id = "storage.query",
56 response = QueryCompletion,
57 policy = IO_POLICY,
58 output = QueryOutput,
59 visibility = Shared,
60 replay = ReplaySafety::Idempotent,
61 grpc(package = "harmos.storage.v1", service = "Storage", method = "Query"),
62 grant = Self::grant_for,
63)]
64#[derive(Clone, Debug)]
65pub struct Query {
66 pub at: StorageCursor,
67 pub source: Source,
68 pub transforms: Vec<Transform>,
69 pub slice: RowSlice,
70}
71
72impl Query {
79 fn grant_for(request: &Query) -> Option<harmos_domain::plugin::Grant> {
80 Some(harmos_domain::plugin::Grant::of(<Self as harmos_domain::OperationDeclaration>::ID, request.source.grant_scope()))
81 }
82}
83
84impl MemorySize for Query {
85 fn memory_size(&self) -> usize {
86 std::mem::size_of::<Self>()
87 .saturating_add(self.at.memory_size().saturating_sub(std::mem::size_of_val(&self.at)))
88 .saturating_add(self.source.memory_size().saturating_sub(std::mem::size_of_val(&self.source)))
89 .saturating_add(self.transforms.memory_size().saturating_sub(std::mem::size_of_val(&self.transforms)))
90 .saturating_add(self.slice.memory_size().saturating_sub(std::mem::size_of_val(&self.slice)))
91 }
92}
93
94#[cfg(feature = "runtime")]
95mod execution {
96 use super::*;
97
98 use crate::storage::IO_TIMEOUT;
101 use crate::{Error, Result};
102 use harmos_domain::plugin::Grant;
103 use harmos_sdk::operations::{Operation, OperationDeclaration};
104 use harmos_storage::query::QueryCompletion;
105
106 impl Operation for super::Query {
113 type Error = Error;
114
115 async fn run(self, context: harmos_sdk::Context<'_, Self>) -> Result<harmos_sdk::operations::Response<QueryCompletion>> {
116 let storage = context.resource::<harmos_storage::StorageManager<harmos_storage::ErasedCatalog>>();
117 let grants = context.resource::<harmos_sdk::operations::access::GrantAccess>();
118 let output = context.streams();
119
120 let host = grants;
121 let ctx = &host.0;
122 let grant = Grant::of(Query::ID, self.source.grant_scope());
123 ctx.admit(&grant).map_err(Error::Access)?;
124 let mut reader = storage
125 .query(harmos_storage::Query {
126 at: self.at.clone(),
127 source: self.source.clone(),
128 transforms: self.transforms.clone(),
129 slice: self.slice,
130 })
131 .await?;
132 while let Some(item) = reader.next().await? {
133 ctx.admit(&grant).map_err(Error::Access)?;
134 let bytes = item.output_memory_size();
135 tokio::time::timeout(IO_TIMEOUT, output.send_with(bytes, || Ok(item.output())))
136 .await
137 .map_err(|_| crate::storage::output_timeout())??;
138 }
139 Ok(reader.completion()?.into())
140 }
141 fn classify(error: &Error) -> harmos_sdk::operations::FailureKind {
142 crate::storage::classify(error)
143 }
144 }
145}
146
147