The Sequential Appender is Duckling’s core innovation, providing 60,000+ rows/sec bulk loading performance using DuckDB’s native Appender API. This is 6x faster than traditional INSERT statements and enables syncing massive datasets (60M+ records) in minutes instead of hours.
The staging table swap is atomic. If the server crashes mid-sync, the production table remains unchanged and the orphan staging table is automatically cleaned up on next sync.
private appendValueByType(appender: any, value: any, mysqlType: string): void { const lowerType = mysqlType.toLowerCase(); if (value === null || value === undefined) { appender.appendNull(); return; } // Integer types if (lowerType.includes('bigint')) { appender.appendBigInt(BigInt(value)); } // TIMESTAMP/DATETIME - use typed method to prevent corruption else if (lowerType.includes('timestamp') || lowerType.includes('datetime')) { const m = String(value).match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?/); if (m) { const micros = m[7] ? parseInt(m[7].padEnd(6, '0').slice(0, 6), 10) : 0; appender.appendTimestamp(DuckDBTimestampValue.fromParts({ date: { year: parseInt(m[1], 10), month: parseInt(m[2], 10), day: parseInt(m[3], 10) }, time: { hour: parseInt(m[4], 10), min: parseInt(m[5], 10), sec: parseInt(m[6], 10), micros }, })); } else { appender.appendNull(); } } // ... (see source for full implementation)}
Critical: Use typed append methods (appendTimestamp, appendTime) for fractional-second values. Using appendVarchar for TIMESTAMP/TIME columns with fractional seconds silently corrupts the Appender.
Flush appender every 5,000 records (configurable via APPENDER_FLUSH_INTERVAL):
if (recordsProcessed - lastFlushed >= FLUSH_INTERVAL) { logger.info(`Flushing appender at ${recordsProcessed} records to free memory...`); appender.flushSync(); lastFlushed = recordsProcessed; // Force GC if available if (global.gc) { global.gc(); }}
Memory usage remains flat regardless of table size thanks to streaming batches and periodic flushing. A 60M row table processes with <500MB memory footprint.