or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

changes-monitoring.mddatabase-management.mddocument-operations.mdindex.mdplugins-adapters.mdreplication-sync.md

replication-sync.mddocs/

0

# Replication and Synchronization

1

2

Bidirectional synchronization with remote databases, including continuous replication and conflict resolution. PouchDB's replication system enables offline-first applications with seamless data synchronization.

3

4

## Capabilities

5

6

### One-Way Replication

7

8

Replicate data from a source database to a target database in one direction.

9

10

```javascript { .api }

11

/**

12

* Replicate from another database to this database

13

* @param source - Source database (URL string or PouchDB instance)

14

* @param options - Replication options

15

* @returns Replication object with event emitter interface

16

*/

17

db.replicate.from(source: string | PouchDB, options?: ReplicationOptions): Replication;

18

19

/**

20

* Replicate from this database to another database

21

* @param target - Target database (URL string or PouchDB instance)

22

* @param options - Replication options

23

* @returns Replication object with event emitter interface

24

*/

25

db.replicate.to(target: string | PouchDB, options?: ReplicationOptions): Replication;

26

27

/**

28

* Static method for one-way replication between any two databases

29

* @param source - Source database

30

* @param target - Target database

31

* @param options - Replication options

32

* @returns Replication object

33

*/

34

PouchDB.replicate(source: string | PouchDB, target: string | PouchDB, options?: ReplicationOptions): Replication;

35

```

36

37

**Usage Examples:**

38

39

```javascript

40

// Replicate from remote to local

41

const replication = db.replicate.from('http://localhost:5984/remote-db', {

42

continuous: true,

43

retry: true

44

}).on('change', function(info) {

45

console.log('Received changes:', info.docs.length);

46

}).on('complete', function(info) {

47

console.log('Replication completed');

48

}).on('error', function(err) {

49

console.error('Replication error:', err);

50

});

51

52

// Replicate from local to remote

53

db.replicate.to('http://localhost:5984/remote-db')

54

.on('complete', function(info) {

55

console.log('Upload completed:', info.docs_written);

56

});

57

58

// Static replication method

59

PouchDB.replicate(localDb, remoteDb, {

60

filter: 'myapp/active_users'

61

});

62

```

63

64

### Bidirectional Synchronization

65

66

Synchronize data in both directions between databases with automatic conflict detection.

67

68

```javascript { .api }

69

/**

70

* Bidirectional synchronization with another database

71

* @param remoteDB - Remote database to sync with

72

* @param options - Synchronization options

73

* @returns Sync object with event emitter interface

74

*/

75

db.sync(remoteDB: string | PouchDB, options?: SyncOptions): Sync;

76

77

/**

78

* Static method for bidirectional sync between any two databases

79

* @param source - First database

80

* @param target - Second database

81

* @param options - Synchronization options

82

* @returns Sync object

83

*/

84

PouchDB.sync(source: string | PouchDB, target: string | PouchDB, options?: SyncOptions): Sync;

85

86

interface SyncOptions extends ReplicationOptions {

87

// Inherits all replication options

88

}

89

90

interface Sync extends EventEmitter {

91

cancel(): void;

92

on(event: 'change', listener: (info: SyncChangeInfo) => void): Sync;

93

on(event: 'complete', listener: (info: SyncComplete) => void): Sync;

94

on(event: 'error', listener: (error: Error) => void): Sync;

95

on(event: 'active', listener: () => void): Sync;

96

on(event: 'paused', listener: () => void): Sync;

97

}

98

```

99

100

**Usage Examples:**

101

102

```javascript

103

// Basic bidirectional sync

104

const sync = db.sync('http://localhost:5984/remote-db', {

105

live: true,

106

retry: true

107

}).on('change', function(info) {

108

console.log('Sync change:', info.direction);

109

if (info.direction === 'pull') {

110

console.log('Downloaded:', info.change.docs.length);

111

} else {

112

console.log('Uploaded:', info.change.docs_written);

113

}

114

}).on('active', function() {

115

console.log('Sync active');

116

}).on('paused', function() {

117

console.log('Sync paused');

118

});

119

120

// Stop sync

121

sync.cancel();

122

```

123

124

### Replication Options

125

126

Comprehensive configuration options for customizing replication behavior.

127

128

