mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
fix(core): commit/reload barrier before dedup GC reference count
cleanup_unused_content decides whether to delete a deduplicated blob by running a Tantivy Count of envelopes referencing each content_hash. The searcher it used reflected only the committed index state at the time it was built, so an envelope that shared a content hash but was still sitting uncommitted in the IndexWriter buffer (for example added by the background ingest task before the delete operation acquired the writer lock) was invisible to the count. The count read 0 and a still-referenced blob was deleted, permanently 404ing that envelope's download-message. Pass the locked IndexWriter into cleanup_unused_content and fatal_commit it immediately before creating the searcher. create_searcher already reloads the reader, so the Count is now evaluated against a fully committed, freshly-reloaded index state. The barrier is local to the GC path and self-contained, independent of what the caller committed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
6eca351994
commit
895ea543a9
@@ -733,7 +733,11 @@ impl IndexManager {
|
||||
.await?;
|
||||
|
||||
if !eml_content_hashes.is_empty() || !attachments_content_hashes.is_empty() {
|
||||
self.cleanup_unused_content(eml_content_hashes, attachments_content_hashes)?;
|
||||
self.cleanup_unused_content(
|
||||
&mut writer,
|
||||
eml_content_hashes,
|
||||
attachments_content_hashes,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -772,7 +776,11 @@ impl IndexManager {
|
||||
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
|
||||
|
||||
if !eml_content_hashes.is_empty() || !attachments_content_hashes.is_empty() {
|
||||
self.cleanup_unused_content(eml_content_hashes, attachments_content_hashes)?;
|
||||
self.cleanup_unused_content(
|
||||
&mut writer,
|
||||
eml_content_hashes,
|
||||
attachments_content_hashes,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -817,9 +825,18 @@ impl IndexManager {
|
||||
|
||||
fn cleanup_unused_content(
|
||||
&self,
|
||||
writer: &mut IndexWriter,
|
||||
eml_content_hashes: HashSet<String>,
|
||||
attachments_content_hashes: HashSet<String>,
|
||||
) -> BichonResult<()> {
|
||||
// Reference-count barrier: commit the writer and reload the reader so the
|
||||
// `Count` below is evaluated against a fully committed, freshly-reloaded
|
||||
// index state. Without this, an envelope that shares a content hash but
|
||||
// is still sitting uncommitted in the writer buffer (e.g. added by the
|
||||
// background ingest task before this delete acquired the writer lock)
|
||||
// would be invisible to the searcher, the count would read 0, and a
|
||||
// still-referenced blob would be deleted.
|
||||
fatal_commit(writer);
|
||||
let searcher = self.create_searcher()?;
|
||||
let fields = SchemaTools::email_fields();
|
||||
let mut eml: HashSet<String> = HashSet::new();
|
||||
@@ -900,7 +917,11 @@ impl IndexManager {
|
||||
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
|
||||
|
||||
if !eml_content_hashes.is_empty() || !attachments_content_hashes.is_empty() {
|
||||
self.cleanup_unused_content(eml_content_hashes, attachments_content_hashes)?;
|
||||
self.cleanup_unused_content(
|
||||
&mut writer,
|
||||
eml_content_hashes,
|
||||
attachments_content_hashes,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user