exonware-xwnode 0.0.1.22__py3-none-any.whl → 0.0.1.24__py3-none-any.whl

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 (249) hide show
  1. exonware/__init__.py +1 -1
  2. exonware/xwnode/__init__.py +18 -5
  3. exonware/xwnode/add_strategy_types.py +165 -0
  4. exonware/xwnode/common/__init__.py +1 -1
  5. exonware/xwnode/common/graph/__init__.py +30 -0
  6. exonware/xwnode/common/graph/caching.py +131 -0
  7. exonware/xwnode/common/graph/contracts.py +100 -0
  8. exonware/xwnode/common/graph/errors.py +44 -0
  9. exonware/xwnode/common/graph/indexing.py +260 -0
  10. exonware/xwnode/common/graph/manager.py +568 -0
  11. exonware/xwnode/common/management/__init__.py +3 -5
  12. exonware/xwnode/common/management/manager.py +2 -2
  13. exonware/xwnode/common/management/migration.py +3 -3
  14. exonware/xwnode/common/monitoring/__init__.py +3 -5
  15. exonware/xwnode/common/monitoring/metrics.py +6 -2
  16. exonware/xwnode/common/monitoring/pattern_detector.py +1 -1
  17. exonware/xwnode/common/monitoring/performance_monitor.py +5 -1
  18. exonware/xwnode/common/patterns/__init__.py +3 -5
  19. exonware/xwnode/common/patterns/flyweight.py +5 -1
  20. exonware/xwnode/common/patterns/registry.py +202 -183
  21. exonware/xwnode/common/utils/__init__.py +25 -11
  22. exonware/xwnode/common/utils/simple.py +1 -1
  23. exonware/xwnode/config.py +3 -8
  24. exonware/xwnode/contracts.py +4 -105
  25. exonware/xwnode/defs.py +413 -159
  26. exonware/xwnode/edges/strategies/__init__.py +86 -4
  27. exonware/xwnode/edges/strategies/_base_edge.py +2 -2
  28. exonware/xwnode/edges/strategies/adj_list.py +287 -121
  29. exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
  30. exonware/xwnode/edges/strategies/base.py +1 -1
  31. exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
  32. exonware/xwnode/edges/strategies/bitemporal.py +520 -0
  33. exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
  34. exonware/xwnode/edges/strategies/bv_graph.py +664 -0
  35. exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
  36. exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
  37. exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
  38. exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
  39. exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
  40. exonware/xwnode/edges/strategies/edge_list.py +168 -0
  41. exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
  42. exonware/xwnode/edges/strategies/euler_tour.py +560 -0
  43. exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
  44. exonware/xwnode/edges/strategies/graphblas.py +449 -0
  45. exonware/xwnode/edges/strategies/hnsw.py +637 -0
  46. exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
  47. exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
  48. exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
  49. exonware/xwnode/edges/strategies/k2_tree.py +613 -0
  50. exonware/xwnode/edges/strategies/link_cut.py +626 -0
  51. exonware/xwnode/edges/strategies/multiplex.py +532 -0
  52. exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
  53. exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
  54. exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
  55. exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
  56. exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
  57. exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
  58. exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
  59. exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
  60. exonware/xwnode/errors.py +3 -6
  61. exonware/xwnode/facade.py +20 -20
  62. exonware/xwnode/nodes/strategies/__init__.py +29 -9
  63. exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
  64. exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
  65. exonware/xwnode/nodes/strategies/array_list.py +36 -3
  66. exonware/xwnode/nodes/strategies/art.py +581 -0
  67. exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
  68. exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
  69. exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
  70. exonware/xwnode/nodes/strategies/base.py +469 -98
  71. exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
  72. exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
  73. exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
  74. exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
  75. exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
  76. exonware/xwnode/nodes/strategies/contracts.py +1 -1
  77. exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
  78. exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
  79. exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
  80. exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
  81. exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
  82. exonware/xwnode/nodes/strategies/dawg.py +876 -0
  83. exonware/xwnode/nodes/strategies/deque.py +321 -153
  84. exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
  85. exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
  86. exonware/xwnode/nodes/strategies/hamt.py +403 -0
  87. exonware/xwnode/nodes/strategies/hash_map.py +354 -67
  88. exonware/xwnode/nodes/strategies/heap.py +105 -5
  89. exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
  90. exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
  91. exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
  92. exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
  93. exonware/xwnode/nodes/strategies/learned_index.py +533 -0
  94. exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
  95. exonware/xwnode/nodes/strategies/linked_list.py +316 -119
  96. exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
  97. exonware/xwnode/nodes/strategies/masstree.py +130 -0
  98. exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
  99. exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
  100. exonware/xwnode/nodes/strategies/queue.py +249 -120
  101. exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
  102. exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
  103. exonware/xwnode/nodes/strategies/rope.py +717 -0
  104. exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
  105. exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
  106. exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
  107. exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
  108. exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
  109. exonware/xwnode/nodes/strategies/stack.py +244 -112
  110. exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
  111. exonware/xwnode/nodes/strategies/t_tree.py +94 -0
  112. exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
  113. exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
  114. exonware/xwnode/nodes/strategies/trie.py +153 -9
  115. exonware/xwnode/nodes/strategies/union_find.py +111 -5
  116. exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
  117. exonware/xwnode/strategies/__init__.py +5 -51
  118. exonware/xwnode/version.py +3 -3
  119. exonware_xwnode-0.0.1.24.dist-info/METADATA +900 -0
  120. exonware_xwnode-0.0.1.24.dist-info/RECORD +130 -0
  121. exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
  122. exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
  123. exonware/xwnode/nodes/strategies/_base_node.py +0 -307
  124. exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
  125. exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
  126. exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
  127. exonware/xwnode/nodes/strategies/node_heap.py +0 -196
  128. exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
  129. exonware/xwnode/nodes/strategies/node_trie.py +0 -257
  130. exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
  131. exonware/xwnode/queries/executors/__init__.py +0 -47
  132. exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
  133. exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
  134. exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
  135. exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
  136. exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
  137. exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
  138. exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
  139. exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
  140. exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
  141. exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
  142. exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
  143. exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
  144. exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
  145. exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
  146. exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
  147. exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
  148. exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
  149. exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
  150. exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
  151. exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
  152. exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
  153. exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
  154. exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
  155. exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
  156. exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
  157. exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
  158. exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
  159. exonware/xwnode/queries/executors/array/__init__.py +0 -9
  160. exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
  161. exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
  162. exonware/xwnode/queries/executors/base.py +0 -257
  163. exonware/xwnode/queries/executors/capability_checker.py +0 -204
  164. exonware/xwnode/queries/executors/contracts.py +0 -166
  165. exonware/xwnode/queries/executors/core/__init__.py +0 -17
  166. exonware/xwnode/queries/executors/core/create_executor.py +0 -96
  167. exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
  168. exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
  169. exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
  170. exonware/xwnode/queries/executors/core/select_executor.py +0 -152
  171. exonware/xwnode/queries/executors/core/update_executor.py +0 -102
  172. exonware/xwnode/queries/executors/data/__init__.py +0 -13
  173. exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
  174. exonware/xwnode/queries/executors/data/load_executor.py +0 -50
  175. exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
  176. exonware/xwnode/queries/executors/data/store_executor.py +0 -50
  177. exonware/xwnode/queries/executors/defs.py +0 -93
  178. exonware/xwnode/queries/executors/engine.py +0 -221
  179. exonware/xwnode/queries/executors/errors.py +0 -68
  180. exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
  181. exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
  182. exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
  183. exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
  184. exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
  185. exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
  186. exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
  187. exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
  188. exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
  189. exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
  190. exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
  191. exonware/xwnode/queries/executors/graph/__init__.py +0 -15
  192. exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
  193. exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
  194. exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
  195. exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
  196. exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
  197. exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
  198. exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
  199. exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
  200. exonware/xwnode/queries/executors/projection/__init__.py +0 -9
  201. exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
  202. exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
  203. exonware/xwnode/queries/executors/registry.py +0 -173
  204. exonware/xwnode/queries/parsers/__init__.py +0 -26
  205. exonware/xwnode/queries/parsers/base.py +0 -86
  206. exonware/xwnode/queries/parsers/contracts.py +0 -46
  207. exonware/xwnode/queries/parsers/errors.py +0 -53
  208. exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
  209. exonware/xwnode/queries/strategies/__init__.py +0 -24
  210. exonware/xwnode/queries/strategies/base.py +0 -236
  211. exonware/xwnode/queries/strategies/cql.py +0 -201
  212. exonware/xwnode/queries/strategies/cypher.py +0 -181
  213. exonware/xwnode/queries/strategies/datalog.py +0 -70
  214. exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
  215. exonware/xwnode/queries/strategies/eql.py +0 -70
  216. exonware/xwnode/queries/strategies/flux.py +0 -70
  217. exonware/xwnode/queries/strategies/gql.py +0 -70
  218. exonware/xwnode/queries/strategies/graphql.py +0 -240
  219. exonware/xwnode/queries/strategies/gremlin.py +0 -181
  220. exonware/xwnode/queries/strategies/hiveql.py +0 -214
  221. exonware/xwnode/queries/strategies/hql.py +0 -70
  222. exonware/xwnode/queries/strategies/jmespath.py +0 -219
  223. exonware/xwnode/queries/strategies/jq.py +0 -66
  224. exonware/xwnode/queries/strategies/json_query.py +0 -66
  225. exonware/xwnode/queries/strategies/jsoniq.py +0 -248
  226. exonware/xwnode/queries/strategies/kql.py +0 -70
  227. exonware/xwnode/queries/strategies/linq.py +0 -238
  228. exonware/xwnode/queries/strategies/logql.py +0 -70
  229. exonware/xwnode/queries/strategies/mql.py +0 -68
  230. exonware/xwnode/queries/strategies/n1ql.py +0 -210
  231. exonware/xwnode/queries/strategies/partiql.py +0 -70
  232. exonware/xwnode/queries/strategies/pig.py +0 -215
  233. exonware/xwnode/queries/strategies/promql.py +0 -70
  234. exonware/xwnode/queries/strategies/sparql.py +0 -220
  235. exonware/xwnode/queries/strategies/sql.py +0 -275
  236. exonware/xwnode/queries/strategies/xml_query.py +0 -66
  237. exonware/xwnode/queries/strategies/xpath.py +0 -223
  238. exonware/xwnode/queries/strategies/xquery.py +0 -258
  239. exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
  240. exonware/xwnode/queries/strategies/xwquery.py +0 -456
  241. exonware_xwnode-0.0.1.22.dist-info/METADATA +0 -168
  242. exonware_xwnode-0.0.1.22.dist-info/RECORD +0 -214
  243. /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
  244. /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
  245. /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
  246. /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
  247. /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
  248. {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.24.dist-info}/WHEEL +0 -0
  249. {exonware_xwnode-0.0.1.22.dist-info → exonware_xwnode-0.0.1.24.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,629 @@
1
+ """
2
+ #exonware/xwnode/src/exonware/xwnode/nodes/strategies/crdt_map.py
3
+
4
+ CRDT Map Node Strategy Implementation
5
+
6
+ This module implements the CRDT_MAP strategy for conflict-free replicated
7
+ data type with Last-Write-Wins semantics for distributed systems.
8
+
9
+ Company: eXonware.com
10
+ Author: Eng. Muhammad AlShehri
11
+ Email: connect@exonware.com
12
+ Version: 0.0.1.24
13
+ Generation Date: 12-Oct-2025
14
+ """
15
+
16
+ import time
17
+ from typing import Any, Iterator, List, Dict, Optional, Tuple
18
+ from .base import ANodeTreeStrategy
19
+ from .contracts import NodeType
20
+ from ...defs import NodeMode, NodeTrait
21
+ from ...errors import XWNodeError, XWNodeValueError
22
+
23
+
24
+ class VectorClock:
25
+ """
26
+ Vector clock for causality tracking.
27
+
28
+ WHY vector clocks:
29
+ - Tracks causality between operations
30
+ - Enables conflict detection
31
+ - Ensures eventual consistency
32
+ """
33
+
34
+ def __init__(self, replica_id: str = "default"):
35
+ """
36
+ Initialize vector clock.
37
+
38
+ Args:
39
+ replica_id: Unique replica identifier
40
+ """
41
+ self.clocks: Dict[str, int] = {replica_id: 0}
42
+ self.replica_id = replica_id
43
+
44
+ def increment(self) -> None:
45
+ """Increment this replica's clock."""
46
+ self.clocks[self.replica_id] = self.clocks.get(self.replica_id, 0) + 1
47
+
48
+ def update(self, other: 'VectorClock') -> None:
49
+ """
50
+ Update with another vector clock.
51
+
52
+ Args:
53
+ other: Other vector clock
54
+
55
+ WHY merge clocks:
56
+ - Takes maximum of each replica's clock
57
+ - Preserves causality information
58
+ - Enables happened-before detection
59
+ """
60
+ for replica, clock in other.clocks.items():
61
+ self.clocks[replica] = max(self.clocks.get(replica, 0), clock)
62
+
63
+ def happens_before(self, other: 'VectorClock') -> bool:
64
+ """
65
+ Check if this clock happened before other.
66
+
67
+ Args:
68
+ other: Other vector clock
69
+
70
+ Returns:
71
+ True if this happened before other
72
+ """
73
+ # All clocks must be ≤ corresponding clocks in other
74
+ for replica, clock in self.clocks.items():
75
+ if clock > other.clocks.get(replica, 0):
76
+ return False
77
+
78
+ # At least one must be strictly less
79
+ return any(
80
+ self.clocks.get(replica, 0) < clock
81
+ for replica, clock in other.clocks.items()
82
+ )
83
+
84
+ def concurrent_with(self, other: 'VectorClock') -> bool:
85
+ """Check if concurrent (neither happened before the other)."""
86
+ return not self.happens_before(other) and not other.happens_before(self)
87
+
88
+ def copy(self) -> 'VectorClock':
89
+ """Create copy of vector clock."""
90
+ vc = VectorClock(self.replica_id)
91
+ vc.clocks = self.clocks.copy()
92
+ return vc
93
+
94
+ def __repr__(self) -> str:
95
+ """String representation."""
96
+ return f"VectorClock({self.clocks})"
97
+
98
+
99
+ class CRDTEntry:
100
+ """
101
+ CRDT map entry with timestamp and vector clock.
102
+
103
+ WHY timestamped entries:
104
+ - Enables Last-Write-Wins resolution
105
+ - Tracks causality with vector clocks
106
+ - Supports tombstones for deletions
107
+ """
108
+
109
+ def __init__(self, value: Any, timestamp: float, vector_clock: VectorClock,
110
+ is_tombstone: bool = False):
111
+ """
112
+ Initialize CRDT entry.
113
+
114
+ Args:
115
+ value: Stored value
116
+ timestamp: Physical timestamp
117
+ vector_clock: Logical vector clock
118
+ is_tombstone: Whether this is a deletion marker
119
+ """
120
+ self.value = value
121
+ self.timestamp = timestamp
122
+ self.vector_clock = vector_clock
123
+ self.is_tombstone = is_tombstone
124
+
125
+
126
+ class CRDTMapStrategy(ANodeTreeStrategy):
127
+ """
128
+ CRDT Map strategy for conflict-free distributed data.
129
+
130
+ WHY CRDT:
131
+ - Enables offline-first applications
132
+ - Guarantees eventual consistency without coordination
133
+ - Supports multi-master replication
134
+ - Handles network partitions gracefully
135
+ - Perfect for collaborative editing and distributed databases
136
+
137
+ WHY this implementation:
138
+ - Last-Write-Wins (LWW) provides simple conflict resolution
139
+ - Vector clocks track causality for concurrent detection
140
+ - Tombstones handle deletion conflicts correctly
141
+ - Physical timestamps break ties deterministically
142
+ - Replica IDs ensure uniqueness
143
+
144
+ Time Complexity:
145
+ - Put: O(1) local operation
146
+ - Get: O(1) lookup
147
+ - Merge: O(m) where m is entries in other map
148
+ - Delete: O(1) (creates tombstone)
149
+
150
+ Space Complexity: O(n + d) where n is live entries, d is tombstones
151
+
152
+ Trade-offs:
153
+ - Advantage: Conflict-free merging (strong eventual consistency)
154
+ - Advantage: No coordination needed between replicas
155
+ - Advantage: Works offline, syncs when connected
156
+ - Limitation: Tombstones accumulate (needs garbage collection)
157
+ - Limitation: LWW may lose concurrent writes
158
+ - Limitation: Requires synchronized clocks for tie-breaking
159
+ - Compared to HashMap: Adds conflict resolution, more memory
160
+ - Compared to Operational Transform: Simpler, more robust
161
+
162
+ Best for:
163
+ - Distributed databases (Cassandra-style)
164
+ - Offline-first mobile applications
165
+ - Collaborative editing (presence, metadata)
166
+ - Multi-master replication scenarios
167
+ - Shopping carts and user preferences
168
+ - Distributed caching systems
169
+
170
+ Not recommended for:
171
+ - Single-server applications (use normal HashMap)
172
+ - When last-write semantics are unacceptable
173
+ - Financial transactions (need stronger consistency)
174
+ - When tombstone growth is problematic
175
+ - Real-time gaming (use operational transform)
176
+
177
+ Following eXonware Priorities:
178
+ 1. Security: Validates replica IDs, prevents malformed merges
179
+ 2. Usability: Simple put/get API, automatic conflict resolution
180
+ 3. Maintainability: Clean CRDT semantics, well-documented
181
+ 4. Performance: O(1) operations, efficient merging
182
+ 5. Extensibility: Easy to add OR-Set, counter CRDTs
183
+
184
+ Industry Best Practices:
185
+ - Follows Shapiro et al. CRDT specification
186
+ - Implements LWW-Element-Set variant
187
+ - Uses vector clocks for causality
188
+ - Provides tombstone garbage collection
189
+ - Compatible with Riak, Cassandra approaches
190
+ """
191
+
192
+ # Tree node type for classification
193
+ STRATEGY_TYPE: NodeType = NodeType.TREE
194
+
195
+ def __init__(self, mode: NodeMode = NodeMode.CRDT_MAP,
196
+ traits: NodeTrait = NodeTrait.NONE,
197
+ replica_id: Optional[str] = None, **options):
198
+ """
199
+ Initialize CRDT map strategy.
200
+
201
+ Args:
202
+ mode: Node mode
203
+ traits: Node traits
204
+ replica_id: Unique replica identifier
205
+ **options: Additional options
206
+ """
207
+ super().__init__(mode, traits, **options)
208
+
209
+ self.replica_id = replica_id or f"replica_{id(self)}"
210
+ self._entries: Dict[Any, CRDTEntry] = {}
211
+ self._vector_clock = VectorClock(self.replica_id)
212
+ self._size = 0
213
+
214
+ def get_supported_traits(self) -> NodeTrait:
215
+ """Get supported traits."""
216
+ return NodeTrait.INDEXED | NodeTrait.PERSISTENT | NodeTrait.STREAMING
217
+
218
+ # ============================================================================
219
+ # CORE CRDT OPERATIONS
220
+ # ============================================================================
221
+
222
+ def put(self, key: Any, value: Any = None) -> None:
223
+ """
224
+ Put value with LWW semantics.
225
+
226
+ Args:
227
+ key: Key
228
+ value: Value
229
+
230
+ WHY timestamping:
231
+ - Enables conflict resolution
232
+ - Provides total ordering
233
+ - Breaks ties deterministically
234
+ """
235
+ # Security: Validate key
236
+ if key is None:
237
+ raise XWNodeValueError("Key cannot be None")
238
+
239
+ # Increment vector clock
240
+ self._vector_clock.increment()
241
+
242
+ # Create entry with current timestamp and vector clock
243
+ timestamp = time.time()
244
+ vector_clock = self._vector_clock.copy()
245
+ entry = CRDTEntry(value, timestamp, vector_clock, is_tombstone=False)
246
+
247
+ # Check if we need to update
248
+ if key in self._entries:
249
+ existing = self._entries[key]
250
+
251
+ # Only update if new entry wins
252
+ if self._should_replace(existing, entry):
253
+ old_tombstone = existing.is_tombstone
254
+ self._entries[key] = entry
255
+
256
+ # Update size if transitioning from tombstone to value
257
+ if old_tombstone and not entry.is_tombstone:
258
+ self._size += 1
259
+ else:
260
+ self._entries[key] = entry
261
+ self._size += 1
262
+
263
+ def _should_replace(self, existing: CRDTEntry, new: CRDTEntry) -> bool:
264
+ """
265
+ Determine if new entry should replace existing.
266
+
267
+ Args:
268
+ existing: Current entry
269
+ new: New entry
270
+
271
+ Returns:
272
+ True if new should replace existing
273
+
274
+ WHY LWW resolution:
275
+ - Timestamp provides total order
276
+ - Replica ID breaks ties
277
+ - Deterministic across all replicas
278
+ """
279
+ # If vector clocks show causal ordering, use that
280
+ if new.vector_clock.happens_before(existing.vector_clock):
281
+ return False
282
+ if existing.vector_clock.happens_before(new.vector_clock):
283
+ return True
284
+
285
+ # Concurrent updates - use timestamp
286
+ if new.timestamp > existing.timestamp:
287
+ return True
288
+ elif new.timestamp < existing.timestamp:
289
+ return False
290
+
291
+ # Same timestamp - use replica ID for deterministic tie-breaking
292
+ return new.vector_clock.replica_id > existing.vector_clock.replica_id
293
+
294
+ def get(self, key: Any, default: Any = None) -> Any:
295
+ """
296
+ Get value.
297
+
298
+ Args:
299
+ key: Key
300
+ default: Default value
301
+
302
+ Returns:
303
+ Value or default (None if tombstone)
304
+ """
305
+ if key not in self._entries:
306
+ return default
307
+
308
+ entry = self._entries[key]
309
+
310
+ # Return None for tombstones
311
+ if entry.is_tombstone:
312
+ return default
313
+
314
+ return entry.value
315
+
316
+ def has(self, key: Any) -> bool:
317
+ """
318
+ Check if key exists (and not deleted).
319
+
320
+ Args:
321
+ key: Key
322
+
323
+ Returns:
324
+ True if exists and not tombstone
325
+ """
326
+ if key not in self._entries:
327
+ return False
328
+
329
+ return not self._entries[key].is_tombstone
330
+
331
+ def delete(self, key: Any) -> bool:
332
+ """
333
+ Delete key using tombstone.
334
+
335
+ Args:
336
+ key: Key to delete
337
+
338
+ Returns:
339
+ True if deleted, False if not found
340
+
341
+ WHY tombstones:
342
+ - Deletion must be replicated
343
+ - Tombstone prevents resurrection
344
+ - Eventually garbage collected
345
+ """
346
+ if key not in self._entries or self._entries[key].is_tombstone:
347
+ return False
348
+
349
+ # Increment vector clock
350
+ self._vector_clock.increment()
351
+
352
+ # Create tombstone entry
353
+ timestamp = time.time()
354
+ vector_clock = self._vector_clock.copy()
355
+ tombstone = CRDTEntry(None, timestamp, vector_clock, is_tombstone=True)
356
+
357
+ self._entries[key] = tombstone
358
+ self._size -= 1
359
+
360
+ return True
361
+
362
+ # ============================================================================
363
+ # CRDT MERGE OPERATION
364
+ # ============================================================================
365
+
366
+ def merge(self, other: 'CRDTMapStrategy') -> None:
367
+ """
368
+ Merge another CRDT map into this one.
369
+
370
+ Args:
371
+ other: Other CRDT map to merge
372
+
373
+ WHY conflict-free merge:
374
+ - Applies LWW resolution to all conflicts
375
+ - Updates vector clock with merged knowledge
376
+ - Handles tombstones correctly
377
+ - Guarantees strong eventual consistency
378
+ """
379
+ # Merge vector clocks
380
+ self._vector_clock.update(other._vector_clock)
381
+
382
+ # Merge entries
383
+ for key, other_entry in other._entries.items():
384
+ if key not in self._entries:
385
+ # New key, just add it
386
+ self._entries[key] = other_entry
387
+ if not other_entry.is_tombstone:
388
+ self._size += 1
389
+ else:
390
+ existing = self._entries[key]
391
+
392
+ # Apply LWW resolution
393
+ if self._should_replace(existing, other_entry):
394
+ old_tombstone = existing.is_tombstone
395
+ new_tombstone = other_entry.is_tombstone
396
+
397
+ self._entries[key] = other_entry
398
+
399
+ # Update size
400
+ if old_tombstone and not new_tombstone:
401
+ self._size += 1
402
+ elif not old_tombstone and new_tombstone:
403
+ self._size -= 1
404
+
405
+ def get_replica_state(self) -> Dict[str, Any]:
406
+ """
407
+ Get current replica state for synchronization.
408
+
409
+ Returns:
410
+ Serializable replica state
411
+ """
412
+ return {
413
+ 'replica_id': self.replica_id,
414
+ 'vector_clock': self._vector_clock.clocks.copy(),
415
+ 'entries': {
416
+ key: {
417
+ 'value': entry.value,
418
+ 'timestamp': entry.timestamp,
419
+ 'vector_clock': entry.vector_clock.clocks.copy(),
420
+ 'is_tombstone': entry.is_tombstone
421
+ }
422
+ for key, entry in self._entries.items()
423
+ }
424
+ }
425
+
426
+ def apply_replica_state(self, state: Dict[str, Any]) -> None:
427
+ """
428
+ Apply replica state from synchronization.
429
+
430
+ Args:
431
+ state: Replica state dictionary
432
+
433
+ WHY state application:
434
+ - Enables sync from remote replicas
435
+ - Reconstructs CRDT from serialized state
436
+ - Maintains vector clock consistency
437
+ """
438
+ # Create temporary CRDT with remote state
439
+ temp_crdt = CRDTMapStrategy(replica_id=state['replica_id'])
440
+ temp_crdt._vector_clock.clocks = state['vector_clock'].copy()
441
+
442
+ # Reconstruct entries
443
+ for key, entry_data in state['entries'].items():
444
+ vc = VectorClock(state['replica_id'])
445
+ vc.clocks = entry_data['vector_clock'].copy()
446
+
447
+ entry = CRDTEntry(
448
+ entry_data['value'],
449
+ entry_data['timestamp'],
450
+ vc,
451
+ entry_data['is_tombstone']
452
+ )
453
+ temp_crdt._entries[key] = entry
454
+
455
+ # Merge into current state
456
+ self.merge(temp_crdt)
457
+
458
+ # ============================================================================
459
+ # GARBAGE COLLECTION
460
+ # ============================================================================
461
+
462
+ def garbage_collect_tombstones(self, age_threshold: float = 3600) -> int:
463
+ """
464
+ Remove old tombstones to reclaim memory.
465
+
466
+ Args:
467
+ age_threshold: Minimum age in seconds for tombstone removal
468
+
469
+ Returns:
470
+ Number of tombstones removed
471
+
472
+ WHY garbage collection:
473
+ - Tombstones accumulate over time
474
+ - Safe to remove after all replicas have seen them
475
+ - Reclaims memory for deleted entries
476
+ """
477
+ current_time = time.time()
478
+ removed = 0
479
+
480
+ to_remove = []
481
+ for key, entry in self._entries.items():
482
+ if entry.is_tombstone:
483
+ age = current_time - entry.timestamp
484
+ if age > age_threshold:
485
+ to_remove.append(key)
486
+
487
+ for key in to_remove:
488
+ del self._entries[key]
489
+ removed += 1
490
+
491
+ return removed
492
+
493
+ # ============================================================================
494
+ # STANDARD OPERATIONS
495
+ # ============================================================================
496
+
497
+ def keys(self) -> Iterator[Any]:
498
+ """Get iterator over live keys (excluding tombstones)."""
499
+ for key, entry in self._entries.items():
500
+ if not entry.is_tombstone:
501
+ yield key
502
+
503
+ def values(self) -> Iterator[Any]:
504
+ """Get iterator over live values."""
505
+ for entry in self._entries.values():
506
+ if not entry.is_tombstone:
507
+ yield entry.value
508
+
509
+ def items(self) -> Iterator[tuple[Any, Any]]:
510
+ """Get iterator over live key-value pairs."""
511
+ for key, entry in self._entries.items():
512
+ if not entry.is_tombstone:
513
+ yield (key, entry.value)
514
+
515
+ def __len__(self) -> int:
516
+ """Get number of live entries."""
517
+ return self._size
518
+
519
+ def to_native(self) -> Any:
520
+ """Convert to native dict (live entries only)."""
521
+ return dict(self.items())
522
+
523
+ # ============================================================================
524
+ # UTILITY METHODS
525
+ # ============================================================================
526
+
527
+ def clear(self) -> None:
528
+ """
529
+ Clear all entries with tombstones.
530
+
531
+ WHY tombstones on clear:
532
+ - Ensures deletion propagates to other replicas
533
+ - Prevents resurrection of old data
534
+ """
535
+ # Mark all as tombstones
536
+ for key in list(self._entries.keys()):
537
+ if not self._entries[key].is_tombstone:
538
+ self.delete(key)
539
+
540
+ def is_empty(self) -> bool:
541
+ """Check if empty (no live entries)."""
542
+ return self._size == 0
543
+
544
+ def size(self) -> int:
545
+ """Get number of live entries."""
546
+ return self._size
547
+
548
+ def get_mode(self) -> NodeMode:
549
+ """Get strategy mode."""
550
+ return self.mode
551
+
552
+ def get_traits(self) -> NodeTrait:
553
+ """Get strategy traits."""
554
+ return self.traits
555
+
556
+ # ============================================================================
557
+ # STATISTICS
558
+ # ============================================================================
559
+
560
+ def get_statistics(self) -> Dict[str, Any]:
561
+ """
562
+ Get CRDT statistics.
563
+
564
+ Returns:
565
+ Statistics dictionary
566
+ """
567
+ live_count = sum(1 for e in self._entries.values() if not e.is_tombstone)
568
+ tombstone_count = sum(1 for e in self._entries.values() if e.is_tombstone)
569
+
570
+ return {
571
+ 'replica_id': self.replica_id,
572
+ 'live_entries': live_count,
573
+ 'tombstones': tombstone_count,
574
+ 'total_entries': len(self._entries),
575
+ 'vector_clock_size': len(self._vector_clock.clocks),
576
+ 'vector_clock': self._vector_clock.clocks.copy()
577
+ }
578
+
579
+ # ============================================================================
580
+ # COMPATIBILITY METHODS
581
+ # ============================================================================
582
+
583
+ def find(self, key: Any) -> Optional[Any]:
584
+ """Find value by key."""
585
+ return self.get(key)
586
+
587
+ def insert(self, key: Any, value: Any = None) -> None:
588
+ """Insert key-value pair."""
589
+ self.put(key, value)
590
+
591
+ def __str__(self) -> str:
592
+ """String representation."""
593
+ stats = self.get_statistics()
594
+ return (f"CRDTMapStrategy(replica={self.replica_id}, live={stats['live_entries']}, "
595
+ f"tombstones={stats['tombstones']})")
596
+
597
+ def __repr__(self) -> str:
598
+ """Detailed representation."""
599
+ return f"CRDTMapStrategy(mode={self.mode.name}, replica={self.replica_id}, size={self._size}, traits={self.traits})"
600
+
601
+ # ============================================================================
602
+ # FACTORY METHOD
603
+ # ============================================================================
604
+
605
+ @classmethod
606
+ def create_from_data(cls, data: Any, replica_id: Optional[str] = None) -> 'CRDTMapStrategy':
607
+ """
608
+ Create CRDT map from data.
609
+
610
+ Args:
611
+ data: Dictionary or iterable
612
+ replica_id: Unique replica ID
613
+
614
+ Returns:
615
+ New CRDTMapStrategy instance
616
+ """
617
+ instance = cls(replica_id=replica_id)
618
+
619
+ if isinstance(data, dict):
620
+ for key, value in data.items():
621
+ instance.put(key, value)
622
+ elif isinstance(data, (list, tuple)):
623
+ for i, value in enumerate(data):
624
+ instance.put(i, value)
625
+ else:
626
+ instance.put('value', data)
627
+
628
+ return instance
629
+
@@ -8,11 +8,11 @@ worst-case lookup time with efficient space utilization.
8
8
  from typing import Any, Iterator, List, Dict, Optional, Tuple
9
9
  import hashlib
10
10
  import random
11
- from ._base_node import aNodeStrategy
11
+ from .base import ANodeStrategy
12
12
  from ...defs import NodeMode, NodeTrait
13
13
 
14
14
 
15
- class xCuckooHashStrategy(aNodeStrategy):
15
+ class CuckooHashStrategy(ANodeStrategy):
16
16
  """
17
17
  Cuckoo Hash node strategy for guaranteed O(1) worst-case lookups.
18
18
 
@@ -1,6 +1,13 @@
1
1
  """
2
+ #exonware/xwnode/src/exonware/xwnode/nodes/strategies/data_interchange_optimized.py
3
+
2
4
  DATA_INTERCHANGE_OPTIMIZED Node Strategy Implementation
3
5
 
6
+ Status: Production Ready ✅
7
+ True Purpose: Ultra-lightweight data interchange with COW and object pooling
8
+ Complexity: O(1) operations with minimal overhead
9
+ Production Features: ✓ COW Semantics, ✓ Object Pooling, ✓ Structural Hashing, ✓ __slots__
10
+
4
11
  Ultra-lightweight strategy specifically optimized for data interchange patterns:
5
12
  - Copy-on-write semantics for data interchange
6
13
  - Object pooling support for factory patterns
@@ -8,22 +15,31 @@ Ultra-lightweight strategy specifically optimized for data interchange patterns:
8
15
  - Minimal metadata overhead
9
16
  - Zero graph features for maximum performance
10
17
  - __slots__ optimization for memory efficiency
18
+
19
+ Company: eXonware.com
20
+ Author: Eng. Muhammad AlShehri
21
+ Email: connect@exonware.com
22
+ Version: 0.0.1.24
23
+ Generation Date: October 12, 2025
11
24
  """
12
25
 
13
26
  import weakref
14
27
  from typing import Any, Iterator, Dict, List, Optional
15
- from ._base_node import aNodeStrategy
28
+ from .base import ANodeStrategy
16
29
  from ...defs import NodeMode, NodeTrait
17
30
  from ...errors import XWNodeUnsupportedCapabilityError
18
31
 
32
+ # Import contracts
33
+ from .contracts import NodeType
34
+
19
35
  # Import shared utilities
20
- from ..utils import (
36
+ from ...common.utils import (
21
37
  recursive_to_native, is_sequential_numeric_keys,
22
38
  calculate_structural_hash, create_performance_tracker
23
39
  )
24
40
 
25
41
 
26
- class DataInterchangeOptimizedStrategy(aNodeStrategy):
42
+ class DataInterchangeOptimizedStrategy(ANodeStrategy):
27
43
  """
28
44
  Ultra-lightweight node strategy optimized for data interchange patterns.
29
45
 
@@ -48,7 +64,7 @@ class DataInterchangeOptimizedStrategy(aNodeStrategy):
48
64
  def __init__(self, traits: NodeTrait = NodeTrait.INDEXED, **options):
49
65
  """Initialize the xData-optimized strategy."""
50
66
  # Initialize parent without calling super() to avoid dict overhead
51
- self.mode = NodeMode.HASH_MAP # Using HASH_MAP as base mode
67
+ self.mode = NodeMode.DATA_INTERCHANGE_OPTIMIZED # Dedicated mode for data interchange
52
68
  self.traits = traits
53
69
  self.options = options
54
70
 
@@ -371,3 +387,4 @@ def create_data_interchange_optimized_strategy(**options) -> DataInterchangeOpti
371
387
  data_interchange_options.update(options)
372
388
 
373
389
  return DataInterchangeOptimizedStrategy(NodeTrait.INDEXED, **data_interchange_options)
390
+