```javascript { .api }

129

interface ReplicationOptions {

130

continuous?: boolean; // Continuous replication

131

retry?: boolean; // Retry on failure

132

filter?: string | Function; // Replication filter

133

query_params?: object; // Filter parameters

134

doc_ids?: string[]; // Replicate specific documents only

135

checkpoint?: string | boolean; // Checkpoint handling

136

batch_size?: number; // Number of docs per batch (default: 100)

137

batches_limit?: number; // Number of concurrent batches (default: 10)

138

since?: number | string; // Starting sequence number

139

timeout?: number; // Request timeout

140

heartbeat?: number; // Heartbeat interval

141

selector?: MangoSelector; // Mango query selector (if supported)

142

}

143

144

interface Replication extends EventEmitter {

145

cancel(): void;

146

on(event: 'change', listener: (info: ReplicationChangeInfo) => void): Replication;

147

on(event: 'complete', listener: (info: ReplicationComplete) => void): Replication;

148

on(event: 'error', listener: (error: Error) => void): Replication;

149

on(event: 'active', listener: () => void): Replication;

150

on(event: 'paused', listener: () => void): Replication;

151

}

152

153

interface ReplicationChangeInfo {

154

docs: Document[]; // Documents that changed

155

docs_read: number; // Number of docs read

156

docs_written: number; // Number of docs written

157

doc_write_failures: number; // Number of write failures

158

errors: Error[]; // Array of errors

159

last_seq: number | string; // Last sequence processed

160

start_time: string; // Replication start time

161

}

162

```

163

164

**Usage Examples:**

165

166

```javascript

167

// Filtered replication

168

const filteredReplication = db.replicate.from(remoteDb, {

169

filter: function(doc) {

170

return doc.type === 'user' && doc.active === true;

171

},

172

continuous: true,

173

retry: true,

174

batch_size: 50,

175

batches_limit: 5

176

});

177

178

// Replicate specific documents

179

const specificReplication = db.replicate.from(remoteDb, {

180

doc_ids: ['user1', 'user2', 'config'],

181

continuous: true

182

});

183

184

// Replicate with design document filter

185

const designDocReplication = db.replicate.from(remoteDb, {

186

filter: 'myapp/active_users',

187

query_params: {

188

department: 'engineering'

189

}

190

});

191

```

192

193

### Authentication and Security

194

195

Handle authentication for remote database replication.

196

197

```javascript

198

// Basic authentication

199

const authReplication = db.replicate.from('http://username:password@localhost:5984/remote-db');

200

201

// URL with credentials

202

const remoteUrl = 'http://localhost:5984/remote-db';

203

const replication = db.replicate.from(remoteUrl, {

204

auth: {

205

username: 'myuser',

206

password: 'mypassword'

207

}

208

});

209

210

// Custom headers

211

const headerReplication = db.replicate.from(remoteUrl, {

212

headers: {

213

'Authorization': 'Bearer ' + token,

214

'X-Custom-Header': 'value'

215

}

216

});

217

```

218

219

### Conflict Resolution

220

221

Handle and resolve conflicts that occur during replication.

222

223

```javascript

224

// Monitor for conflicts during sync

225

const conflictSync = db.sync(remoteDb, {

226

live: true,

227

retry: true

228

}).on('change', function(info) {

229

// Check for conflicts in replicated documents

230

if (info.direction === 'pull') {

231

info.change.docs.forEach(async (doc) => {

232

if (doc._conflicts) {

233

await handleConflict(doc);

234

}

235

});

236

}

237

});

238

239

async function handleConflict(doc) {

240

console.log(`Conflict in document ${doc._id}`);

241

242

// Get all conflicting revisions

243

const conflicts = await Promise.all(

244

doc._conflicts.map(rev => db.get(doc._id, { rev }))

245

);

246

247

// Resolve conflict (example: merge strategy)

248

const resolved = mergeDocuments(doc, conflicts);

249

250

// Save resolved version

251

await db.put(resolved);

252

253

// Delete conflicting revisions

254

for (const conflict of conflicts) {

255

await db.remove(conflict._id, conflict._rev);

256

}

257

}

258

259

function mergeDocuments(main, conflicts) {

260

// Implement your merge strategy

261

// This example combines all unique fields

262

const merged = { ...main };

263

264

conflicts.forEach(conflict => {

265

Object.keys(conflict).forEach(key => {

266

if (!key.startsWith('_') && !merged[key]) {

267

merged[key] = conflict[key];

268

}

269

});

270

});

271

272

return merged;

273

}

274

```

275

276

### Continuous Sync with Error Handling

277

278

Implement robust continuous synchronization with proper error handling and recovery.

279

280

```javascript

281

function startContinuousSync(localDb, remoteUrl) {

282

let syncHandler;

283

284

function createSync() {

285

syncHandler = localDb.sync(remoteUrl, {

286

live: true,

287

retry: true,

288

heartbeat: 10000,

289

timeout: 30000

290

});

291

292

syncHandler.on('change', function(info) {

293

console.log(`Sync ${info.direction}:`, info.change.docs.length, 'docs');

294

updateSyncStatus('active', info);

295

});

296

297

syncHandler.on('active', function() {

298

console.log('Sync resumed');

299

updateSyncStatus('active');

300

});

301

302

syncHandler.on('paused', function(err) {

303

if (err) {

304

console.log('Sync paused due to error:', err);

305

updateSyncStatus('error', err);

306

} else {

307

console.log('Sync paused');

308

updateSyncStatus('paused');

309

}

310

});

311

312

syncHandler.on('error', function(err) {

313

console.error('Sync error:', err);

314

updateSyncStatus('error', err);

315

316

// Restart sync after delay

317

setTimeout(() => {

318

console.log('Restarting sync...');

319

createSync();

320

}, 5000);

321

});

322

323

syncHandler.on('complete', function(info) {

324

console.log('Sync completed');

325

updateSyncStatus('complete', info);

326

});

327

328

return syncHandler;

329

}

330

331

function updateSyncStatus(status, data) {

332

// Update UI or application state

333

document.dispatchEvent(new CustomEvent('syncStatusChange', {

334

detail: { status, data }

335

}));

336

}

337

338

// Start initial sync

339

return createSync();

340

}

341

342

// Usage

343

const syncHandler = startContinuousSync(db, 'http://localhost:5984/remote-db');

344

345

// Stop sync when needed

346

function stopSync() {

347

if (syncHandler) {

348

syncHandler.cancel();

349

}

350

}

351

```

