fix: reconcile interrupted upload pins

This commit is contained in:
2026-09-06 03:15:44 +09:00
parent 8e4b7deaa4
commit 9d55ce0a87
5 changed files with 134 additions and 3 deletions
+14 -1
View File
@@ -23,7 +23,8 @@ use crate::uploaded_file::{
bind_uploaded_file, clear_uploaded_file_binding, copy_committed_uploaded_files,
delete_uncommitted_uploaded_files, delete_uploaded_file, finalize_uploaded_file_binding,
list_uploaded_file_refs, pin_uploaded_file, read_uploaded_file, read_uploaded_file_by_id,
release_uploaded_file_pin, uploaded_file_has_pending_owner, write_uploaded_file,
reconcile_uploaded_file_pins, release_uploaded_file_pin, uploaded_file_has_pending_owner,
write_uploaded_file,
};
use crate::{
PasteArtifactLimits, SegmentId, SessionId, UploadedFileLimits, UploadedFileUploadContext,
@@ -562,6 +563,18 @@ impl Store for FsStore {
)
}
fn reconcile_uploaded_file_pins(
&self,
session_id: SessionId,
live_owner_ids: &[String],
) -> Result<u64, StoreError> {
let _guard = self
.append_lock
.lock()
.map_err(|_| std::io::Error::other("session store append lock was poisoned"))?;
reconcile_uploaded_file_pins(&self.paste_artifact_dir(session_id), live_owner_ids)
}
fn delete_uploaded_file(
&self,
session_id: SessionId,
+11
View File
@@ -256,6 +256,17 @@ pub trait Store: Send + Sync {
Err(StoreError::PasteArtifactUnsupported)
}
/// Clear pending-operation pins that have no owner in restored durable
/// Worker Session state. This repairs an interrupted pin-before-checkpoint
/// acceptance without disturbing live queue owners or committed history.
fn reconcile_uploaded_file_pins(
&self,
_session_id: SessionId,
_live_owner_ids: &[String],
) -> Result<u64, StoreError> {
Ok(0)
}
/// Delete an uncommitted uploaded file owned by `session_id`.
fn delete_uploaded_file(
&self,
+34
View File
@@ -586,6 +586,40 @@ pub(crate) fn copy_committed_uploaded_files(source_dir: &Path, target_dir: &Path
Ok(copied)
}
pub(crate) fn reconcile_uploaded_file_pins(dir: &Path, live_owner_ids: &[String]) -> Result<u64> {
fs::create_dir_all(dir)?;
let aggregate_lock = fs::OpenOptions::new()
.create(true)
.read(true)
.write(true)
.open(dir.join(".aggregate.lock"))?;
FileExt::lock_exclusive(&aggregate_lock)?;
let mut reconciled = 0_u64;
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else {
continue;
};
let Some(artifact_id) = file_name.strip_suffix(".file.json") else {
continue;
};
let mut stored: StoredUploadedFile = serde_json::from_slice(&fs::read(&path)?)?;
let Some(owner_id) = stored.pending_owner_id.as_deref() else {
continue;
};
if live_owner_ids.iter().any(|live| live == owner_id) {
continue;
}
stored.pending_owner_id = None;
let temp = dir.join(format!(".{artifact_id}.file.reconcile.tmp"));
fs::write(&temp, serde_json::to_vec(&stored)?)?;
fs::rename(temp, path)?;
reconciled = reconciled.saturating_add(1);
}
Ok(reconciled)
}
pub(crate) fn delete_uncommitted_uploaded_files(dir: &Path) -> Result<u64> {
fs::create_dir_all(dir)?;
let aggregate_lock = fs::OpenOptions::new()