blob: 57c9f419dd86b9326dd841da72d7e8add583a1f0 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_INDEXED_DB_INSTANCE_LEVELDB_CLEANUP_SCHEDULER_H_
#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_LEVELDB_CLEANUP_SCHEDULER_H_
#include <memory>
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "content/browser/indexed_db/status.h"
#include "content/common/content_export.h"
namespace blink {
struct IndexedDBDatabaseMetadata;
} // namespace blink
namespace leveldb {
class DB;
} // namespace leveldb
namespace content::indexed_db::level_db {
CONTENT_EXPORT BASE_DECLARE_FEATURE(kIdbInSessionDbCleanup);
class LevelDbTombstoneSweeper;
// Sweeps the IndexedDB LevelDB database looking for index tombstones, followed
// by a round of DB compaction. Sweeping is broken into phases so as to not
// impact ongoing transactions. Please check `Phase` description for more
// information. Also sets the last run time of the tombstone sweeper after a
// successful run. Ref: crbug.com/374691835
class CONTENT_EXPORT LevelDBCleanupScheduler {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// `kRunScheduled` is the state when the `running_state_` has been set. When
// `RunCleanupTask` is called, the scheduler will start the tombstone sweeper.
// `kTombstoneSweeper` is the state when the tombstone sweeper is running.
// The tombstone sweeper runs in rounds so this state can last multiple task
// rounds.
// `kDatabaseCompaction` is set when the tombstone sweeper completes and the
// next time the `RunCleanupTask` gets called, the DB compaction task is run.
// `kLoggingAndCleanup` is set when the DB compaction task completes and the
// next time logging operations are carried followed by resetting the state.
// LINT.IfChange(Phase)
enum class Phase {
kRunScheduled = 0,
kTombstoneSweeper = 1,
kDatabaseCompaction = 2,
kLoggingAndCleanup = 3,
kMaxValue = kLoggingAndCleanup,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/storage/enums.xml:LevelDBCleanupSchedulerPhase)
// Abstraction of backing store calls which are required
// by the scheduler.
class Delegate {
public:
// This function updates the next run timestamp for the
// tombstone sweeper in the database metadata.
// Virtual for testing.
// Returns `true` if the update was successful.
virtual bool UpdateEarliestSweepTime() = 0;
// This function updates the next run timestamp for the
// level db compaction in the database metadata.
// Virtual for testing.
// Returns `true` if the update was successful.
virtual bool UpdateEarliestCompactionTime() = 0;
virtual Status GetCompleteMetadata(
std::vector<std::unique_ptr<blink::IndexedDBDatabaseMetadata>>*
output) = 0;
};
struct RunningState {
RunningState();
RunningState(const RunningState&) = delete;
RunningState& operator=(const RunningState&) = delete;
~RunningState();
std::vector<std::unique_ptr<blink::IndexedDBDatabaseMetadata>>
metadata_vector;
std::unique_ptr<LevelDbTombstoneSweeper> tombstone_sweeper;
base::TimeDelta tombstone_sweeper_duration;
base::TimeDelta db_compaction_duration;
// The timer for running the cleanup tasks. Can be scheduled for either
// `kDeferTimeOnNoTransactions` when there are not active transactions
// or `kDeferTimeAfterLastTransaction` when there are active
// transactions.
base::RetainingOneShotTimer clean_up_scheduling_timer_;
Phase cleanup_phase = Phase::kRunScheduled;
int postpone_count = 0;
};
// `database` and `backing_store` must outlive this instance.
LevelDBCleanupScheduler(leveldb::DB* database, Delegate* backing_store);
LevelDBCleanupScheduler(const LevelDBCleanupScheduler&) = delete;
LevelDBCleanupScheduler& operator=(const LevelDBCleanupScheduler&) = delete;
virtual ~LevelDBCleanupScheduler();
// Initializes the running state if a certain amount of time has passed
// since the last run. This is invoked during transaction completion when
// enough number of tombstones are encountered by the cursor.
void Initialize();
const std::optional<RunningState>& GetRunningStateForTesting() const {
return running_state_;
}
// Stops any scheduled cleanup operations, which will be resumed once all
// active transactions are complete.
void OnTransactionStart();
// Schedules a cleanup task if `running_state_` is set and there are no other
// active transactions.
void OnTransactionComplete();
// To avoid interrupting active usage, defer runs until there has been no
// activity for 0.4 seconds.
static constexpr base::TimeDelta kDeferTime = base::Seconds(0.4);
// Threshold for the tombstones which were encountered during the
// lifetime of the cursor. Crossing it will cause scheduling of the
// `LevelDBCleanupScheduler`.
static constexpr int kTombstoneThreshold = 1000;
private:
// Starts the timer for the cleanup tasks.
void ScheduleNextCleanupTask();
// Logs the histograms for the current clean up tasks, resets the running
// state and marks the last run time.
void LogAndResetState();
// Orchestrates the clean up tasks. Decides if we need to schedule a sweeper
// run or DB compaction run or log and reset the scheduler state.
void RunCleanupTask();
bool RunTombstoneSweeper();
// The actual DB reference inside `TransactionalLevelDBDatabase` owned by
// `BackingStore`. It's instantiated before the scheduler and hence also
// destroyed after the scheduler.
const raw_ptr<leveldb::DB> database_;
const raw_ptr<Delegate> delegate_;
base::TimeTicks last_run_;
std::optional<RunningState> running_state_;
int active_transactions_count_ = 0;
};
} // namespace content::indexed_db::level_db
#endif // CONTENT_BROWSER_INDEXED_DB_INSTANCE_LEVELDB_CLEANUP_SCHEDULER_H_