Remove Update and Reconstruct from GraphDbAccessor
Summary: Code simplification made possible by making `locks_` `mutable` in `tx::Transaction`. Reviewers: dgleich, buda Reviewed By: buda Differential Revision: https://phabricator.memgraph.io/D1015
This commit is contained in:
parent
2b2de245d1
commit
3bd5a882cf
@ -519,56 +519,6 @@ class GraphDbAccessor {
|
|||||||
/** Return's the database's write-ahead log */
|
/** Return's the database's write-ahead log */
|
||||||
durability::WriteAheadLog &wal();
|
durability::WriteAheadLog &wal();
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the record pointers in the given accessor.
|
|
||||||
* The old_ and new_ pointers need to be initialized
|
|
||||||
* with appropriate values, and current_ set to old_
|
|
||||||
* if it exists and to new_ otherwise.
|
|
||||||
*
|
|
||||||
* @return True if accessor is valid after reconstruction.
|
|
||||||
* This means that at least one record pointer was found
|
|
||||||
* (either new_ or old_), possibly both.
|
|
||||||
*/
|
|
||||||
template <typename TRecord>
|
|
||||||
bool Reconstruct(RecordAccessor<TRecord> &accessor) {
|
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
|
||||||
accessor.vlist_->find_set_old_new(*transaction_, accessor.old_,
|
|
||||||
accessor.new_);
|
|
||||||
accessor.current_ = accessor.old_ ? accessor.old_ : accessor.new_;
|
|
||||||
return accessor.old_ != nullptr || accessor.new_ != nullptr;
|
|
||||||
// TODO review: we should never use a record accessor that
|
|
||||||
// does not have either old_ or new_ (both are null), but
|
|
||||||
// we can't assert that here because we construct such an accessor
|
|
||||||
// and filter it out in GraphDbAccessor::[Vertices|Edges]
|
|
||||||
// any ideas?
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update accessor record with vlist.
|
|
||||||
*
|
|
||||||
* It is not legal
|
|
||||||
* to call this function on a Vertex/Edge that has
|
|
||||||
* been deleted in the current transaction+command.
|
|
||||||
*
|
|
||||||
* @args accessor whose record to update if possible.
|
|
||||||
*/
|
|
||||||
template <typename TRecord>
|
|
||||||
void Update(RecordAccessor<TRecord> &accessor) {
|
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
|
||||||
// can't update a deleted record if:
|
|
||||||
// - we only have old_ and it hasn't been deleted
|
|
||||||
// - we have new_ and it hasn't been deleted
|
|
||||||
if (!accessor.new_) {
|
|
||||||
DCHECK(!accessor.old_->is_expired_by(*transaction_))
|
|
||||||
<< "Can't update a record deleted in the current transaction+commad";
|
|
||||||
} else {
|
|
||||||
DCHECK(!accessor.new_->is_expired_by(*transaction_))
|
|
||||||
<< "Can't update a record deleted in the current transaction+command";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!accessor.new_) accessor.new_ = accessor.vlist_->update(*transaction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current value of the counter with the given name, and
|
* Returns the current value of the counter with the given name, and
|
||||||
* increments that counter. If the counter with the given name does not exist,
|
* increments that counter. If the counter with the given name does not exist,
|
||||||
|
@ -28,7 +28,7 @@ class VersionList {
|
|||||||
* creating the first Record (Version) in this VersionList.
|
* creating the first Record (Version) in this VersionList.
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
VersionList(tx::Transaction &t, int64_t id, Args &&... args) : id_(id) {
|
VersionList(const tx::Transaction &t, int64_t id, Args &&... args) : id_(id) {
|
||||||
// TODO replace 'new' with something better
|
// TODO replace 'new' with something better
|
||||||
auto *v1 = new T(std::forward<Args>(args)...);
|
auto *v1 = new T(std::forward<Args>(args)...);
|
||||||
v1->mark_created(t);
|
v1->mark_created(t);
|
||||||
@ -191,7 +191,7 @@ class VersionList {
|
|||||||
*
|
*
|
||||||
* @param t The transaction
|
* @param t The transaction
|
||||||
*/
|
*/
|
||||||
T *update(tx::Transaction &t) {
|
T *update(const tx::Transaction &t) {
|
||||||
DCHECK(head_ != nullptr) << "Head is nullptr on update.";
|
DCHECK(head_ != nullptr) << "Head is nullptr on update.";
|
||||||
T *old_record = nullptr;
|
T *old_record = nullptr;
|
||||||
T *new_record = nullptr;
|
T *new_record = nullptr;
|
||||||
@ -208,7 +208,7 @@ class VersionList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Makes the given record as being expired by the given transaction. */
|
/** Makes the given record as being expired by the given transaction. */
|
||||||
void remove(T *record, tx::Transaction &t) {
|
void remove(T *record, const tx::Transaction &t) {
|
||||||
DCHECK(record != nullptr) << "Record is nullptr on removal.";
|
DCHECK(record != nullptr) << "Record is nullptr on removal.";
|
||||||
lock_and_validate(record, t);
|
lock_and_validate(record, t);
|
||||||
record->mark_expired(t);
|
record->mark_expired(t);
|
||||||
@ -217,7 +217,7 @@ class VersionList {
|
|||||||
const int64_t id_;
|
const int64_t id_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void lock_and_validate(T *record, tx::Transaction &t) {
|
void lock_and_validate(T *record, const tx::Transaction &t) {
|
||||||
DCHECK(record != nullptr) << "Record is nullptr on lock and validation.";
|
DCHECK(record != nullptr) << "Record is nullptr on lock and validation.";
|
||||||
|
|
||||||
// take a lock on this node
|
// take a lock on this node
|
||||||
@ -231,7 +231,7 @@ class VersionList {
|
|||||||
throw SerializationError();
|
throw SerializationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
T *update(T *record, tx::Transaction &t) {
|
T *update(T *record, const tx::Transaction &t) {
|
||||||
DCHECK(record != nullptr) << "Record is nullptr on update.";
|
DCHECK(record != nullptr) << "Record is nullptr on update.";
|
||||||
lock_and_validate(record, t);
|
lock_and_validate(record, t);
|
||||||
|
|
||||||
@ -257,4 +257,3 @@ class VersionList {
|
|||||||
RecordLock lock_;
|
RecordLock lock_;
|
||||||
};
|
};
|
||||||
} // namespace mvcc
|
} // namespace mvcc
|
||||||
|
|
||||||
|
@ -108,12 +108,29 @@ RecordAccessor<TRecord> &RecordAccessor<TRecord>::SwitchOld() {
|
|||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
bool RecordAccessor<TRecord>::Reconstruct() {
|
bool RecordAccessor<TRecord>::Reconstruct() {
|
||||||
return db_accessor().Reconstruct(*this);
|
vlist_->find_set_old_new(db_accessor_->transaction(), old_, new_);
|
||||||
|
current_ = old_ ? old_ : new_;
|
||||||
|
return old_ != nullptr || new_ != nullptr;
|
||||||
|
// We should never use a record accessor that does not have either old_ or
|
||||||
|
// new_ (both are null), but we can't assert that here because we construct
|
||||||
|
// such an accessor and filter it out in GraphDbAccessor::[Vertices|Edges].
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TRecord>
|
template <typename TRecord>
|
||||||
TRecord &RecordAccessor<TRecord>::update() {
|
TRecord &RecordAccessor<TRecord>::update() {
|
||||||
db_accessor().Update(*this);
|
auto &t = db_accessor_->transaction();
|
||||||
|
// can't update a deleted record if:
|
||||||
|
// - we only have old_ and it hasn't been deleted
|
||||||
|
// - we have new_ and it hasn't been deleted
|
||||||
|
if (!new_) {
|
||||||
|
DCHECK(!old_->is_expired_by(t))
|
||||||
|
<< "Can't update a record deleted in the current transaction+commad";
|
||||||
|
} else {
|
||||||
|
DCHECK(!new_->is_expired_by(t))
|
||||||
|
<< "Can't update a record deleted in the current transaction+command";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_) new_ = vlist_->update(t);
|
||||||
DCHECK(new_ != nullptr) << "RecordAccessor.new_ is null after update";
|
DCHECK(new_ != nullptr) << "RecordAccessor.new_ is null after update";
|
||||||
return *new_;
|
return *new_;
|
||||||
}
|
}
|
||||||
|
@ -130,34 +130,31 @@ class RecordAccessor : public TotalOrdering<RecordAccessor<TRecord>> {
|
|||||||
RecordAccessor<TRecord> &SwitchNew();
|
RecordAccessor<TRecord> &SwitchNew();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to switch this accessor to use the
|
* Attempts to switch this accessor to use the latest version not updated by
|
||||||
* latest version not updated by the current transaction+command.
|
* the current transaction+command. If that is not possible (vertex/edge was
|
||||||
* If that is not possible (vertex/edge was created
|
* created by the current transaction/command), it does nothing (current
|
||||||
* by the current transaction/command), it does nothing
|
* remains pointing to the new version).
|
||||||
* (current remains pointing to the new version).
|
|
||||||
*
|
|
||||||
* @return A reference to this.
|
* @return A reference to this.
|
||||||
*/
|
*/
|
||||||
RecordAccessor<TRecord> &SwitchOld();
|
RecordAccessor<TRecord> &SwitchOld();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconstructs the internal state of the record
|
Reconstructs the internal state of the record accessor so it uses the
|
||||||
* accessor so it uses the versions appropriate
|
versions appropriate to this transaction+command.
|
||||||
* to this transaction+command.
|
|
||||||
*
|
*
|
||||||
* @return True if this accessor is valid after reconstruction.
|
@return True if this accessor is valid after reconstruction. This means that
|
||||||
* This means that at least one record pointer was found
|
at least one record pointer was found (either new_ or old_), possibly both.
|
||||||
* (either new_ or old_), possibly both.
|
|
||||||
*/
|
*/
|
||||||
bool Reconstruct();
|
bool Reconstruct();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Ensures there is an updateable version of the record
|
* Ensures there is an updateable version of the record in the version_list,
|
||||||
* in the version_list, and that the `new_` pointer
|
* and that the `new_` pointer points to it. Returns a reference to that
|
||||||
* points to it. Returns a reference to that version.
|
* version.
|
||||||
*
|
*
|
||||||
* @return See above.
|
* It is not legal to call this function on a Vertex/Edge that has been
|
||||||
|
* deleted in the current transaction+command.
|
||||||
*/
|
*/
|
||||||
TRecord &update();
|
TRecord &update();
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ Transaction::Transaction(transaction_id_t id, const Snapshot &snapshot,
|
|||||||
Engine &engine)
|
Engine &engine)
|
||||||
: id_(id), engine_(engine), snapshot_(snapshot) {}
|
: id_(id), engine_(engine), snapshot_(snapshot) {}
|
||||||
|
|
||||||
void Transaction::TakeLock(RecordLock &lock) {
|
void Transaction::TakeLock(RecordLock &lock) const {
|
||||||
locks_.Take(&lock, *this, engine_);
|
locks_.Take(&lock, *this, engine_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class Transaction {
|
|||||||
public:
|
public:
|
||||||
/** Acquires the lock over the given RecordLock, preventing other transactions
|
/** Acquires the lock over the given RecordLock, preventing other transactions
|
||||||
* from doing the same */
|
* from doing the same */
|
||||||
void TakeLock(RecordLock &lock);
|
void TakeLock(RecordLock &lock) const;
|
||||||
|
|
||||||
/** Commits this transaction. After this call this transaction object is no
|
/** Commits this transaction. After this call this transaction object is no
|
||||||
* longer valid for use (it gets deleted by the engine that owns it). */
|
* longer valid for use (it gets deleted by the engine that owns it). */
|
||||||
@ -82,7 +82,7 @@ class Transaction {
|
|||||||
const Snapshot snapshot_;
|
const Snapshot snapshot_;
|
||||||
|
|
||||||
// Record locks held by this transaction.
|
// Record locks held by this transaction.
|
||||||
LockStore locks_;
|
mutable LockStore locks_;
|
||||||
|
|
||||||
// True if transaction should abort. Used to signal query executor that it
|
// True if transaction should abort. Used to signal query executor that it
|
||||||
// should stop execution, it is only a hint, transaction can disobey.
|
// should stop execution, it is only a hint, transaction can disobey.
|
||||||
|
Loading…
Reference in New Issue
Block a user