fix: speed up panel startup pod probes
This commit is contained in:
parent
caf18dbaab
commit
69ab9f7c22
0
.yoi/tickets/00001KVF0ZJM5/artifacts/.gitkeep
Normal file
0
.yoi/tickets/00001KVF0ZJM5/artifacts/.gitkeep
Normal file
21
.yoi/tickets/00001KVF0ZJM5/artifacts/relations.json
Normal file
21
.yoi/tickets/00001KVF0ZJM5/artifacts/relations.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"relations": [
|
||||||
|
{
|
||||||
|
"ticket_id": "00001KVF0ZJM5",
|
||||||
|
"kind": "related",
|
||||||
|
"target": "00001KVDETSN6",
|
||||||
|
"note": "Implements live startup latency improvement after dashboard content-ready measurement exposed Pod probe bottleneck.",
|
||||||
|
"author": "yoi ticket",
|
||||||
|
"at": "2026-06-19T04:19:09Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ticket_id": "00001KVF0ZJM5",
|
||||||
|
"kind": "related",
|
||||||
|
"target": "00001KVDQH839",
|
||||||
|
"note": "Uses shell/live startup measurements added by the E2E launch-path work.",
|
||||||
|
"author": "yoi ticket",
|
||||||
|
"at": "2026-06-19T04:19:09Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
43
.yoi/tickets/00001KVF0ZJM5/item.md
Normal file
43
.yoi/tickets/00001KVF0ZJM5/item.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
title: 'Panel startup で Pod status probe を重複実行せず初回一覧表示を高速化する'
|
||||||
|
state: 'closed'
|
||||||
|
created_at: '2026-06-19T04:07:17Z'
|
||||||
|
updated_at: '2026-06-19T04:19:09Z'
|
||||||
|
assignee: null
|
||||||
|
readiness: 'implementation_ready'
|
||||||
|
risk_flags: ['panel', 'startup-latency', 'pod-status-probe', 'live-path', 'performance']
|
||||||
|
---
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Live workspace で `yoi panel` を起動すると、first frame は約 50ms で出る一方、実際の Ticket / Pod rows が表示されるまで約 8 秒かかっている。実測 breakdown では `pod_metadata_status_probe.initial`、`companion.presence`、`orchestrator.presence` がそれぞれ約 2.5 秒かかり、同じ Pod metadata / live status scan が初回 dashboard render 前に直列で重複実行されている。
|
||||||
|
|
||||||
|
この Ticket では初回一覧表示前の重複 Pod status probe をなくし、live Pod summary の重い session log scan を避け、ユーザー目線の「一覧が表示されるまで」を短縮する。
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- `load_multi_pod_snapshot` で初回 `load_pod_list` の結果を Companion / Orchestrator presence 判定に再利用する。
|
||||||
|
- 初回 render 前に `load_exact_companion_pod_presence` / `load_exact_pod_presence` 相当の追加 full probe を直列実行しない。
|
||||||
|
- Live status probe は session log 全読みの preview/summary 作成を初回 path で行わない。
|
||||||
|
- stored metadata summary を優先して使う。
|
||||||
|
- live-only row は minimal live summary でよい。
|
||||||
|
- Companion / Orchestrator spawn/restore が必要な場合の reload は維持する。
|
||||||
|
- Existing Panel behavior を壊さない。
|
||||||
|
- Companion / Orchestrator live status 表示
|
||||||
|
- Queue action
|
||||||
|
- Pod rows open/attach
|
||||||
|
- E2E dashboard readiness
|
||||||
|
- Live workspace に近い例外的計測で、rows 表示までの時間が改善していることを確認する。
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- Panel startup source breakdown で `companion.presence` / `orchestrator.presence` が追加 full Pod probe として秒単位で出ない。
|
||||||
|
- Live workspace 計測で first non-empty rows 表示が従来約 8 秒から明確に短縮する。
|
||||||
|
- `cargo test -p yoi-e2e --features e2e --test panel` が通る。
|
||||||
|
- `cargo check -p yoi-e2e -p yoi -p tui --features tui/e2e-test` が通る。
|
||||||
|
- `cargo fmt --check` / `git diff --check` / `target/debug/yoi ticket doctor` が通る。
|
||||||
|
|
||||||
|
## Related work
|
||||||
|
|
||||||
|
- `00001KVDETSN6` — Panel startup latency をユーザー目線の dashboard content ready 基準で計測・改善する。
|
||||||
|
- `00001KVDQH839` — Panel E2E に shell Enter 起動経路の dashboard readiness 計測を追加する。
|
||||||
22
.yoi/tickets/00001KVF0ZJM5/resolution.md
Normal file
22
.yoi/tickets/00001KVF0ZJM5/resolution.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
Implemented and validated.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Reused the initial `load_pod_list` result for Companion and Orchestrator presence in `load_multi_pod_snapshot`, removing two duplicate full Pod status probes before the first dashboard rows render.
|
||||||
|
- Renamed the E2E source timings to `companion.presence.from_initial_list` and `orchestrator.presence.from_initial_list` so regressions show whether the initial list is reused.
|
||||||
|
- Changed Pod list startup summarization to avoid reading active session logs while building initial Pod rows. Stored metadata now uses a cheap active-segment marker and live-only rows keep existing minimal live/pending summaries.
|
||||||
|
- Preserved spawn/restore behavior after Companion/Orchestrator lifecycle changes; if lifecycle changes require reload, the existing reload path remains.
|
||||||
|
|
||||||
|
Live-path measurement in the current workspace:
|
||||||
|
- Before this fix: first non-empty Panel rows appeared at about 7967ms; `pod_metadata_status_probe.initial`, `companion.presence`, and `orchestrator.presence` were each about 2.5s.
|
||||||
|
- After removing duplicate probes only: first non-empty rows appeared at about 2964ms; duplicate presence probes dropped to 0ms but initial Pod metadata/status probe was still about 2386ms.
|
||||||
|
- After also removing session-log reads from the startup Pod summary path: first non-empty rows appeared at about 754ms; `pod_metadata_status_probe.initial` was about 138ms; total dashboard source breakdown was about 649ms.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- cargo test -p tui pod_list --lib
|
||||||
|
- cargo test -p yoi-e2e --features e2e --test panel
|
||||||
|
- cargo check -p yoi-e2e -p yoi -p tui --features tui/e2e-test
|
||||||
|
- cargo build -p yoi
|
||||||
|
- cargo fmt --check
|
||||||
|
- git diff --check
|
||||||
|
- target/debug/yoi ticket doctor
|
||||||
|
- nix build .#yoi --no-link
|
||||||
46
.yoi/tickets/00001KVF0ZJM5/thread.md
Normal file
46
.yoi/tickets/00001KVF0ZJM5/thread.md
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!-- event: create author: "yoi ticket" at: 2026-06-19T04:07:17Z -->
|
||||||
|
|
||||||
|
## 作成
|
||||||
|
|
||||||
|
LocalTicketBackend によって作成されました。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: state_changed author: hare at: 2026-06-19T04:19:09Z from: inprogress to: closed reason: closed field: state -->
|
||||||
|
|
||||||
|
## State changed
|
||||||
|
|
||||||
|
Ticket を closed にしました。
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: close author: hare at: 2026-06-19T04:19:09Z status: closed -->
|
||||||
|
|
||||||
|
## 完了
|
||||||
|
|
||||||
|
Implemented and validated.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Reused the initial `load_pod_list` result for Companion and Orchestrator presence in `load_multi_pod_snapshot`, removing two duplicate full Pod status probes before the first dashboard rows render.
|
||||||
|
- Renamed the E2E source timings to `companion.presence.from_initial_list` and `orchestrator.presence.from_initial_list` so regressions show whether the initial list is reused.
|
||||||
|
- Changed Pod list startup summarization to avoid reading active session logs while building initial Pod rows. Stored metadata now uses a cheap active-segment marker and live-only rows keep existing minimal live/pending summaries.
|
||||||
|
- Preserved spawn/restore behavior after Companion/Orchestrator lifecycle changes; if lifecycle changes require reload, the existing reload path remains.
|
||||||
|
|
||||||
|
Live-path measurement in the current workspace:
|
||||||
|
- Before this fix: first non-empty Panel rows appeared at about 7967ms; `pod_metadata_status_probe.initial`, `companion.presence`, and `orchestrator.presence` were each about 2.5s.
|
||||||
|
- After removing duplicate probes only: first non-empty rows appeared at about 2964ms; duplicate presence probes dropped to 0ms but initial Pod metadata/status probe was still about 2386ms.
|
||||||
|
- After also removing session-log reads from the startup Pod summary path: first non-empty rows appeared at about 754ms; `pod_metadata_status_probe.initial` was about 138ms; total dashboard source breakdown was about 649ms.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- cargo test -p tui pod_list --lib
|
||||||
|
- cargo test -p yoi-e2e --features e2e --test panel
|
||||||
|
- cargo check -p yoi-e2e -p yoi -p tui --features tui/e2e-test
|
||||||
|
- cargo build -p yoi
|
||||||
|
- cargo fmt --check
|
||||||
|
- git diff --check
|
||||||
|
- target/debug/yoi ticket doctor
|
||||||
|
- nix build .#yoi --no-link
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
@ -2503,10 +2503,10 @@ async fn load_multi_pod_snapshot(
|
||||||
|
|
||||||
#[cfg(feature = "e2e-test")]
|
#[cfg(feature = "e2e-test")]
|
||||||
let source_started = Instant::now();
|
let source_started = Instant::now();
|
||||||
let companion_presence = load_exact_companion_pod_presence(&companion_pod_name).await?;
|
let companion_presence = companion_pod_presence(&companion_pod_name, &list);
|
||||||
#[cfg(feature = "e2e-test")]
|
#[cfg(feature = "e2e-test")]
|
||||||
source_timings.push(PanelE2eSourceTiming {
|
source_timings.push(PanelE2eSourceTiming {
|
||||||
source: "companion.presence",
|
source: "companion.presence.from_initial_list",
|
||||||
elapsed_ms: source_started.elapsed().as_millis(),
|
elapsed_ms: source_started.elapsed().as_millis(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2557,12 +2557,12 @@ async fn load_multi_pod_snapshot(
|
||||||
let orchestrator_presence = match &config {
|
let orchestrator_presence = match &config {
|
||||||
TicketConfigAvailability::Absent | TicketConfigAvailability::Unusable(_) => None,
|
TicketConfigAvailability::Absent | TicketConfigAvailability::Unusable(_) => None,
|
||||||
TicketConfigAvailability::Usable => {
|
TicketConfigAvailability::Usable => {
|
||||||
Some(load_exact_pod_presence(&orchestrator_pod_name).await?)
|
Some(orchestrator_pod_presence(&orchestrator_pod_name, &list))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#[cfg(feature = "e2e-test")]
|
#[cfg(feature = "e2e-test")]
|
||||||
source_timings.push(PanelE2eSourceTiming {
|
source_timings.push(PanelE2eSourceTiming {
|
||||||
source: "orchestrator.presence",
|
source: "orchestrator.presence.from_initial_list",
|
||||||
elapsed_ms: source_started.elapsed().as_millis(),
|
elapsed_ms: source_started.elapsed().as_millis(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3375,18 +3375,6 @@ fn existing_ticket_claim_notice(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_exact_companion_pod_presence(
|
|
||||||
pod_name: &str,
|
|
||||||
) -> Result<CompanionPodPresence, MultiPodError> {
|
|
||||||
let list = load_pod_list(Some(pod_name.to_string()), usize::MAX).await?;
|
|
||||||
Ok(companion_pod_presence(pod_name, &list))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn load_exact_pod_presence(pod_name: &str) -> Result<OrchestratorPodPresence, MultiPodError> {
|
|
||||||
let list = load_pod_list(Some(pod_name.to_string()), usize::MAX).await?;
|
|
||||||
Ok(orchestrator_pod_presence(pod_name, &list))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn load_pod_list(
|
async fn load_pod_list(
|
||||||
selected_name: Option<String>,
|
selected_name: Option<String>,
|
||||||
max_entries: usize,
|
max_entries: usize,
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ use client::PodClient;
|
||||||
use pod_registry::{LockFileGuard, default_registry_path};
|
use pod_registry::{LockFileGuard, default_registry_path};
|
||||||
use pod_store::{PodActiveSegmentRef, PodMetadata, PodMetadataStore};
|
use pod_store::{PodActiveSegmentRef, PodMetadata, PodMetadataStore};
|
||||||
use protocol::{Event, PodStatus};
|
use protocol::{Event, PodStatus};
|
||||||
use session_store::{
|
use session_store::{FsStore, SegmentId, SessionId};
|
||||||
FsStore, LogEntry, LoggedContentPart, LoggedItem, SegmentId, SessionId, Store,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct PodList {
|
pub(crate) struct PodList {
|
||||||
|
|
@ -27,14 +25,6 @@ impl PodList {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut entries_by_name: BTreeMap<String, PodListEntry> = BTreeMap::new();
|
let mut entries_by_name: BTreeMap<String, PodListEntry> = BTreeMap::new();
|
||||||
|
|
||||||
for live_info in live {
|
|
||||||
let name = live_info.pod_name.clone();
|
|
||||||
entries_by_name
|
|
||||||
.entry(name.clone())
|
|
||||||
.or_insert_with(|| PodListEntry::new(name, source))
|
|
||||||
.merge_live(live_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
for stored_info in stored {
|
for stored_info in stored {
|
||||||
let name = stored_info.pod_name.clone();
|
let name = stored_info.pod_name.clone();
|
||||||
entries_by_name
|
entries_by_name
|
||||||
|
|
@ -43,6 +33,14 @@ impl PodList {
|
||||||
.merge_stored(stored_info);
|
.merge_stored(stored_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for live_info in live {
|
||||||
|
let name = live_info.pod_name.clone();
|
||||||
|
entries_by_name
|
||||||
|
.entry(name.clone())
|
||||||
|
.or_insert_with(|| PodListEntry::new(name, source))
|
||||||
|
.merge_live(live_info);
|
||||||
|
}
|
||||||
|
|
||||||
let mut entries: Vec<PodListEntry> = entries_by_name.into_values().collect();
|
let mut entries: Vec<PodListEntry> = entries_by_name.into_values().collect();
|
||||||
for entry in &mut entries {
|
for entry in &mut entries {
|
||||||
entry.finalize();
|
entry.finalize();
|
||||||
|
|
@ -358,7 +356,7 @@ pub(crate) async fn read_reachable_live_pod_infos(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn probe_reachable_live_pod_infos(
|
async fn probe_reachable_live_pod_infos(
|
||||||
store: &FsStore,
|
_store: &FsStore,
|
||||||
records: Vec<LivePodInfo>,
|
records: Vec<LivePodInfo>,
|
||||||
) -> Result<Vec<LivePodInfo>, io::Error> {
|
) -> Result<Vec<LivePodInfo>, io::Error> {
|
||||||
let mut handles = Vec::with_capacity(records.len());
|
let mut handles = Vec::with_capacity(records.len());
|
||||||
|
|
@ -371,10 +369,9 @@ async fn probe_reachable_live_pod_infos(
|
||||||
let result = handle
|
let result = handle
|
||||||
.await
|
.await
|
||||||
.map_err(|e| io::Error::other(format!("live status probe task failed: {e}")))?;
|
.map_err(|e| io::Error::other(format!("live status probe task failed: {e}")))?;
|
||||||
let Ok(mut record) = result else {
|
let Ok(record) = result else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
record.summary = summarize_live_pod(store, &record);
|
|
||||||
reachable.push(record);
|
reachable.push(record);
|
||||||
}
|
}
|
||||||
Ok(reachable)
|
Ok(reachable)
|
||||||
|
|
@ -462,109 +459,22 @@ struct SegmentSummary {
|
||||||
preview: Option<String>,
|
preview: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn summarize_live_pod(store: &FsStore, live: &LivePodInfo) -> PodEntrySummary {
|
fn summarize_metadata(_store: &FsStore, active: Option<&PodActiveSegmentRef>) -> SegmentSummary {
|
||||||
let Some(segment_id) = live.segment_id else {
|
|
||||||
return PodEntrySummary::default();
|
|
||||||
};
|
|
||||||
let session_id = store.lookup_session_of(segment_id).ok().flatten();
|
|
||||||
let Some(session_id) = session_id else {
|
|
||||||
return PodEntrySummary {
|
|
||||||
active_session_id: None,
|
|
||||||
active_segment_id: Some(segment_id),
|
|
||||||
updated_at: 0,
|
|
||||||
preview: None,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let summary = summarize_segment(store, session_id, segment_id);
|
|
||||||
PodEntrySummary {
|
|
||||||
active_session_id: Some(session_id),
|
|
||||||
active_segment_id: Some(segment_id),
|
|
||||||
updated_at: summary.updated_at,
|
|
||||||
preview: summary.preview,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn summarize_metadata(store: &FsStore, active: Option<&PodActiveSegmentRef>) -> SegmentSummary {
|
|
||||||
let Some(active) = active else {
|
let Some(active) = active else {
|
||||||
return SegmentSummary {
|
return SegmentSummary {
|
||||||
updated_at: 0,
|
updated_at: 0,
|
||||||
preview: None,
|
preview: None,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let Some(segment_id) = active.segment_id else {
|
match active.segment_id {
|
||||||
return SegmentSummary {
|
Some(segment_id) => SegmentSummary {
|
||||||
|
updated_at: 0,
|
||||||
|
preview: Some(format!("active segment {segment_id}")),
|
||||||
|
},
|
||||||
|
None => SegmentSummary {
|
||||||
updated_at: 0,
|
updated_at: 0,
|
||||||
preview: Some("[pending segment]".to_string()),
|
preview: Some("[pending segment]".to_string()),
|
||||||
};
|
|
||||||
};
|
|
||||||
summarize_segment(store, active.session_id, segment_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn summarize_segment(
|
|
||||||
store: &FsStore,
|
|
||||||
session_id: SessionId,
|
|
||||||
segment_id: SegmentId,
|
|
||||||
) -> SegmentSummary {
|
|
||||||
match store.read_all(session_id, segment_id) {
|
|
||||||
Ok(entries) => SegmentSummary {
|
|
||||||
updated_at: last_entry_ts(&entries).unwrap_or(0),
|
|
||||||
preview: last_message_preview(&entries).or_else(|| Some("[empty]".to_string())),
|
|
||||||
},
|
},
|
||||||
Err(_) => SegmentSummary {
|
|
||||||
updated_at: 0,
|
|
||||||
preview: Some("[corrupt segment]".to_string()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last_entry_ts(entries: &[LogEntry]) -> Option<u64> {
|
|
||||||
entries.iter().map(log_entry_ts).max()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log_entry_ts(entry: &LogEntry) -> u64 {
|
|
||||||
match entry {
|
|
||||||
LogEntry::SegmentStart { ts, .. }
|
|
||||||
| LogEntry::Invoke { ts, .. }
|
|
||||||
| LogEntry::UserInput { ts, .. }
|
|
||||||
| LogEntry::AssistantItem { ts, .. }
|
|
||||||
| LogEntry::ToolResult { ts, .. }
|
|
||||||
| LogEntry::SystemItem { ts, .. }
|
|
||||||
| LogEntry::TurnEnd { ts, .. }
|
|
||||||
| LogEntry::RunCompleted { ts, .. }
|
|
||||||
| LogEntry::RunErrored { ts, .. }
|
|
||||||
| LogEntry::ConfigChanged { ts, .. }
|
|
||||||
| LogEntry::LlmUsage { ts, .. }
|
|
||||||
| LogEntry::Extension { ts, .. } => *ts,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last_message_preview(entries: &[LogEntry]) -> Option<String> {
|
|
||||||
for entry in entries.iter().rev() {
|
|
||||||
match entry {
|
|
||||||
LogEntry::UserInput { segments, .. } => {
|
|
||||||
let text = protocol::Segment::flatten_to_text(segments);
|
|
||||||
if !text.is_empty() {
|
|
||||||
return Some(format!("user: {}", trim_one_line(&text, 60)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogEntry::AssistantItem { item, .. } => {
|
|
||||||
if let Some(text) = first_text_logged(item) {
|
|
||||||
return Some(format!("assistant: {}", trim_one_line(&text, 60)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn first_text_logged(item: &LoggedItem) -> Option<String> {
|
|
||||||
match item {
|
|
||||||
LoggedItem::Message { content, .. } => content.iter().find_map(|p| match p {
|
|
||||||
LoggedContentPart::Text { text } => Some(text.clone()),
|
|
||||||
_ => None,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -652,7 +562,7 @@ mod tests {
|
||||||
use pod_store::FsPodStore;
|
use pod_store::FsPodStore;
|
||||||
use pod_store::{PodActiveSegmentRef, PodMetadataStore};
|
use pod_store::{PodActiveSegmentRef, PodMetadataStore};
|
||||||
use protocol::stream::JsonLineWriter;
|
use protocol::stream::JsonLineWriter;
|
||||||
use session_store::{new_segment_id, new_session_id};
|
use session_store::{LogEntry, Store, new_segment_id, new_session_id};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use tokio::net::UnixListener;
|
use tokio::net::UnixListener;
|
||||||
use tokio::sync::Barrier;
|
use tokio::sync::Barrier;
|
||||||
|
|
@ -660,44 +570,35 @@ mod tests {
|
||||||
const SOURCE: PodVisibilitySource = PodVisibilitySource::ResumePicker;
|
const SOURCE: PodVisibilitySource = PodVisibilitySource::ResumePicker;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pod_list_rows_are_sorted_by_active_segment_timestamp() {
|
fn stored_metadata_summary_uses_segment_marker_without_reading_session_log() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
let store = FsStore::new(dir.path()).unwrap();
|
let store = FsStore::new(dir.path()).unwrap();
|
||||||
let earlier_session = new_session_id();
|
let session = new_session_id();
|
||||||
let later_session = new_session_id();
|
let segment = new_segment_id();
|
||||||
let earlier_segment = new_segment_id();
|
|
||||||
let later_segment = new_segment_id();
|
|
||||||
|
|
||||||
append_start(&store, earlier_session, earlier_segment, 10);
|
append_start(&store, session, segment, 10);
|
||||||
append_user(
|
append_user(
|
||||||
&store,
|
&store,
|
||||||
earlier_session,
|
session,
|
||||||
earlier_segment,
|
segment,
|
||||||
100,
|
100,
|
||||||
"old pod update",
|
"session log text should not be scanned",
|
||||||
);
|
);
|
||||||
append_start(&store, later_session, later_segment, 20);
|
|
||||||
append_user(&store, later_session, later_segment, 200, "new pod update");
|
|
||||||
|
|
||||||
let entries = PodList::from_sources(
|
let entry = single_entry(PodList::from_sources(
|
||||||
SOURCE,
|
SOURCE,
|
||||||
vec![
|
vec![metadata_info(&store, "stored", session, segment)],
|
||||||
metadata_info(&store, "older", earlier_session, earlier_segment),
|
|
||||||
metadata_info(&store, "newer", later_session, later_segment),
|
|
||||||
],
|
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
None,
|
||||||
10,
|
10,
|
||||||
)
|
));
|
||||||
.entries;
|
|
||||||
|
|
||||||
assert_eq!(entries[0].name, "newer");
|
assert_eq!(entry.name, "stored");
|
||||||
assert_eq!(entries[0].summary.updated_at, 200);
|
assert_eq!(entry.summary.updated_at, 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
entries[0].summary.preview.as_deref(),
|
entry.summary.preview.as_deref(),
|
||||||
Some("user: new pod update")
|
Some(format!("active segment {segment}").as_str())
|
||||||
);
|
);
|
||||||
assert_eq!(entries[1].name, "older");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user