So I ended up making a nodejs script for it that runs periodically.
Some things are hard coded since this was for testing, and not production (yet). Anyone referencing this should watch for hard coded references.
I used pouchdb to handle the replication, as it was able to run all 400,000 tombstones in a few minutes, whereas I never actually got couchdb to finish replicating after a full day of running.
This is my first node script ever, so I apologize if it's awful.
var PouchDB = require('pouchdb');
var fs = require('fs');
var child = require('child_process');
var originalDB = "dbName";
var tempDB = "dbName_clean";
var serviceName = "Apache CouchDB";
var couchDBDataPath = "C:\\bin\\couchdb\\couchdb.2.1.1\\data\\";
var db = new PouchDB('http://URL/' + originalDB);
var db2 = new PouchDB('http://URL/' + tempDB);
console.log("Compacting");
return db.compact().then(function (result) {
console.log("Compacting done, replicating");
var batch_size = 100;
return db2.destroy().then(function () {
db2 = new PouchDB('http://URL/' + tempDB);
return db.replicate.to(db2, {
batch_size: batch_size,
filter: function (doc, req) {
return !doc._deleted;
}
}).on('change', function (info) {
console.log("batch done");
}).on('complete', function () {
console.log('complete');
}).on('paused', function (err) {
// replication paused (e.g. replication up to date, user went offline)
console.log("paused", err);
}).on('active', function () {
// replicate resumed (e.g. new changes replicating, user went back online)
console.log("Active");
}).on('denied', function (err) {
// a document failed to replicate (e.g. due to permissions)
console.log("Denied", err);
}).on('error', function (err) {
// handle error
console.log("error", err);
// reject(err);
}).then(function () {
//replicate done, rename everything
var date = new Date();
console.log("Stopping service");
child.execSync('net stop "' + serviceName + '"');
console.log("Service Stopped");
var newName;
var counter = 0;
do {
newName = "_" + date.getFullYear() + date.getMonth() + date.getDay() + "_" + counter;
counter++;
} while (fs.existsSync(couchDBDataPath + originalDB + newName + ".couch") || fs.existsSync(couchDBDataPath + "." + originalDB + "_design" + newName));
console.log("Renaming original couch to backup labeled", originalDB + newName);
fs.renameSync(couchDBDataPath + originalDB + ".couch", couchDBDataPath + originalDB + newName + ".couch");
fs.renameSync(couchDBDataPath + "." + originalDB + "_design", couchDBDataPath + "." + originalDB + newName + "_design");
console.log("Renaming clean couch back to original", tempDB);
fs.renameSync(couchDBDataPath + tempDB + ".couch", couchDBDataPath + originalDB + ".couch");
fs.mkdirSync(couchDBDataPath + "." + originalDB + "_design");
console.log("Starting service");
child.execSync('net start "' + serviceName + '"');
console.log("Service started");
});
});
}).catch(function (err) {
console.log(err);
});