diff --git a/entropylab_qpudb/_qpudatabase.py b/entropylab_qpudb/_qpudatabase.py index 188f3b6..e737059 100644 --- a/entropylab_qpudb/_qpudatabase.py +++ b/entropylab_qpudb/_qpudatabase.py @@ -130,7 +130,13 @@ def create_new_qpu_database( connection_hist = db_hist.open() root_hist = connection_hist.root() root_hist["entries"] = PersistentList( - [{"timestamp": datetime.utcnow(), "message": "initial commit"}] + [ + { + "timestamp": datetime.utcnow(), + "connected_tx": None, + "message": "initial commit", + } + ] ) transaction.commit() db_hist.close() @@ -176,17 +182,15 @@ def __init__(self, dbname, history_index=None, path=None): self._db = None super().__init__() self._con_hist = self._open_hist_db() - self._readonly, self._con = self._open_data_db(history_index) + 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 history_index is not None: - readonly = True message_index = history_index - at = self._con_hist.root()["entries"][history_index]["timestamp"] + at = self._con_hist.root()["entries"][history_index]["connected_tx"] else: - readonly = False message_index = len(hist_entries) - 1 at = None try: @@ -198,15 +202,12 @@ def _open_data_db(self, history_index): ) 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 {self._dbname} from " f"commit {self._str_hist_entry(hist_entries[message_index])} at index {message_index}" ) - return readonly, con + return con def _open_hist_db(self): histfilename = _hist_file_from_path(self._path, self._dbname) @@ -226,7 +227,7 @@ def __enter__(self): @property def readonly(self): - return self._readonly + return self._con.isReadOnly() def close(self) -> None: """ @@ -331,7 +332,7 @@ def commit(self, message: Optional[str] = None) -> None: Permanently store the existing state to the DB and add a new commit to the history list :param message: an optional message for the commit """ - if self._readonly: + 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() @@ -340,7 +341,9 @@ def commit(self, message: Optional[str] = None) -> None: hist_root = self._con_hist.root() hist_entries = hist_root["entries"] now = datetime.utcnow() - hist_entries.append({"timestamp": now, "message": message}) + hist_entries.append( + {"timestamp": now, "connected_tx": lt_after, "message": message} + ) self._con_hist.transaction_manager.commit() print( f"commiting qpu database {self._dbname} " @@ -384,7 +387,7 @@ def restore_from_history(self, history_index: int) -> None: :param history_index: History index from which to restore """ - readonly, con = self._open_data_db(history_index) + con = self._open_data_db(history_index) self._con.root()["elements"] = deepcopy(con.root()["elements"]) con.close() diff --git a/entropylab_qpudb/tests/test_qpudatabase.py b/entropylab_qpudb/tests/test_qpudatabase.py index 3209d09..aa9578f 100644 --- a/entropylab_qpudb/tests/test_qpudatabase.py +++ b/entropylab_qpudb/tests/test_qpudatabase.py @@ -239,13 +239,13 @@ def test_fail_on_commit_to_readonly(testdb): # open historical connection # should fail when DB is modified - with QpuDatabaseConnection(testdb, simp_resolver, history_index=0) as db: + with QpuDatabaseConnection(testdb, simp_resolver, history_index=1) as db: db.set("q1", "p1", 444) with pytest.raises(ReadOnlyError): db.commit("trying") # should fail when DB is not modified - with QpuDatabaseConnection(testdb, simp_resolver, history_index=0) as db: + with QpuDatabaseConnection(testdb, simp_resolver, history_index=1) as db: with pytest.raises(ReadOnlyError): db.commit("trying") @@ -262,15 +262,17 @@ def test_commit_unmodified(testdb): def test_restore_from_history(testdb): with QpuDatabaseConnection(testdb, simp_resolver) as db: + db.set("q2", "p1", 3.4) + db.commit("my first commit") print() db.set("q2", "p1", 11.0) print(db.get("q2", "p1")) - db.commit("my first commit") + db.commit("my second commit") with QpuDatabaseConnection(testdb, simp_resolver) as db: print() assert db.get("q2", "p1").value == 11.0 - db.restore_from_history(0) + db.restore_from_history(1) print(db.get("q2", "p1")) assert db.get("q2", "p1").value == 3.4 @@ -381,3 +383,46 @@ def test_use_in_entropy(testdb, simp_resolver): experiment_resources.get_resource(testdb).close() finally: os.remove("entropy.db") + + +def test_we_can_open_all_history_items(testdb): + indices = [] + expected_values = [] + actual_values = [] + + # prepare the database with items in the history + with QpuDatabaseConnection(testdb) as db: + for v_bias in range(6): + db.set("q1", "p1", v_bias) + db.commit() + indices.append(len(db._con_hist.root()["entries"]) - 1) + expected_values.append(db.q(1).p1.value) + + # try to pull an item from all history entries + with QpuDatabaseConnection(testdb) as db: + for i in indices: + db.restore_from_history(i) + actual_values.append(db.q(1).p1.value) + + assert expected_values == actual_values + + +def test_we_can_open_all_history_items_in_same_connection(testdb): + indices = [] + expected_values = [] + actual_values = [] + + # prepare the database with items in the history + with QpuDatabaseConnection(testdb) as db: + for v_bias in range(6): + db.set("q1", "p1", v_bias) + db.commit() + indices.append(len(db._con_hist.root()["entries"]) - 1) + expected_values.append(db.q(1).p1.value) + + # try to pull an item from all history entries + for i in indices: + db.restore_from_history(i) + actual_values.append(db.q(1).p1.value) + + assert expected_values == actual_values