dimples 1.4.0__tar.gz → 1.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dimples-1.4.0 → dimples-1.4.2}/PKG-INFO +1 -1
- {dimples-1.4.0 → dimples-1.4.2}/dimples/__init__.py +1 -1
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/__init__.py +2 -2
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_base.py +76 -56
- dimples-1.4.2/dimples/database/t_document.py +173 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_group.py +71 -65
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_group_history.py +29 -37
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_group_keys.py +25 -34
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_login.py +34 -36
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_message.py +17 -30
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_meta.py +30 -30
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_private.py +19 -27
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_station.py +40 -57
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_user.py +27 -32
- {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/config.py +47 -14
- {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/http.py +4 -6
- {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/log.py +20 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/PKG-INFO +1 -1
- {dimples-1.4.0 → dimples-1.4.2}/setup.py +17 -1
- dimples-1.4.0/dimples/database/t_document.py +0 -138
- {dimples-1.4.0 → dimples-1.4.2}/LICENSE +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/README.md +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/checker.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/checkpoint.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/commands.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/creator.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/customized.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/group.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_expel.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_invite.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_join.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_query.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_quit.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_reset.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_resign.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/handshake.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/facebook.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/messenger.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/session.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/state.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/transition.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/packer.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/processor.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/client/terminal.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/anonymous.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/ans.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/archivist.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/checker.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/address.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/compatible.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/entity.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/loader.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/meta.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/network.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/account.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/message.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/session.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/facebook.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/messenger.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/packer.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/processer.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/ans.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/block.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/customized.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/group.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/handshake.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/login.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/mute.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/password.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/report.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/utils.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/version.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/queue.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/register.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/common/session.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/flexible.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/gate.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/gatekeeper.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/mars.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/mtp.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/protocol/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/protocol/mars.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/protocol/ws.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/queue.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/seeker.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/session.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/ws.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/account.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/base.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/document.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/group.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/group_history.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/group_keys.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/login.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/meta.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/private.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/station.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/user.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/message.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/base.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/document.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/group.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/grp_history.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/grp_keys.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/login.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/message.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/meta.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/station.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/user.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/session.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_cipherkey.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/messenger.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/octopus.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/shared.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/start.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/emitter.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/admin.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/builder.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/delegate.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/emitter.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/helper.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/manager.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/packer.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/group/shared.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/register/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/register/base.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/register/ext.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/register/run.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/register/shared.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/checker.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/ans.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/creator.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/document.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/handshake.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/login.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/report.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/deliver.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/dis_roamer.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/dispatcher.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/facebook.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/messenger.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/packer.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/processor.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/push.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/session.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/session_center.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/server/trace.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/station/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/station/handler.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/station/shared.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/station/start.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/__init__.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/cache.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/checker.py +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/SOURCES.txt +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/dependency_links.txt +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/entry_points.txt +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/requires.txt +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/top_level.txt +0 -0
- {dimples-1.4.0 → dimples-1.4.2}/setup.cfg +0 -0
|
@@ -34,7 +34,7 @@ from ..common.dbi import *
|
|
|
34
34
|
from .dos import *
|
|
35
35
|
from .redis import *
|
|
36
36
|
|
|
37
|
-
from .t_base import DbTask
|
|
37
|
+
from .t_base import DbTask, DataCache
|
|
38
38
|
|
|
39
39
|
from .t_private import PrivateKeyTable
|
|
40
40
|
from .t_meta import MetaTable
|
|
@@ -100,7 +100,7 @@ __all__ = [
|
|
|
100
100
|
# Table
|
|
101
101
|
#
|
|
102
102
|
|
|
103
|
-
'DbTask',
|
|
103
|
+
'DbTask', 'DataCache',
|
|
104
104
|
|
|
105
105
|
'PrivateKeyTable', 'MetaTable', 'DocumentTable',
|
|
106
106
|
'UserTable', 'GroupTable', 'GroupHistoryTable',
|
|
@@ -32,39 +32,97 @@ from typing import Optional
|
|
|
32
32
|
from aiou.mem.cache import K, V
|
|
33
33
|
from aiou.mem import CachePool
|
|
34
34
|
|
|
35
|
+
from ..utils import Logging
|
|
36
|
+
from ..utils import SharedCacheManager
|
|
35
37
|
|
|
36
|
-
class DbTask(Generic[K, V], ABC):
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
class DataCache(Logging, Generic[K, V], ABC):
|
|
40
|
+
|
|
41
|
+
def __init__(self, pool_name: str):
|
|
39
42
|
super().__init__()
|
|
40
|
-
|
|
43
|
+
man = SharedCacheManager()
|
|
44
|
+
self._cache_pool = man.get_pool(name=pool_name)
|
|
45
|
+
self._mutex_lock = threading.Lock()
|
|
46
|
+
|
|
47
|
+
@property # protected
|
|
48
|
+
def cache(self) -> CachePool[K, V]:
|
|
49
|
+
return self._cache_pool
|
|
50
|
+
|
|
51
|
+
@property # protected
|
|
52
|
+
def lock(self) -> threading.Lock:
|
|
53
|
+
return self._mutex_lock
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DbTask(Logging, Generic[K, V], ABC):
|
|
57
|
+
|
|
58
|
+
MEM_CACHE_EXPIRES = 300 # seconds
|
|
59
|
+
MEM_CACHE_REFRESH = 32 # seconds
|
|
60
|
+
|
|
61
|
+
def __init__(self, mutex_lock: threading.Lock, cache_pool: CachePool,
|
|
62
|
+
cache_expires: float = None, cache_refresh: float = None):
|
|
63
|
+
super().__init__()
|
|
64
|
+
self._lock = mutex_lock
|
|
41
65
|
# memory cache
|
|
42
|
-
self.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
self._cache_pool = cache_pool
|
|
67
|
+
# memory expires
|
|
68
|
+
if cache_expires is None:
|
|
69
|
+
self._cache_expires = self.MEM_CACHE_EXPIRES
|
|
70
|
+
else:
|
|
71
|
+
assert cache_expires > 0, 'cache expires durations error: %s' % cache_expires
|
|
72
|
+
self._cache_expires = cache_expires
|
|
73
|
+
# memory refresh
|
|
74
|
+
if cache_refresh is None:
|
|
75
|
+
self._cache_refresh = self.MEM_CACHE_REFRESH
|
|
76
|
+
else:
|
|
77
|
+
assert cache_refresh > 0, 'cache refresh durations error: %s' % cache_refresh
|
|
78
|
+
self._cache_refresh = cache_refresh
|
|
47
79
|
|
|
48
80
|
@property # protected
|
|
49
|
-
def
|
|
50
|
-
return self.
|
|
81
|
+
def lock(self) -> threading.Lock:
|
|
82
|
+
return self._lock
|
|
83
|
+
|
|
84
|
+
@property # protected
|
|
85
|
+
def cache_pool(self) -> CachePool[K, V]:
|
|
86
|
+
return self._cache_pool
|
|
51
87
|
|
|
52
88
|
@property # protected
|
|
53
89
|
def cache_expires(self) -> float:
|
|
54
|
-
return self.
|
|
90
|
+
return self._cache_expires
|
|
55
91
|
|
|
56
92
|
@property # protected
|
|
57
93
|
def cache_refresh(self) -> float:
|
|
58
|
-
return self.
|
|
94
|
+
return self._cache_refresh
|
|
59
95
|
|
|
96
|
+
@property # protected
|
|
60
97
|
@abstractmethod
|
|
61
98
|
def cache_key(self) -> K:
|
|
62
99
|
""" key for memory cache """
|
|
63
100
|
raise NotImplemented
|
|
64
101
|
|
|
102
|
+
@abstractmethod
|
|
103
|
+
async def _read_data(self) -> Optional[V]:
|
|
104
|
+
""" load value from local storage """
|
|
105
|
+
raise NotImplemented
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
async def _write_data(self, value: V) -> bool:
|
|
109
|
+
""" save value into local storage """
|
|
110
|
+
raise NotImplemented
|
|
111
|
+
|
|
112
|
+
async def save(self, value: V) -> bool:
|
|
113
|
+
""" Task Save """
|
|
114
|
+
with self.lock:
|
|
115
|
+
# save into local storage
|
|
116
|
+
ok = await self._write_data(value)
|
|
117
|
+
if ok:
|
|
118
|
+
# update memory cache
|
|
119
|
+
self.cache_pool.update(key=self.cache_key, value=value, life_span=self.cache_expires)
|
|
120
|
+
return ok
|
|
121
|
+
|
|
65
122
|
async def load(self) -> Optional[V]:
|
|
123
|
+
""" Task Load """
|
|
66
124
|
now = time.time()
|
|
67
|
-
key = self.cache_key
|
|
125
|
+
key = self.cache_key
|
|
68
126
|
cache_pool = self.cache_pool
|
|
69
127
|
#
|
|
70
128
|
# 1. check memory cache
|
|
@@ -84,64 +142,26 @@ class DbTask(Generic[K, V], ABC):
|
|
|
84
142
|
#
|
|
85
143
|
# 2. lock for querying
|
|
86
144
|
#
|
|
87
|
-
with self.
|
|
145
|
+
with self.lock:
|
|
88
146
|
# locked, check again to make sure the cache not exists.
|
|
89
147
|
# (maybe the cache was updated by other threads while waiting the lock)
|
|
90
148
|
value, holder = cache_pool.fetch(key=key, now=now)
|
|
91
149
|
if value is not None:
|
|
92
150
|
return value
|
|
93
151
|
elif holder is None:
|
|
152
|
+
# not load yet, wait to load
|
|
94
153
|
pass
|
|
95
154
|
elif holder.is_alive(now=now):
|
|
155
|
+
# value not exists
|
|
96
156
|
return None
|
|
97
157
|
else:
|
|
98
158
|
# holder exists, renew the expired time for other threads
|
|
99
159
|
holder.renewal(duration=self.cache_refresh, now=now)
|
|
100
|
-
#
|
|
101
|
-
value = await self.
|
|
102
|
-
if value is None:
|
|
103
|
-
# 2.2. check local storage
|
|
104
|
-
value = await self._load_local_storage()
|
|
105
|
-
if value is not None:
|
|
106
|
-
# 2.3. update redis server
|
|
107
|
-
await self._save_redis_cache(value=value)
|
|
160
|
+
# load from local storage
|
|
161
|
+
value = await self._read_data()
|
|
108
162
|
# update memory cache
|
|
109
163
|
cache_pool.update(key=key, value=value, life_span=self.cache_expires, now=now)
|
|
110
164
|
#
|
|
111
165
|
# 3. OK, return cached value
|
|
112
166
|
#
|
|
113
167
|
return value
|
|
114
|
-
|
|
115
|
-
async def save(self, value: V) -> bool:
|
|
116
|
-
now = time.time()
|
|
117
|
-
key = self.cache_key()
|
|
118
|
-
cache_pool = self.cache_pool
|
|
119
|
-
with self.__lock:
|
|
120
|
-
# store into memory cache
|
|
121
|
-
cache_pool.update(key=key, value=value, life_span=self.cache_expires, now=now)
|
|
122
|
-
# store into redis server
|
|
123
|
-
ok1 = await self._save_redis_cache(value=value)
|
|
124
|
-
# save into local storage
|
|
125
|
-
ok2 = await self._save_local_storage(value=value)
|
|
126
|
-
# OK
|
|
127
|
-
return ok1 or ok2
|
|
128
|
-
|
|
129
|
-
@abstractmethod
|
|
130
|
-
async def _load_redis_cache(self) -> Optional[V]:
|
|
131
|
-
""" get value from redis server """
|
|
132
|
-
raise NotImplemented
|
|
133
|
-
|
|
134
|
-
@abstractmethod
|
|
135
|
-
async def _save_redis_cache(self, value: V) -> bool:
|
|
136
|
-
""" save value into redis server """
|
|
137
|
-
raise NotImplemented
|
|
138
|
-
|
|
139
|
-
@abstractmethod
|
|
140
|
-
async def _load_local_storage(self) -> Optional[V]:
|
|
141
|
-
""" get value from local storage """
|
|
142
|
-
raise NotImplemented
|
|
143
|
-
|
|
144
|
-
@abstractmethod
|
|
145
|
-
async def _save_local_storage(self, value: V) -> bool:
|
|
146
|
-
""" save value into local storage """
|
|
147
|
-
raise NotImplemented
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# ==============================================================================
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2022 Albert Moky
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
# ==============================================================================
|
|
25
|
+
|
|
26
|
+
import threading
|
|
27
|
+
from typing import Optional, List
|
|
28
|
+
|
|
29
|
+
from aiou.mem import CachePool
|
|
30
|
+
|
|
31
|
+
from dimsdk import ID, Document
|
|
32
|
+
|
|
33
|
+
from ..utils import Config
|
|
34
|
+
from ..common import DocumentDBI
|
|
35
|
+
|
|
36
|
+
from .dos import DocumentStorage
|
|
37
|
+
from .redis import DocumentCache
|
|
38
|
+
|
|
39
|
+
from .t_base import DbTask, DataCache
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DocTask(DbTask[ID, List[Document]]):
|
|
43
|
+
|
|
44
|
+
def __init__(self, identifier: ID, new_document: Optional[Document],
|
|
45
|
+
redis: DocumentCache, storage: DocumentStorage,
|
|
46
|
+
mutex_lock: threading.Lock, cache_pool: CachePool):
|
|
47
|
+
super().__init__(mutex_lock=mutex_lock, cache_pool=cache_pool)
|
|
48
|
+
self._identifier = identifier
|
|
49
|
+
self._new_doc = new_document
|
|
50
|
+
self._redis = redis
|
|
51
|
+
self._dos = storage
|
|
52
|
+
|
|
53
|
+
@property # Override
|
|
54
|
+
def cache_key(self) -> ID:
|
|
55
|
+
return self._identifier
|
|
56
|
+
|
|
57
|
+
# Override
|
|
58
|
+
async def _read_data(self) -> Optional[List[Document]]:
|
|
59
|
+
# 1. the redis server will return None when cache not found
|
|
60
|
+
# 2. when redis server return an empty array, no need to check local storage again
|
|
61
|
+
docs = await self._redis.load_documents(identifier=self._identifier)
|
|
62
|
+
if docs is not None:
|
|
63
|
+
return docs
|
|
64
|
+
# 3. the local storage will return an empty array, when no document for this id
|
|
65
|
+
docs = await self._dos.load_documents(identifier=self._identifier)
|
|
66
|
+
if docs is None:
|
|
67
|
+
# 4. return empty array as a placeholder for the memory cache
|
|
68
|
+
docs = []
|
|
69
|
+
# 5. update redis server
|
|
70
|
+
await self._redis.save_documents(documents=docs, identifier=self._identifier)
|
|
71
|
+
return docs
|
|
72
|
+
|
|
73
|
+
# Override
|
|
74
|
+
async def _write_data(self, documents: List[Document]) -> bool:
|
|
75
|
+
new_doc = self._new_doc
|
|
76
|
+
if new_doc is None:
|
|
77
|
+
assert False, 'should not happen: %s' % self._identifier
|
|
78
|
+
# return False
|
|
79
|
+
else:
|
|
80
|
+
identifier = new_doc.identifier
|
|
81
|
+
doc_type = new_doc.type
|
|
82
|
+
#
|
|
83
|
+
# 0. check old documents
|
|
84
|
+
#
|
|
85
|
+
index = len(documents)
|
|
86
|
+
while index > 0:
|
|
87
|
+
index -= 1
|
|
88
|
+
item = documents[index]
|
|
89
|
+
if not isinstance(item, Document) or item.identifier != identifier:
|
|
90
|
+
self.error(msg='document error: %s, %s' % (identifier, item))
|
|
91
|
+
continue
|
|
92
|
+
elif item.get('type') != doc_type:
|
|
93
|
+
self.info(msg='skip document: %s, type=%s, %s' % (identifier, doc_type, item))
|
|
94
|
+
continue
|
|
95
|
+
elif item == new_doc:
|
|
96
|
+
self.warning(msg='same document, no need to update: %s' % identifier)
|
|
97
|
+
return True
|
|
98
|
+
# old record found, update it
|
|
99
|
+
documents[index] = new_doc
|
|
100
|
+
# break
|
|
101
|
+
if index == 0:
|
|
102
|
+
# same type not found
|
|
103
|
+
documents.append(new_doc)
|
|
104
|
+
#
|
|
105
|
+
# 1. store into redis server
|
|
106
|
+
#
|
|
107
|
+
ok1 = await self._redis.save_documents(documents=documents, identifier=self._identifier)
|
|
108
|
+
#
|
|
109
|
+
# 2. save into local storage
|
|
110
|
+
#
|
|
111
|
+
ok2 = await self._dos.save_documents(documents=documents, identifier=self._identifier)
|
|
112
|
+
return ok1 or ok2
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class DocumentTable(DataCache, DocumentDBI):
|
|
116
|
+
""" Implementations of DocumentDBI """
|
|
117
|
+
|
|
118
|
+
def __init__(self, config: Config):
|
|
119
|
+
super().__init__(pool_name='documents') # ID => List[Document]
|
|
120
|
+
self._redis = DocumentCache(config=config)
|
|
121
|
+
self._dos = DocumentStorage(config=config)
|
|
122
|
+
|
|
123
|
+
def show_info(self):
|
|
124
|
+
self._dos.show_info()
|
|
125
|
+
|
|
126
|
+
def _new_task(self, identifier: ID, new_document: Document = None) -> DocTask:
|
|
127
|
+
return DocTask(identifier=identifier, new_document=new_document,
|
|
128
|
+
redis=self._redis, storage=self._dos,
|
|
129
|
+
mutex_lock=self._mutex_lock, cache_pool=self._cache_pool)
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# Document DBI
|
|
133
|
+
#
|
|
134
|
+
|
|
135
|
+
# Override
|
|
136
|
+
async def save_document(self, document: Document) -> bool:
|
|
137
|
+
#
|
|
138
|
+
# 0. check valid
|
|
139
|
+
#
|
|
140
|
+
identifier = document.identifier
|
|
141
|
+
if not document.valid:
|
|
142
|
+
self.error(msg='document not valid: %s' % identifier)
|
|
143
|
+
return False
|
|
144
|
+
#
|
|
145
|
+
# 1. load old records
|
|
146
|
+
#
|
|
147
|
+
task = self._new_task(identifier=identifier)
|
|
148
|
+
docs = await task.load()
|
|
149
|
+
if docs is None:
|
|
150
|
+
docs = []
|
|
151
|
+
else:
|
|
152
|
+
# check time
|
|
153
|
+
new_time = document.time
|
|
154
|
+
if new_time is not None:
|
|
155
|
+
for item in docs:
|
|
156
|
+
old_time = item.time
|
|
157
|
+
if old_time is not None and old_time > new_time:
|
|
158
|
+
self.warning(msg='ignore expired document: %s' % document)
|
|
159
|
+
return False
|
|
160
|
+
#
|
|
161
|
+
# 2. save new record
|
|
162
|
+
#
|
|
163
|
+
task = self._new_task(identifier=identifier, new_document=document)
|
|
164
|
+
return await task.save(docs)
|
|
165
|
+
|
|
166
|
+
# Override
|
|
167
|
+
async def get_documents(self, identifier: ID) -> List[Document]:
|
|
168
|
+
#
|
|
169
|
+
# build task for loading
|
|
170
|
+
#
|
|
171
|
+
task = self._new_task(identifier=identifier)
|
|
172
|
+
docs = await task.load()
|
|
173
|
+
return [] if docs is None else docs
|
|
@@ -42,23 +42,17 @@ from .t_base import DbTask
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
# noinspection PyAbstractClass
|
|
45
|
-
class GrpTask(DbTask, ABC):
|
|
46
|
-
|
|
47
|
-
MEM_CACHE_EXPIRES = 300 # seconds
|
|
48
|
-
MEM_CACHE_REFRESH = 32 # seconds
|
|
45
|
+
class GrpTask(DbTask[ID, List[ID]], ABC):
|
|
49
46
|
|
|
50
47
|
def __init__(self, group: ID,
|
|
51
|
-
|
|
52
|
-
mutex_lock: threading.Lock):
|
|
53
|
-
super().__init__(cache_pool=cache_pool
|
|
54
|
-
cache_expires=self.MEM_CACHE_EXPIRES,
|
|
55
|
-
cache_refresh=self.MEM_CACHE_REFRESH,
|
|
56
|
-
mutex_lock=mutex_lock)
|
|
48
|
+
redis: GroupCache, storage: GroupStorage,
|
|
49
|
+
mutex_lock: threading.Lock, cache_pool: CachePool):
|
|
50
|
+
super().__init__(mutex_lock=mutex_lock, cache_pool=cache_pool)
|
|
57
51
|
self._group = group
|
|
58
52
|
self._redis = redis
|
|
59
53
|
self._dos = storage
|
|
60
54
|
|
|
61
|
-
# Override
|
|
55
|
+
@property # Override
|
|
62
56
|
def cache_key(self) -> ID:
|
|
63
57
|
return self._group
|
|
64
58
|
|
|
@@ -66,70 +60,82 @@ class GrpTask(DbTask, ABC):
|
|
|
66
60
|
class MemberTask(GrpTask):
|
|
67
61
|
|
|
68
62
|
# Override
|
|
69
|
-
async def
|
|
63
|
+
async def _read_data(self) -> Optional[List[ID]]:
|
|
70
64
|
# 1. the redis server will return None when cache not found
|
|
71
65
|
# 2. when redis server return an empty array, no need to check local storage again
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
# Override
|
|
85
|
-
async def
|
|
86
|
-
|
|
66
|
+
members = await self._redis.get_members(group=self._group)
|
|
67
|
+
if members is not None:
|
|
68
|
+
return members
|
|
69
|
+
# 3. the local storage will return an empty array, when no member in this group
|
|
70
|
+
members = await self._dos.get_members(group=self._group)
|
|
71
|
+
if members is None:
|
|
72
|
+
# 4. return empty array as a placeholder for the memory cache
|
|
73
|
+
members = []
|
|
74
|
+
# 5. update redis server
|
|
75
|
+
await self._redis.save_members(members=members, group=self._group)
|
|
76
|
+
return members
|
|
77
|
+
|
|
78
|
+
# Override
|
|
79
|
+
async def _write_data(self, value: List[ID]) -> bool:
|
|
80
|
+
# 1. store into redis server
|
|
81
|
+
ok1 = await self._redis.save_members(members=value, group=self._group)
|
|
82
|
+
# 2. save into local storage
|
|
83
|
+
ok2 = await self._dos.save_members(members=value, group=self._group)
|
|
84
|
+
return ok1 or ok2
|
|
87
85
|
|
|
88
86
|
|
|
89
87
|
class BotTask(GrpTask):
|
|
90
88
|
|
|
91
89
|
# Override
|
|
92
|
-
async def
|
|
90
|
+
async def _read_data(self) -> Optional[List[ID]]:
|
|
93
91
|
# 1. the redis server will return None when cache not found
|
|
94
92
|
# 2. when redis server return an empty array, no need to check local storage again
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
# Override
|
|
108
|
-
async def
|
|
109
|
-
|
|
93
|
+
bots = await self._redis.get_assistants(group=self._group)
|
|
94
|
+
if bots is not None:
|
|
95
|
+
return bots
|
|
96
|
+
# 3. the local storage will return an empty array, when no bot for this group
|
|
97
|
+
bots = await self._dos.get_assistants(group=self._group)
|
|
98
|
+
if bots is None:
|
|
99
|
+
# 4. return empty array as a placeholder for the memory cache
|
|
100
|
+
bots = []
|
|
101
|
+
# 5. update redis server
|
|
102
|
+
await self._redis.save_assistants(assistants=bots, group=self._group)
|
|
103
|
+
return bots
|
|
104
|
+
|
|
105
|
+
# Override
|
|
106
|
+
async def _write_data(self, value: List[ID]) -> bool:
|
|
107
|
+
# 1. store into redis server
|
|
108
|
+
ok1 = await self._redis.save_assistants(assistants=value, group=self._group)
|
|
109
|
+
# 2. save into local storage
|
|
110
|
+
ok2 = await self._dos.save_assistants(assistants=value, group=self._group)
|
|
111
|
+
return ok1 or ok2
|
|
110
112
|
|
|
111
113
|
|
|
112
114
|
class AdminTask(GrpTask):
|
|
113
115
|
|
|
114
116
|
# Override
|
|
115
|
-
async def
|
|
117
|
+
async def _read_data(self) -> Optional[List[ID]]:
|
|
116
118
|
# 1. the redis server will return None when cache not found
|
|
117
119
|
# 2. when redis server return an empty array, no need to check local storage again
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
# Override
|
|
131
|
-
async def
|
|
132
|
-
|
|
120
|
+
admins = await self._redis.get_administrators(group=self._group)
|
|
121
|
+
if admins is not None:
|
|
122
|
+
return admins
|
|
123
|
+
# 3. the local storage will return an empty array, when no admin in this group
|
|
124
|
+
admins = await self._dos.get_administrators(group=self._group)
|
|
125
|
+
if admins is None:
|
|
126
|
+
# 4. return empty array as a placeholder for the memory cache
|
|
127
|
+
admins = []
|
|
128
|
+
# 5. update redis server
|
|
129
|
+
await self._redis.save_administrators(administrators=admins, group=self._group)
|
|
130
|
+
return admins
|
|
131
|
+
|
|
132
|
+
# Override
|
|
133
|
+
async def _write_data(self, value: List[ID]) -> bool:
|
|
134
|
+
# 1. store into redis server
|
|
135
|
+
ok1 = await self._redis.save_administrators(administrators=value, group=self._group)
|
|
136
|
+
# 2. save into local storage
|
|
137
|
+
ok2 = await self._dos.save_administrators(administrators=value, group=self._group)
|
|
138
|
+
return ok1 or ok2
|
|
133
139
|
|
|
134
140
|
|
|
135
141
|
class GroupTable(GroupDBI):
|
|
@@ -150,18 +156,18 @@ class GroupTable(GroupDBI):
|
|
|
150
156
|
|
|
151
157
|
def _new_member_task(self, group: ID) -> GrpTask:
|
|
152
158
|
return MemberTask(group=group,
|
|
153
|
-
|
|
154
|
-
mutex_lock=self._lock)
|
|
159
|
+
redis=self._redis, storage=self._dos,
|
|
160
|
+
mutex_lock=self._lock, cache_pool=self._member_cache)
|
|
155
161
|
|
|
156
162
|
def _new_bot_task(self, group: ID) -> GrpTask:
|
|
157
163
|
return BotTask(group=group,
|
|
158
|
-
|
|
159
|
-
mutex_lock=self._lock)
|
|
164
|
+
redis=self._redis, storage=self._dos,
|
|
165
|
+
mutex_lock=self._lock, cache_pool=self._bot_cache)
|
|
160
166
|
|
|
161
167
|
def _new_admin_task(self, group: ID) -> GrpTask:
|
|
162
168
|
return AdminTask(group=group,
|
|
163
|
-
|
|
164
|
-
mutex_lock=self._lock)
|
|
169
|
+
redis=self._redis, storage=self._dos,
|
|
170
|
+
mutex_lock=self._lock, cache_pool=self._admin_cache)
|
|
165
171
|
|
|
166
172
|
#
|
|
167
173
|
# Group DBI
|