mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-13 09:27:03 +01:00
feat(core,fuse,gui,cli): pause/resume sync and a visible upload queue (phase 2)
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.
This commit is contained in:
1 parent
cf9e1665ca
commit
067561564e
17 files changed
+995
-13
No files matched your search
@@ -0,0 +1,60 @@
|
||||
//! 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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user