AbstractDbConnection

This commit is contained in:
AF 2023-02-27 11:38:36 +00:00
parent ba3d392328
commit fffff4973e
4 changed files with 69 additions and 49 deletions

View File

@ -286,6 +286,7 @@ async def main():
LogEE(__import__('ptvp35').FutureContext, '__init__').enter(es)
ALogEE(__import__('ptvp35').FutureContext, '__aenter__').enter(es)
ALogEE(__import__('ptvp35').FutureContext, '__aexit__').enter(es)
ALogEE(__import__('ptvp35').FutureContext, 'wait').enter(es)
LogEE(__import__('ptvp35').TransactionView, '__init__').enter(es)
LogEE(__import__('ptvp35').TransactionView, 'future_context').enter(es)

View File

@ -5,9 +5,9 @@ Main-Memory DataBase
--------------------
* Represented via Python dictionary (:code:`dict`).
Future version may include more abstract options, i.e. :class:`ptvp35.DbConnection` would be generic over :attr:`ptvp35.DbConnection.__mmdb` field
Future version may include more abstract options, i.e. :class:`~ptvp35.DbConnection` would be generic over :attr:`~ptvp35._DbConnection.__mmdb` field
* Keys and values are almost guaranteed to be serializable.
All KVPs loaded on :code:`__aenter__` are re-serialized during file reload.
All KVPs loaded on :meth:`~ptvp35.DbManager.__aenter__` are re-serialized during file reload.
All KVPs added via submit-like methods are serialized before being added to the MMDB.
DataBase Stream File
@ -37,32 +37,34 @@ DataBase Stream File
Request Queue
-------------
Transaction View (:class:`ptvp35.TransactionView`)
Transaction View (:class:`~ptvp35.TransactionView`)
--------------------------------------------------
Connection-like interface on top of another connection-like interface.
* Provides most of the same methods as :class:`ptvp35.DbConnection`.
* From the common :class:`ptvp35.VirtualConnection` interface/base class:
* :meth:`ptvp35.TransactionView.get`
* :meth:`ptvp35.TransactionView.commit_transaction`
* :meth:`ptvp35.TransactionView.submit_transaction_request`
* :meth:`ptvp35.TransactionView.loop`
* :meth:`ptvp35.TransactionView.transaction` (default implementation)
* Non-standard common methods:
* :meth:`ptvp35.TransactionView.set_nowait`
* :meth:`ptvp35.TransactionView.submit_transaction`
* :meth:`ptvp35.TransactionView.commit`
* Does not have the the analogue for the :meth:`ptvp35.DbConnection.set` method.
* Provides most of the same methods as :class:`~ptvp35.DbConnection`.
* From the common :class:`~ptvp35.VirtualConnection` interface/base class:
* :meth:`~ptvp35.TransactionView.get`
* :meth:`~ptvp35.TransactionView.commit_transaction`
* :meth:`~ptvp35.TransactionView.submit_transaction_request`
* :meth:`~ptvp35.TransactionView.loop`
* :meth:`~ptvp35.VirtualConnection.transaction` (default implementation)
* Extra common methods (:class:`~ptvp35.ExtendedVirtualConnection`):
* :meth:`~ptvp35.TransactionView.set_nowait`
* :meth:`~ptvp35.TransactionView.submit_transaction`
* :meth:`~ptvp35.TransactionView.commit`
* Does not have the the analogue for the :meth:`~ptvp35.DbInterface.set` method.
* The reason for that is :code:`set` method having semantics contradictory to transactions.
* The :code:`set` provides a way to set a *single* value and wait until it's committed.
* Transaction are meant for a more fine control.
* The equivalent would consist of the three method calls:
* :meth:`ptvp35.TransactionView.set_nowait` to set the value in :code:`__delta`.
* :meth:`ptvp35.TransactionView.submit` to pass all the :code:`__delta` values.
* :meth:`ptvp35.TransactionView.commit` wait until all changes are commited.
* Transactions are meant for a more fine control.
* The equivalent would consist of using a subtransaction or of the following method calls:
* :meth:`~ptvp35.TransactionView.set_nowait` to set the value in :code:`__delta`.
* :meth:`~ptvp35.TransactionView.submit` to pass all the :code:`__delta` values.
* :meth:`~ptvp35.TransactionView.future_context` to get the context for that specific key.
* :meth:`~ptvp35.FutureContext.wait` to wait until that key is committed.
* :meth:`~ptvp35.TransactionView.illuminate` to clear :code:`__shadow` thus resetting the view to DB state.
Transaction (:class:`ptvp35.Transaction`)
Transaction (:class:`~ptvp35.Transaction`)
-----------------------------------------
Manages a Transaction View.

View File

@ -13,7 +13,7 @@ Default installation option is to use pip+git
Basic functionality
-------------------
:class:`ptvp35.DbFactory` class provides context management for database connections (:class:`ptvp35.DbConnection`) via :code:`async with` statement.
:class:`~ptvp35.DbFactory` class provides context management for database connections (:class:`~ptvp35.DbConnection`) via :code:`async with` statement.
The connection isn't just a "connection", it's also the MMDB itself, so **using two connections to one database is an undefined behaviour**.
Also, that means that each connection start/shutdown is quite time expensive.
These two facts together tell that, if you intend on using the connection, you should probably wrap the main program in an :code:`async with` block.
@ -60,16 +60,4 @@ Different ways to get/set a value:
transaction.set_nowait('increment-5', value5 + 1)
await connection.commit()
* :meth:`ptvp35.VirtualConnection.get`
this method is instant.
* :meth:`ptvp35.DbInterface.set`
this method may take time to run.
ordering may not be guaranteed (depends on event loop implementation).
* :meth:`ptvp35.ExtendedVirtualConnection.set_nowait`
this method is instant.
ordering is guaranteed.
* :meth:`ptvp35.ExtendedVirtualConnection.commit`
this method may take time to run.
respects the ordering of previously called :code:`set_nowait` methods.
will, depending on event loop implementation, also execute later changes.
* :meth:`ptvp35.VirtualConnection.transaction`
For common methods see: :class:`~ptvp35.AbstractDbConnection`

