This project includes interactive examples that demonstrate the sync system in action.
The sync client provides real-time event callbacks for document changes, sync operations, and connection status. These examples show practical usage patterns.
Monitor document changes to update your user interface in real-time:
// C example for UI updates
struct AppContext {
int total_documents;
char status_message[256];
};
void ui_update_callback(const SyncEventData* event, void* context) {
struct AppContext* app = (struct AppContext*)context;
switch (event->event_type) {
case SYNC_EVENT_DOCUMENT_CREATED:
app->total_documents++;
snprintf(app->status_message, sizeof(app->status_message),
"Document created: %s", event->title);
update_ui_status(app->status_message);
refresh_document_list();
break;
case SYNC_EVENT_SYNC_COMPLETED:
snprintf(app->status_message, sizeof(app->status_message),
"Sync complete: %llu documents updated", event->numeric_data);
update_ui_status(app->status_message);
break;
case SYNC_EVENT_CONNECTION_STATE_CHANGED:
if (event->boolean_data) {
strcpy(app->status_message, "✅ Connected to server");
enable_sync_features();
} else {
strcpy(app->status_message, "❌ Disconnected - working offline");
disable_sync_features();
}
update_ui_status(app->status_message);
break;
}
}
// In your application initialization
struct AppContext app_context = {0};
sync_engine_register_event_callback(engine, ui_update_callback, &app_context, -1);Track synchronization progress and handle errors:
// C++ example for sync monitoring
class SyncMonitor {
private:
std::chrono::steady_clock::time_point sync_start_time;
int sync_progress = 0;
public:
void handle_event(const SyncEventData* event) {
switch (event->event_type) {
case SYNC_EVENT_SYNC_STARTED:
sync_start_time = std::chrono::steady_clock::now();
sync_progress = 0;
std::cout << "🔄 Starting sync..." << std::endl;
show_progress_bar(true);
break;
case SYNC_EVENT_DOCUMENT_UPDATED:
sync_progress++;
update_progress_bar(sync_progress);
std::cout << "📄 Synced: " << event->title << std::endl;
break;
case SYNC_EVENT_SYNC_COMPLETED: {
auto duration = std::chrono::steady_clock::now() - sync_start_time;
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
std::cout << "✅ Sync completed in " << ms << "ms" << std::endl;
std::cout << "📊 Total documents: " << event->numeric_data << std::endl;
hide_progress_bar();
break;
}
case SYNC_EVENT_SYNC_ERROR:
std::cerr << "🚨 Sync error: " << event->error << std::endl;
hide_progress_bar();
show_error_dialog(event->error);
break;
}
}
};
// Register with lambda
SyncMonitor monitor;
callbacks.add_callback([&monitor](const SyncEventData* event) {
monitor.handle_event(event);
});Log all document operations for audit trails:
// Rust example for activity logging
use std::fs::OpenOptions;
use std::io::Write;
use chrono::Utc;
fn setup_activity_logging(events: &EventDispatcher) -> Result<(), Box<dyn std::error::Error>> {
events.register_callback(
|event_type, document_id, title, content, error, numeric_data, boolean_data, context| {
let timestamp = Utc::now().format("%Y-%m-%d %H:%M:%S UTC");
let log_entry = match event_type {
EventType::DocumentCreated => {
format!("[{}] CREATED: {} - {}",
timestamp,
document_id.unwrap_or("unknown"),
title.unwrap_or("untitled"))
},
EventType::DocumentUpdated => {
format!("[{}] UPDATED: {} - {}",
timestamp,
document_id.unwrap_or("unknown"),
title.unwrap_or("untitled"))
},
EventType::DocumentDeleted => {
format!("[{}] DELETED: {}",
timestamp,
document_id.unwrap_or("unknown"))
},
EventType::SyncCompleted => {
format!("[{}] SYNC: Completed {} documents", timestamp, numeric_data)
},
EventType::SyncError => {
format!("[{}] ERROR: {}", timestamp, error.unwrap_or("unknown error"))
},
_ => return, // Skip other events
};
// Write to log file
if let Ok(mut file) = OpenOptions::new()
.create(true)
.append(true)
.open("sync_activity.log")
{
writeln!(file, "{}", log_entry).ok();
}
// Also print to console
println!("{}", log_entry);
},
std::ptr::null_mut(),
None // Log all events
)?;
Ok(())
}Handle conflicts with user interaction:
// C example for conflict resolution
void conflict_handler(const SyncEventData* event, void* context) {
if (event->event_type == SYNC_EVENT_CONFLICT_DETECTED) {
printf("⚠️ Conflict detected for document: %s\n", event->document_id);
// Show conflict resolution dialog
ConflictResolution resolution = show_conflict_dialog(
event->document_id,
"Local version conflicts with server version"
);
switch (resolution) {
case KEEP_LOCAL:
printf("User chose to keep local version\n");
// Force push local version
break;
case ACCEPT_SERVER:
printf("User chose to accept server version\n");
// Reload from server
break;
case MERGE_MANUALLY:
printf("User chose manual merge\n");
// Open merge editor
open_merge_editor(event->document_id);
break;
}
}
}Monitor sync performance and statistics:
// C++ example for performance monitoring
class PerformanceMonitor {
private:
struct {
int documents_created = 0;
int documents_updated = 0;
int documents_deleted = 0;
int sync_operations = 0;
int sync_errors = 0;
std::chrono::milliseconds total_sync_time{0};
} stats;
std::chrono::steady_clock::time_point sync_start;
public:
void handle_event(const SyncEventData* event) {
switch (event->event_type) {
case SYNC_EVENT_DOCUMENT_CREATED:
stats.documents_created++;
break;
case SYNC_EVENT_DOCUMENT_UPDATED:
stats.documents_updated++;
break;
case SYNC_EVENT_DOCUMENT_DELETED:
stats.documents_deleted++;
break;
case SYNC_EVENT_SYNC_STARTED:
sync_start = std::chrono::steady_clock::now();
break;
case SYNC_EVENT_SYNC_COMPLETED:
stats.sync_operations++;
stats.total_sync_time += std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - sync_start
);
break;
case SYNC_EVENT_SYNC_ERROR:
stats.sync_errors++;
break;
}
}
void print_stats() const {
std::cout << "\n📊 Performance Statistics:" << std::endl;
std::cout << "Documents created: " << stats.documents_created << std::endl;
std::cout << "Documents updated: " << stats.documents_updated << std::endl;
std::cout << "Documents deleted: " << stats.documents_deleted << std::endl;
std::cout << "Sync operations: " << stats.sync_operations << std::endl;
std::cout << "Sync errors: " << stats.sync_errors << std::endl;
if (stats.sync_operations > 0) {
auto avg_time = stats.total_sync_time.count() / stats.sync_operations;
std::cout << "Average sync time: " << avg_time << "ms" << std::endl;
}
}
};For comprehensive documentation and more examples, see EVENT_CALLBACKS.md.
- PostgreSQL running (use Docker Compose or local installation)
- Database created and accessible
First, start the server:
# Option 1: Using Docker (recommended)
docker-compose up -d
# Option 2: Manual setup
export DATABASE_URL="postgres://postgres:postgres@localhost/sync_db"
cargo run --bin replicant-server
# Option 3: With monitoring enabled (shows real-time activity)
export DATABASE_URL="postgres://postgres:postgres@localhost/sync_db"
MONITORING=true cargo run --bin replicant-serverWith monitoring enabled, the server displays:
- Real-time connection events
- All messages sent/received
- JSON patch diffs when documents are updated
- Conflict detection alerts
- Colorized output for easy debugging
In another terminal, run the interactive client:
# Run with defaults (creates databases/alice.sqlite3)
cargo run --package replicant-client --example interactive_client
# Or specify a different database name
cargo run --package replicant-client --example interactive_client -- --database bob
# Or specify custom options
cargo run --package replicant-client --example interactive_client -- \
--database my_tasks \
--server ws://localhost:8080/ws \
--token my-auth-tokenThe client provides a modern task management interface with:
- 📋 List tasks with status and priority indicators
- ➕ Create new tasks with form-based input
- ✏️ Edit tasks with guided field editing
- 🔍 View detailed task information
- ✅ Mark tasks as completed
- 🗑️ Delete tasks with confirmation
- 🔄 Check sync status
- 📱 Works offline with automatic sync when reconnected
- Start the server - You'll see it's ready when it displays the listening address
- Start a client - It will create a new user ID automatically
- Create a task - Use the guided interface to create a task with title, description, priority, and tags
- Watch the server - If monitoring is enabled, see real-time logs showing the task creation
- Edit the task - Make changes through the form interface and see JSON patches in server logs
- Start another client - Use the same database name to see tasks sync across clients
- Try different operations - Mark tasks complete, change priorities, add tags
🚀 Sync Server Monitor
=====================
📊 Connecting to database: postgres://postgres:postgres@localhost/sync_db
✅ Created demo user: 123e4567-e89b-12d3-a456-426614174000
🌐 Server listening on: 0.0.0.0:8080
🔌 WebSocket endpoint: ws://0.0.0.0:8080/ws
📋 Activity Log:
────────────────────────────────────────────────────────────────────────────────
14:23:15.123 → Client connected: a1b2c3d4-e5f6-7890-abcd-ef1234567890
14:23:15.456 ↓ Authenticate from a1b2c3d4-e5f6-7890-abcd-ef1234567890
14:23:15.457 ↑ AuthSuccess to a1b2c3d4-e5f6-7890-abcd-ef1234567890
14:23:20.789 ↓ CreateDocument from a1b2c3d4-e5f6-7890-abcd-ef1234567890
14:23:20.790 ↑ DocumentCreated to a1b2c3d4-e5f6-7890-abcd-ef1234567890
14:23:25.123 ↓ UpdateDocument from a1b2c3d4-e5f6-7890-abcd-ef1234567890
14:23:25.124 🔧 Patch applied to document 987fcdeb-51a2-43b7-8c9d-0e1f2a3b4c5d:
[
{
"op": "replace",
"path": "/content",
"value": "Updated content"
},
{
"op": "add",
"path": "/tags",
"value": ["example", "demo"]
}
]
14:23:25.125 ↑ DocumentUpdated to a1b2c3d4-e5f6-7890-abcd-ef1234567890
🚀 JSON Database Sync Client
============================
📁 Database: databases/alice.sqlite3
👤 User ID: 123e4567-e89b-12d3-a456-426614174000
🌐 Server: ws://localhost:8080/ws
✅ Connected to sync server!
What would you like to do?
> 📋 List tasks
➕ Create new task
✏️ Edit task
🔍 View task details
✅ Mark task completed
🗑️ Delete task
🔄 Sync status
❌ Exit
📋 Your Tasks:
────────────────────────────────────────────────────────────────────────────────────────────────
⏳ 🔴 987fcdeb Fix critical bug - Investigate database connection issues 📤
✅ 🟡 abc12345 Complete project documentation - Write API documentation
🔄 🟢 def67890 Code review - Review pull request #123
────────────────────────────────────────────────────────────────────────────────────────────────
Legend: ✅=done 🔄=progress ⏳=pending | 🔴=high 🟡=med 🟢=low | 📤=sync pending
The system supports demo mode for easy testing and development:
# Uses demo-token by default - no setup required
cargo run --package replicant-client --example interactive_client
# Server automatically creates users for demo-token
# Each client gets a unique user ID# Use your own auth token
cargo run --package replicant-client --example interactive_client -- \
--token my-custom-token \
--user-id 550e8400-e29b-41d4-a716-446655440000
# Server will auto-register users with custom tokens in demo mode- The client works offline - tasks show sync status indicators (📤 = pending sync)
- You can run multiple clients with the same database name to test real-time sync
- Use monitoring mode (
MONITORING=true) to see JSON patch diffs and debug sync issues - The task interface provides guided input - no need to write raw JSON
- Tasks support rich metadata: priorities, tags, descriptions, and completion status
- Use Ctrl+C to cleanly exit either application