This feature is available in SDK version 4.12.0 and later.
What Are Commit IDs?
Commit IDs are unique, monotonically increasing identifiers assigned to each data modification on your device:let result = try await ditto.store.execute(
query: "UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
)
print("Commit ID: \(result.commitID ?? -1)") // e.g., 1547
ditto.store.execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'",
) { result ->
println("Commit ID: ${result.commitId}") // e.g., 1547
}
const result = await ditto.store.execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
);
console.log('Commit ID:', result.commitID); // e.g., 1547
import { Ditto, QueryResult } from '@dittolive/ditto'
const result: QueryResult = await ditto.store.execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
)
const commitID: number | undefined = result.commitID
console.log('Commit ID:', commitID) // e.g., 1547
final result = await ditto.store.execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
);
print("Commit ID: ${result.commitID}"); // e.g., 1547
try (DittoQueryResult result = ditto.store.execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
)) {
System.out.println("Commit ID: " + result.getCommitId()); // e.g., 1547
}
using var result = await ditto.Store.ExecuteAsync(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
);
Console.WriteLine($"Commit ID: {result.CommitID}"); // e.g., 1547
auto result = store.execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'"
);
std::cout << "Commit ID: " << result.commitID() << std::endl; // e.g., 1547
let result = ditto.store().execute((
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'",
serde_json::Value::Null
)).await?;
println!("Commit ID: {:?}", result.commit_id()); // e.g., Some(1547)
result, err := dit.Store().Execute(
"UPDATE products SET price = 99.99 WHERE _id = 'product-123'")
if err != nil {
return err
}
defer result.Close() // cleanup
if commitID, ok := result.CommitID(); ok {
fmt.Printf("Commit ID: %s\n", commitID) // e.g. 1547
}
Key Properties
- Monotonic - Always increasing (1547, 1548, 1549β¦)
- Device-specific - Each device has its own sequence
- Per-operation - Every INSERT, UPDATE, DELETE gets a new ID
- Trackable - Use with
system:data_sync_infoto monitor sync progress
Tracking Sync Progress
Compare your commit ID withsynced_up_to_local_commit_id to see if peers have received your commit:
// Step 1: Execute statement and get commit ID
let result = try await ditto.store.execute(
query: "UPDATE orders SET status = 'shipped' WHERE _id = :id",
arguments: ["id": "order-456"]
)
let commitId = result.commitID!
// Step 2: Check if it synced to cloud
let syncResult = try await ditto.store.execute(
query: "SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
)
if let item = syncResult.items.first,
let documents = item.value["documents"] as? [String: Any],
let syncedUpTo = documents["synced_up_to_local_commit_id"] as? Int64 {
if syncedUpTo >= commitId {
print("Commit synced to cloud")
} else {
print("Syncing...")
}
}
// Step 1: Execute the write and capture its commit ID.
val commitId: ULong = ditto.store.execute(
query = "UPDATE orders SET status = 'shipped' WHERE _id = :id",
arguments = mapOf("id" to "order-456"),
) { result -> result.commitId!! }
// Step 2: Check if it synced to cloud.
ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
) { syncResult ->
val item = syncResult.items.firstOrNull()
val syncedUpTo = item?.value?.get("documents")?.dictionaryOrNull
?.let { it["synced_up_to_local_commit_id"].uLongOrNull }
?: 0u.toULong()
if (syncedUpTo >= commitId) {
println("Commit synced to cloud")
} else {
println("Syncingβ¦")
}
}
// Step 1: Execute statement and get commit ID
const result = await ditto.store.execute(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
{ id: "order-456" }
);
const commitId = result.commitID;
// Step 2: Check if it synced to cloud
const syncResult = await ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
);
if (syncResult.items.length > 0) {
const syncedUpTo = syncResult.items[0].value.documents.synced_up_to_local_commit_id;
if (syncedUpTo >= commitId) {
console.log("Commit synced to cloud");
} else {
console.log("Syncing...");
}
}
import { Ditto, QueryResult } from '@dittolive/ditto'
interface SyncDocuments {
sync_session_status: string
synced_up_to_local_commit_id: number
last_update_received_time: number
}
interface PeerSyncInfo {
_id: string
is_ditto_server: boolean
documents: SyncDocuments
}
// Step 1: Execute statement and get commit ID
const result: QueryResult = await ditto.store.execute(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
{ id: "order-456" }
)
const commitId: number = result.commitID!
// Step 2: Check if it synced to cloud
const syncResult: QueryResult = await ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
)
if (syncResult.items.length > 0) {
const peer = syncResult.items[0].value as PeerSyncInfo
const syncedUpTo: number = peer.documents.synced_up_to_local_commit_id
if (syncedUpTo >= commitId) {
console.log("Commit synced to cloud")
} else {
console.log("Syncing...")
}
}
// Step 1: Execute statement and get commit ID
final result = await ditto.store.execute(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
{"id": "order-456"}
);
final commitId = result.commitID!;
// Step 2: Check if it synced to cloud
final syncResult = await ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
);
if (syncResult.items.isNotEmpty) {
final documents = syncResult.items[0].value["documents"] as Map<String, dynamic>;
final syncedUpTo = documents["synced_up_to_local_commit_id"] as int;
if (syncedUpTo >= commitId) {
print("Commit synced to cloud");
} else {
print("Syncing...");
}
}
// Step 1: Execute statement and get commit ID
try (DittoQueryResult result = ditto.store.execute(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
Collections.singletonMap("id", "order-456")
)) {
long commitId = result.getCommitId();
// Step 2: Check if it synced to cloud
try (DittoQueryResult syncResult = ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
)) {
if (!syncResult.getItems().isEmpty()) {
Map<String, Object> value = syncResult.getItems().get(0).getValue();
Map<String, Object> documents = (Map<String, Object>) value.get("documents");
long syncedUpTo = (Long) documents.get("synced_up_to_local_commit_id");
if (syncedUpTo >= commitId) {
System.out.println("Commit synced to cloud");
} else {
System.out.println("Syncing...");
}
}
}
}
// Step 1: Execute statement and get commit ID
using var result = await ditto.Store.ExecuteAsync(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
new Dictionary<string, object> { { "id", "order-456" } }
);
var commitId = result.CommitID;
// Step 2: Check if it synced to cloud
using var syncResult = await ditto.Store.ExecuteAsync(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
);
if (syncResult.Items.Count > 0) {
var value = (Dictionary<string, object>)syncResult.Items[0].Value;
var documents = (Dictionary<string, object>)value["documents"];
var syncedUpTo = (long)documents["synced_up_to_local_commit_id"];
if (syncedUpTo >= commitId) {
Console.WriteLine("Commit synced to cloud");
} else {
Console.WriteLine("Syncing...");
}
}
// Step 1: Execute statement and get commit ID
auto result = store.execute(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
{{"id", "order-456"}}
);
auto commitId = result.commitID();
// Step 2: Check if it synced to cloud
auto syncResult = store.execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
);
if (!syncResult.items().empty()) {
const auto& value = syncResult.items()[0].value();
const auto& documents = value.at("documents").get<std::map<std::string, std::any>>();
auto syncedUpTo = std::any_cast<int64_t>(documents.at("synced_up_to_local_commit_id"));
if (syncedUpTo >= commitId) {
std::cout << "Commit synced to cloud" << std::endl;
} else {
std::cout << "Syncing..." << std::endl;
}
}
// Step 1: Execute statement and get commit ID
let result = ditto.store().execute((
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
serde_json::json!({"id": "order-456"})
)).await?;
let commit_id = result.commit_id().unwrap();
// Step 2: Check if it synced to cloud
let sync_result = ditto.store().execute((
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
serde_json::Value::Null
)).await?;
if let Some(item) = sync_result.get_item(0) {
if let Some(value) = item.value().as_object() {
if let Some(documents) = value.get("documents").and_then(|d| d.as_object()) {
if let Some(synced_up_to) = documents.get("synced_up_to_local_commit_id").and_then(|s| s.as_i64()) {
if synced_up_to >= commit_id as i64 {
println!("Commit synced to cloud");
} else {
println!("Syncing...");
}
}
}
}
}
// Step 1: Execute statement and get commit ID
result, err := dit.Store().Execute(
"UPDATE orders SET status = 'shipped' WHERE _id = :id",
ditto.QueryArguments{"id" : "order-456"})
if err != nil {
return err
}
defer result.Close() // cleanup
commitID, _ := result.CommitID()
// Step 2: Check if it synced to cloud
syncResult, err := dit.Store().Execute(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true")
if err != nil {
return err
}
if result.ItemCount() > 0 {
item := result.Item(0)
defer item.Close() // cleanup
if documents, ok := item.Value()["documents"].(map[string]any); ok; {
if syncedUpTo, ok := documents["synced_up_to_local_commit_id].(int64); ok {
if syncedUpTo > commitID {
fmt.Println("Commit synced to cloud")
} else {
fmt.Println("Syncing...)
}
}
}
}
Monitoring Multiple Commits
Observers on
system:data_sync_info result in the callback triggering every 500ms even when the result remains the same. This frequency can be
configured using the live_query_system_collection_refresh_interval system parameter.var pendingCommits: [Int64] = []
// Make several commits
let result1 = try await ditto.store.execute(
query: "UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
)
if let commitId1 = result1.commitID {
pendingCommits.append(commitId1)
}
let result2 = try await ditto.store.execute(
query: "INSERT INTO orders DOCUMENTS (:order)",
arguments: ["order": ["_id": "order-789", "status": "pending"]]
)
if let commitId2 = result2.commitID {
pendingCommits.append(commitId2)
}
// Watch them sync
let observer = ditto.store.registerObserver(
query: "SELECT * FROM system:data_sync_info WHERE is_ditto_server = true"
) { result in
guard let item = result.items.first,
let documents = item.value["documents"] as? [String: Any],
let syncedUpTo = documents["synced_up_to_local_commit_id"] as? Int64 else { return }
// Check which commits have synced
var syncedCount = 0
for commitId in pendingCommits {
if syncedUpTo >= commitId {
print("Commit \(commitId) synced")
syncedCount += 1
} else {
break
}
}
// Remove synced commits
if syncedCount > 0 {
pendingCommits.removeFirst(syncedCount)
print("\(pendingCommits.count) commits remaining")
}
}
val pendingCommits = mutableListOf<ULong>()
// Make several commits β pass a handler to capture each commit ID.
ditto.store.execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'",
) { result -> result.commitId?.let { pendingCommits.add(it) } }
ditto.store.execute(
query = "INSERT INTO orders DOCUMENTS (:order)",
arguments = mapOf("order" to mapOf("_id" to "order-789", "status" to "pending")),
) { result -> result.commitId?.let { pendingCommits.add(it) } }
// Watch them sync. The handler is suspend; the result is auto-closed when it
// returns. Read nested fields with typed accessors β not Kotlin casts.
ditto.store.registerObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
) { result ->
val item = result.items.firstOrNull() ?: return@registerObserver
val syncedUpTo = item.value["documents"].dictionaryOrNull
?.let { it["synced_up_to_local_commit_id"].uLongOrNull }
?: return@registerObserver
// Check which commits have synced
var syncedCount = 0
for (commitId in pendingCommits) {
if (syncedUpTo >= commitId) {
println("β
Commit $commitId synced")
syncedCount++
} else {
break
}
}
if (syncedCount > 0) {
repeat(syncedCount) { pendingCommits.removeAt(0) }
println("${pendingCommits.size} commits remaining")
}
}
const pendingCommits = [];
// Make several commits
const result1 = await ditto.store.execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
);
pendingCommits.push(result1.commitID);
const result2 = await ditto.store.execute(
"INSERT INTO orders DOCUMENTS (:order)",
{ order: { _id: "order-789", status: "pending" } }
);
pendingCommits.push(result2.commitID);
// Watch them sync
const observer = ditto.store.registerObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
(result) => {
if (!result.items.length) return;
const syncedUpTo = result.items[0].value.documents.synced_up_to_local_commit_id;
// Check which commits have synced
let syncedCount = 0;
for (const commitId of pendingCommits) {
if (syncedUpTo >= commitId) {
console.log(`Commit ${commitId} synced`);
syncedCount++;
} else {
break;
}
}
// Remove synced commits
if (syncedCount > 0) {
pendingCommits.splice(0, syncedCount);
console.log(`${pendingCommits.length} commits remaining`);
}
}
);
import { Ditto, QueryResult, StoreObserver } from '@dittolive/ditto'
interface SyncDocuments {
sync_session_status: string
synced_up_to_local_commit_id: number
last_update_received_time: number
}
interface PeerSyncInfo {
_id: string
is_ditto_server: boolean
documents: SyncDocuments
}
const pendingCommits: number[] = []
// Make several commits
const result1: QueryResult = await ditto.store.execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
)
if (result1.commitID !== undefined) {
pendingCommits.push(result1.commitID)
}
const result2: QueryResult = await ditto.store.execute(
"INSERT INTO orders DOCUMENTS (:order)",
{ order: { _id: "order-789", status: "pending" } }
)
if (result2.commitID !== undefined) {
pendingCommits.push(result2.commitID)
}
// Watch them sync
const observer: StoreObserver = ditto.store.registerObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
(result: QueryResult) => {
if (!result.items.length) return
const peer = result.items[0].value as PeerSyncInfo
const syncedUpTo: number = peer.documents.synced_up_to_local_commit_id
// Check which commits have synced
let syncedCount: number = 0
for (const commitId of pendingCommits) {
if (syncedUpTo >= commitId) {
console.log(`Commit ${commitId} synced`)
syncedCount++
} else {
break
}
}
// Remove synced commits
if (syncedCount > 0) {
pendingCommits.splice(0, syncedCount)
console.log(`${pendingCommits.length} commits remaining`)
}
}
)
final pendingCommits = <int>[];
// Make several commits
final result1 = await ditto.store.execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
);
if (result1.commitID != null) {
pendingCommits.add(result1.commitID!);
}
final result2 = await ditto.store.execute(
"INSERT INTO orders DOCUMENTS (:order)",
{"order": {"_id": "order-789", "status": "pending"}}
);
if (result2.commitID != null) {
pendingCommits.add(result2.commitID!);
}
// Watch them sync
final observer = ditto.store.registerObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
(result) {
if (result.items.isEmpty) return;
final documents = result.items[0].value["documents"] as Map<String, dynamic>;
final syncedUpTo = documents["synced_up_to_local_commit_id"] as int;
// Check which commits have synced
int syncedCount = 0;
for (final commitId in pendingCommits) {
if (syncedUpTo >= commitId) {
print("Commit $commitId synced");
syncedCount++;
} else {
break;
}
}
// Remove synced commits
if (syncedCount > 0) {
pendingCommits.removeRange(0, syncedCount);
print("${pendingCommits.length} commits remaining");
}
}
);
List<BigInteger> pendingCommits = new ArrayList<>();
// Make several commits
try (DittoQueryResult result1 = ditto.store.execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
)){
if (result1.getCommitId() != null) {
pendingCommits.add(result1.getCommitId());
}
}
try (DittoQueryResult result2 = ditto.store.execute(
"INSERT INTO orders DOCUMENTS (:order)",
Collections.singletonMap("order", Map.of("_id", "order-789", "status", "pending"))
)){
if (result2.getCommitId() != null) {
pendingCommits.add(result2.getCommitId());
}
}
// Watch them sync
ditto.store.registerObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
(result) -> {
try (result)
{
if (result.getItems().isEmpty()) return;
Map<String, Object> value = result.getItems().get(0).getValue();
Map<String, Object> documents = (Map<String, Object>) value.get("documents");
long syncedUpTo = (Long) documents.get("synced_up_to_local_commit_id");
// Check which commits have synced
int syncedCount = 0;
for (BigInteger commitId : pendingCommits) {
if (syncedUpTo >= commitId.longValue()) {
System.out.println("Commit " + commitId + " synced");
syncedCount++;
} else {
break;
}
}
// Remove synced commits
if (syncedCount > 0) {
pendingCommits.subList(0, syncedCount).clear();
System.out.println(pendingCommits.size() + " commits remaining");
}
}
}
);
var pendingCommits = new List<long>();
// Make several commits
using var result1 = await ditto.Store.ExecuteAsync(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
);
if (result1.CommitID.HasValue) {
pendingCommits.Add(result1.CommitID.Value);
}
using var result2 = await ditto.Store.ExecuteAsync(
"INSERT INTO orders DOCUMENTS (:order)",
new Dictionary<string, object> {
{ "order", new Dictionary<string, object> {
{ "_id", "order-789" },
{ "status", "pending" }
}}
}
);
if (result2.CommitID.HasValue) {
pendingCommits.Add(result2.CommitID.Value);
}
// Watch them sync
var observer = ditto.Store.RegisterObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
(result) => {
using (result)
{
if (result.Items.Count == 0) return;
var value = (Dictionary<string, object>)result.Items[0].Value;
var documents = (Dictionary<string, object>)value["documents"];
var syncedUpTo = (long)documents["synced_up_to_local_commit_id"];
// Check which commits have synced
int syncedCount = 0;
foreach (var commitId in pendingCommits) {
if (syncedUpTo >= commitId) {
Console.WriteLine($"Commit {commitId} synced");
syncedCount++;
} else {
break;
}
}
// Remove synced commits
if (syncedCount > 0) {
pendingCommits.RemoveRange(0, syncedCount);
Console.WriteLine($"{pendingCommits.Count} commits remaining");
}
}
}
);
std::vector<int64_t> pendingCommits;
// Make several commits
auto result1 = store.execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"
);
if (result1.commitID().has_value()) {
pendingCommits.push_back(result1.commitID().value());
}
auto result2 = store.execute(
"INSERT INTO orders DOCUMENTS (:order)",
{{"order", std::map<std::string, std::any>{
{"_id", std::string("order-789")},
{"status", std::string("pending")}
}}}
);
if (result2.commitID().has_value()) {
pendingCommits.push_back(result2.commitID().value());
}
// Watch them sync
auto observer = store.register_observer(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
[&pendingCommits](const auto& result) {
if (result.items().empty()) return;
const auto& value = result.items()[0].value();
const auto& documents = value.at("documents").get<std::map<std::string, std::any>>();
auto syncedUpTo = std::any_cast<int64_t>(documents.at("synced_up_to_local_commit_id"));
// Check which commits have synced
size_t syncedCount = 0;
for (const auto& commitId : pendingCommits) {
if (syncedUpTo >= commitId) {
std::cout << "Commit " << commitId << " synced" << std::endl;
syncedCount++;
} else {
break;
}
}
// Remove synced commits
if (syncedCount > 0) {
pendingCommits.erase(pendingCommits.begin(), pendingCommits.begin() + syncedCount);
std::cout << pendingCommits.size() << " commits remaining" << std::endl;
}
}
);
let mut pending_commits: Vec<u64> = Vec::new();
// Make several commits
let result1 = ditto.store().execute((
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'",
serde_json::Value::Null
)).await?;
if let Some(commit_id) = result1.commit_id() {
pending_commits.push(commit_id);
}
let result2 = ditto.store().execute((
"INSERT INTO orders DOCUMENTS (:order)",
serde_json::json!({"order": {"_id": "order-789", "status": "pending"}})
)).await?;
if let Some(commit_id) = result2.commit_id() {
pending_commits.push(commit_id);
}
// Watch them sync
let observer = ditto.store().register_observer(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
move |result| {
if result.item_count() == 0 { return; }
if let Some(item) = result.get_item(0) {
if let Some(value) = item.value().as_object() {
if let Some(documents) = value.get("documents").and_then(|d| d.as_object()) {
if let Some(synced_up_to) = documents.get("synced_up_to_local_commit_id").and_then(|s| s.as_u64()) {
// Check which commits have synced
let mut synced_count = 0;
for &commit_id in &pending_commits {
if synced_up_to >= commit_id {
println!("Commit {} synced", commit_id);
synced_count += 1;
} else {
break;
}
}
// Remove synced commits
if synced_count > 0 {
pending_commits.drain(0..synced_count);
println!("{} commits remaining", pending_commits.len());
}
}
}
}
}
}
)?;
var pendingCommits []uint64
// Make several Commits
result1, err := dit.Store().Execute(
"UPDATE inventory SET quantity = quantity - 1 WHERE _id = 'item-1'"))
if err != nil {
return err
}
defer result1.Close() // cleanup
if commitID, ok := result1.CommitID(); ok {
pendingCommits = append(pendingCommits, commitID)
}
// Make several Commits
result2, err := dit.Store().Execute(
"INSERT INTO orders DOCUMENTS (:order)",
ditto.QueryArguments{"order" : ditto.Document{"_id" : "order-789", "status" : "pending}})
if err != nil {
return err
}
defer result2.Close() // cleanup
if commitID, ok := result2.CommitID(); ok {
pendingCommits = append(pendingCommits, commitID)
}
// Watch them sync
observer, err := dit.Store().RegisterObserver(
"SELECT * FROM system:data_sync_info WHERE is_ditto_server = true",
nil, // no query arguments
func(result *ditto.QueryResult) {
defer result.Close() // cleanup
if result.ItemCount() == 0 {
return
}
item := result.Item(0)
defer item.Close() // cleanup
if documents, ok := item.Value()["documents"].(map[string]any); ok {
if syncedUpTo, ok := documents["synced_up_to_local_commit_id"].(uint64); ok {
// Check which commits have synced
var syncedCount int
for _, commitID := range pendingCommits {
if syncedUpTo > commitID {
fmt.Printf("Commit %u synced\n", commitID)
syncedCount++
} else {
break
}
}
// Remove synced commits
if syncedCount > 0 {
pendingCommits = pendingCommits[syncedCount:]
fmt.Printf("%d commits remaining\n", len(pendingCommits))
}
}
}
}
)
if err != nil {
return err
}
Transaction Behavior
Commit IDs are only assigned after transactions complete:var queryResult: DittoQueryResult?
try await ditto.store.transaction { transaction in
queryResult = try await transaction.execute(
query: "INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
)
print(queryResult?.commitID ?? "nil") // nil - not committed yet
return try await transaction.commit()
}
print(queryResult?.commitID ?? "nil") // 1548 - now has commit ID
// Use transaction.executeRaw() to get the result inside the block, and have
// the block return the value (or just the commit ID). The transaction commits
// when you return Result.Commit. The commit ID is only assigned once the
// transaction commits.
val commitId: ULong? = (ditto.store.transaction { transaction ->
transaction.execute(
query = "INSERT INTO orders DOCUMENTS (:doc)",
arguments = mapOf("doc" to mapOf("_id" to "order-123", "total" to 99.99))
) { txnResult ->
// Before commit, no commit ID has been assigned yet.
println(txnResult.commitId ?: "null") // null
DittoTransaction.Result.Commit(txnResult.commitId)
}
} as? DittoTransaction.Result.Commit)?.value
println(commitId ?: "null") // e.g. 1548 β assigned now that the txn committed
let queryResult;
await ditto.store.transaction(async (transaction) => {
queryResult = await transaction.execute(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
);
console.log(queryResult.commitID); // null - not committed yet
return transaction.commit();
});
console.log(queryResult.commitID); // 1548 - now has commit ID
import { Ditto, QueryResult } from '@dittolive/ditto'
let queryResult: QueryResult | undefined
await ditto.store.transaction(async (transaction) => {
queryResult = await transaction.execute(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
)
console.log(queryResult.commitID) // undefined - not committed yet
return transaction.commit()
})
console.log(queryResult?.commitID) // 1548 - now has commit ID
late QueryResult queryResult;
await ditto.store.transaction((transaction) async {
queryResult = await transaction.execute(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
);
print(queryResult.commitID); // null - not committed yet
return TransactionCompletionAction.commit;
});
print(queryResult.commitID); // 1548 - now has commit ID
DittoQueryResult queryResult = null;
ditto.store.transaction((transaction) -> {
queryResult = transaction.execute(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
);
System.out.println(queryResult.getCommitId() != null ? queryResult.getCommitId() : "null"); // null - not committed yet
return transaction.commit();
});
queryResult.close();
System.out.println(queryResult.getCommitId() != null ? queryResult.getCommitId() : "null"); // 1548 - now has commit ID
DittoQueryResult queryResult = null;
await ditto.Store.TransactionAsync(async (transaction) => {
queryResult = await transaction.ExecuteAsync(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
);
Console.WriteLine(queryResult.CommitID?.ToString() ?? "null"); // null - not committed yet
return await transaction.CommitAsync();
});
Console.WriteLine(queryResult.CommitID?.ToString() ?? "null"); // 1548 - now has commit ID
queryResult.Dispose();
std::optional<DittoQueryResult> queryResult;
ditto.get_store().transaction([&](auto& transaction) {
queryResult = transaction.execute(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })"
);
std::cout << (queryResult->commitID().has_value() ?
std::to_string(queryResult->commitID().value()) : "null") << std::endl; // null - not committed yet
return transaction.commit();
});
std::cout << (queryResult->commitID().has_value() ?
std::to_string(queryResult->commitID().value()) : "null") << std::endl; // 1548 - now has commit ID
let mut query_result: Option<DittoQueryResult> = None;
ditto.store().transaction(|transaction| async move {
query_result = Some(transaction.execute((
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })",
serde_json::Value::Null
)).await?);
println!("{:?}", query_result.as_ref().and_then(|r| r.commit_id())); // None - not committed yet
transaction.commit().await
}).await?;
println!("{:?}", query_result.as_ref().and_then(|r| r.commit_id())); // Some(1548) - now has commit ID
var queryResult *ditto.queryResult
_, err := dit.Store().Transaction(
nil, // default operations
func(transaction *ditto.Transaction) (ditto.TransactionCompletionAction, err) {
var err error
queryResult, err = transaction.Execute(
"INSERT INTO orders DOCUMENTS ({ _id: 'order-123', total: 99.99 })")
if err != nil {
return ditto.TransactionCompletionActionRollback, err
}
if commitID, ok := queryResult.CommitID(); ok {
// This branch won't be taken, as commit ID is not set yet
fmt.Printf("%d\n", commitID)
} else {
fmt.Println("No commit ID")
}
return ditto.TransactionCallbackActionCommit, nil
}
)
if err != nil {
return err
}
defer queryResult.Close() // cleanup
if commitID, ok := queryResult.CommitID(); ok {
// Now has commit ID
fmt.Printf("%d\n", commitID)
} else {
// This branch won't be taken
fmt.Println("No commit ID")
}
Checking Specific Peers
Track sync progress to any peer (not just cloud servers):// Check sync to a specific peer device
let syncResult = try await ditto.store.execute(
query: "SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
)
if let item = syncResult.items.first,
let documents = item.value["documents"] as? [String: Any],
let peerSyncedUpTo = documents["synced_up_to_local_commit_id"] as? Int64 {
print("Peer has synced up to commit \(peerSyncedUpTo)")
}
// Check sync to a specific peer device. Use typed accessors on the
// DittoCborSerializable.Dictionary fields; the handler form auto-closes the
// result.
ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'",
) { syncResult ->
val item = syncResult.items.firstOrNull() ?: return@execute
val peerSyncedUpTo = item.value["documents"].dictionaryOrNull
?.let { it["synced_up_to_local_commit_id"].uLongOrNull }
println("Peer has synced up to commit ${peerSyncedUpTo ?: "?"}")
}
// Check sync to a specific peer device
const syncResult = await ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
);
if (syncResult.items.length > 0) {
const peerSyncedUpTo = syncResult.items[0].value.documents.synced_up_to_local_commit_id;
console.log(`Peer has synced up to commit ${peerSyncedUpTo}`);
}
import { Ditto, QueryResult } from '@dittolive/ditto'
interface SyncDocuments {
sync_session_status: string
synced_up_to_local_commit_id: number
last_update_received_time: number
}
interface PeerSyncInfo {
_id: string
is_ditto_server: boolean
documents: SyncDocuments
}
// Check sync to a specific peer device
const syncResult: QueryResult = await ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
)
if (syncResult.items.length > 0) {
const peer = syncResult.items[0].value as PeerSyncInfo
const peerSyncedUpTo: number = peer.documents.synced_up_to_local_commit_id
console.log(`Peer has synced up to commit ${peerSyncedUpTo}`)
}
// Check sync to a specific peer device
final syncResult = await ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
);
if (syncResult.items.isNotEmpty) {
final documents = syncResult.items[0].value["documents"] as Map<String, dynamic>;
final peerSyncedUpTo = documents["synced_up_to_local_commit_id"] as int;
print("Peer has synced up to commit $peerSyncedUpTo");
}
// Check sync to a specific peer device
try (DittoQueryResult syncResult = ditto.store.execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
)) {
if (!syncResult.getItems().isEmpty()) {
Map<String, Object> value = syncResult.getItems().get(0).getValue();
Map<String, Object> documents = (Map<String, Object>) value.get("documents");
long peerSyncedUpTo = (Long) documents.get("synced_up_to_local_commit_id");
System.out.println("Peer has synced up to commit " + peerSyncedUpTo);
}
}
// Check sync to a specific peer device
using var syncResult = await ditto.Store.ExecuteAsync(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
);
if (syncResult.Items.Count > 0) {
var value = (Dictionary<string, object>)syncResult.Items[0].Value;
var documents = (Dictionary<string, object>)value["documents"];
var peerSyncedUpTo = (long)documents["synced_up_to_local_commit_id"];
Console.WriteLine($"Peer has synced up to commit {peerSyncedUpTo}");
}
// Check sync to a specific peer device
auto syncResult = store.execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'"
);
if (!syncResult.items().empty()) {
const auto& value = syncResult.items()[0].value();
const auto& documents = value.at("documents").get<std::map<std::string, std::any>>();
auto peerSyncedUpTo = std::any_cast<int64_t>(documents.at("synced_up_to_local_commit_id"));
std::cout << "Peer has synced up to commit " << peerSyncedUpTo << std::endl;
}
// Check sync to a specific peer device
let sync_result = ditto.store().execute((
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'",
serde_json::Value::Null
)).await?;
if let Some(item) = sync_result.get_item(0) {
if let Some(value) = item.value().as_object() {
if let Some(documents) = value.get("documents").and_then(|d| d.as_object()) {
if let Some(peer_synced_up_to) = documents.get("synced_up_to_local_commit_id").and_then(|s| s.as_u64()) {
println!("Peer has synced up to commit {}", peer_synced_up_to);
}
}
}
}
// Check sync to a specific peer device
syncResult, err := dit.Store().Execute(
"SELECT * FROM system:data_sync_info WHERE _id = 'peer-device-456'")
if err != nil {
return err
}
defer syncResult.Close() // cleanup
if syncResult.ItemCount() > 0 {
item := syncResult.Item(0)
defer item.Close() // cleanup
if documents, ok := item.Value()["documents"].(map[string]any); ok {
if peerSyncedUpTo, ok := documents["synced_up_to_local_commit_id"].(uint64); ok {
fmt.Printf("Peer has synced up to commit %u\n", peerSyncedUpTo)
}
}
}
Best Practices
Remember that Ditto is offline-first. Use commit ID tracking to enhance user experience, not to block operations.
- Donβt block operations - Let users continue working while tracking sync in background
- Show progress indicators - Use commit tracking to display sync status in your UI
- Handle concurrency gracefully - Tracking should enhance the experience, not block it
- Monitor critical data - Focus tracking on important operations like payments or submissions
- Clean up tracking - Remove old commit IDs from your tracking lists to avoid memory issues
Related Topics
- Monitoring Sync Status - Overview of the system:data_sync_info collection
- Track Ditto Cloud Sync Status - Cloud-specific sync monitoring patterns