mirror of
https://github.com/thegeneralist01/archivr
synced 2026-07-21 18:55:36 +02:00
fix(core+frontend): exclude avatar blobs from tweet % cached display
tweet/tweet_thread entries have avatar artifacts that are always deduplicated from the first capture of each author. Counting them in the cached-bytes percentage makes it artificially high (or incorrect when the real media content is new but avatars are cached). database.rs: - refresh_entry_cached_bytes: AND ea.artifact_role != 'avatar' - cascade_cached_bytes_after_delete: same filter - cascade_cached_bytes_after_subtree_delete: same filter - Initial cached_bytes migration: same filter - Re-migration (else branch): recomputes cached_bytes for existing entries that have avatar artifacts, scoped to only those entries archive.rs: - EntrySummary gains cacheable_bytes: i64 — non-avatar total bytes, computed inline in every SQL query as the denominator for % cached - ENTRY_SELECT_COLS adds cacheable_bytes at index 13 (with children subquery, same as total_artifact_bytes) - list_root_entries adds same expression - All 6 row-mapping closures include cacheable_bytes: row.get(13)? EntryRow.jsx: - SIZE display stays: formatBytes(entry.total_artifact_bytes) - % cached badge uses cacheable_bytes as denominator: cached_bytes / cacheable_bytes * 100
This commit is contained in:
parent
916146b4a8
commit
b8cec96256
5 changed files with 49 additions and 7 deletions
|
|
@ -32,6 +32,8 @@ pub struct EntrySummary {
|
|||
pub cached_bytes: i64,
|
||||
/// Number of direct child entries; 0 for non-container entries.
|
||||
pub child_count: i64,
|
||||
/// Total non-avatar artifact bytes (query-time; used as denominator for cache-hit %).
|
||||
pub cacheable_bytes: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
|
||||
|
|
@ -218,7 +220,8 @@ pub fn list_root_entries(
|
|||
NULL AS parent_entry_uid,
|
||||
EXISTS(SELECT 1 FROM entry_artifacts fav WHERE fav.entry_id = e.id AND fav.artifact_role = 'favicon') AS has_favicon,
|
||||
e.cached_bytes,
|
||||
(SELECT COUNT(*) FROM archived_entries child WHERE child.parent_entry_id = e.id) AS child_count
|
||||
(SELECT COUNT(*) FROM archived_entries child WHERE child.parent_entry_id = e.id) AS child_count,
|
||||
COALESCE(SUM(CASE WHEN ea.artifact_role != 'avatar' THEN b.byte_size ELSE 0 END), 0) + COALESCE((SELECT SUM(b2.byte_size) FROM archived_entries c2 JOIN entry_artifacts ea2 ON ea2.entry_id = c2.id JOIN blobs b2 ON b2.id = ea2.blob_id WHERE c2.parent_entry_id = e.id), 0) AS cacheable_bytes
|
||||
FROM archived_entries e
|
||||
JOIN source_identities si ON si.id = e.source_identity_id
|
||||
LEFT JOIN entry_artifacts ea ON ea.entry_id = e.id
|
||||
|
|
@ -252,6 +255,7 @@ pub fn list_root_entries(
|
|||
has_favicon: row.get::<_, i64>(10)? != 0,
|
||||
cached_bytes: row.get(11)?,
|
||||
child_count: row.get(12)?,
|
||||
cacheable_bytes: row.get(13)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
|
@ -286,6 +290,7 @@ fn get_entry_summary(
|
|||
has_favicon: row.get::<_, i64>(10)? != 0,
|
||||
cached_bytes: row.get(11)?,
|
||||
child_count: row.get(12)?,
|
||||
cacheable_bytes: row.get(13)?,
|
||||
})
|
||||
})
|
||||
.optional()?;
|
||||
|
|
@ -474,6 +479,7 @@ pub fn list_entries_for_collection(
|
|||
has_favicon: row.get::<_, i64>(10)? != 0,
|
||||
cached_bytes: row.get(11)?,
|
||||
child_count: row.get(12)?,
|
||||
cacheable_bytes: row.get(13)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
|
@ -522,6 +528,7 @@ pub fn list_child_entries(
|
|||
has_favicon: row.get::<_, i64>(10)? != 0,
|
||||
cached_bytes: row.get(11)?,
|
||||
child_count: row.get(12)?,
|
||||
cacheable_bytes: row.get(13)?,
|
||||
})
|
||||
},
|
||||
)?
|
||||
|
|
@ -693,7 +700,8 @@ const ENTRY_SELECT_COLS: &str = "SELECT e.entry_uid, e.archived_at, e.source_kin
|
|||
parent.entry_uid AS parent_entry_uid, \
|
||||
EXISTS(SELECT 1 FROM entry_artifacts fav WHERE fav.entry_id = e.id AND fav.artifact_role = 'favicon') AS has_favicon, \
|
||||
e.cached_bytes, \
|
||||
(SELECT COUNT(*) FROM archived_entries child WHERE child.parent_entry_id = e.id) AS child_count";
|
||||
(SELECT COUNT(*) FROM archived_entries child WHERE child.parent_entry_id = e.id) AS child_count, \
|
||||
COALESCE(SUM(CASE WHEN ea.artifact_role != 'avatar' THEN b.byte_size ELSE 0 END), 0) + COALESCE((SELECT SUM(b2.byte_size) FROM archived_entries c2 JOIN entry_artifacts ea2 ON ea2.entry_id = c2.id JOIN blobs b2 ON b2.id = ea2.blob_id WHERE c2.parent_entry_id = e.id), 0) AS cacheable_bytes";
|
||||
|
||||
const ENTRY_FROM_JOINS: &str = "FROM archived_entries e \
|
||||
JOIN source_identities si ON si.id = e.source_identity_id \
|
||||
|
|
@ -802,6 +810,7 @@ pub fn search_entries(
|
|||
has_favicon: row.get::<_, i64>(10)? != 0,
|
||||
cached_bytes: row.get(11)?,
|
||||
child_count: row.get(12)?,
|
||||
cacheable_bytes: row.get(13)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
|
@ -1010,7 +1019,8 @@ pub fn entries_for_tag(
|
|||
parent.entry_uid AS parent_entry_uid,
|
||||
EXISTS(SELECT 1 FROM entry_artifacts fav WHERE fav.entry_id = e.id AND fav.artifact_role = 'favicon') AS has_favicon,
|
||||
e.cached_bytes,
|
||||
(SELECT COUNT(*) FROM archived_entries child WHERE child.parent_entry_id = e.id) AS child_count
|
||||
(SELECT COUNT(*) FROM archived_entries child WHERE child.parent_entry_id = e.id) AS child_count,
|
||||
COALESCE(SUM(CASE WHEN ea.artifact_role != 'avatar' THEN b.byte_size ELSE 0 END), 0) AS cacheable_bytes
|
||||
FROM archived_entries e
|
||||
JOIN source_identities si ON si.id = e.source_identity_id
|
||||
LEFT JOIN entry_artifacts ea ON ea.entry_id = e.id
|
||||
|
|
@ -1037,6 +1047,7 @@ pub fn entries_for_tag(
|
|||
has_favicon: row.get::<_, i64>(10)? != 0,
|
||||
cached_bytes: row.get(11)?,
|
||||
child_count: row.get(12)?,
|
||||
cacheable_bytes: row.get(13)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
|
|
|||
|
|
@ -390,6 +390,7 @@ pub fn initialize_schema(conn: &Connection) -> Result<()> {
|
|||
JOIN blobs b ON b.id = ea.blob_id
|
||||
WHERE ea.entry_id = archived_entries.id
|
||||
AND ea.blob_id IS NOT NULL
|
||||
AND ea.artifact_role != 'avatar'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM entry_artifacts ea2
|
||||
|
|
@ -401,6 +402,33 @@ pub fn initialize_schema(conn: &Connection) -> Result<()> {
|
|||
)
|
||||
);",
|
||||
)?;
|
||||
} else {
|
||||
// Re-migration: strip avatar blobs from cached_bytes on entries that
|
||||
// already had the column populated before this filter was introduced.
|
||||
// Scoped to entries with avatar artifacts only; fast and idempotent.
|
||||
conn.execute_batch(
|
||||
"UPDATE archived_entries
|
||||
SET cached_bytes = (
|
||||
SELECT COALESCE(SUM(b.byte_size), 0)
|
||||
FROM entry_artifacts ea
|
||||
JOIN blobs b ON b.id = ea.blob_id
|
||||
WHERE ea.entry_id = archived_entries.id
|
||||
AND ea.blob_id IS NOT NULL
|
||||
AND ea.artifact_role != 'avatar'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM entry_artifacts ea2
|
||||
JOIN archived_entries e2 ON e2.id = ea2.entry_id
|
||||
WHERE ea2.blob_id = ea.blob_id
|
||||
AND (e2.archived_at < archived_entries.archived_at
|
||||
OR (e2.archived_at = archived_entries.archived_at
|
||||
AND e2.id < archived_entries.id))
|
||||
)
|
||||
)
|
||||
WHERE id IN (
|
||||
SELECT DISTINCT entry_id FROM entry_artifacts WHERE artifact_role = 'avatar'
|
||||
);",
|
||||
)?;
|
||||
}
|
||||
|
||||
// Migration: add notes_json column to existing capture_jobs tables.
|
||||
|
|
@ -1482,6 +1510,7 @@ pub fn refresh_entry_cached_bytes(conn: &Connection, entry_id: i64) -> Result<()
|
|||
JOIN archived_entries e ON e.id = ea.entry_id
|
||||
WHERE ea.entry_id = ?1
|
||||
AND ea.blob_id IS NOT NULL
|
||||
AND ea.artifact_role != 'avatar'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM entry_artifacts ea2
|
||||
|
|
@ -1518,6 +1547,7 @@ pub fn cascade_cached_bytes_after_delete(conn: &Connection, entry_id: i64) -> Re
|
|||
JOIN blobs b ON b.id = ea.blob_id
|
||||
WHERE ea.entry_id = archived_entries.id
|
||||
AND ea.blob_id IS NOT NULL
|
||||
AND ea.artifact_role != 'avatar'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM entry_artifacts ea3
|
||||
|
|
@ -1570,6 +1600,7 @@ fn cascade_cached_bytes_after_subtree_delete(conn: &Connection, subtree_ids: &[i
|
|||
JOIN blobs b ON b.id = ea.blob_id
|
||||
WHERE ea.entry_id = archived_entries.id
|
||||
AND ea.blob_id IS NOT NULL
|
||||
AND ea.artifact_role != 'avatar'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM entry_artifacts ea3
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Archivr</title>
|
||||
<script type="module" crossorigin src="/assets/index-BlDAGZHO.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-C9dla3pB.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D8ic-z4p.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -143,9 +143,9 @@ export default function EntryRow({ entry, archiveId, rowIndex, isSelected, isMul
|
|||
</div>
|
||||
<div className="col-size">
|
||||
<span className="size-total">{formatBytes(entry.total_artifact_bytes)}</span>
|
||||
{entry.cached_bytes > 0 && entry.total_artifact_bytes > 0 && (
|
||||
{entry.cached_bytes > 0 && entry.cacheable_bytes > 0 && (
|
||||
<span className="size-cached-pct" title={`${formatBytes(entry.cached_bytes)} already on disk from an earlier entry`}>
|
||||
{Math.round(entry.cached_bytes / entry.total_artifact_bytes * 100)}% cached
|
||||
{Math.round(entry.cached_bytes / entry.cacheable_bytes * 100)}% cached
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue