AbstractDbConnection
This commit is contained in:
parent
ba3d392328
commit
fffff4973e
@ -286,6 +286,7 @@ async def main():
|
|||||||
LogEE(__import__('ptvp35').FutureContext, '__init__').enter(es)
|
LogEE(__import__('ptvp35').FutureContext, '__init__').enter(es)
|
||||||
ALogEE(__import__('ptvp35').FutureContext, '__aenter__').enter(es)
|
ALogEE(__import__('ptvp35').FutureContext, '__aenter__').enter(es)
|
||||||
ALogEE(__import__('ptvp35').FutureContext, '__aexit__').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, '__init__').enter(es)
|
||||||
LogEE(__import__('ptvp35').TransactionView, 'future_context').enter(es)
|
LogEE(__import__('ptvp35').TransactionView, 'future_context').enter(es)
|
||||||
|
@ -5,9 +5,9 @@ Main-Memory DataBase
|
|||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
* Represented via Python dictionary (:code:`dict`).
|
* 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.
|
* 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.
|
All KVPs added via submit-like methods are serialized before being added to the MMDB.
|
||||||
|
|
||||||
DataBase Stream File
|
DataBase Stream File
|
||||||
@ -37,32 +37,34 @@ DataBase Stream File
|
|||||||
Request Queue
|
Request Queue
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Transaction View (:class:`ptvp35.TransactionView`)
|
Transaction View (:class:`~ptvp35.TransactionView`)
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
Connection-like interface on top of another connection-like interface.
|
Connection-like interface on top of another connection-like interface.
|
||||||
|
|
||||||
* Provides most of the same methods as :class:`ptvp35.DbConnection`.
|
* Provides most of the same methods as :class:`~ptvp35.DbConnection`.
|
||||||
* From the common :class:`ptvp35.VirtualConnection` interface/base class:
|
* From the common :class:`~ptvp35.VirtualConnection` interface/base class:
|
||||||
* :meth:`ptvp35.TransactionView.get`
|
* :meth:`~ptvp35.TransactionView.get`
|
||||||
* :meth:`ptvp35.TransactionView.commit_transaction`
|
* :meth:`~ptvp35.TransactionView.commit_transaction`
|
||||||
* :meth:`ptvp35.TransactionView.submit_transaction_request`
|
* :meth:`~ptvp35.TransactionView.submit_transaction_request`
|
||||||
* :meth:`ptvp35.TransactionView.loop`
|
* :meth:`~ptvp35.TransactionView.loop`
|
||||||
* :meth:`ptvp35.TransactionView.transaction` (default implementation)
|
* :meth:`~ptvp35.VirtualConnection.transaction` (default implementation)
|
||||||
* Non-standard common methods:
|
* Extra common methods (:class:`~ptvp35.ExtendedVirtualConnection`):
|
||||||
* :meth:`ptvp35.TransactionView.set_nowait`
|
* :meth:`~ptvp35.TransactionView.set_nowait`
|
||||||
* :meth:`ptvp35.TransactionView.submit_transaction`
|
* :meth:`~ptvp35.TransactionView.submit_transaction`
|
||||||
* :meth:`ptvp35.TransactionView.commit`
|
* :meth:`~ptvp35.TransactionView.commit`
|
||||||
* Does not have the the analogue for the :meth:`ptvp35.DbConnection.set` method.
|
* 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 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.
|
* 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.
|
* Transactions are meant for a more fine control.
|
||||||
* The equivalent would consist of the three method calls:
|
* 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.set_nowait` to set the value in :code:`__delta`.
|
||||||
* :meth:`ptvp35.TransactionView.submit` to pass all the :code:`__delta` values.
|
* :meth:`~ptvp35.TransactionView.submit` to pass all the :code:`__delta` values.
|
||||||
* :meth:`ptvp35.TransactionView.commit` wait until all changes are commited.
|
* :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.
|
Manages a Transaction View.
|
||||||
|
@ -13,7 +13,7 @@ Default installation option is to use pip+git
|
|||||||
Basic functionality
|
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**.
|
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.
|
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.
|
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)
|
transaction.set_nowait('increment-5', value5 + 1)
|
||||||
await connection.commit()
|
await connection.commit()
|
||||||
|
|
||||||
* :meth:`ptvp35.VirtualConnection.get`
|
For common methods see: :class:`~ptvp35.AbstractDbConnection`
|
||||||
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`
|
|
||||||
|
@ -10,6 +10,7 @@ __all__ = (
|
|||||||
'VirtualConnection',
|
'VirtualConnection',
|
||||||
'ExtendedVirtualConnection',
|
'ExtendedVirtualConnection',
|
||||||
'DbInterface',
|
'DbInterface',
|
||||||
|
'AbstractDbConnection',
|
||||||
'DbConnection',
|
'DbConnection',
|
||||||
'DbManager',
|
'DbManager',
|
||||||
'DbFactory',
|
'DbFactory',
|
||||||
@ -28,7 +29,7 @@ import pathlib
|
|||||||
import threading
|
import threading
|
||||||
from collections.abc import Hashable
|
from collections.abc import Hashable
|
||||||
from io import StringIO, UnsupportedOperation
|
from io import StringIO, UnsupportedOperation
|
||||||
from typing import IO, Any, TypeAlias
|
from typing import IO, Any, Protocol, TypeAlias
|
||||||
|
|
||||||
|
|
||||||
class Request:
|
class Request:
|
||||||
@ -50,7 +51,7 @@ class Request:
|
|||||||
if self.__future is not None:
|
if self.__future is not None:
|
||||||
self.__future.set_exception(exception)
|
self.__future.set_exception(exception)
|
||||||
|
|
||||||
async def wait(self, /):
|
async def wait(self, /) -> None:
|
||||||
if self.__future is not None:
|
if self.__future is not None:
|
||||||
await self.__future
|
await self.__future
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ class KVRequest(LineRequest):
|
|||||||
'value',
|
'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)
|
super().__init__(factory.line(key, value), future=future)
|
||||||
self.__factory = factory
|
self.__factory = factory
|
||||||
self.key = key
|
self.key = key
|
||||||
@ -213,7 +214,7 @@ class TransactionRequest(LineRequest):
|
|||||||
'buffer',
|
'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)
|
super().__init__(buffer.getvalue(), future=future)
|
||||||
self.buffer = buffer
|
self.buffer = buffer
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ class VirtualConnection(
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get(self, key: Any, default: Any, /):
|
def get(self, key: Any, default: Any, /) -> Any:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
@ -270,7 +271,7 @@ class VirtualConnection(
|
|||||||
|
|
||||||
|
|
||||||
class ExtendedVirtualConnection(
|
class ExtendedVirtualConnection(
|
||||||
VirtualConnection
|
VirtualConnection, abc.ABC
|
||||||
):
|
):
|
||||||
"""maximal intersection of DbConnection and TransactionView functionality"""
|
"""maximal intersection of DbConnection and TransactionView functionality"""
|
||||||
|
|
||||||
@ -288,13 +289,38 @@ class ExtendedVirtualConnection(
|
|||||||
|
|
||||||
|
|
||||||
class DbInterface(
|
class DbInterface(
|
||||||
ExtendedVirtualConnection
|
ExtendedVirtualConnection, abc.ABC
|
||||||
):
|
):
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def set(self, key: Any, value: Any, /) -> None:
|
async def set(self, key: Any, value: Any, /) -> None:
|
||||||
raise NotImplementedError
|
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:
|
class _Loop:
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'__loop',
|
'__loop',
|
||||||
@ -663,7 +689,7 @@ class _WriteableBuffer:
|
|||||||
case _:
|
case _:
|
||||||
raise UnknownRequestType
|
raise UnknownRequestType
|
||||||
|
|
||||||
async def _close(self, /):
|
async def _close(self, /) -> None:
|
||||||
await self._commit()
|
await self._commit()
|
||||||
if not self.__buffer_future.done():
|
if not self.__buffer_future.done():
|
||||||
self.__buffer_future.set_exception(RequestToClosedConnection())
|
self.__buffer_future.set_exception(RequestToClosedConnection())
|
||||||
@ -920,7 +946,7 @@ class DbManager:
|
|||||||
self.__db = await _DbConnection.create(self.__parameters)
|
self.__db = await _DbConnection.create(self.__parameters)
|
||||||
return self.__db
|
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()
|
await self.__db.aclose()
|
||||||
|
|
||||||
|
|
||||||
@ -932,7 +958,7 @@ class Db(_DbConnection):
|
|||||||
|
|
||||||
__slots__ = ()
|
__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__(
|
_DbConnection.__init__(
|
||||||
self,
|
self,
|
||||||
DbParameters(
|
DbParameters(
|
||||||
@ -944,7 +970,7 @@ class Db(_DbConnection):
|
|||||||
await self._initialize()
|
await self._initialize()
|
||||||
return self
|
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()
|
await self.aclose()
|
||||||
|
|
||||||
|
|
||||||
@ -955,7 +981,10 @@ class FutureContext:
|
|||||||
async def __aenter__(self) -> None:
|
async def __aenter__(self) -> None:
|
||||||
pass
|
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:
|
if self.__future is not None:
|
||||||
await self.__future
|
await self.__future
|
||||||
|
|
||||||
@ -1170,7 +1199,7 @@ class Transaction:
|
|||||||
async def __aenter__(self) -> TransactionView:
|
async def __aenter__(self) -> TransactionView:
|
||||||
return self.__enter__()
|
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:
|
if exc_type is None:
|
||||||
await self.__view.commit()
|
await self.__view.commit()
|
||||||
else:
|
else:
|
||||||
@ -1188,7 +1217,7 @@ class Transaction:
|
|||||||
self.__view = TransactionView({}, self.__connection)
|
self.__view = TransactionView({}, self.__connection)
|
||||||
return self.__view
|
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:
|
if exc_type is None:
|
||||||
self.__view.submit()
|
self.__view.submit()
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user