mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
feat: note how far each account's sync caught up
This commit is contained in:
1 parent
ce44504ba9
commit
a38c77eb06
7 files changed
+49
-5
No files matched your search
@@ -48,6 +48,7 @@ const MIGRATIONS: &[Migration] = &[
|
||||
Migration::Plain(include_str!("migrations/029_mail_transfers.sql")),
|
||||
Migration::Plain(include_str!("migrations/030_folder_server_only_unseen.sql")),
|
||||
Migration::Plain(include_str!("migrations/031_notification_tracking.sql")),
|
||||
Migration::Plain(include_str!("migrations/032_account_caught_up_at.sql")),
|
||||
];
|
||||
pub const LATEST_VERSION: i64 = MIGRATIONS.len() as i64;
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- When the account's last complete sync started: everything its server had
|
||||
-- received by then is stored here. Mail that arrives after it, while no copy
|
||||
-- of the app is following the account, is what a check in the background
|
||||
-- tells the user about.
|
||||
ALTER TABLE accounts ADD COLUMN caught_up_at INTEGER;
|
||||
@@ -555,6 +555,16 @@ pub fn update_sync_state_with_error(
|
||||
)? != 0)
|
||||
}
|
||||
|
||||
/// Note that a sync of the account that started at `started` stored all the
|
||||
/// mail its Inbox had by then.
|
||||
pub fn set_caught_up(conn: &Connection, id: i64, started: i64) -> Result<()> {
|
||||
conn.execute(
|
||||
"UPDATE accounts SET caught_up_at = ?2 WHERE id = ?1",
|
||||
params![id, started],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Move one account before or after another and compact the persisted order.
|
||||
/// A complete rewrite keeps ordering deterministic after any number of moves
|
||||
/// and avoids fragile fractional positions.
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::events::CoreEvent;
|
||||
use crate::jmap::client::{self, ConnectedClient};
|
||||
use crate::models::{AccountConfig, Address, MailProtocol, SenderDeliveryStatus, now_ms, roles};
|
||||
use crate::sync::engine::{
|
||||
PriorityFetchCmd, SyncCmd, SyncCtx, configured_sync_interval, set_state, set_state_error,
|
||||
PriorityFetchCmd, SyncCmd, SyncCtx, caught_up, configured_sync_interval, set_state,
|
||||
set_state_error,
|
||||
};
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use jmap_client::core::error::MethodErrorType;
|
||||
@@ -122,9 +123,11 @@ async fn actor(
|
||||
),
|
||||
}
|
||||
}
|
||||
let started = now_ms();
|
||||
set_state(&ctx, config.id, "syncing").await;
|
||||
sync_mailboxes(&ctx, config.id, &connected).await?;
|
||||
sync_emails(&ctx, &config, &connected, live_since).await?;
|
||||
caught_up(&ctx, config.id, started).await;
|
||||
execute_actions(&ctx, &config, &connected).await?;
|
||||
// The counts only explain the badges, so they never fail a sync.
|
||||
if let Err(error) = sync_mailbox_counts(&ctx, config.id, &connected).await {
|
||||
|
||||
@@ -1280,6 +1280,20 @@ pub(crate) async fn set_state_error(ctx: &SyncCtx, account_id: i64, state: &str,
|
||||
set_state_inner(ctx, account_id, state, Some(error)).await;
|
||||
}
|
||||
|
||||
/// Note that a sync of the account that started at `started` stored all the
|
||||
/// mail its Inbox had by then. A check in the background tells of what
|
||||
/// arrives after it while the app isn't running (see
|
||||
/// `Core::announce_missed`).
|
||||
pub(crate) async fn caught_up(ctx: &SyncCtx, account_id: i64, started: i64) {
|
||||
let noted = ctx
|
||||
.db
|
||||
.write(move |conn| repo::accounts::set_caught_up(conn, account_id, started))
|
||||
.await;
|
||||
if let Err(error) = noted {
|
||||
tracing::warn!(account_id, %error, "could not note how far a sync caught up");
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_state_inner(ctx: &SyncCtx, account_id: i64, state: &str, error: Option<&str>) {
|
||||
let st = state.to_string();
|
||||
let error = error.map(str::to_owned);
|
||||
@@ -1469,6 +1483,7 @@ async fn run_cycle(
|
||||
inbox_pushed,
|
||||
} = mode;
|
||||
let account_id = config.id;
|
||||
let started = now_ms();
|
||||
|
||||
// The folder list: first thing, then once it is FOLDER_LIST_INTERVAL old.
|
||||
let listed_now = !immediate_follow_up && listing.due(tokio::time::Instant::now());
|
||||
@@ -1545,6 +1560,9 @@ async fn run_cycle(
|
||||
inbox_baseline,
|
||||
)
|
||||
.await?;
|
||||
if !inbox_forward_remaining {
|
||||
caught_up(ctx, account_id, started).await;
|
||||
}
|
||||
}
|
||||
|
||||
// A manual Sync Now resolves at the foreground Inbox boundary. Finish any
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
//! pending-action queue remain provider-neutral and fully offline-first.
|
||||
|
||||
use super::engine::{
|
||||
PriorityFetchCmd, SyncCmd, SyncCtx, configured_sync_interval, emit_sync_status, set_state,
|
||||
set_state_error,
|
||||
PriorityFetchCmd, SyncCmd, SyncCtx, caught_up, configured_sync_interval, emit_sync_status,
|
||||
set_state, set_state_error,
|
||||
};
|
||||
use crate::db::repo::{self, messages::NewMessage};
|
||||
use crate::error::{CoreError, Result};
|
||||
@@ -2556,6 +2556,7 @@ async fn sync_once(
|
||||
live_since: i64,
|
||||
) -> Result<bool> {
|
||||
let account_id = config.id;
|
||||
let started = now_ms();
|
||||
let mut state = ctx
|
||||
.db
|
||||
.read(move |conn| repo::gmail::state(conn, account_id))
|
||||
@@ -2764,6 +2765,10 @@ async fn sync_once(
|
||||
if page.next_page_token.is_none() && !page.affected_threads.is_empty() {
|
||||
sync_draft_map(ctx, config, api).await?;
|
||||
}
|
||||
if page.next_page_token.is_none() {
|
||||
// The history ran out: everything up to this pass is stored.
|
||||
caught_up(ctx, account_id, started).await;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
//! was.
|
||||
|
||||
use super::engine::{
|
||||
PriorityFetchCmd, RestoreRequest, SyncCmd, SyncCtx, configured_sync_interval, set_state,
|
||||
set_state_error,
|
||||
PriorityFetchCmd, RestoreRequest, SyncCmd, SyncCtx, caught_up, configured_sync_interval,
|
||||
set_state, set_state_error,
|
||||
};
|
||||
use super::ingest::{self, Delivery, Importer};
|
||||
use crate::accounts::credentials::{self, Slot};
|
||||
@@ -75,6 +75,7 @@ pub(crate) async fn run(ctx: SyncCtx, mut config: AccountConfig, mut inputs: Inp
|
||||
tracing::warn!(account_id, %error, "completing local POP3 actions failed");
|
||||
}
|
||||
if next_sync.is_some_and(|at| at <= Instant::now()) {
|
||||
let started = now_ms();
|
||||
next_sync = match sync(&ctx, &config, &mut catch_up).await {
|
||||
Ok(synced) => {
|
||||
backoff = Duration::from_secs(1);
|
||||
@@ -83,6 +84,7 @@ pub(crate) async fn run(ctx: SyncCtx, mut config: AccountConfig, mut inputs: Inp
|
||||
ctx.bus.emit(CoreEvent::NetworkState { online: true });
|
||||
let next = match synced {
|
||||
Synced::Complete => {
|
||||
caught_up(&ctx, account_id, started).await;
|
||||
finish(&mut waiters, Ok(()));
|
||||
Instant::now() + configured_sync_interval(&ctx.db).await
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user