352

353

### Selective Replication

354

355

Replicate only specific types of documents or data subsets.

356

357

```javascript

358

// Replicate only user documents

359

const userReplication = db.replicate.from(remoteDb, {

360

filter: function(doc) {

361

return doc._id.startsWith('user_');

362

},

363

continuous: true

364

});

365

366

// Replicate documents modified after specific date

367

const recentReplication = db.replicate.from(remoteDb, {

368

filter: function(doc) {

369

const modifiedDate = new Date(doc.modified);

370

const cutoffDate = new Date('2023-01-01');

371

return modifiedDate > cutoffDate;

372

}

373

});

374

375

// Use query parameters with design document filter

376

const parameterizedFilter = db.replicate.from(remoteDb, {

377

filter: 'myapp/by_department',

378

query_params: {

379

department: 'sales',

380

active: true

381

}

382

});

383

```

384

385

## Event Handling Patterns

386

387

### Promise-based Replication

388

389

Convert replication to Promise for one-time operations:

390

391

```javascript

392

function replicateOnce(source, target, options = {}) {

393

return new Promise((resolve, reject) => {

394

PouchDB.replicate(source, target, {

395

...options,

396

continuous: false

397

}).on('complete', resolve).on('error', reject);

398

});

399

}

400

401

// Usage

402

try {

403

const result = await replicateOnce(localDb, remoteDb);

404

console.log('Replication completed:', result.docs_written);

405

} catch (error) {

406

console.error('Replication failed:', error);

407

}

408

```

409

410

### Sync State Management

411

412

Track synchronization state across application lifecycle:

413

414

```javascript

415

class SyncManager {

416

constructor(localDb, remoteUrl) {

417

this.localDb = localDb;

418

this.remoteUrl = remoteUrl;

419

this.syncHandler = null;

420

this.status = 'stopped';

421

this.listeners = new Set();

422

}

423

424

start() {

425

if (this.syncHandler) {

426

this.stop();

427

}

428

429

this.syncHandler = this.localDb.sync(this.remoteUrl, {

430

live: true,

431

retry: true

432

});

433

434

this.syncHandler.on('change', (info) => {

435

this.emit('change', info);

436

});

437

438

this.syncHandler.on('active', () => {

439

this.status = 'active';

440

this.emit('statusChange', 'active');

441

});

442

443

this.syncHandler.on('paused', () => {

444

this.status = 'paused';

445

this.emit('statusChange', 'paused');

446

});

447

448

this.syncHandler.on('error', (err) => {

449

this.status = 'error';

450

this.emit('error', err);

451

});

452

}

453

454

stop() {

455

if (this.syncHandler) {

456

this.syncHandler.cancel();

457

this.syncHandler = null;

458

}

459

this.status = 'stopped';

460

this.emit('statusChange', 'stopped');

461

}

462

463

on(event, callback) {

464

this.listeners.add({ event, callback });

465

}

466

467

emit(event, data) {

468

this.listeners.forEach(listener => {

469

if (listener.event === event) {

470

listener.callback(data);

471

}

472

});

473

}

474

}

475

476

// Usage

477

const syncManager = new SyncManager(db, 'http://localhost:5984/remote-db');

478

syncManager.on('statusChange', (status) => {

479

console.log('Sync status:', status);

480

});

481

syncManager.start();

482

```

483

484

## Error Handling

485

486

Handle various error scenarios in replication:

487

488

```javascript

489

const replication = db.replicate.from(remoteDb)

490

.on('error', function(err) {

491

if (err.status === 401) {

492

console.log('Authentication failed');

493

// Handle auth error

494

} else if (err.status === 404) {

495

console.log('Remote database not found');

496

// Handle missing database

497

} else if (err.name === 'timeout') {

498

console.log('Replication timeout');

499

// Retry or handle timeout

500

} else if (err.error === 'forbidden') {

501

console.log('Insufficient permissions');

502

// Handle permissions error

503

} else {

504

console.error('Replication error:', err);

505

}

506

});

507

```

508

509

Common error scenarios:

510

- Network connectivity issues

511

- Authentication and authorization failures

512

- Database not found or deleted

513

- Insufficient disk space

514

- Replication conflicts

515

- Invalid document structures