Skip to content

Commit a1803f0

Browse files
pimpinclaude
andcommitted
Fix tombstone detection using _sync.flags field
The _sync xattr does not have a _deleted field at the root level. Based on actual _sync structure analysis, tombstone status is indicated by: Primary indicator: - flags == 1: Document is a tombstone Secondary indicators: - tombstoned_at: Timestamp when document became tombstone (only present for tombstones) - channels.*.del == true: Per-channel deletion marker - history.deleted: Array of deleted revision indices Changes: - Check _sync.flags == 1 to detect tombstones - Also check for tombstoned_at field as confirmation - Display flags value and tombstoned_at in output - Add #[allow(deprecated)] to test examples to suppress warnings for deprecated Database methods This fix enables proper tombstone detection in CBS when using shared bucket access mode. Real _sync structure discovered via N1QL query: - Live document: flags absent or 0, no tombstoned_at - Tombstone: flags == 1, tombstoned_at present, channels.*.del == true 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 3935e48 commit a1803f0

File tree

4 files changed

+52
-10
lines changed

4 files changed

+52
-10
lines changed

examples/tombstone_purge_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::path::Path;
44
use couchbase_lite::*;
55
use utils::*;
66

7+
#[allow(deprecated)]
78
fn main() {
89
println!("=== Tombstone Purge Test (FULL - 1 hour) ===");
910
println!("This test validates complete tombstone purge following Thomas's recommendation.");

examples/tombstone_purge_test_short.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::path::Path;
44
use couchbase_lite::*;
55
use utils::*;
66

7+
#[allow(deprecated)]
78
fn main() {
89
println!("=== Tombstone Purge Test (SHORT - 5 minutes) ===");
910
println!("This test validates tombstone purge logic with a short interval.");

examples/tombstone_quick_check.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use couchbase_lite::*;
44
use std::path::Path;
55
use utils::*;
66

7+
#[allow(deprecated)]
78
fn main() {
89
println!("=== Tombstone Quick Check (30 seconds) ===");
910
println!("This is a rapid validation test for tombstone detection via XATTRs.\n");

examples/utils/cbs_admin.rs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ pub fn check_doc_in_cbs(doc_id: &str) {
4949
// This is only for testing/debugging purposes. The _sync structure can change between versions.
5050
// Reference: https://docs.couchbase.com/sync-gateway/current/shared-bucket-access.html
5151
let url = "http://localhost:8093/query/service";
52+
53+
// Query the entire _sync xattr to see its structure
54+
// This helps debug what fields are actually available
5255
let query = format!(
53-
"SELECT META().id, META().xattrs._sync._deleted as deleted FROM `{CBS_BUCKET}` USE KEYS ['{doc_id}']"
56+
"SELECT META().id, META().xattrs._sync as sync_metadata FROM `{CBS_BUCKET}` USE KEYS ['{doc_id}']"
5457
);
5558
let body = serde_json::json!({"statement": query});
5659

@@ -74,17 +77,53 @@ pub fn check_doc_in_cbs(doc_id: &str) {
7477
} else {
7578
println!("CBS check for {doc_id}: Found {} result(s)", results.len());
7679
for result in results {
77-
let is_deleted = result["deleted"].as_bool().unwrap_or(false);
78-
if is_deleted {
79-
println!(" - Document exists as TOMBSTONE (deleted: true)");
80-
println!(
81-
" {}",
82-
serde_json::to_string_pretty(result).unwrap()
83-
);
80+
// Display the full sync_metadata to understand its structure
81+
if let Some(sync_meta) = result.get("sync_metadata") {
82+
if sync_meta.is_null() {
83+
println!(
84+
" ⚠ sync_metadata is NULL - may lack permissions to read system xattrs"
85+
);
86+
println!(
87+
" 💡 System xattrs (starting with _) may require special RBAC roles"
88+
);
89+
} else {
90+
println!(" 📦 Full _sync xattr content:");
91+
println!(
92+
"{}",
93+
serde_json::to_string_pretty(sync_meta).unwrap()
94+
);
95+
96+
// Detect tombstone status from _sync.flags field
97+
// flags == 1 indicates a deleted/tombstone document
98+
// Other indicators: tombstoned_at field, channels.*.del == true
99+
let flags = sync_meta
100+
.get("flags")
101+
.and_then(|v| v.as_i64())
102+
.unwrap_or(0);
103+
104+
let has_tombstoned_at =
105+
sync_meta.get("tombstoned_at").is_some();
106+
107+
let is_tombstone = flags == 1 || has_tombstoned_at;
108+
109+
if is_tombstone {
110+
println!("\n ✓ Document is TOMBSTONE");
111+
println!(" - flags: {}", flags);
112+
if has_tombstoned_at {
113+
println!(
114+
" - tombstoned_at: {}",
115+
sync_meta["tombstoned_at"]
116+
);
117+
}
118+
} else {
119+
println!("\n ✓ Document is LIVE");
120+
println!(" - flags: {}", flags);
121+
}
122+
}
84123
} else {
85-
println!(" - Document exists as LIVE document");
124+
println!(" ⚠ No sync_metadata field in result");
86125
println!(
87-
" {}",
126+
" Full result: {}",
88127
serde_json::to_string_pretty(result).unwrap()
89128
);
90129
}

0 commit comments

Comments
 (0)