AbstractDbConnection
This commit is contained in:
parent
ba3d392328
commit
fffff4973e
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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`
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user