mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
fix: shared reader locks, resolved lock paths, install-only sync lock
Third Codex (gpt-6.1-sol, xhigh) pass over 1e55138, each item verified
before fixing:
- readers now hold the ownership lock shared for the life of their
connection, so a snapshot swap (exclusive) never happens under an
open SQLite connection whose WAL cleanup could hit a later import.
- the lock file name uses the resolved database path, so a symlinked
path shares the real database's lock; sync also installs at the
resolved path, keeping a symlinked path a symlink (it used to be
overwritten with a regular file).
- sync downloads without the lock and takes it only to recheck and
install; snapshot downloads have connect (15 s) and total (10 min)
timeouts. idx-mcp drops its in-process RwLock: idx-core's lock covers
it across processes.
- XLSX parsing caps rows at 1,000,000 and checks a shared string's size
before copying it.
- the temp snapshot's -wal/-shm files are removed instead of left in the
data directory.
Remaining, by design: `changes` loads all rows of two releases from the
local database before filtering; its size is bounded by the snapshot.
CLI goldens unchanged. Live A/B vs 0.2.4: 16 passed; discover hits the
IDX 403 on both builds (exit 4 vs 1, intended).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1e551385d0
commit
5e0831d8fe
4 changed files with 133 additions and 51 deletions
|
|
@ -15,7 +15,7 @@ use rmcp::model::{CallToolResult, Implementation, ServerCapabilities, ServerConf
|
|||
use rmcp::{ErrorData, ServerHandler, tool, tool_handler, tool_router};
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map, Value, json};
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::params::{
|
||||
AnalysisKind, AnalyzeParams, ChangesParams, CompanyParams, CompanySection, EntityParams,
|
||||
|
|
@ -36,9 +36,6 @@ const MAX_CHANGES_LIMIT: usize = 2000;
|
|||
|
||||
/// Tool calls running at once; the rest wait their turn.
|
||||
static CALL_SLOTS: Semaphore = Semaphore::const_new(4);
|
||||
/// `ownership_sync` replaces the database file: it runs alone, while
|
||||
/// ownership reads may run together.
|
||||
static OWNERSHIP: RwLock<()> = RwLock::const_new(());
|
||||
|
||||
const INSTRUCTIONS: &str = "Indonesian stock market (IDX) data. Market and financial \
|
||||
tools fetch live data (cached on disk) and wrap it as {data, source, fetched_at, stale}; \
|
||||
|
|
@ -92,7 +89,6 @@ impl IdxServer {
|
|||
} else {
|
||||
"run `idx ownership sync`, or restart idx-mcp with --allow-sync"
|
||||
};
|
||||
let _read = OWNERSHIP.read().await;
|
||||
blocking(move || {
|
||||
if !investor.status()?.populated {
|
||||
return Err(IdxError::NoMarketData(format!(
|
||||
|
|
@ -323,7 +319,6 @@ KSEI release dates."
|
|||
)]
|
||||
async fn ownership_status(&self) -> ToolResult {
|
||||
let investor = Investor::new(&self.config);
|
||||
let _read = OWNERSHIP.read().await;
|
||||
blocking(move || investor.status()).await
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +329,7 @@ the local database."
|
|||
async fn ownership_sync(&self, params: Parameters<SyncParams>) -> ToolResult {
|
||||
let SyncParams { force } = params.0;
|
||||
let investor = Investor::new(&self.config);
|
||||
let _write = OWNERSHIP.write().await;
|
||||
// idx-core's database lock keeps the swap away from open readers.
|
||||
blocking(move || investor.sync(None, force)).await
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue