Add auto-compact LMDB schedule setting with UI and tests
Closes: https://github.com/damus-io/damus/issues/3718 Changelog-Added: Automatic lossless storage optimization Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -17,6 +17,9 @@ final class NdbCompactionTests: XCTestCase {
|
||||
try FileManager.default.createDirectory(at: testDirectory, withIntermediateDirectories: true)
|
||||
// Ensure the flag is cleared before each test.
|
||||
UserDefaults.standard.set(false, forKey: Ndb.compact_on_next_launch_key)
|
||||
// Reset scheduling-related keys so tests start from a clean state.
|
||||
UserDefaults.standard.removeObject(forKey: Ndb.auto_compact_schedule_key)
|
||||
UserDefaults.standard.removeObject(forKey: Ndb.last_compact_date_key)
|
||||
}
|
||||
|
||||
override func tearDown() async throws {
|
||||
@@ -25,6 +28,9 @@ final class NdbCompactionTests: XCTestCase {
|
||||
}
|
||||
// Leave the flag cleared after each test.
|
||||
UserDefaults.standard.set(false, forKey: Ndb.compact_on_next_launch_key)
|
||||
// Clean up scheduling keys.
|
||||
UserDefaults.standard.removeObject(forKey: Ndb.auto_compact_schedule_key)
|
||||
UserDefaults.standard.removeObject(forKey: Ndb.last_compact_date_key)
|
||||
try await super.tearDown()
|
||||
}
|
||||
|
||||
@@ -171,4 +177,117 @@ final class NdbCompactionTests: XCTestCase {
|
||||
"lock.mdb should be recreated by LMDB after opening the compacted database"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - AutoCompactSchedule: interval values
|
||||
|
||||
func testAutoCompactSchedule_intervalValues() {
|
||||
XCTAssertEqual(AutoCompactSchedule.daily.interval, 60 * 60 * 24, "daily interval should be 24 hours")
|
||||
XCTAssertEqual(AutoCompactSchedule.weekly.interval, 60 * 60 * 24 * 7, "weekly interval should be 7 days")
|
||||
XCTAssertEqual(AutoCompactSchedule.monthly.interval, 60 * 60 * 24 * 30, "monthly interval should be 30 days")
|
||||
XCTAssertNil(AutoCompactSchedule.never.interval, ".never should have no interval")
|
||||
}
|
||||
|
||||
// MARK: - get/set auto-compact schedule
|
||||
|
||||
func testGetAutoCompactSchedule_returnsWeeklyByDefault() {
|
||||
// No key stored → default should be .weekly
|
||||
XCTAssertEqual(Ndb.get_auto_compact_schedule(), .weekly)
|
||||
}
|
||||
|
||||
func testSetAndGetAutoCompactSchedule_roundTrips() {
|
||||
for schedule in AutoCompactSchedule.allCases {
|
||||
Ndb.set_auto_compact_schedule(schedule)
|
||||
XCTAssertEqual(Ndb.get_auto_compact_schedule(), schedule,
|
||||
"Round-trip failed for schedule: \(schedule)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - schedule_auto_compact_if_needed
|
||||
|
||||
func testScheduleAutoCompact_doesNothingWhenNever() {
|
||||
Ndb.set_auto_compact_schedule(.never)
|
||||
|
||||
Ndb.schedule_auto_compact_if_needed()
|
||||
|
||||
XCTAssertFalse(
|
||||
UserDefaults.standard.bool(forKey: Ndb.compact_on_next_launch_key),
|
||||
"schedule=never should never set the compact flag"
|
||||
)
|
||||
}
|
||||
|
||||
func testScheduleAutoCompact_setsFlag_whenIntervalHasElapsed() {
|
||||
// Given: schedule is weekly, last compaction was 8 days ago
|
||||
Ndb.set_auto_compact_schedule(.weekly)
|
||||
let eightDaysAgo = Date().addingTimeInterval(-(60 * 60 * 24 * 8))
|
||||
UserDefaults.standard.set(eightDaysAgo, forKey: Ndb.last_compact_date_key)
|
||||
|
||||
// When
|
||||
Ndb.schedule_auto_compact_if_needed()
|
||||
|
||||
// Then: flag should be set
|
||||
XCTAssertTrue(
|
||||
UserDefaults.standard.bool(forKey: Ndb.compact_on_next_launch_key),
|
||||
"compact flag should be set when scheduled interval has elapsed"
|
||||
)
|
||||
}
|
||||
|
||||
func testScheduleAutoCompact_doesNotSetFlag_whenIntervalHasNotElapsed() {
|
||||
// Given: schedule is weekly, last compaction was 1 day ago
|
||||
Ndb.set_auto_compact_schedule(.weekly)
|
||||
let oneDayAgo = Date().addingTimeInterval(-(60 * 60 * 24))
|
||||
UserDefaults.standard.set(oneDayAgo, forKey: Ndb.last_compact_date_key)
|
||||
|
||||
// When
|
||||
Ndb.schedule_auto_compact_if_needed()
|
||||
|
||||
// Then: flag should NOT be set
|
||||
XCTAssertFalse(
|
||||
UserDefaults.standard.bool(forKey: Ndb.compact_on_next_launch_key),
|
||||
"compact flag should not be set when the scheduled interval has not yet elapsed"
|
||||
)
|
||||
}
|
||||
|
||||
func testScheduleAutoCompact_setsFlag_whenNoLastCompactDate() {
|
||||
// Given: schedule is daily, no last compact date stored (first launch)
|
||||
Ndb.set_auto_compact_schedule(.daily)
|
||||
// last_compact_date_key is absent (cleaned in setUp)
|
||||
|
||||
// When
|
||||
Ndb.schedule_auto_compact_if_needed()
|
||||
|
||||
// Then: flag should be set (distantPast triggers compaction on first launch)
|
||||
XCTAssertTrue(
|
||||
UserDefaults.standard.bool(forKey: Ndb.compact_on_next_launch_key),
|
||||
"compact flag should be set on first launch (no previous compaction date)"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - compact_if_needed: records last compact date
|
||||
|
||||
func testCompactIfNeeded_recordsLastCompactDate_afterSuccessfulCompaction() {
|
||||
let dbPath = testDirectory.appendingPathComponent("date_record_db").path
|
||||
try? FileManager.default.createDirectory(atPath: dbPath, withIntermediateDirectories: true)
|
||||
|
||||
guard let ndb = Ndb(path: dbPath) else {
|
||||
XCTFail("Could not open Ndb at \(dbPath)")
|
||||
return
|
||||
}
|
||||
ndb.close()
|
||||
|
||||
XCTAssertNil(Ndb.get_last_compact_date(), "No last compact date should exist before first compaction")
|
||||
|
||||
Ndb.set_compact_on_next_launch()
|
||||
|
||||
let beforeCompact = Date()
|
||||
Ndb.compact_if_needed(db_path: dbPath)
|
||||
let afterCompact = Date()
|
||||
|
||||
guard let lastDate = Ndb.get_last_compact_date() else {
|
||||
XCTFail("Last compact date should be set after successful compaction")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertGreaterThanOrEqual(lastDate, beforeCompact, "last compact date should be >= time before compaction started")
|
||||
XCTAssertLessThanOrEqual(lastDate, afterCompact, "last compact date should be <= time after compaction ended")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user