View File

@ -10,6 +10,7 @@ __all__ = (
'VirtualConnection',
'ExtendedVirtualConnection',
'DbInterface',
'AbstractDbConnection',
'DbConnection',
'DbManager',
'DbFactory',
@ -28,7 +29,7 @@ import pathlib
import threading
from collections.abc import Hashable
from io import StringIO, UnsupportedOperation
from typing import IO, Any, TypeAlias
from typing import IO, Any, Protocol, TypeAlias
class Request:
@ -50,7 +51,7 @@ class Request:
if self.__future is not None:
self.__future.set_exception(exception)
async def wait(self, /):
async def wait(self, /) -> None:
if self.__future is not None:
await self.__future
@ -164,7 +165,7 @@ class KVRequest(LineRequest):
'value',
)
def __init__(self, key: Any, value: Any, /, *, future: asyncio.Future | None, factory: KVFactory):
def __init__(self, key: Any, value: Any, /, *, future: asyncio.Future | None, factory: KVFactory) -> None:
super().__init__(factory.line(key, value), future=future)
self.__factory = factory
self.key = key
@ -213,7 +214,7 @@ class TransactionRequest(LineRequest):
'buffer',
)
def __init__(self, buffer: StringIO, /, *, future: asyncio.Future | None):
def __init__(self, buffer: StringIO, /, *, future: asyncio.Future | None) -> None:
super().__init__(buffer.getvalue(), future=future)
self.buffer = buffer
@ -246,7 +247,7 @@ class VirtualConnection(
__slots__ = ()
@abc.abstractmethod
def get(self, key: Any, default: Any, /):
def get(self, key: Any, default: Any, /) -> Any:
raise NotImplementedError
@abc.abstractmethod
@ -270,7 +271,7 @@ class VirtualConnection(
class ExtendedVirtualConnection(
VirtualConnection
VirtualConnection, abc.ABC
):
"""maximal intersection of DbConnection and TransactionView functionality"""
@ -288,13 +289,38 @@ class ExtendedVirtualConnection(
class DbInterface(
ExtendedVirtualConnection
ExtendedVirtualConnection, abc.ABC
):
@abc.abstractmethod
async def set(self, key: Any, value: Any, /) -> None:
raise NotImplementedError
class AbstractDbConnection(Protocol):
def get(self, key: Any, default: Any, /) -> Any:
"""this method is instant."""
raise NotImplementedError
async def set(self, key: Any, value: Any, /) -> None:
"""this method may take time to run.
ordering may not be guaranteed (depends on event loop implementation)."""
raise NotImplementedError
def set_nowait(self, key: Any, value: Any, /) -> None:
"""this method is instant.
ordering is guaranteed."""
raise NotImplementedError
async def commit(self, /) -> None:
"""this method may take time to run.
respects the ordering of previously called :meth:`~ptvp35.AbstractDbConnection.set_nowait` methods.
will, depending on event loop implementation, also execute later changes."""
raise NotImplementedError
def transaction(self, /) -> Transaction:
raise NotImplementedError
class _Loop:
__slots__ = (
'__loop',
@ -663,7 +689,7 @@ class _WriteableBuffer:
case _:
raise UnknownRequestType
async def _close(self, /):
async def _close(self, /) -> None:
await self._commit()
if not self.__buffer_future.done():
self.__buffer_future.set_exception(RequestToClosedConnection())
@ -920,7 +946,7 @@ class DbManager:
self.__db = await _DbConnection.create(self.__parameters)
return self.__db
async def __aexit__(self, exc_type, exc_val, exc_tb):
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
await self.__db.aclose()
@ -932,7 +958,7 @@ class Db(_DbConnection):
__slots__ = ()
def __init__(self, path: str | pathlib.Path, /, *, kvfactory: KVFactory, buffersize=1048576):
def __init__(self, path: str | pathlib.Path, /, *, kvfactory: KVFactory, buffersize=1048576) -> None:
_DbConnection.__init__(
self,
DbParameters(
@ -944,7 +970,7 @@ class Db(_DbConnection):
await self._initialize()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
await self.aclose()
@ -955,7 +981,10 @@ class FutureContext:
async def __aenter__(self) -> None:
pass
async def __aexit__(self, exc_type, exc_val, exc_tb):
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
await self.wait()
async def wait(self, /) -> None:
if self.__future is not None:
await self.__future
@ -1170,7 +1199,7 @@ class Transaction:
async def __aenter__(self) -> TransactionView:
return self.__enter__()
async def __aexit__(self, exc_type, exc_val, exc_tb):
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
if exc_type is None:
await self.__view.commit()
else:
@ -1188,7 +1217,7 @@ class Transaction:
self.__view = TransactionView({}, self.__connection)
return self.__view
def __exit__(self, exc_type, exc_val, exc_tb):
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
if exc_type is None:
self.__view.submit()
else: