forked from scylladb/python-driver
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtablets.py
More file actions
146 lines (118 loc) · 4.8 KB
/
Copy pathtablets.py
File metadata and controls
146 lines (118 loc) · 4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from threading import Lock
from typing import Optional
from uuid import UUID
class Tablet(object):
"""
Represents a single ScyllaDB tablet.
It stores information about each replica, its host and shard,
and the token interval in the format (first_token, last_token].
"""
first_token = 0
last_token = 0
replicas = None
def __init__(self, first_token=0, last_token=0, replicas=None):
self.first_token = first_token
self.last_token = last_token
self.replicas = replicas
def __str__(self):
return "<Tablet: first_token=%s last_token=%s replicas=%s>" \
% (self.first_token, self.last_token, self.replicas)
__repr__ = __str__
@staticmethod
def _is_valid_tablet(replicas):
return replicas is not None and len(replicas) != 0
@staticmethod
def from_row(first_token, last_token, replicas):
if Tablet._is_valid_tablet(replicas):
tablet = Tablet(first_token, last_token, replicas)
return tablet
return None
def replica_contains_host_id(self, uuid: UUID) -> bool:
for replica in self.replicas:
if replica[0] == uuid:
return True
return False
class Tablets(object):
_lock = None
_tablets = {}
def __init__(self, tablets):
self._tablets = tablets
self._lock = Lock()
def table_has_tablets(self, keyspace, table) -> bool:
return bool(self._tablets.get((keyspace, table), []))
def get_tablet_for_key(self, keyspace, table, t):
tablet = self._tablets.get((keyspace, table), [])
if not tablet:
return None
id = bisect_left(tablet, t.value, key=lambda tablet: tablet.last_token)
if id < len(tablet) and t.value > tablet[id].first_token:
return tablet[id]
return None
def drop_tablets(self, keyspace: str, table: Optional[str] = None):
with self._lock:
if table is not None:
self._tablets.pop((keyspace, table), None)
return
to_be_deleted = []
for key in self._tablets.keys():
if key[0] == keyspace:
to_be_deleted.append(key)
for key in to_be_deleted:
del self._tablets[key]
def drop_tablets_by_host_id(self, host_id: Optional[UUID]):
if host_id is None:
return
with self._lock:
for key, tablets in self._tablets.items():
to_be_deleted = []
for tablet_id, tablet in enumerate(tablets):
if tablet.replica_contains_host_id(host_id):
to_be_deleted.append(tablet_id)
for tablet_id in reversed(to_be_deleted):
tablets.pop(tablet_id)
def add_tablet(self, keyspace, table, tablet):
with self._lock:
tablets_for_table = self._tablets.setdefault((keyspace, table), [])
# find first overlapping range
start = bisect_left(tablets_for_table, tablet.first_token, key=lambda t: t.first_token)
if start > 0 and tablets_for_table[start - 1].last_token > tablet.first_token:
start = start - 1
# find last overlapping range
end = bisect_left(tablets_for_table, tablet.last_token, key=lambda t: t.last_token)
if end < len(tablets_for_table) and tablets_for_table[end].first_token >= tablet.last_token:
end = end - 1
if start <= end:
del tablets_for_table[start:end + 1]
tablets_for_table.insert(start, tablet)
# bisect.bisect_left implementation from Python 3.11, needed untill support for
# Python < 3.10 is dropped, it is needed to use `key` to extract last_token from
# Tablet list - better solution performance-wise than materialize list of last_tokens
def bisect_left(a, x, lo=0, hi=None, *, key=None):
"""Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e < x, and all e in
a[i:] have e >= x. So if x already appears in the list, a.insert(i, x) will
insert just before the leftmost x already there.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
# Note, the comparison uses "<" to match the
# __lt__() logic in list.sort() and in heapq.
if key is None:
while lo < hi:
mid = (lo + hi) // 2
if a[mid] < x:
lo = mid + 1
else:
hi = mid
return
while lo < hi:
mid = (lo + hi) // 2
if key(a[mid]) < x:
lo = mid + 1
else:
hi = mid
return lo