better sync progress reporting

This commit is contained in:
olcxja 2026-10-06 08:11:31 +02:00
commit d4996988d9
3 changed files with 34 additions and 16 deletions

View file

@ -112,7 +112,7 @@ function createPusher(api) {
})
}
function putFile(device, token, unitKey, rel, abs) {
function putFile(device, token, unitKey, rel, abs, onBytes) {
return new Promise((resolve, reject) => {
const settle = (fn, value) => {
clearTimeout(timer)
@ -151,6 +151,9 @@ function createPusher(api) {
req.on('error', err => settle(reject, err))
const stream = fs.createReadStream(abs)
stream.on('error', err => settle(reject, err))
// live bytes: a multi-GB file has to move the counter while it flies,
// not only when its response lands (read = what this side hands over)
if (onBytes) stream.on('data', chunk => onBytes(chunk.length))
// pipe ends the request when the file ends; a body shorter than the
// declared content-length (file changed mid-sync) wedges the receiver
// - its own idle watchdog breaks that, this side's timer breaks every
@ -366,9 +369,12 @@ function createPusher(api) {
}
return data
}
// snapshot per file, forward a few times per second (phase changes always)
const gate = typeof api.progressGate === 'function' ? api.progressGate(progress) : data => progress(data)
const emit = (phase, unit, done, total, force) => gate(snapshot(phase, unit, done, total), !!force)
// Snapshot per file, forward a few times per second (phase changes
// always). The snapshot is built when the gate flushes, not when it is
// pushed: a throttled byte count must not be one gate window old.
const emitNow = data => progress(typeof data === 'function' ? data() : data)
const gate = typeof api.progressGate === 'function' ? api.progressGate(emitNow) : emitNow
const emit = (phase, unit, done, total, force) => gate(() => snapshot(phase, unit, done, total), !!force)
// consent may wait on a human: say so before anything else happens
emit('wait', null, null, null, true)
@ -488,15 +494,19 @@ function createPusher(api) {
let done = 0
const send = async entry => {
if (failed || abort.dead) return
const res = await putFile(device, token, unit.key, entry.path, filesPath(entry.path))
const res = await putFile(device, token, unit.key, entry.path, filesPath(entry.path), delta => {
summary.bytes += delta
barBytes += delta
emit('copy', unit, allDone + done, allTotal + pending.length)
})
if (res.status !== 200) {
failed = new Error(`upload-failed-${res.status}`)
return
}
done++
summary.uploaded++
summary.bytes += entry.size || 0
barBytes += entry.size || 0
// the byte counters already moved while the file was read; the
// completion still has to push the file count next
emit('copy', unit, allDone + done, allTotal + pending.length)
}
if (typeof api.mapLimit === 'function') await api.mapLimit(pending, UPLOAD_CONCURRENCY, send)