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.
Files changed (174) hide show
  1. {dimples-1.4.0 → dimples-1.4.2}/PKG-INFO +1 -1
  2. {dimples-1.4.0 → dimples-1.4.2}/dimples/__init__.py +1 -1
  3. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/__init__.py +2 -2
  4. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_base.py +76 -56
  5. dimples-1.4.2/dimples/database/t_document.py +173 -0
  6. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_group.py +71 -65
  7. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_group_history.py +29 -37
  8. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_group_keys.py +25 -34
  9. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_login.py +34 -36
  10. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_message.py +17 -30
  11. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_meta.py +30 -30
  12. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_private.py +19 -27
  13. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_station.py +40 -57
  14. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_user.py +27 -32
  15. {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/config.py +47 -14
  16. {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/http.py +4 -6
  17. {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/log.py +20 -0
  18. {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/PKG-INFO +1 -1
  19. {dimples-1.4.0 → dimples-1.4.2}/setup.py +17 -1
  20. dimples-1.4.0/dimples/database/t_document.py +0 -138
  21. {dimples-1.4.0 → dimples-1.4.2}/LICENSE +0 -0
  22. {dimples-1.4.0 → dimples-1.4.2}/README.md +0 -0
  23. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/__init__.py +0 -0
  24. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/checker.py +0 -0
  25. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/checkpoint.py +0 -0
  26. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/__init__.py +0 -0
  27. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/commands.py +0 -0
  28. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/creator.py +0 -0
  29. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/customized.py +0 -0
  30. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/group.py +0 -0
  31. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_expel.py +0 -0
  32. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_invite.py +0 -0
  33. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_join.py +0 -0
  34. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_query.py +0 -0
  35. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_quit.py +0 -0
  36. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_reset.py +0 -0
  37. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/grp_resign.py +0 -0
  38. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/cpu/handshake.py +0 -0
  39. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/facebook.py +0 -0
  40. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/messenger.py +0 -0
  41. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/__init__.py +0 -0
  42. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/session.py +0 -0
  43. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/state.py +0 -0
  44. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/network/transition.py +0 -0
  45. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/packer.py +0 -0
  46. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/processor.py +0 -0
  47. {dimples-1.4.0 → dimples-1.4.2}/dimples/client/terminal.py +0 -0
  48. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/__init__.py +0 -0
  49. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/anonymous.py +0 -0
  50. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/ans.py +0 -0
  51. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/archivist.py +0 -0
  52. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/checker.py +0 -0
  53. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/__init__.py +0 -0
  54. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/address.py +0 -0
  55. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/compatible.py +0 -0
  56. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/entity.py +0 -0
  57. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/loader.py +0 -0
  58. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/meta.py +0 -0
  59. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/compat/network.py +0 -0
  60. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/__init__.py +0 -0
  61. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/account.py +0 -0
  62. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/message.py +0 -0
  63. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/dbi/session.py +0 -0
  64. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/facebook.py +0 -0
  65. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/messenger.py +0 -0
  66. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/packer.py +0 -0
  67. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/processer.py +0 -0
  68. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/__init__.py +0 -0
  69. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/ans.py +0 -0
  70. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/block.py +0 -0
  71. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/customized.py +0 -0
  72. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/group.py +0 -0
  73. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/handshake.py +0 -0
  74. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/login.py +0 -0
  75. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/mute.py +0 -0
  76. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/password.py +0 -0
  77. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/report.py +0 -0
  78. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/utils.py +0 -0
  79. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/protocol/version.py +0 -0
  80. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/queue.py +0 -0
  81. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/register.py +0 -0
  82. {dimples-1.4.0 → dimples-1.4.2}/dimples/common/session.py +0 -0
  83. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/__init__.py +0 -0
  84. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/flexible.py +0 -0
  85. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/gate.py +0 -0
  86. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/gatekeeper.py +0 -0
  87. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/mars.py +0 -0
  88. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/mtp.py +0 -0
  89. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/protocol/__init__.py +0 -0
  90. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/protocol/mars.py +0 -0
  91. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/protocol/ws.py +0 -0
  92. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/queue.py +0 -0
  93. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/seeker.py +0 -0
  94. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/session.py +0 -0
  95. {dimples-1.4.0 → dimples-1.4.2}/dimples/conn/ws.py +0 -0
  96. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/account.py +0 -0
  97. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/__init__.py +0 -0
  98. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/base.py +0 -0
  99. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/document.py +0 -0
  100. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/group.py +0 -0
  101. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/group_history.py +0 -0
  102. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/group_keys.py +0 -0
  103. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/login.py +0 -0
  104. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/meta.py +0 -0
  105. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/private.py +0 -0
  106. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/station.py +0 -0
  107. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/dos/user.py +0 -0
  108. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/message.py +0 -0
  109. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/__init__.py +0 -0
  110. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/base.py +0 -0
  111. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/document.py +0 -0
  112. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/group.py +0 -0
  113. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/grp_history.py +0 -0
  114. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/grp_keys.py +0 -0
  115. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/login.py +0 -0
  116. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/message.py +0 -0
  117. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/meta.py +0 -0
  118. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/station.py +0 -0
  119. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/redis/user.py +0 -0
  120. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/session.py +0 -0
  121. {dimples-1.4.0 → dimples-1.4.2}/dimples/database/t_cipherkey.py +0 -0
  122. {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/__init__.py +0 -0
  123. {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/messenger.py +0 -0
  124. {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/octopus.py +0 -0
  125. {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/shared.py +0 -0
  126. {dimples-1.4.0 → dimples-1.4.2}/dimples/edge/start.py +0 -0
  127. {dimples-1.4.0 → dimples-1.4.2}/dimples/emitter.py +0 -0
  128. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/__init__.py +0 -0
  129. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/admin.py +0 -0
  130. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/builder.py +0 -0
  131. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/delegate.py +0 -0
  132. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/emitter.py +0 -0
  133. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/helper.py +0 -0
  134. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/manager.py +0 -0
  135. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/packer.py +0 -0
  136. {dimples-1.4.0 → dimples-1.4.2}/dimples/group/shared.py +0 -0
  137. {dimples-1.4.0 → dimples-1.4.2}/dimples/register/__init__.py +0 -0
  138. {dimples-1.4.0 → dimples-1.4.2}/dimples/register/base.py +0 -0
  139. {dimples-1.4.0 → dimples-1.4.2}/dimples/register/ext.py +0 -0
  140. {dimples-1.4.0 → dimples-1.4.2}/dimples/register/run.py +0 -0
  141. {dimples-1.4.0 → dimples-1.4.2}/dimples/register/shared.py +0 -0
  142. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/__init__.py +0 -0
  143. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/checker.py +0 -0
  144. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/__init__.py +0 -0
  145. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/ans.py +0 -0
  146. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/creator.py +0 -0
  147. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/document.py +0 -0
  148. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/handshake.py +0 -0
  149. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/login.py +0 -0
  150. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/cpu/report.py +0 -0
  151. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/deliver.py +0 -0
  152. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/dis_roamer.py +0 -0
  153. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/dispatcher.py +0 -0
  154. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/facebook.py +0 -0
  155. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/messenger.py +0 -0
  156. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/packer.py +0 -0
  157. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/processor.py +0 -0
  158. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/push.py +0 -0
  159. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/session.py +0 -0
  160. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/session_center.py +0 -0
  161. {dimples-1.4.0 → dimples-1.4.2}/dimples/server/trace.py +0 -0
  162. {dimples-1.4.0 → dimples-1.4.2}/dimples/station/__init__.py +0 -0
  163. {dimples-1.4.0 → dimples-1.4.2}/dimples/station/handler.py +0 -0
  164. {dimples-1.4.0 → dimples-1.4.2}/dimples/station/shared.py +0 -0
  165. {dimples-1.4.0 → dimples-1.4.2}/dimples/station/start.py +0 -0
  166. {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/__init__.py +0 -0
  167. {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/cache.py +0 -0
  168. {dimples-1.4.0 → dimples-1.4.2}/dimples/utils/checker.py +0 -0
  169. {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/SOURCES.txt +0 -0
  170. {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/dependency_links.txt +0 -0
  171. {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/entry_points.txt +0 -0
  172. {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/requires.txt +0 -0
  173. {dimples-1.4.0 → dimples-1.4.2}/dimples.egg-info/top_level.txt +0 -0
  174. {dimples-1.4.0 → dimples-1.4.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -470,7 +470,7 @@ __all__ = [
470
470
  # Table
471
471
  #
472
472
 
473
- 'DbTask',
473
+ 'DbTask', 'DataCache',
474
474
 
475
475
  'PrivateKeyTable', 'MetaTable', 'DocumentTable',
476
476
  'UserTable', 'GroupTable', 'GroupHistoryTable',
@@ -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
- def __init__(self, cache_pool: CachePool, cache_expires: float, cache_refresh: float, mutex_lock: threading.Lock):
39
+ class DataCache(Logging, Generic[K, V], ABC):
40
+
41
+ def __init__(self, pool_name: str):
39
42
  super().__init__()
40
- assert cache_expires > 0 and cache_refresh > 0, 'cache durations error: %s, %s' % (cache_expires, cache_refresh)
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.__cache_pool = cache_pool
43
- self.__cache_expires = cache_expires
44
- self.__cache_refresh = cache_refresh
45
- # lock
46
- self.__lock = mutex_lock
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 cache_pool(self) -> CachePool:
50
- return self.__cache_pool
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.__cache_expires
90
+ return self._cache_expires
55
91
 
56
92
  @property # protected
57
93
  def cache_refresh(self) -> float:
58
- return self.__cache_refresh
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.__lock:
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
- # 2.1. check redis server
101
- value = await self._load_redis_cache()
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
- cache_pool: CachePool, redis: GroupCache, storage: GroupStorage,
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 _load_redis_cache(self) -> Optional[List[ID]]:
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
- return await self._redis.get_members(group=self._group)
73
-
74
- # Override
75
- async def _save_redis_cache(self, value: List[ID]) -> bool:
76
- return await self._redis.save_members(members=value, group=self._group)
77
-
78
- # Override
79
- async def _load_local_storage(self) -> Optional[List[ID]]:
80
- # 1. the local storage will return an empty array, when no member in this group
81
- # 2. return empty array as a placeholder for the memory cache
82
- return await self._dos.get_members(group=self._group)
83
-
84
- # Override
85
- async def _save_local_storage(self, value: List[ID]) -> bool:
86
- return await self._dos.save_members(members=value, group=self._group)
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 _load_redis_cache(self) -> Optional[List[ID]]:
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
- return await self._redis.get_assistants(group=self._group)
96
-
97
- # Override
98
- async def _save_redis_cache(self, value: List[ID]) -> bool:
99
- return await self._redis.save_assistants(assistants=value, group=self._group)
100
-
101
- # Override
102
- async def _load_local_storage(self) -> Optional[List[ID]]:
103
- # 1. the local storage will return an empty array, when no bot for this group
104
- # 2. return empty array as a placeholder for the memory cache
105
- return await self._dos.get_assistants(group=self._group)
106
-
107
- # Override
108
- async def _save_local_storage(self, value: List[ID]) -> bool:
109
- return await self._dos.save_assistants(assistants=value, group=self._group)
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 _load_redis_cache(self) -> Optional[List[ID]]:
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
- return await self._redis.get_administrators(group=self._group)
119
-
120
- # Override
121
- async def _save_redis_cache(self, value: List[ID]) -> bool:
122
- return await self._redis.save_administrators(administrators=value, group=self._group)
123
-
124
- # Override
125
- async def _load_local_storage(self) -> Optional[List[ID]]:
126
- # 1. the local storage will return an empty array, when no admin in this group
127
- # 2. return empty array as a placeholder for the memory cache
128
- return await self._dos.get_administrators(group=self._group)
129
-
130
- # Override
131
- async def _save_local_storage(self, value: List[ID]) -> bool:
132
- return await self._dos.save_administrators(administrators=value, group=self._group)
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
- cache_pool=self._member_cache, redis=self._redis, storage=self._dos,
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
- cache_pool=self._bot_cache, redis=self._redis, storage=self._dos,
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
- cache_pool=self._admin_cache, redis=self._redis, storage=self._dos,
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