Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

Commit

Permalink
additional fix + added feature: restore from history
Browse files Browse the repository at this point in the history
fix: history index will not be incremented for commits without modifications

feature: restore current DB to a historical DB
  • Loading branch information
liorella-qm committed May 8, 2021
1 parent 8075d09 commit 640944a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 30 deletions.
86 changes: 57 additions & 29 deletions entropylab_qpudb/_qpudatabase.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import dataclasses
import json
import os
from copy import deepcopy
from dataclasses import dataclass
from datetime import datetime
from enum import Enum, auto
Expand Down Expand Up @@ -133,35 +134,45 @@ def deserialize_function(snapshot: str, class_object: Type):
def __init__(self, dbname, history_index=None, path=None):
if path is None:
path = os.getcwd()
dbfilename = _db_file_from_path(path, dbname)
histfilename = _hist_file_from_path(path, dbname)
self._path = path
self._dbname = dbname
dbfilename = _db_file_from_path(self._path, self._dbname)
if not os.path.exists(dbfilename):
raise FileNotFoundError(f"QPU DB {dbname} does not exist")
raise FileNotFoundError(f"QPU DB {self._dbname} does not exist")
self._db = None
super().__init__()
self._dbname = dbname
self._history_index = history_index
db_hist = ZODB.DB(histfilename)
self._con_hist = db_hist.open(
transaction_manager=transaction.TransactionManager()
)
self._con_hist.transaction_manager.begin()
self._con_hist = self.open_hist_db()
self._readonly, self._con = self.open_data_db(history_index)

def open_data_db(self, history_index):
dbfilename = _db_file_from_path(self._path, self._dbname)
hist_entries = self._con_hist.root()["entries"]
if self._history_index is not None:
self._readonly = True
message_index = self._history_index
at = self._con_hist.root()["entries"][self._history_index]["timestamp"]
if history_index is not None:
readonly = True
message_index = history_index
at = self._con_hist.root()["entries"][history_index]["timestamp"]
else:
self._readonly = False
readonly = False
message_index = len(hist_entries) - 1
at = None
db = ZODB.DB(dbfilename)
self._con = db.open(transaction_manager=transaction.TransactionManager(), at=at)
assert self._con.isReadOnly() == self._readonly, "internal error: Inconsistent readonly state"
self._con.transaction_manager.begin()
self._db = ZODB.DB(dbfilename) if self._db is None else self._db
con = self._db.open(transaction_manager=transaction.TransactionManager(), at=at)
assert con.isReadOnly() == readonly, "internal error: Inconsistent readonly state"
con.transaction_manager.begin()
print(
f"opening qpu database {dbname} from "
f"opening qpu database {self._dbname} from "
f"commit {self._str_hist_entry(hist_entries[message_index])} at index {message_index}"
)
return readonly, con

def open_hist_db(self):
histfilename = _hist_file_from_path(self._path, self._dbname)
db_hist = ZODB.DB(histfilename)
con_hist = db_hist.open(
transaction_manager=transaction.TransactionManager()
)
con_hist.transaction_manager.begin()
return con_hist

def __enter__(self):
return self
Expand Down Expand Up @@ -248,16 +259,21 @@ def commit(self, message: Optional[str] = None) -> None:
"""
if self._readonly:
raise ReadOnlyError("Attempting to commit to a DB in a readonly state")
lt_before = self._con._db.lastTransaction()
self._con.transaction_manager.commit()
hist_root = self._con_hist.root()
hist_entries = hist_root["entries"]
now = datetime.utcnow()
hist_entries.append({"timestamp": now, "message": message})
self._con_hist.transaction_manager.commit()
print(
f"commiting qpu database {self._dbname} "
f"with commit {self._str_hist_entry(hist_entries[-1])} at index {len(hist_entries) - 1}"
)
lt_after = self._con._db.lastTransaction()
if lt_before != lt_after: # this means a commit actually took place
hist_root = self._con_hist.root()
hist_entries = hist_root["entries"]
now = datetime.utcnow()
hist_entries.append({"timestamp": now, "message": message})
self._con_hist.transaction_manager.commit()
print(
f"commiting qpu database {self._dbname} "
f"with commit {self._str_hist_entry(hist_entries[-1])} at index {len(hist_entries) - 1}"
)
else:
print('did not commit')

def abort(self):
self._con.transaction_manager.abort()
Expand All @@ -283,6 +299,18 @@ def get_history(self) -> pd.DataFrame:
def _str_hist_entry(hist_entry):
return f"<timestamp: {hist_entry['timestamp'].strftime('%m/%d/%Y %H:%M:%S')}, message: {hist_entry['message']}>"

def restore_from_history(self, history_index: int) -> None:
"""
restore the current unmodified and open DB data to be the same as the one from `history_index`.
Will not commit the restored data.
:param history_index: History index from which to restore
"""
readonly, con = self.open_data_db(history_index)
self._con.root()["elements"] = deepcopy(con.root()["elements"])
con.close()
print(con)


class QpuDatabaseConnection(QpuDatabaseConnectionBase):
def __init__(self, dbname, resolver, **kwargs):
Expand Down
35 changes: 35 additions & 0 deletions entropylab_qpudb/tests/test_qpudatabase.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ def test_simple_set_with_commit(testdb):
db.set("q2", "p1", 11.0)
print(db.get("q2", "p1"))
db.commit()
assert len(db.get_history()) == 2

with QpuDatabaseConnectionBase(testdb) as db:
print()
assert len(db.get_history()) == 2
print(db.get("q2", "p1"))
assert db.get("q2", "p1").value == 11.0

Expand All @@ -118,11 +120,14 @@ def test_set_with_commit_multiple(testdb):
print()
db.set("q2", "p1", 11.0)
print(db.get("q2", "p1"))
assert len(db.get_history()) == 1
db.commit()
assert len(db.get_history()) == 2

with QpuDatabaseConnectionBase(testdb) as db:
db.set("q1", "p1", [1, 2])
db.commit()
assert len(db.get_history()) == 3

with QpuDatabaseConnectionBase(testdb) as db:
print()
Expand Down Expand Up @@ -188,6 +193,36 @@ def test_fail_on_commit_to_readonly(testdb):
db.commit('trying')


def test_commit_unmodified(testdb):
with QpuDatabaseConnection(testdb, simp_resolver) as db:
print()
print(_parseRaw(db._con._db.lastTransaction()))
assert len(db.get_history()) == 1
db.commit("my first commit - unmodified")
print(_parseRaw(db._con._db.lastTransaction()))
assert len(db.get_history()) == 1


def test_restore_from_history(testdb):
with QpuDatabaseConnection(testdb, simp_resolver) as db:
print()
db.set("q2", "p1", 11.0)
print(db.get("q2", "p1"))
db.commit("my first commit")

with QpuDatabaseConnection(testdb, simp_resolver) as db:
print()
assert db.get("q2", "p1").value == 11.0
db.restore_from_history(0)
print(db.get("q2", "p1"))
assert db.get("q2", "p1").value == 3.4

# make sure wasn't committed
with QpuDatabaseConnection(testdb, simp_resolver) as db:
print()
assert db.get("q2", "p1").value == 11.0


def test_print(testdb):
with QpuDatabaseConnectionBase(testdb) as db:
print()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "entropylab-qpudb"
version = "0.0.1-alpha0"
version = "0.0.3"
description = "A extension of entropy lab for persistent storage of calibration parameters of a quantum processing unit (QPU)"
authors = ["Lior Ella <[email protected]>"]

Expand Down

0 comments on commit 640944a

Please sign in to comment.