blob: e6cb6003a1e36e4aa42757933fbd2d57a24c1a67 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use super::*;
const PAGE_BYTES: usize = 48 * 1024;
pub(super) async fn query(
owner: &Owner,
input: Diagnostics,
context: &Context,
) -> Result<Value, String> {
owner.session(&input.session_id).await?;
let (through, before) = match input.position {
DiagnosticPosition::Latest => (None, None),
DiagnosticPosition::Before { through, sequence } => (Some(through), Some(sequence)),
};
let page = owner
.store
.diagnostics_page(&input.session_id, through, before, 32, PAGE_BYTES)
.await
.map_err(|_| "agent_diagnostics_unavailable")?;
let mut page = DiagnosticPage {
through: page.through,
next_before: page.next_before,
entries: page
.entries
.into_iter()
.map(|entry| Diagnostic {
sequence: entry.sequence,
event_id: entry.event_id,
recorded_at: entry
.recorded_at
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs_f64()
* 1000.0,
detail: entry.detail,
})
.collect(),
};
let value = loop {
let value = encode(&page)?;
if maka_plugin_core::json::check_delivery(&value, PAGE_BYTES).is_ok() {
break value;
}
if page.entries.len() <= 1 {
break value;
}
page.entries.pop();
page.next_before = page.entries.last().map(|entry| entry.sequence);
};
owner.session(&input.session_id).await?;
if context.scope().cancellation.is_cancelled() {
return Err("agent_observation_cancelled".into());
}
Ok(value)
}
pub(super) async fn watch(
owner: &Owner,
input: SessionRead,
context: &Context,
) -> Result<Value, String> {
owner.session(&input.session_id).await?;
let mut commits = owner.store.subscribe_commits();
let mut previous = None;
loop {
if context.scope().cancellation.is_cancelled() {
return Err("agent_observation_cancelled".into());
}
owner.session(&input.session_id).await?;
let through = *commits.borrow_and_update();
let revision = owner
.store
.latest_diagnostic_sequence(&input.session_id, through)
.await
.map_err(|_| "agent_diagnostics_unavailable")?
.unwrap_or(0);
owner.session(&input.session_id).await?;
if context.scope().cancellation.is_cancelled() {
return Err("agent_observation_cancelled".into());
}
if previous != Some(revision) {
let value = encode(DiagnosticChange { revision })?;
maka_plugin_core::json::check_delivery(&value, PAGE_BYTES)
.map_err(|_| "agent_diagnostics_unavailable")?;
context.emit(value).await?;
previous = Some(revision);
}
tokio::select! {
biased;
_ = context.scope().cancellation.cancelled() => return Err("agent_observation_cancelled".into()),
result = commits.changed() => result.map_err(|_| "agent_observation_closed")?,
}
}
}