update: SessionId / SessionStart / SessionOrigin 等を Segment 系名称へ
- Type/Function/Variantを Segment* 系へ統一 - SessionId/SessionStart/SessionOrigin/SessionStartState/SessionState/SessionLogSink/SessionLockInfo - new_session_id / session_id / create_session* / list_sessions / lookup_session / update_session / find_by_session - protocol Event::SessionRotated → SegmentRotated、CompactDone.new_session_id → new_segment_id - Module: session_log → segment_log / session → segment (file mv 含む) pod 側の session_log_sink → segment_log_sink も同様 - crate 名 (session-store)、CLI flag (--session)、ResumeWithSession (CLI tied) は据え置き - session-tests/session_metrics_test 等の Store impl も追従
This commit is contained in:
@@ -4,7 +4,7 @@ use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use manifest::ScopeRule;
|
||||
use session_store::SessionId;
|
||||
use session_store::SegmentId;
|
||||
|
||||
/// Errors raised by the mutating pod-registry operations.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -27,11 +27,11 @@ pub enum ScopeLockError {
|
||||
#[error("pod `{0}` is not registered")]
|
||||
UnknownPod(String),
|
||||
#[error(
|
||||
"session {session_id} is already held by pod `{pod_name}` at {}",
|
||||
"session {segment_id} is already held by pod `{pod_name}` at {}",
|
||||
.socket.display()
|
||||
)]
|
||||
SessionConflict {
|
||||
session_id: SessionId,
|
||||
segment_id: SegmentId,
|
||||
pod_name: String,
|
||||
socket: PathBuf,
|
||||
},
|
||||
|
||||
@@ -27,8 +27,8 @@ pub use conflict::{
|
||||
};
|
||||
pub use error::ScopeLockError;
|
||||
pub use lifecycle::{
|
||||
ScopeAllocationGuard, SessionLockInfo, adopt_allocation, install_top_level,
|
||||
install_top_level_with_deny, lookup_session, update_session,
|
||||
ScopeAllocationGuard, SegmentLockInfo, adopt_allocation, install_top_level,
|
||||
install_top_level_with_deny, lookup_segment, update_segment,
|
||||
};
|
||||
pub use mutate::{
|
||||
delegate_scope, reclaim_stale, reclaim_stale_with, register_pod, register_pod_with_deny,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use manifest::ScopeRule;
|
||||
use session_store::SessionId;
|
||||
use session_store::SegmentId;
|
||||
|
||||
use crate::error::ScopeLockError;
|
||||
use crate::mutate::release_pod;
|
||||
@@ -45,9 +45,9 @@ pub fn install_top_level(
|
||||
pid: u32,
|
||||
socket: PathBuf,
|
||||
scope_allow: Vec<ScopeRule>,
|
||||
session_id: SessionId,
|
||||
segment_id: SegmentId,
|
||||
) -> Result<ScopeAllocationGuard, ScopeLockError> {
|
||||
install_top_level_with_deny(pod_name, pid, socket, scope_allow, Vec::new(), session_id)
|
||||
install_top_level_with_deny(pod_name, pid, socket, scope_allow, Vec::new(), segment_id)
|
||||
}
|
||||
|
||||
/// Open the default lock file, register a top-level Pod with explicit
|
||||
@@ -59,7 +59,7 @@ pub fn install_top_level_with_deny(
|
||||
socket: PathBuf,
|
||||
scope_allow: Vec<ScopeRule>,
|
||||
scope_deny: Vec<ScopeRule>,
|
||||
session_id: SessionId,
|
||||
segment_id: SegmentId,
|
||||
) -> Result<ScopeAllocationGuard, ScopeLockError> {
|
||||
let lock_path = default_registry_path()?;
|
||||
let mut guard = LockFileGuard::open(&lock_path)?;
|
||||
@@ -70,7 +70,7 @@ pub fn install_top_level_with_deny(
|
||||
socket,
|
||||
scope_allow,
|
||||
scope_deny,
|
||||
session_id,
|
||||
segment_id,
|
||||
)?;
|
||||
Ok(ScopeAllocationGuard {
|
||||
pod_name,
|
||||
@@ -83,14 +83,14 @@ pub fn install_top_level_with_deny(
|
||||
///
|
||||
/// The spawning flow is two-stage: the spawner calls
|
||||
/// [`crate::delegate_scope`] (with its own pid as a live placeholder,
|
||||
/// `session_id = None`), then exec's the child; the child, once
|
||||
/// `segment_id = None`), then exec's the child; the child, once
|
||||
/// running, calls this function to rewrite the allocation's pid +
|
||||
/// session_id to its own and claim the [`ScopeAllocationGuard`] so
|
||||
/// segment_id to its own and claim the [`ScopeAllocationGuard`] so
|
||||
/// the entry is released when the child exits.
|
||||
pub fn adopt_allocation(
|
||||
pod_name: String,
|
||||
new_pid: u32,
|
||||
session_id: SessionId,
|
||||
segment_id: SegmentId,
|
||||
) -> Result<ScopeAllocationGuard, ScopeLockError> {
|
||||
let lock_path = default_registry_path()?;
|
||||
let mut guard = LockFileGuard::open(&lock_path)?;
|
||||
@@ -99,7 +99,7 @@ pub fn adopt_allocation(
|
||||
.find_mut(&pod_name)
|
||||
.ok_or_else(|| ScopeLockError::UnknownPod(pod_name.clone()))?;
|
||||
alloc.pid = new_pid;
|
||||
alloc.session_id = Some(session_id);
|
||||
alloc.segment_id = Some(segment_id);
|
||||
guard.save()?;
|
||||
Ok(ScopeAllocationGuard {
|
||||
pod_name,
|
||||
@@ -107,32 +107,32 @@ pub fn adopt_allocation(
|
||||
})
|
||||
}
|
||||
|
||||
/// Rewrite the `session_id` recorded for `pod_name` to
|
||||
/// `new_session_id`.
|
||||
/// Rewrite the `segment_id` recorded for `pod_name` to
|
||||
/// `new_segment_id`.
|
||||
///
|
||||
/// The Pod's in-memory `session_id` can change underneath the
|
||||
/// The Pod's in-memory `segment_id` can change underneath the
|
||||
/// allocation in two normal places:
|
||||
///
|
||||
/// - `Pod::compact` mints a fresh session and swaps it in.
|
||||
/// - `session_store::ensure_head_or_fork` auto-forks when another
|
||||
/// writer has advanced the store head behind our back.
|
||||
///
|
||||
/// Both paths must call this so subsequent [`lookup_session`] queries
|
||||
/// Both paths must call this so subsequent [`lookup_segment`] queries
|
||||
/// find the live session id, not the old one. Without this update a
|
||||
/// concurrent `restore_from_manifest(new_id)` would see "no live
|
||||
/// writer" and proceed to register a competing allocation on the
|
||||
/// session this Pod just moved into.
|
||||
///
|
||||
/// The lock is opened once and the allocation is rewritten inside the
|
||||
/// guard, so the session_id collision check is atomic with the
|
||||
/// guard, so the segment_id collision check is atomic with the
|
||||
/// rewrite.
|
||||
pub fn update_session(pod_name: &str, new_session_id: SessionId) -> Result<(), ScopeLockError> {
|
||||
pub fn update_segment(pod_name: &str, new_segment_id: SegmentId) -> Result<(), ScopeLockError> {
|
||||
let lock_path = default_registry_path()?;
|
||||
let mut guard = LockFileGuard::open(&lock_path)?;
|
||||
if let Some(other) = guard.data().find_by_session(new_session_id) {
|
||||
if let Some(other) = guard.data().find_by_segment(new_segment_id) {
|
||||
if other.pod_name != pod_name {
|
||||
return Err(ScopeLockError::SessionConflict {
|
||||
session_id: new_session_id,
|
||||
segment_id: new_segment_id,
|
||||
pod_name: other.pod_name.clone(),
|
||||
socket: other.socket.clone(),
|
||||
});
|
||||
@@ -142,7 +142,7 @@ pub fn update_session(pod_name: &str, new_session_id: SessionId) -> Result<(), S
|
||||
.data_mut()
|
||||
.find_mut(pod_name)
|
||||
.ok_or_else(|| ScopeLockError::UnknownPod(pod_name.into()))?;
|
||||
alloc.session_id = Some(new_session_id);
|
||||
alloc.segment_id = Some(new_segment_id);
|
||||
guard.save()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -150,25 +150,25 @@ pub fn update_session(pod_name: &str, new_session_id: SessionId) -> Result<(), S
|
||||
/// Information about a Pod that currently holds an allocation for a
|
||||
/// given session.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SessionLockInfo {
|
||||
pub struct SegmentLockInfo {
|
||||
pub pod_name: String,
|
||||
pub socket: PathBuf,
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
/// Open the default lock file, reclaim stale entries, and return the
|
||||
/// allocation currently writing to `session_id`, if any.
|
||||
/// allocation currently writing to `segment_id`, if any.
|
||||
///
|
||||
/// Used by `Pod::restore_from_manifest` to refuse a resume that would
|
||||
/// race a live writer on the same source session.
|
||||
pub fn lookup_session(session_id: SessionId) -> Result<Option<SessionLockInfo>, ScopeLockError> {
|
||||
pub fn lookup_segment(segment_id: SegmentId) -> Result<Option<SegmentLockInfo>, ScopeLockError> {
|
||||
let lock_path = default_registry_path()?;
|
||||
let mut guard = LockFileGuard::open(&lock_path)?;
|
||||
crate::mutate::reclaim_stale(&mut guard);
|
||||
Ok(guard
|
||||
.data()
|
||||
.find_by_session(session_id)
|
||||
.map(|a| SessionLockInfo {
|
||||
.find_by_segment(segment_id)
|
||||
.map(|a| SegmentLockInfo {
|
||||
pod_name: a.pod_name.clone(),
|
||||
socket: a.socket.clone(),
|
||||
pid: a.pid,
|
||||
@@ -193,7 +193,7 @@ mod tests {
|
||||
scope_allow: vec![write_rule("/tmp/child", true)],
|
||||
scope_deny: Vec::new(),
|
||||
delegated_from: None,
|
||||
session_id: None,
|
||||
segment_id: None,
|
||||
});
|
||||
g.save().unwrap();
|
||||
}
|
||||
@@ -267,12 +267,12 @@ mod tests {
|
||||
s,
|
||||
)
|
||||
.unwrap();
|
||||
let info = lookup_session(s).unwrap().expect("expected live writer");
|
||||
let info = lookup_segment(s).unwrap().expect("expected live writer");
|
||||
assert_eq!(info.pod_name, "live");
|
||||
assert_eq!(info.socket, sock("live"));
|
||||
drop(guard);
|
||||
// After the guard's release, the lookup goes back to None.
|
||||
assert!(lookup_session(s).unwrap().is_none());
|
||||
assert!(lookup_segment(s).unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -289,10 +289,10 @@ mod tests {
|
||||
original,
|
||||
)
|
||||
.unwrap();
|
||||
update_session("p", updated).unwrap();
|
||||
update_segment("p", updated).unwrap();
|
||||
// lookup against the original is now empty, the updated id wins.
|
||||
assert!(lookup_session(original).unwrap().is_none());
|
||||
assert_eq!(lookup_session(updated).unwrap().unwrap().pod_name, "p");
|
||||
assert!(lookup_segment(original).unwrap().is_none());
|
||||
assert_eq!(lookup_segment(updated).unwrap().unwrap().pod_name, "p");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -318,15 +318,15 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
// `a` cannot adopt b's live session id.
|
||||
let err = update_session("a", s_b).unwrap_err();
|
||||
let err = update_segment("a", s_b).unwrap_err();
|
||||
match err {
|
||||
ScopeLockError::SessionConflict {
|
||||
pod_name,
|
||||
session_id,
|
||||
segment_id,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(pod_name, "b");
|
||||
assert_eq!(session_id, s_b);
|
||||
assert_eq!(segment_id, s_b);
|
||||
}
|
||||
other => panic!("expected SessionConflict, got {other:?}"),
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use manifest::{Permission, ScopeRule};
|
||||
use session_store::SessionId;
|
||||
use session_store::SegmentId;
|
||||
|
||||
use crate::conflict::{find_conflict_owner, find_conflict_owners, is_within_effective_write};
|
||||
use crate::error::ScopeLockError;
|
||||
@@ -16,7 +16,7 @@ use crate::table::{Allocation, LockFileGuard};
|
||||
/// conflicts so a crashed Pod's allocation doesn't block the new one.
|
||||
///
|
||||
/// Rejects when another live allocation is already writing to
|
||||
/// `session_id`, so two `restore_from_manifest` calls under different
|
||||
/// `segment_id`, so two `restore_from_manifest` calls under different
|
||||
/// `pod_name`s cannot both grab the same session log.
|
||||
pub fn register_pod(
|
||||
guard: &mut LockFileGuard,
|
||||
@@ -24,7 +24,7 @@ pub fn register_pod(
|
||||
pid: u32,
|
||||
socket: PathBuf,
|
||||
scope_allow: Vec<ScopeRule>,
|
||||
session_id: SessionId,
|
||||
segment_id: SegmentId,
|
||||
) -> Result<(), ScopeLockError> {
|
||||
register_pod_with_deny(
|
||||
guard,
|
||||
@@ -33,7 +33,7 @@ pub fn register_pod(
|
||||
socket,
|
||||
scope_allow,
|
||||
Vec::new(),
|
||||
session_id,
|
||||
segment_id,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,15 +56,15 @@ pub fn register_pod_with_deny(
|
||||
socket: PathBuf,
|
||||
scope_allow: Vec<ScopeRule>,
|
||||
scope_deny: Vec<ScopeRule>,
|
||||
session_id: SessionId,
|
||||
segment_id: SegmentId,
|
||||
) -> Result<(), ScopeLockError> {
|
||||
reclaim_stale(guard);
|
||||
if guard.data().find(&pod_name).is_some() {
|
||||
return Err(ScopeLockError::DuplicatePodName(pod_name));
|
||||
}
|
||||
if let Some(existing) = guard.data().find_by_session(session_id) {
|
||||
if let Some(existing) = guard.data().find_by_segment(segment_id) {
|
||||
return Err(ScopeLockError::SessionConflict {
|
||||
session_id,
|
||||
segment_id,
|
||||
pod_name: existing.pod_name.clone(),
|
||||
socket: existing.socket.clone(),
|
||||
});
|
||||
@@ -99,7 +99,7 @@ pub fn register_pod_with_deny(
|
||||
scope_allow,
|
||||
scope_deny,
|
||||
delegated_from: None,
|
||||
session_id: Some(session_id),
|
||||
segment_id: Some(segment_id),
|
||||
});
|
||||
guard.save()?;
|
||||
Ok(())
|
||||
@@ -147,9 +147,9 @@ pub fn delegate_scope(
|
||||
scope_allow,
|
||||
scope_deny: Vec::new(),
|
||||
delegated_from: Some(spawner.into()),
|
||||
// Pre-reservation. The child fills in its own session_id when
|
||||
// Pre-reservation. The child fills in its own segment_id when
|
||||
// it calls `adopt_allocation` after the worker is built.
|
||||
session_id: None,
|
||||
segment_id: None,
|
||||
});
|
||||
guard.save()?;
|
||||
Ok(())
|
||||
@@ -587,7 +587,7 @@ mod tests {
|
||||
shared_session,
|
||||
)
|
||||
.unwrap();
|
||||
// Second registration tries to grab the same session_id under
|
||||
// Second registration tries to grab the same segment_id under
|
||||
// a different pod_name. Without the SessionConflict check both
|
||||
// would succeed and race on the same jsonl.
|
||||
let err = register_pod(
|
||||
@@ -601,11 +601,11 @@ mod tests {
|
||||
.unwrap_err();
|
||||
match err {
|
||||
ScopeLockError::SessionConflict {
|
||||
session_id,
|
||||
segment_id,
|
||||
pod_name,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(session_id, shared_session);
|
||||
assert_eq!(segment_id, shared_session);
|
||||
assert_eq!(pod_name, "first");
|
||||
}
|
||||
other => panic!("expected SessionConflict, got {other:?}"),
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
|
||||
use fs4::fs_std::FileExt;
|
||||
use manifest::{ScopeRule, paths};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use session_store::SessionId;
|
||||
use session_store::SegmentId;
|
||||
|
||||
/// On-disk representation of the allocation table.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
@@ -43,12 +43,12 @@ pub struct Allocation {
|
||||
/// Name of the Pod that delegated scope to this one, or `None` for
|
||||
/// a top-level Pod started directly by a human.
|
||||
pub delegated_from: Option<String>,
|
||||
/// Session ID this Pod is currently writing to. `None` means this
|
||||
/// Segment ID this Pod is currently writing to. `None` means this
|
||||
/// is a pre-reservation made by a spawner via [`crate::delegate_scope`]
|
||||
/// before the child has come up; the child fills it in at
|
||||
/// [`crate::adopt_allocation`] time.
|
||||
#[serde(default)]
|
||||
pub session_id: Option<SessionId>,
|
||||
pub segment_id: Option<SegmentId>,
|
||||
}
|
||||
|
||||
impl LockFile {
|
||||
@@ -60,12 +60,12 @@ impl LockFile {
|
||||
self.allocations.iter_mut().find(|a| a.pod_name == pod_name)
|
||||
}
|
||||
|
||||
/// Find the allocation currently writing to `session_id`. Skips
|
||||
/// pre-reservations whose `session_id` is still `None`.
|
||||
pub fn find_by_session(&self, session_id: SessionId) -> Option<&Allocation> {
|
||||
/// Find the allocation currently writing to `segment_id`. Skips
|
||||
/// pre-reservations whose `segment_id` is still `None`.
|
||||
pub fn find_by_segment(&self, segment_id: SegmentId) -> Option<&Allocation> {
|
||||
self.allocations
|
||||
.iter()
|
||||
.find(|a| a.session_id == Some(session_id))
|
||||
.find(|a| a.segment_id == Some(segment_id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,8 +225,8 @@ mod tests {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let path = dir.path().join("pods.json");
|
||||
let mut g = open_empty(&path);
|
||||
// Pre-reservation: delegate_scope leaves session_id = None
|
||||
// until adopt_allocation rewrites it. find_by_session must not
|
||||
// Pre-reservation: delegate_scope leaves segment_id = None
|
||||
// until adopt_allocation rewrites it. find_by_segment must not
|
||||
// match those placeholders, otherwise a freshly-spawning child
|
||||
// would shadow itself before it has even chosen a session.
|
||||
register_pod(
|
||||
@@ -249,13 +249,13 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let target_session = sid();
|
||||
// The placeholder allocation has session_id = None and must
|
||||
// The placeholder allocation has segment_id = None and must
|
||||
// not be returned for any lookup.
|
||||
assert!(g.data().find_by_session(target_session).is_none());
|
||||
assert!(g.data().find_by_segment(target_session).is_none());
|
||||
|
||||
// After adopt-style rewrite, the same allocation is now found.
|
||||
g.data_mut().find_mut("child").unwrap().session_id = Some(target_session);
|
||||
let found = g.data().find_by_session(target_session).unwrap();
|
||||
g.data_mut().find_mut("child").unwrap().segment_id = Some(target_session);
|
||||
let found = g.data().find_by_segment(target_session).unwrap();
|
||||
assert_eq!(found.pod_name, "child");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ use std::path::{Path, PathBuf};
|
||||
use std::sync::{LazyLock, Mutex, MutexGuard};
|
||||
|
||||
use manifest::{Permission, ScopeRule};
|
||||
use session_store::SessionId;
|
||||
use session_store::SegmentId;
|
||||
|
||||
use crate::table::LockFileGuard;
|
||||
|
||||
pub(crate) fn sid() -> SessionId {
|
||||
session_store::new_session_id()
|
||||
pub(crate) fn sid() -> SegmentId {
|
||||
session_store::new_segment_id()
|
||||
}
|
||||
|
||||
/// Serialises tests that mutate runtime-dir env vars. The test
|
||||
|
||||
Reference in New Issue
Block a user