#ifndef CONTENT_BROWSER_INDEXED_DB_INSTANCE_TRANSACTION_H_
#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_TRANSACTION_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <tuple>
#include "base/containers/queue.h"
#include "base/containers/stack.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock_id.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h"
#include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h"
#include "components/services/storage/privileged/mojom/indexed_db_internals_types.mojom-forward.h"
#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_external_object_storage.h"
#include "content/browser/indexed_db/instance/backing_store.h"
#include "content/browser/indexed_db/instance/bucket_context_handle.h"
#include "content/browser/indexed_db/status.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
namespace content::indexed_db {
class Connection;
class Cursor;
class Database;
class CONTENT_EXPORT Transaction : public blink::mojom::IDBTransaction {
public:
enum State {
CREATED,
STARTED,
COMMITTING,
FINISHED,
};
static void DisableInactivityTimeoutForTesting();
Transaction(
int64_t id,
Connection* connection,
const std::set<int64_t>& object_store_ids,
blink::mojom::IDBTransactionMode mode,
blink::mojom::IDBTransactionDurability durability,
BucketContextHandle bucket_context,
std::unique_ptr<BackingStore::Transaction> backing_store_transaction);
~Transaction() override;
void BindReceiver(
mojo::PendingAssociatedReceiver<blink::mojom::IDBTransaction>
mojo_receiver);
void SetCommitFlag();
bool IsAcceptingRequests() {
return !is_commit_pending_ && state_ != COMMITTING && state_ != FINISHED;
}
Status Abort(const DatabaseError& error);
void Start();
void DontAllowInactiveClientToBlockOthers(
storage::mojom::DisallowInactiveClientReason reason);
bool IsTransactionBlockingOtherClients(bool consider_priority = false) const;
void OnSchedulingPriorityUpdated(int new_priority);
blink::mojom::IDBTransactionMode mode() const { return mode_; }
const std::set<int64_t>& scope() const { return object_store_ids_; }
using Operation = base::OnceCallback<Status(Transaction*)>;
using VerificationCallback = base::OnceCallback<Status(Transaction&)>;
void ScheduleTask(std::string operation_name_for_metrics,
Operation operation,
VerificationCallback verify = {}) {
ScheduleTask(blink::mojom::IDBTaskType::Normal,
std::move(operation_name_for_metrics), std::move(operation),
std::move(verify));
}
void ScheduleTask(blink::mojom::IDBTaskType type,
std::string operation_name_for_metrics,
Operation operation,
VerificationCallback verify = {});
void RegisterOpenCursor(Cursor* cursor);
void UnregisterOpenCursor(Cursor* cursor);
void AddPreemptiveEvent() { pending_preemptive_events_++; }
void DidCompletePreemptiveEvent() {
pending_preemptive_events_--;
DCHECK_GE(pending_preemptive_events_, 0);
}
static VerificationCallback ObjectStoreMustExist(int64_t object_store_id);
static VerificationCallback ObjectStoreAndIndexMustExist(
int64_t object_store_id,
std::optional<int64_t> index_id);
blink::mojom::IDBValuePtr BuildMojoValue(IndexedDBValue value);
Status RunTasks();
storage::mojom::IdbTransactionMetadataPtr GetIdbInternalsMetadata() const;
void NotifyOfIdbInternalsRelevantChange();
BackingStore::Transaction* BackingStoreTransaction() {
return backing_store_transaction_.get();
}
int64_t id() const { return id_; }
Connection* connection() const { return connection_.get(); }
bool is_commit_pending() const { return is_commit_pending_; }
int64_t num_errors_sent() const { return num_errors_sent_; }
int64_t num_errors_handled() const { return num_errors_handled_; }
void IncrementNumErrorsSent() { ++num_errors_sent_; }
State state() const { return state_; }
bool aborted() const { return aborted_; }
bool IsTimeoutTimerRunning() const { return timeout_timer_.IsRunning(); }
struct Diagnostics {
base::Time creation_time;
base::Time start_time;
int tasks_scheduled = 0;
int tasks_completed = 0;
bool mojo_receiver_disconnected = false;
};
const Diagnostics& diagnostics() const { return diagnostics_; }
base::WeakPtr<Transaction> AsWeakPtr() { return ptr_factory_.GetWeakPtr(); }
BucketContext* bucket_context() { return bucket_context_.bucket_context(); }
const base::flat_set<PartitionedLockId> lock_ids() const { return lock_ids_; }
PartitionedLockHolder* mutable_locks_receiver() { return &locks_receiver_; }
size_t in_flight_memory() const { return in_flight_memory_.ValueOrDie(); }
private:
friend class IndexedDBClassFactory;
friend class Connection;
friend class base::RefCounted<Transaction>;
friend class DatabaseOperationTest;
FRIEND_TEST_ALL_PREFIXES(TransactionTestMode, AbortPreemptive);
FRIEND_TEST_ALL_PREFIXES(TransactionTestMode, AbortTasks);
FRIEND_TEST_ALL_PREFIXES(TransactionTest, NoTimeoutReadOnly);
FRIEND_TEST_ALL_PREFIXES(TransactionTest, SchedulePreemptiveTask);
FRIEND_TEST_ALL_PREFIXES(TransactionTestMode, ScheduleNormalTask);
FRIEND_TEST_ALL_PREFIXES(TransactionTestMode, TaskFails);
FRIEND_TEST_ALL_PREFIXES(TransactionTest, Timeout);
FRIEND_TEST_ALL_PREFIXES(TransactionTest, TimeoutPreemptive);
FRIEND_TEST_ALL_PREFIXES(TransactionTest, TimeoutWithPriorities);
FRIEND_TEST_ALL_PREFIXES(DatabaseOperationTest, CreatePutDelete);
void CreateObjectStore(int64_t object_store_id,
const std::u16string& name,
const blink::IndexedDBKeyPath& key_path,
bool auto_increment) override;
void DeleteObjectStore(int64_t object_store_id) override;
void Put(int64_t object_store_id,
blink::mojom::IDBValuePtr value,
blink::IndexedDBKey key,
blink::mojom::IDBPutMode mode,
std::vector<blink::IndexedDBIndexKeys> index_keys,
blink::mojom::IDBTransaction::PutCallback callback) override;
void SetIndexKeys(int64_t object_store_id,
blink::IndexedDBKey primary_key,
blink::IndexedDBIndexKeys index_keys) override;
void SetIndexKeysDone() override;
void Commit(int64_t num_errors_handled) override;
void OnQuotaCheckDone(bool allowed);
bool CreateExternalObjects(
blink::mojom::IDBValuePtr& value,
std::vector<IndexedDBExternalObject>* external_objects,
uint64_t* total_size);
Status DoPendingCommit();
Status DoPut(int64_t object_store_id,
IndexedDBValue value,
blink::IndexedDBKey key,
blink::mojom::IDBPutMode put_mode,
std::vector<blink::IndexedDBIndexKeys> index_keys,
blink::mojom::IDBTransaction::PutCallback callback,
Transaction* transaction);
Status DoSetIndexKeys(int64_t object_store_id,
blink::IndexedDBKey primary_key,
blink::IndexedDBIndexKeys index_keys,
Transaction* transaction);
static Status CommitPhaseTwoProxy(Transaction* transaction);
bool IsTaskQueueEmpty() const;
bool HasPendingTasks() const;
Status BlobWriteComplete(StatusOr<BlobWriteResult> result);
void CloseOpenCursors();
Status CommitPhaseTwo();
void TimeoutFired();
void ResetTimeoutTimer();
void SetState(State state);
blink::IndexedDBKey GenerateAutoIncrementKey(int64_t object_store_id);
void OnMojoReceiverDisconnected();
const int64_t id_;
const std::set<int64_t> object_store_ids_;
const blink::mojom::IDBTransactionMode mode_;
const blink::mojom::IDBTransactionDurability durability_;
bool used_ = false;
State state_ = CREATED;
std::optional<int> scheduling_priority_at_last_state_change_;
base::flat_set<PartitionedLockId> lock_ids_;
PartitionedLockHolder locks_receiver_;
bool is_commit_pending_ = false;
base::WeakPtr<Connection> connection_;
base::WeakPtr<Database> database_;
BucketContextHandle bucket_context_;
base::CheckedNumeric<size_t> in_flight_memory_ = 0;
struct Task {
Task(std::string operation_name_for_metrics,
Operation operation,
VerificationCallback verify);
Task(const Task&) = delete;
Task& operator=(const Task&) = delete;
Task(Task&&);
Task& operator=(Task&&);
~Task();
std::string operation_name_for_metrics;
Operation operation;
VerificationCallback verify;
};
typedef base::queue<Task> TaskQueue;
TaskQueue task_queue_;
TaskQueue preemptive_task_queue_;
std::unique_ptr<BackingStore::Transaction> backing_store_transaction_;
bool backing_store_transaction_begun_ = false;
int pending_preemptive_events_ = 0;
bool processing_event_queue_ = false;
bool aborted_ = false;
int64_t num_errors_sent_ = 0;
int64_t num_errors_handled_ = 0;
int64_t preliminary_size_estimate_ = 0;
std::set<raw_ptr<Cursor, SetExperimental>> open_cursors_;
base::RepeatingTimer timeout_timer_;
int timeout_strikes_ = 0;
static constexpr base::TimeDelta kInactivityTimeoutPollPeriod =
base::Seconds(20);
static const int kMaxTimeoutStrikes = 3;
Diagnostics diagnostics_;
mojo::AssociatedReceiver<blink::mojom::IDBTransaction> receiver_;
base::WeakPtrFactory<Transaction> ptr_factory_{this};
};
}
#endif