Compact runs tombstone compaction from view and/or GSI indexes - ensuring there's nothing left in the indexes for tombstoned documents that have been purged by the server. Several Sync Gateway indexes server tombstones (deleted documents with an xattr). There currently isn't a mechanism for server
(ctx context.Context, skipRunningStateCheck bool, optionalProgressCallback compactProgressCallbackFunc, terminator *base.SafeTerminator, isScheduledBackgroundTask bool)
| 1470 | // |
| 1471 | // The `isScheduledBackgroundTask` parameter is used to indicate if the compaction is being run as part of a scheduled background task, or an ad-hoc user-initiated `/{db}/_compact` request. |
| 1472 | func (db *Database) Compact(ctx context.Context, skipRunningStateCheck bool, optionalProgressCallback compactProgressCallbackFunc, terminator *base.SafeTerminator, isScheduledBackgroundTask bool) (purgedDocCount int, err error) { |
| 1473 | if !skipRunningStateCheck { |
| 1474 | if !atomic.CompareAndSwapUint32(&db.CompactState, DBCompactNotRunning, DBCompactRunning) { |
| 1475 | return 0, base.HTTPErrorf(http.StatusServiceUnavailable, "Compaction already running") |
| 1476 | } |
| 1477 | |
| 1478 | defer atomic.CompareAndSwapUint32(&db.CompactState, DBCompactRunning, DBCompactNotRunning) |
| 1479 | } |
| 1480 | |
| 1481 | // Compact should be a no-op if not running w/ xattrs |
| 1482 | if !db.UseXattrs() { |
| 1483 | return 0, nil |
| 1484 | } |
| 1485 | |
| 1486 | purgeInterval := db.GetMetadataPurgeInterval(ctx, true) |
| 1487 | base.InfofCtx(ctx, base.KeyAll, "Tombstone compaction using the metadata purge interval of %.2f days.", purgeInterval.Hours()/24) |
| 1488 | |
| 1489 | // Trigger view compaction for all tombstoned documents older than the purge interval |
| 1490 | startTime := time.Now() |
| 1491 | purgeOlderThan := startTime.Add(-purgeInterval) |
| 1492 | |
| 1493 | purgeErrorCount := 0 |
| 1494 | addErrorCount := 0 |
| 1495 | deleteErrorCount := 0 |
| 1496 | |
| 1497 | if optionalProgressCallback != nil { |
| 1498 | defer optionalProgressCallback(&purgedDocCount) |
| 1499 | } |
| 1500 | |
| 1501 | base.InfofCtx(ctx, base.KeyAll, "Starting compaction of purged tombstones for %s ...", base.MD(db.Name)) |
| 1502 | |
| 1503 | purgeBody := Body{"_purged": true} |
| 1504 | for _, c := range db.CollectionByID { |
| 1505 | // shadow ctx, so that we can't misuse the parent's inside the loop |
| 1506 | ctx := c.AddCollectionContext(ctx) |
| 1507 | |
| 1508 | // create admin collection interface |
| 1509 | collection, err := db.GetDatabaseCollectionWithUser(c.ScopeName, c.Name) |
| 1510 | if err != nil { |
| 1511 | base.WarnfCtx(ctx, "Tombstone compaction could not get collection: %s", err) |
| 1512 | continue |
| 1513 | } |
| 1514 | |
| 1515 | for { |
| 1516 | purgedDocs := make([]string, 0) |
| 1517 | results, err := collection.QueryTombstones(ctx, purgeOlderThan, QueryTombstoneBatch) |
| 1518 | if isScheduledBackgroundTask { |
| 1519 | base.SyncGatewayStats.GlobalStats.ResourceUtilizationStats().NumIdleQueryOps.Add(1) |
| 1520 | } |
| 1521 | if err != nil { |
| 1522 | return 0, err |
| 1523 | } |
| 1524 | var tombstonesRow QueryIdRow |
| 1525 | var resultCount int |
| 1526 | for results.Next(ctx, &tombstonesRow) { |
| 1527 | select { |
| 1528 | case <-terminator.Done(): |
| 1529 | closeErr := results.Close() |