mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 00:47:00 +01:00
Backend (docs/UI_UX_PLAN.md B-1, B-2):
- Request::SetSyncPaused { paused, until } pauses the drain worker and the
mirror reconcile. FUSE reads, write staging, the watcher and the poll keep
running. The pause is persisted in the `state` table (`sync_paused_until`),
so it survives a restart; a timed pause ends by itself through a timer
thread, re-armed on mount. Resuming wakes the drain and runs a full
reconcile. Response::Status gains `paused` and `paused_until`.
- Request::ListPendingOps lists the queue with paths, attempts, last error,
next attempt and parked/failing flags. Request::RetryPendingOp { id } makes
one backed-off op (or every failed one) due now. Parked ops are never
un-parked and nothing is dropped from the queue.
Front ends:
- Sync page: a status card with Pause/Resume (split button with 1 h, 8 h and
24 h pauses) and a "Waiting to Upload" list with Retry now and Retry All.
- The sidebar strip and the tray show the paused state; the tray gains Pause
Sync / Resume Sync and names failing changes.
- CLI: `pdfs sync pause [--for 30m|1h|2d]`, `pdfs sync resume`,
`pdfs sync queue`, `pdfs sync retry [id]`; `pdfs status` reports a pause.
Not in this phase: merging Activity into the Sync page, discarding a queued
op (needs a recovery copy first), and per-folder pause.
61 lines
2.2 KiB
Rust
61 lines
2.2 KiB
Rust
//! The user's view of the pending-op queue: what has not reached the remote
|
|
//! yet, why, and a way to stop waiting out a backoff.
|
|
//!
|
|
//! Read-only apart from "retry now", which only moves a row's next-attempt time.
|
|
//! Nothing here drops a queued op: its staged blob is the only copy of the
|
|
//! user's write (see `docs/RECOVERY.md`).
|
|
|
|
use super::*;
|
|
use pdfs_core::control::PendingOpInfo;
|
|
use pdfs_core::db::{FAILING_ATTEMPTS, PARK_UNTIL};
|
|
|
|
impl Core {
|
|
/// Every queued op, oldest first, with a path a person can read.
|
|
pub(crate) fn pending_op_infos(&self) -> CoreResult<Vec<PendingOpInfo>> {
|
|
let ops = self
|
|
.db
|
|
.pending_ops()
|
|
.map_err(|e| CoreError::internal(format!("listing the queue: {e}")))?;
|
|
Ok(ops
|
|
.into_iter()
|
|
.map(|op| {
|
|
let parked = op.next_attempt_at >= PARK_UNTIL;
|
|
let path = self
|
|
.db
|
|
.node_path(&op.uid)
|
|
.ok()
|
|
.flatten()
|
|
.filter(|path| !path.is_empty())
|
|
.or_else(|| op.name.clone())
|
|
.unwrap_or_else(|| op.uid.clone());
|
|
PendingOpInfo {
|
|
id: op.id,
|
|
kind: op.kind,
|
|
path,
|
|
attempts: op.attempts,
|
|
last_error: op.last_error,
|
|
queued_at: op.created_at / 1000,
|
|
next_attempt_at: (!parked).then_some(op.next_attempt_at / 1000),
|
|
parked,
|
|
failing: op.attempts >= FAILING_ATTEMPTS,
|
|
}
|
|
})
|
|
.collect())
|
|
}
|
|
|
|
/// Retry one backed-off op now, or every failed one when `id` is `None`.
|
|
/// Returns how many ops were moved up.
|
|
pub(crate) fn retry_pending_ops(&self, id: Option<i64>) -> CoreResult<usize> {
|
|
let now = now_millis();
|
|
let moved = match id {
|
|
Some(id) => self.db.retry_op_now(id, now).map(usize::from),
|
|
None => self.db.retry_failed_ops_now(now),
|
|
}
|
|
.map_err(|e| CoreError::internal(format!("retrying the queue: {e}")))?;
|
|
if moved > 0 {
|
|
self.wake_drain();
|
|
}
|
|
Ok(moved)
|
|
}
|
|
}
|