1use std::{
2 marker::PhantomData,
3 sync::{Arc, Weak},
4};
5
6use ankurah_proto::{self as proto, CollectionId};
7
8use ankurah_signals::{
9 broadcast::BroadcastId,
10 porcelain::subscribe::{IntoSubscribeListener, SubscriptionGuard},
11 signal::{Listener, ListenerGuard},
12 Get, Mut, Peek, Read, Signal, Subscribe,
13};
14use tracing::{debug, warn};
15
16use crate::{
17 changes::ChangeSet,
18 entity::Entity,
19 error::RetrievalError,
20 model::View,
21 node::{MatchArgs, NodeInner, TNodeErased},
22 policy::PolicyAgent,
23 reactor::{
24 fetch_gap::{GapFetcher, QueryGapFetcher},
25 ReactorSubscription, ReactorUpdate,
26 },
27 resultset::{EntityResultSet, ResultSet},
28 storage::StorageEngine,
29 Node,
30};
31
32#[derive(Clone)]
38pub struct EntityLiveQuery(Arc<Inner>);
39
40trait NodeRef: Send + Sync {
42 fn upgrade(&self) -> Option<Box<dyn TNodeErased>>;
43}
44
45struct StrongNodeRef<SE, PA: PolicyAgent>(Arc<NodeInner<SE, PA>>);
47
48impl<SE, PA> NodeRef for StrongNodeRef<SE, PA>
49where
50 SE: StorageEngine + Send + Sync + 'static,
51 PA: PolicyAgent + Send + Sync + 'static,
52{
53 fn upgrade(&self) -> Option<Box<dyn TNodeErased>> { Some(Box::new(Node(self.0.clone()))) }
54}
55
56struct WeakNodeRefImpl<SE, PA: PolicyAgent>(Weak<NodeInner<SE, PA>>);
58
59impl<SE, PA> NodeRef for WeakNodeRefImpl<SE, PA>
60where
61 SE: StorageEngine + Send + Sync + 'static,
62 PA: PolicyAgent + Send + Sync + 'static,
63{
64 fn upgrade(&self) -> Option<Box<dyn TNodeErased>> { self.0.upgrade().map(|inner| Box::new(Node(inner)) as Box<dyn TNodeErased>) }
65}
66
67struct Inner {
68 pub(crate) query_id: proto::QueryId,
69 pub(crate) subscription: ReactorSubscription,
73 pub(crate) node: Box<dyn NodeRef>,
74 pub(crate) resultset: EntityResultSet,
75 pub(crate) error: Mut<Option<RetrievalError>>,
76 pub(crate) initialized: tokio::sync::Notify,
77 pub(crate) initialized_version: std::sync::atomic::AtomicU32,
78 pub(crate) current_version: std::sync::atomic::AtomicU32,
80 pub(crate) selection: Mut<(ankql::ast::Selection, u32)>,
84 pub(crate) collection_id: CollectionId,
86 pub(crate) gap_fetcher: std::sync::Arc<dyn GapFetcher<Entity>>,
88}
89
90pub struct WeakEntityLiveQuery(Weak<Inner>);
92
93impl WeakEntityLiveQuery {
94 pub fn upgrade(&self) -> Option<EntityLiveQuery> { self.0.upgrade().map(EntityLiveQuery) }
95}
96
97impl Clone for WeakEntityLiveQuery {
98 fn clone(&self) -> Self { Self(self.0.clone()) }
99}
100
101#[derive(Clone)]
102pub struct LiveQuery<R: View>(EntityLiveQuery, PhantomData<R>);
103
104impl<R: View> std::ops::Deref for LiveQuery<R> {
105 type Target = EntityLiveQuery;
106 fn deref(&self) -> &Self::Target { &self.0 }
107}
108
109impl Inner {
110 fn node(&self) -> Option<Box<dyn TNodeErased>> { self.node.upgrade() }
111
112 async fn wait_initialized(&self) {
113 if self.initialized_version.load(std::sync::atomic::Ordering::Relaxed)
115 >= self.current_version.load(std::sync::atomic::Ordering::Relaxed)
116 {
117 return;
118 }
119
120 self.initialized.notified().await;
123 }
124
125 async fn activate(&self, version: u32) -> Result<(), RetrievalError> {
131 let (selection, stored_version) = self.selection.value();
133
134 if version < stored_version {
137 warn!("LiveQuery - Dropped stale activation request for version {} (current version is {})", version, stored_version);
138 return Ok(());
139 }
140
141 debug!("LiveQuery.activate() for predicate {} (version {})", self.query_id, version);
142
143 let node = self.node().ok_or_else(|| RetrievalError::Other("Node has been dropped".into()))?;
144 let reactor = node.reactor();
145 let initialized_version = self.initialized_version.load(std::sync::atomic::Ordering::Relaxed);
146
147 let hook = InnerPreNotifyHook(self);
148 if initialized_version == 0 {
150 reactor
153 .add_query_and_notify(
154 self.subscription.id(),
155 self.query_id,
156 self.collection_id.clone(),
157 selection,
158 &*node,
159 self.resultset.clone(),
160 self.gap_fetcher.clone(),
161 &hook,
162 )
163 .await?
164 } else {
165 reactor
168 .update_query_and_notify(
169 self.subscription.id(),
170 self.query_id,
171 self.collection_id.clone(),
172 selection,
173 &*node,
174 version,
175 &hook,
176 )
177 .await?;
178 };
179
180 Ok(())
181 }
182
183 fn mark_initialized(&self, version: u32) {
185 self.initialized_version.store(version, std::sync::atomic::Ordering::Relaxed);
187 self.initialized.notify_waiters();
188 }
189}
190
191struct InnerPreNotifyHook<'a>(&'a Inner);
194impl crate::reactor::PreNotifyHook for &InnerPreNotifyHook<'_> {
195 fn pre_notify(&self, version: u32) {
196 self.0.mark_initialized(version);
198 }
199}
200
201fn create_inner<SE, PA>(
203 node: &Node<SE, PA>,
204 node_ref: Box<dyn NodeRef>,
205 collection_id: CollectionId,
206 mut args: MatchArgs,
207 cdata: PA::ContextData,
208) -> Result<(Arc<Inner>, proto::QueryId), RetrievalError>
209where
210 SE: StorageEngine + Send + Sync + 'static,
211 PA: PolicyAgent + Send + Sync + 'static,
212{
213 node.policy_agent.can_access_collection(&cdata, &collection_id)?;
214 args.selection.predicate = node.policy_agent.filter_predicate(&cdata, &collection_id, args.selection.predicate)?;
215
216 args.selection = node.type_resolver.resolve_selection_types(args.selection);
218
219 let subscription = node.reactor.subscribe();
220
221 let resultset = EntityResultSet::empty();
222 let query_id = proto::QueryId::new();
223 let gap_fetcher: std::sync::Arc<dyn GapFetcher<Entity>> = std::sync::Arc::new(QueryGapFetcher::new(&node, cdata.clone()));
224
225 let inner = Arc::new(Inner {
226 query_id,
227 node: node_ref,
228 subscription,
229 resultset: resultset.clone(),
230 error: Mut::new(None),
231 initialized: tokio::sync::Notify::new(),
232 initialized_version: std::sync::atomic::AtomicU32::new(0), current_version: std::sync::atomic::AtomicU32::new(1), selection: Mut::new((args.selection.clone(), 1)), collection_id: collection_id.clone(),
236 gap_fetcher,
237 });
238
239 let has_relay = node.subscription_relay.is_some();
241
242 if args.cached || !has_relay {
243 let inner2 = inner.clone();
245
246 debug!("LiveQuery::new() spawning initialization task for durable node predicate {}", query_id);
247 crate::task::spawn(async move {
248 debug!("LiveQuery initialization task starting for predicate {}", query_id);
249 if let Err(e) = inner2.activate(1).await {
250 debug!("LiveQuery initialization failed for predicate {}: {}", query_id, e);
251 inner2.error.set(Some(e));
252 } else {
253 debug!("LiveQuery initialization completed for predicate {}", query_id);
254 }
255 });
256 }
257
258 Ok((inner, query_id))
259}
260
261impl EntityLiveQuery {
262 pub fn new<SE, PA>(
263 node: &Node<SE, PA>,
264 collection_id: CollectionId,
265 args: MatchArgs,
266 cdata: PA::ContextData,
267 ) -> Result<Self, RetrievalError>
268 where
269 SE: StorageEngine + Send + Sync + 'static,
270 PA: PolicyAgent + Send + Sync + 'static,
271 {
272 let node_ref: Box<dyn NodeRef> = Box::new(StrongNodeRef(Arc::clone(&node.0)));
273 Self::new_with_node_ref(node, node_ref, collection_id, args, cdata)
274 }
275
276 pub fn new_weak_node<SE, PA>(
283 node: &Node<SE, PA>,
284 collection_id: CollectionId,
285 args: MatchArgs,
286 cdata: PA::ContextData,
287 ) -> Result<Self, RetrievalError>
288 where
289 SE: StorageEngine + Send + Sync + 'static,
290 PA: PolicyAgent + Send + Sync + 'static,
291 {
292 let node_ref: Box<dyn NodeRef> = Box::new(WeakNodeRefImpl(Arc::downgrade(&node.0)));
293 Self::new_with_node_ref(node, node_ref, collection_id, args, cdata)
294 }
295
296 fn new_with_node_ref<SE, PA>(
297 node: &Node<SE, PA>,
298 node_ref: Box<dyn NodeRef>,
299 collection_id: CollectionId,
300 args: MatchArgs,
301 cdata: PA::ContextData,
302 ) -> Result<Self, RetrievalError>
303 where
304 SE: StorageEngine + Send + Sync + 'static,
305 PA: PolicyAgent + Send + Sync + 'static,
306 {
307 let has_relay = node.subscription_relay.is_some();
308 let (inner, query_id) = create_inner(node, node_ref, collection_id.clone(), args, cdata.clone())?;
309
310 let me = Self(inner.clone());
311
312 if has_relay {
315 node.subscribe_remote_query(query_id, collection_id, inner.selection.value().0, cdata, 1, me.weak());
316 }
317
318 Ok(me)
319 }
320 pub fn map<R: View>(self) -> LiveQuery<R> { LiveQuery(self, PhantomData) }
321
322 pub async fn wait_initialized(&self) { self.0.wait_initialized().await; }
324
325 pub fn update_selection(
326 &self,
327 new_selection: impl TryInto<ankql::ast::Selection, Error = impl Into<RetrievalError>>,
328 ) -> Result<(), RetrievalError> {
329 let new_selection = new_selection.try_into().map_err(|e| e.into())?;
330 let node = self.0.node().ok_or_else(|| RetrievalError::Other("Node has been dropped".into()))?;
331
332 let new_version = self.0.current_version.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;
334
335 self.0.resultset.set_loaded(false);
337
338 self.0.selection.set((new_selection.clone(), new_version));
340
341 let has_relay = node.has_subscription_relay();
343
344 if has_relay {
345 node.update_remote_query(self.0.query_id, new_selection.clone(), new_version)?;
347 } else {
348 let inner = self.0.clone();
350 let query_id = self.0.query_id;
351
352 crate::task::spawn(async move {
353 if let Err(e) = inner.activate(new_version).await {
354 tracing::error!("LiveQuery update failed for predicate {}: {}", query_id, e);
355 inner.error.set(Some(e));
356 }
357 });
358 }
359
360 Ok(())
361 }
362
363 pub async fn update_selection_wait(
364 &self,
365 new_selection: impl TryInto<ankql::ast::Selection, Error = impl Into<RetrievalError>>,
366 ) -> Result<(), RetrievalError> {
367 self.update_selection(new_selection)?;
368 self.0.wait_initialized().await;
369 Ok(())
370 }
371
372 pub fn error(&self) -> Read<Option<RetrievalError>> { self.0.error.read() }
373 pub fn query_id(&self) -> proto::QueryId { self.0.query_id }
374 pub fn selection(&self) -> Read<(ankql::ast::Selection, u32)> { self.0.selection.read() }
375 pub fn resultset(&self) -> EntityResultSet { self.0.resultset.clone() }
376
377 pub fn weak(&self) -> WeakEntityLiveQuery { WeakEntityLiveQuery(Arc::downgrade(&self.0)) }
379}
380
381impl Drop for Inner {
382 fn drop(&mut self) {
383 if let Some(node) = self.node.upgrade() {
384 node.unsubscribe_remote_predicate(self.query_id);
385 }
386 }
387}
388
389#[async_trait::async_trait]
391impl crate::peer_subscription::RemoteQuerySubscriber for WeakEntityLiveQuery {
392 async fn subscription_established(&self, version: u32) {
393 if let Some(inner) = self.0.upgrade() {
395 tracing::debug!("Subscription established for query {}: {}", inner.query_id, version);
398 if let Err(e) = inner.activate(version).await {
399 tracing::error!("Failed to activate subscription for query {}: {}", inner.query_id, e);
400 inner.error.set(Some(e));
401 }
402 }
403 }
405
406 fn set_last_error(&self, error: RetrievalError) {
407 if let Some(inner) = self.0.upgrade() {
409 tracing::info!("Setting last error for LiveQuery {}: {}", inner.query_id, error);
410 inner.error.set(Some(error));
411 }
412 }
414}
415
416impl<R: View> LiveQuery<R> {
417 pub async fn wait_initialized(&self) { self.0.wait_initialized().await; }
419
420 pub fn resultset(&self) -> ResultSet<R> { self.0 .0.resultset.wrap::<R>() }
421
422 pub fn loaded(&self) -> bool { self.0 .0.resultset.is_loaded() }
423
424 pub fn ids(&self) -> Vec<proto::EntityId> { self.0 .0.resultset.keys().collect() }
425
426 pub fn ids_sorted(&self) -> Vec<proto::EntityId> {
427 use itertools::Itertools;
428 self.0 .0.resultset.keys().sorted().collect()
429 }
430}
431
432impl<R: View> Signal for LiveQuery<R> {
435 fn listen(&self, listener: Listener) -> ListenerGuard { self.0 .0.subscription.listen(listener) }
436
437 fn broadcast_id(&self) -> BroadcastId { self.0 .0.subscription.broadcast_id() }
438}
439
440impl<R: View + Clone + 'static> Get<Vec<R>> for LiveQuery<R> {
442 fn get(&self) -> Vec<R> {
443 use ankurah_signals::CurrentObserver;
444 CurrentObserver::track(&self);
445 self.0 .0.resultset.wrap::<R>().peek()
446 }
447}
448
449impl<R: View + Clone + 'static> Peek<Vec<R>> for LiveQuery<R> {
451 fn peek(&self) -> Vec<R> { self.0 .0.resultset.wrap().peek() }
452}
453
454impl<R: View> Subscribe<ChangeSet<R>> for LiveQuery<R>
456where R: Clone + Send + Sync + 'static
457{
458 fn subscribe<L>(&self, listener: L) -> SubscriptionGuard
459 where L: IntoSubscribeListener<ChangeSet<R>> {
460 let listener = listener.into_subscribe_listener();
461
462 let me = self.clone();
463 self.0 .0.subscription.subscribe(move |reactor_update: ReactorUpdate| {
465 let changeset: ChangeSet<R> = livequery_change_set_from(me.0 .0.resultset.wrap::<R>(), reactor_update);
466 listener(changeset);
467 })
468 }
469}
470
471fn livequery_change_set_from<R: View>(resultset: ResultSet<R>, reactor_update: ReactorUpdate) -> ChangeSet<R>
473where R: View {
474 use crate::changes::{ChangeSet, ItemChange};
475
476 let mut changes = Vec::new();
477
478 for item in reactor_update.items {
479 let view = R::from_entity(item.entity);
480
481 if let Some((_, membership_change)) = item.predicate_relevance.first() {
484 match membership_change {
485 crate::reactor::MembershipChange::Initial => {
486 changes.push(ItemChange::Initial { item: view });
487 }
488 crate::reactor::MembershipChange::Add => {
489 changes.push(ItemChange::Add { item: view, events: item.events });
490 }
491 crate::reactor::MembershipChange::Remove => {
492 changes.push(ItemChange::Remove { item: view, events: item.events });
493 }
494 }
495 } else {
496 changes.push(ItemChange::Update { item: view, events: item.events });
498 }
499 }
500
501 ChangeSet { changes, resultset }
502}