tikv-python 0.1.0__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.
- tikv/__init__.py +44 -0
- tikv/_async/__init__.py +1 -0
- tikv/_async/_client.py +584 -0
- tikv/_async/_compat.py +53 -0
- tikv/_async/_pd.py +167 -0
- tikv/_async/_transport.py +52 -0
- tikv/_backoff.py +78 -0
- tikv/_errors.py +53 -0
- tikv/_keys.py +109 -0
- tikv/_proto/__init__.py +2 -0
- tikv/_proto/errorpb_pb2.py +68 -0
- tikv/_proto/errorpb_pb2.pyi +200 -0
- tikv/_proto/kvrpcpb_pb2.py +85 -0
- tikv/_proto/kvrpcpb_pb2.pyi +341 -0
- tikv/_proto/metapb_pb2.py +39 -0
- tikv/_proto/metapb_pb2.pyi +92 -0
- tikv/_proto/pdpb_pb2.py +56 -0
- tikv/_proto/pdpb_pb2.pyi +160 -0
- tikv/_region.py +228 -0
- tikv/_rpc.py +109 -0
- tikv/_sync/__init__.py +2 -0
- tikv/_sync/_client.py +585 -0
- tikv/_sync/_compat.py +43 -0
- tikv/_sync/_pd.py +168 -0
- tikv/_sync/_transport.py +53 -0
- tikv/_types.py +34 -0
- tikv/py.typed +0 -0
- tikv_python-0.1.0.dist-info/METADATA +387 -0
- tikv_python-0.1.0.dist-info/RECORD +31 -0
- tikv_python-0.1.0.dist-info/WHEEL +4 -0
- tikv_python-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Generated by scripts/generate_protos.py. DO NOT EDIT.
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
4
|
+
# source: metapb.proto
|
|
5
|
+
# Protobuf Python Version: 4.25.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
10
|
+
from google.protobuf.internal import builder as _builder
|
|
11
|
+
# @@protoc_insertion_point(imports)
|
|
12
|
+
|
|
13
|
+
_sym_db = _symbol_database.Default()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cmetapb.proto\x12\x06metapb\"\xb5\x01\n\x06Region\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x11\n\tstart_key\x18\x02 \x01(\x0c\x12\x0f\n\x07\x65nd_key\x18\x03 \x01(\x0c\x12)\n\x0cregion_epoch\x18\x04 \x01(\x0b\x32\x13.metapb.RegionEpoch\x12\x1b\n\x05peers\x18\x05 \x03(\x0b\x32\x0c.metapb.Peer\x12\x17\n\x0fis_in_flashback\x18\x07 \x01(\x08\x12\x1a\n\x12\x66lashback_start_ts\x18\x08 \x01(\x04\"0\n\x0bRegionEpoch\x12\x10\n\x08\x63onf_ver\x18\x01 \x01(\x04\x12\x0f\n\x07version\x18\x02 \x01(\x04\"X\n\x04Peer\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x10\n\x08store_id\x18\x02 \x01(\x04\x12\x1e\n\x04role\x18\x03 \x01(\x0e\x32\x10.metapb.PeerRole\x12\x12\n\nis_witness\x18\x04 \x01(\x08\"(\n\nStoreLabel\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xaa\x01\n\x05Store\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12!\n\x05state\x18\x03 \x01(\x0e\x32\x12.metapb.StoreState\x12\"\n\x06labels\x18\x04 \x03(\x0b\x32\x12.metapb.StoreLabel\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x14\n\x0cpeer_address\x18\x06 \x01(\t\x12\x16\n\x0estatus_address\x18\x07 \x01(\t*H\n\x08PeerRole\x12\t\n\x05Voter\x10\x00\x12\x0b\n\x07Learner\x10\x01\x12\x11\n\rIncomingVoter\x10\x02\x12\x11\n\rDemotingVoter\x10\x03*0\n\nStoreState\x12\x06\n\x02Up\x10\x00\x12\x0b\n\x07Offline\x10\x01\x12\r\n\tTombstone\x10\x02\x62\x06proto3')
|
|
19
|
+
|
|
20
|
+
_globals = globals()
|
|
21
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
22
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'metapb_pb2', _globals)
|
|
23
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
24
|
+
DESCRIPTOR._options = None
|
|
25
|
+
_globals['_PEERROLE']._serialized_start=563
|
|
26
|
+
_globals['_PEERROLE']._serialized_end=635
|
|
27
|
+
_globals['_STORESTATE']._serialized_start=637
|
|
28
|
+
_globals['_STORESTATE']._serialized_end=685
|
|
29
|
+
_globals['_REGION']._serialized_start=25
|
|
30
|
+
_globals['_REGION']._serialized_end=206
|
|
31
|
+
_globals['_REGIONEPOCH']._serialized_start=208
|
|
32
|
+
_globals['_REGIONEPOCH']._serialized_end=256
|
|
33
|
+
_globals['_PEER']._serialized_start=258
|
|
34
|
+
_globals['_PEER']._serialized_end=346
|
|
35
|
+
_globals['_STORELABEL']._serialized_start=348
|
|
36
|
+
_globals['_STORELABEL']._serialized_end=388
|
|
37
|
+
_globals['_STORE']._serialized_start=391
|
|
38
|
+
_globals['_STORE']._serialized_end=561
|
|
39
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Generated by scripts/generate_protos.py. DO NOT EDIT.
|
|
2
|
+
from google.protobuf.internal import containers as _containers
|
|
3
|
+
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
|
4
|
+
from google.protobuf import descriptor as _descriptor
|
|
5
|
+
from google.protobuf import message as _message
|
|
6
|
+
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
|
7
|
+
|
|
8
|
+
DESCRIPTOR: _descriptor.FileDescriptor
|
|
9
|
+
|
|
10
|
+
class PeerRole(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
11
|
+
__slots__ = ()
|
|
12
|
+
Voter: _ClassVar[PeerRole]
|
|
13
|
+
Learner: _ClassVar[PeerRole]
|
|
14
|
+
IncomingVoter: _ClassVar[PeerRole]
|
|
15
|
+
DemotingVoter: _ClassVar[PeerRole]
|
|
16
|
+
|
|
17
|
+
class StoreState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
18
|
+
__slots__ = ()
|
|
19
|
+
Up: _ClassVar[StoreState]
|
|
20
|
+
Offline: _ClassVar[StoreState]
|
|
21
|
+
Tombstone: _ClassVar[StoreState]
|
|
22
|
+
Voter: PeerRole
|
|
23
|
+
Learner: PeerRole
|
|
24
|
+
IncomingVoter: PeerRole
|
|
25
|
+
DemotingVoter: PeerRole
|
|
26
|
+
Up: StoreState
|
|
27
|
+
Offline: StoreState
|
|
28
|
+
Tombstone: StoreState
|
|
29
|
+
|
|
30
|
+
class Region(_message.Message):
|
|
31
|
+
__slots__ = ("id", "start_key", "end_key", "region_epoch", "peers", "is_in_flashback", "flashback_start_ts")
|
|
32
|
+
ID_FIELD_NUMBER: _ClassVar[int]
|
|
33
|
+
START_KEY_FIELD_NUMBER: _ClassVar[int]
|
|
34
|
+
END_KEY_FIELD_NUMBER: _ClassVar[int]
|
|
35
|
+
REGION_EPOCH_FIELD_NUMBER: _ClassVar[int]
|
|
36
|
+
PEERS_FIELD_NUMBER: _ClassVar[int]
|
|
37
|
+
IS_IN_FLASHBACK_FIELD_NUMBER: _ClassVar[int]
|
|
38
|
+
FLASHBACK_START_TS_FIELD_NUMBER: _ClassVar[int]
|
|
39
|
+
id: int
|
|
40
|
+
start_key: bytes
|
|
41
|
+
end_key: bytes
|
|
42
|
+
region_epoch: RegionEpoch
|
|
43
|
+
peers: _containers.RepeatedCompositeFieldContainer[Peer]
|
|
44
|
+
is_in_flashback: bool
|
|
45
|
+
flashback_start_ts: int
|
|
46
|
+
def __init__(self, id: _Optional[int] = ..., start_key: _Optional[bytes] = ..., end_key: _Optional[bytes] = ..., region_epoch: _Optional[_Union[RegionEpoch, _Mapping]] = ..., peers: _Optional[_Iterable[_Union[Peer, _Mapping]]] = ..., is_in_flashback: bool = ..., flashback_start_ts: _Optional[int] = ...) -> None: ...
|
|
47
|
+
|
|
48
|
+
class RegionEpoch(_message.Message):
|
|
49
|
+
__slots__ = ("conf_ver", "version")
|
|
50
|
+
CONF_VER_FIELD_NUMBER: _ClassVar[int]
|
|
51
|
+
VERSION_FIELD_NUMBER: _ClassVar[int]
|
|
52
|
+
conf_ver: int
|
|
53
|
+
version: int
|
|
54
|
+
def __init__(self, conf_ver: _Optional[int] = ..., version: _Optional[int] = ...) -> None: ...
|
|
55
|
+
|
|
56
|
+
class Peer(_message.Message):
|
|
57
|
+
__slots__ = ("id", "store_id", "role", "is_witness")
|
|
58
|
+
ID_FIELD_NUMBER: _ClassVar[int]
|
|
59
|
+
STORE_ID_FIELD_NUMBER: _ClassVar[int]
|
|
60
|
+
ROLE_FIELD_NUMBER: _ClassVar[int]
|
|
61
|
+
IS_WITNESS_FIELD_NUMBER: _ClassVar[int]
|
|
62
|
+
id: int
|
|
63
|
+
store_id: int
|
|
64
|
+
role: PeerRole
|
|
65
|
+
is_witness: bool
|
|
66
|
+
def __init__(self, id: _Optional[int] = ..., store_id: _Optional[int] = ..., role: _Optional[_Union[PeerRole, str]] = ..., is_witness: bool = ...) -> None: ...
|
|
67
|
+
|
|
68
|
+
class StoreLabel(_message.Message):
|
|
69
|
+
__slots__ = ("key", "value")
|
|
70
|
+
KEY_FIELD_NUMBER: _ClassVar[int]
|
|
71
|
+
VALUE_FIELD_NUMBER: _ClassVar[int]
|
|
72
|
+
key: str
|
|
73
|
+
value: str
|
|
74
|
+
def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ...
|
|
75
|
+
|
|
76
|
+
class Store(_message.Message):
|
|
77
|
+
__slots__ = ("id", "address", "state", "labels", "version", "peer_address", "status_address")
|
|
78
|
+
ID_FIELD_NUMBER: _ClassVar[int]
|
|
79
|
+
ADDRESS_FIELD_NUMBER: _ClassVar[int]
|
|
80
|
+
STATE_FIELD_NUMBER: _ClassVar[int]
|
|
81
|
+
LABELS_FIELD_NUMBER: _ClassVar[int]
|
|
82
|
+
VERSION_FIELD_NUMBER: _ClassVar[int]
|
|
83
|
+
PEER_ADDRESS_FIELD_NUMBER: _ClassVar[int]
|
|
84
|
+
STATUS_ADDRESS_FIELD_NUMBER: _ClassVar[int]
|
|
85
|
+
id: int
|
|
86
|
+
address: str
|
|
87
|
+
state: StoreState
|
|
88
|
+
labels: _containers.RepeatedCompositeFieldContainer[StoreLabel]
|
|
89
|
+
version: str
|
|
90
|
+
peer_address: str
|
|
91
|
+
status_address: str
|
|
92
|
+
def __init__(self, id: _Optional[int] = ..., address: _Optional[str] = ..., state: _Optional[_Union[StoreState, str]] = ..., labels: _Optional[_Iterable[_Union[StoreLabel, _Mapping]]] = ..., version: _Optional[str] = ..., peer_address: _Optional[str] = ..., status_address: _Optional[str] = ...) -> None: ...
|
tikv/_proto/pdpb_pb2.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Generated by scripts/generate_protos.py. DO NOT EDIT.
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
4
|
+
# source: pdpb.proto
|
|
5
|
+
# Protobuf Python Version: 4.25.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
10
|
+
from google.protobuf.internal import builder as _builder
|
|
11
|
+
# @@protoc_insertion_point(imports)
|
|
12
|
+
|
|
13
|
+
_sym_db = _symbol_database.Default()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from . import metapb_pb2 as metapb__pb2
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\npdpb.proto\x12\x04pdpb\x1a\x0cmetapb.proto\"c\n\rRequestHeader\x12\x12\n\ncluster_id\x18\x01 \x01(\x04\x12\x11\n\tsender_id\x18\x02 \x01(\x04\x12\x11\n\tcaller_id\x18\x03 \x01(\t\x12\x18\n\x10\x63\x61ller_component\x18\x04 \x01(\t\"@\n\x0eResponseHeader\x12\x12\n\ncluster_id\x18\x01 \x01(\x04\x12\x1a\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x0b.pdpb.Error\"7\n\x05\x45rror\x12\x1d\n\x04type\x18\x01 \x01(\x0e\x32\x0f.pdpb.ErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\"Q\n\x06Member\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tmember_id\x18\x02 \x01(\x04\x12\x11\n\tpeer_urls\x18\x03 \x03(\t\x12\x13\n\x0b\x63lient_urls\x18\x04 \x03(\t\"8\n\x11GetMembersRequest\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.pdpb.RequestHeader\"w\n\x12GetMembersResponse\x12$\n\x06header\x18\x01 \x01(\x0b\x32\x14.pdpb.ResponseHeader\x12\x1d\n\x07members\x18\x02 \x03(\x0b\x32\x0c.pdpb.Member\x12\x1c\n\x06leader\x18\x03 \x01(\x0b\x32\x0c.pdpb.Member\"H\n\x0fGetStoreRequest\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.pdpb.RequestHeader\x12\x10\n\x08store_id\x18\x02 \x01(\x04\"V\n\x10GetStoreResponse\x12$\n\x06header\x18\x01 \x01(\x0b\x32\x14.pdpb.ResponseHeader\x12\x1c\n\x05store\x18\x02 \x01(\x0b\x32\r.metapb.Store\"K\n\x10GetRegionRequest\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.pdpb.RequestHeader\x12\x12\n\nregion_key\x18\x02 \x01(\x0c\"w\n\x11GetRegionResponse\x12$\n\x06header\x18\x01 \x01(\x0b\x32\x14.pdpb.ResponseHeader\x12\x1e\n\x06region\x18\x02 \x01(\x0b\x32\x0e.metapb.Region\x12\x1c\n\x06leader\x18\x03 \x01(\x0b\x32\x0c.metapb.Peer\"l\n\x12ScanRegionsRequest\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.pdpb.RequestHeader\x12\x11\n\tstart_key\x18\x02 \x01(\x0c\x12\r\n\x05limit\x18\x03 \x01(\x05\x12\x0f\n\x07\x65nd_key\x18\x04 \x01(\x0c\"F\n\x06Region\x12\x1e\n\x06region\x18\x01 \x01(\x0b\x32\x0e.metapb.Region\x12\x1c\n\x06leader\x18\x02 \x01(\x0b\x32\x0c.metapb.Peer\"\x9f\x01\n\x13ScanRegionsResponse\x12$\n\x06header\x18\x01 \x01(\x0b\x32\x14.pdpb.ResponseHeader\x12$\n\x0cregion_metas\x18\x02 \x03(\x0b\x32\x0e.metapb.Region\x12\x1d\n\x07leaders\x18\x03 \x03(\x0b\x32\x0c.metapb.Peer\x12\x1d\n\x07regions\x18\x04 \x03(\x0b\x32\x0c.pdpb.Region*\xab\x02\n\tErrorType\x12\x06\n\x02OK\x10\x00\x12\x0b\n\x07UNKNOWN\x10\x01\x12\x14\n\x10NOT_BOOTSTRAPPED\x10\x02\x12\x13\n\x0fSTORE_TOMBSTONE\x10\x03\x12\x18\n\x14\x41LREADY_BOOTSTRAPPED\x10\x04\x12\x18\n\x14INCOMPATIBLE_VERSION\x10\x05\x12\x14\n\x10REGION_NOT_FOUND\x10\x06\x12\x1b\n\x17GLOBAL_CONFIG_NOT_FOUND\x10\x07\x12\x14\n\x10\x44UPLICATED_ENTRY\x10\x08\x12\x13\n\x0f\x45NTRY_NOT_FOUND\x10\t\x12\x11\n\rINVALID_VALUE\x10\n\x12\x12\n\x0e\x44\x41TA_COMPACTED\x10\x0b\x12%\n!REGIONS_NOT_CONTAIN_ALL_KEY_RANGE\x10\x0c\x32\xce\x02\n\x02PD\x12\x41\n\nGetMembers\x12\x17.pdpb.GetMembersRequest\x1a\x18.pdpb.GetMembersResponse\"\x00\x12;\n\x08GetStore\x12\x15.pdpb.GetStoreRequest\x1a\x16.pdpb.GetStoreResponse\"\x00\x12>\n\tGetRegion\x12\x16.pdpb.GetRegionRequest\x1a\x17.pdpb.GetRegionResponse\"\x00\x12\x42\n\rGetPrevRegion\x12\x16.pdpb.GetRegionRequest\x1a\x17.pdpb.GetRegionResponse\"\x00\x12\x44\n\x0bScanRegions\x12\x18.pdpb.ScanRegionsRequest\x1a\x19.pdpb.ScanRegionsResponse\"\x00\x62\x06proto3')
|
|
20
|
+
|
|
21
|
+
_globals = globals()
|
|
22
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
23
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pdpb_pb2', _globals)
|
|
24
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
25
|
+
DESCRIPTOR._options = None
|
|
26
|
+
_globals['_ERRORTYPE']._serialized_start=1225
|
|
27
|
+
_globals['_ERRORTYPE']._serialized_end=1524
|
|
28
|
+
_globals['_REQUESTHEADER']._serialized_start=34
|
|
29
|
+
_globals['_REQUESTHEADER']._serialized_end=133
|
|
30
|
+
_globals['_RESPONSEHEADER']._serialized_start=135
|
|
31
|
+
_globals['_RESPONSEHEADER']._serialized_end=199
|
|
32
|
+
_globals['_ERROR']._serialized_start=201
|
|
33
|
+
_globals['_ERROR']._serialized_end=256
|
|
34
|
+
_globals['_MEMBER']._serialized_start=258
|
|
35
|
+
_globals['_MEMBER']._serialized_end=339
|
|
36
|
+
_globals['_GETMEMBERSREQUEST']._serialized_start=341
|
|
37
|
+
_globals['_GETMEMBERSREQUEST']._serialized_end=397
|
|
38
|
+
_globals['_GETMEMBERSRESPONSE']._serialized_start=399
|
|
39
|
+
_globals['_GETMEMBERSRESPONSE']._serialized_end=518
|
|
40
|
+
_globals['_GETSTOREREQUEST']._serialized_start=520
|
|
41
|
+
_globals['_GETSTOREREQUEST']._serialized_end=592
|
|
42
|
+
_globals['_GETSTORERESPONSE']._serialized_start=594
|
|
43
|
+
_globals['_GETSTORERESPONSE']._serialized_end=680
|
|
44
|
+
_globals['_GETREGIONREQUEST']._serialized_start=682
|
|
45
|
+
_globals['_GETREGIONREQUEST']._serialized_end=757
|
|
46
|
+
_globals['_GETREGIONRESPONSE']._serialized_start=759
|
|
47
|
+
_globals['_GETREGIONRESPONSE']._serialized_end=878
|
|
48
|
+
_globals['_SCANREGIONSREQUEST']._serialized_start=880
|
|
49
|
+
_globals['_SCANREGIONSREQUEST']._serialized_end=988
|
|
50
|
+
_globals['_REGION']._serialized_start=990
|
|
51
|
+
_globals['_REGION']._serialized_end=1060
|
|
52
|
+
_globals['_SCANREGIONSRESPONSE']._serialized_start=1063
|
|
53
|
+
_globals['_SCANREGIONSRESPONSE']._serialized_end=1222
|
|
54
|
+
_globals['_PD']._serialized_start=1527
|
|
55
|
+
_globals['_PD']._serialized_end=1861
|
|
56
|
+
# @@protoc_insertion_point(module_scope)
|
tikv/_proto/pdpb_pb2.pyi
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Generated by scripts/generate_protos.py. DO NOT EDIT.
|
|
2
|
+
from . import metapb_pb2 as _metapb_pb2
|
|
3
|
+
from google.protobuf.internal import containers as _containers
|
|
4
|
+
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
|
5
|
+
from google.protobuf import descriptor as _descriptor
|
|
6
|
+
from google.protobuf import message as _message
|
|
7
|
+
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
|
8
|
+
|
|
9
|
+
DESCRIPTOR: _descriptor.FileDescriptor
|
|
10
|
+
|
|
11
|
+
class ErrorType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
|
12
|
+
__slots__ = ()
|
|
13
|
+
OK: _ClassVar[ErrorType]
|
|
14
|
+
UNKNOWN: _ClassVar[ErrorType]
|
|
15
|
+
NOT_BOOTSTRAPPED: _ClassVar[ErrorType]
|
|
16
|
+
STORE_TOMBSTONE: _ClassVar[ErrorType]
|
|
17
|
+
ALREADY_BOOTSTRAPPED: _ClassVar[ErrorType]
|
|
18
|
+
INCOMPATIBLE_VERSION: _ClassVar[ErrorType]
|
|
19
|
+
REGION_NOT_FOUND: _ClassVar[ErrorType]
|
|
20
|
+
GLOBAL_CONFIG_NOT_FOUND: _ClassVar[ErrorType]
|
|
21
|
+
DUPLICATED_ENTRY: _ClassVar[ErrorType]
|
|
22
|
+
ENTRY_NOT_FOUND: _ClassVar[ErrorType]
|
|
23
|
+
INVALID_VALUE: _ClassVar[ErrorType]
|
|
24
|
+
DATA_COMPACTED: _ClassVar[ErrorType]
|
|
25
|
+
REGIONS_NOT_CONTAIN_ALL_KEY_RANGE: _ClassVar[ErrorType]
|
|
26
|
+
OK: ErrorType
|
|
27
|
+
UNKNOWN: ErrorType
|
|
28
|
+
NOT_BOOTSTRAPPED: ErrorType
|
|
29
|
+
STORE_TOMBSTONE: ErrorType
|
|
30
|
+
ALREADY_BOOTSTRAPPED: ErrorType
|
|
31
|
+
INCOMPATIBLE_VERSION: ErrorType
|
|
32
|
+
REGION_NOT_FOUND: ErrorType
|
|
33
|
+
GLOBAL_CONFIG_NOT_FOUND: ErrorType
|
|
34
|
+
DUPLICATED_ENTRY: ErrorType
|
|
35
|
+
ENTRY_NOT_FOUND: ErrorType
|
|
36
|
+
INVALID_VALUE: ErrorType
|
|
37
|
+
DATA_COMPACTED: ErrorType
|
|
38
|
+
REGIONS_NOT_CONTAIN_ALL_KEY_RANGE: ErrorType
|
|
39
|
+
|
|
40
|
+
class RequestHeader(_message.Message):
|
|
41
|
+
__slots__ = ("cluster_id", "sender_id", "caller_id", "caller_component")
|
|
42
|
+
CLUSTER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
43
|
+
SENDER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
44
|
+
CALLER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
45
|
+
CALLER_COMPONENT_FIELD_NUMBER: _ClassVar[int]
|
|
46
|
+
cluster_id: int
|
|
47
|
+
sender_id: int
|
|
48
|
+
caller_id: str
|
|
49
|
+
caller_component: str
|
|
50
|
+
def __init__(self, cluster_id: _Optional[int] = ..., sender_id: _Optional[int] = ..., caller_id: _Optional[str] = ..., caller_component: _Optional[str] = ...) -> None: ...
|
|
51
|
+
|
|
52
|
+
class ResponseHeader(_message.Message):
|
|
53
|
+
__slots__ = ("cluster_id", "error")
|
|
54
|
+
CLUSTER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
55
|
+
ERROR_FIELD_NUMBER: _ClassVar[int]
|
|
56
|
+
cluster_id: int
|
|
57
|
+
error: Error
|
|
58
|
+
def __init__(self, cluster_id: _Optional[int] = ..., error: _Optional[_Union[Error, _Mapping]] = ...) -> None: ...
|
|
59
|
+
|
|
60
|
+
class Error(_message.Message):
|
|
61
|
+
__slots__ = ("type", "message")
|
|
62
|
+
TYPE_FIELD_NUMBER: _ClassVar[int]
|
|
63
|
+
MESSAGE_FIELD_NUMBER: _ClassVar[int]
|
|
64
|
+
type: ErrorType
|
|
65
|
+
message: str
|
|
66
|
+
def __init__(self, type: _Optional[_Union[ErrorType, str]] = ..., message: _Optional[str] = ...) -> None: ...
|
|
67
|
+
|
|
68
|
+
class Member(_message.Message):
|
|
69
|
+
__slots__ = ("name", "member_id", "peer_urls", "client_urls")
|
|
70
|
+
NAME_FIELD_NUMBER: _ClassVar[int]
|
|
71
|
+
MEMBER_ID_FIELD_NUMBER: _ClassVar[int]
|
|
72
|
+
PEER_URLS_FIELD_NUMBER: _ClassVar[int]
|
|
73
|
+
CLIENT_URLS_FIELD_NUMBER: _ClassVar[int]
|
|
74
|
+
name: str
|
|
75
|
+
member_id: int
|
|
76
|
+
peer_urls: _containers.RepeatedScalarFieldContainer[str]
|
|
77
|
+
client_urls: _containers.RepeatedScalarFieldContainer[str]
|
|
78
|
+
def __init__(self, name: _Optional[str] = ..., member_id: _Optional[int] = ..., peer_urls: _Optional[_Iterable[str]] = ..., client_urls: _Optional[_Iterable[str]] = ...) -> None: ...
|
|
79
|
+
|
|
80
|
+
class GetMembersRequest(_message.Message):
|
|
81
|
+
__slots__ = ("header",)
|
|
82
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
83
|
+
header: RequestHeader
|
|
84
|
+
def __init__(self, header: _Optional[_Union[RequestHeader, _Mapping]] = ...) -> None: ...
|
|
85
|
+
|
|
86
|
+
class GetMembersResponse(_message.Message):
|
|
87
|
+
__slots__ = ("header", "members", "leader")
|
|
88
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
89
|
+
MEMBERS_FIELD_NUMBER: _ClassVar[int]
|
|
90
|
+
LEADER_FIELD_NUMBER: _ClassVar[int]
|
|
91
|
+
header: ResponseHeader
|
|
92
|
+
members: _containers.RepeatedCompositeFieldContainer[Member]
|
|
93
|
+
leader: Member
|
|
94
|
+
def __init__(self, header: _Optional[_Union[ResponseHeader, _Mapping]] = ..., members: _Optional[_Iterable[_Union[Member, _Mapping]]] = ..., leader: _Optional[_Union[Member, _Mapping]] = ...) -> None: ...
|
|
95
|
+
|
|
96
|
+
class GetStoreRequest(_message.Message):
|
|
97
|
+
__slots__ = ("header", "store_id")
|
|
98
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
99
|
+
STORE_ID_FIELD_NUMBER: _ClassVar[int]
|
|
100
|
+
header: RequestHeader
|
|
101
|
+
store_id: int
|
|
102
|
+
def __init__(self, header: _Optional[_Union[RequestHeader, _Mapping]] = ..., store_id: _Optional[int] = ...) -> None: ...
|
|
103
|
+
|
|
104
|
+
class GetStoreResponse(_message.Message):
|
|
105
|
+
__slots__ = ("header", "store")
|
|
106
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
107
|
+
STORE_FIELD_NUMBER: _ClassVar[int]
|
|
108
|
+
header: ResponseHeader
|
|
109
|
+
store: _metapb_pb2.Store
|
|
110
|
+
def __init__(self, header: _Optional[_Union[ResponseHeader, _Mapping]] = ..., store: _Optional[_Union[_metapb_pb2.Store, _Mapping]] = ...) -> None: ...
|
|
111
|
+
|
|
112
|
+
class GetRegionRequest(_message.Message):
|
|
113
|
+
__slots__ = ("header", "region_key")
|
|
114
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
115
|
+
REGION_KEY_FIELD_NUMBER: _ClassVar[int]
|
|
116
|
+
header: RequestHeader
|
|
117
|
+
region_key: bytes
|
|
118
|
+
def __init__(self, header: _Optional[_Union[RequestHeader, _Mapping]] = ..., region_key: _Optional[bytes] = ...) -> None: ...
|
|
119
|
+
|
|
120
|
+
class GetRegionResponse(_message.Message):
|
|
121
|
+
__slots__ = ("header", "region", "leader")
|
|
122
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
123
|
+
REGION_FIELD_NUMBER: _ClassVar[int]
|
|
124
|
+
LEADER_FIELD_NUMBER: _ClassVar[int]
|
|
125
|
+
header: ResponseHeader
|
|
126
|
+
region: _metapb_pb2.Region
|
|
127
|
+
leader: _metapb_pb2.Peer
|
|
128
|
+
def __init__(self, header: _Optional[_Union[ResponseHeader, _Mapping]] = ..., region: _Optional[_Union[_metapb_pb2.Region, _Mapping]] = ..., leader: _Optional[_Union[_metapb_pb2.Peer, _Mapping]] = ...) -> None: ...
|
|
129
|
+
|
|
130
|
+
class ScanRegionsRequest(_message.Message):
|
|
131
|
+
__slots__ = ("header", "start_key", "limit", "end_key")
|
|
132
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
133
|
+
START_KEY_FIELD_NUMBER: _ClassVar[int]
|
|
134
|
+
LIMIT_FIELD_NUMBER: _ClassVar[int]
|
|
135
|
+
END_KEY_FIELD_NUMBER: _ClassVar[int]
|
|
136
|
+
header: RequestHeader
|
|
137
|
+
start_key: bytes
|
|
138
|
+
limit: int
|
|
139
|
+
end_key: bytes
|
|
140
|
+
def __init__(self, header: _Optional[_Union[RequestHeader, _Mapping]] = ..., start_key: _Optional[bytes] = ..., limit: _Optional[int] = ..., end_key: _Optional[bytes] = ...) -> None: ...
|
|
141
|
+
|
|
142
|
+
class Region(_message.Message):
|
|
143
|
+
__slots__ = ("region", "leader")
|
|
144
|
+
REGION_FIELD_NUMBER: _ClassVar[int]
|
|
145
|
+
LEADER_FIELD_NUMBER: _ClassVar[int]
|
|
146
|
+
region: _metapb_pb2.Region
|
|
147
|
+
leader: _metapb_pb2.Peer
|
|
148
|
+
def __init__(self, region: _Optional[_Union[_metapb_pb2.Region, _Mapping]] = ..., leader: _Optional[_Union[_metapb_pb2.Peer, _Mapping]] = ...) -> None: ...
|
|
149
|
+
|
|
150
|
+
class ScanRegionsResponse(_message.Message):
|
|
151
|
+
__slots__ = ("header", "region_metas", "leaders", "regions")
|
|
152
|
+
HEADER_FIELD_NUMBER: _ClassVar[int]
|
|
153
|
+
REGION_METAS_FIELD_NUMBER: _ClassVar[int]
|
|
154
|
+
LEADERS_FIELD_NUMBER: _ClassVar[int]
|
|
155
|
+
REGIONS_FIELD_NUMBER: _ClassVar[int]
|
|
156
|
+
header: ResponseHeader
|
|
157
|
+
region_metas: _containers.RepeatedCompositeFieldContainer[_metapb_pb2.Region]
|
|
158
|
+
leaders: _containers.RepeatedCompositeFieldContainer[_metapb_pb2.Peer]
|
|
159
|
+
regions: _containers.RepeatedCompositeFieldContainer[Region]
|
|
160
|
+
def __init__(self, header: _Optional[_Union[ResponseHeader, _Mapping]] = ..., region_metas: _Optional[_Iterable[_Union[_metapb_pb2.Region, _Mapping]]] = ..., leaders: _Optional[_Iterable[_Union[_metapb_pb2.Peer, _Mapping]]] = ..., regions: _Optional[_Iterable[_Union[Region, _Mapping]]] = ...) -> None: ...
|
tikv/_region.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Region routing: which region owns a key, and who leads it.
|
|
2
|
+
|
|
3
|
+
Everything here is pure, in-memory bookkeeping shared by the sync and async
|
|
4
|
+
clients. Network I/O lives in ``_async`` (and the generated ``_sync``).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import bisect
|
|
10
|
+
import threading
|
|
11
|
+
from dataclasses import dataclass, replace
|
|
12
|
+
|
|
13
|
+
from ._backoff import Wait
|
|
14
|
+
from ._errors import RegionError, UndeterminedError
|
|
15
|
+
from ._proto import errorpb_pb2, kvrpcpb_pb2, metapb_pb2
|
|
16
|
+
|
|
17
|
+
# Reverse scans need an exclusive upper bound, and TiKV treats an empty one as
|
|
18
|
+
# "nothing" rather than "+inf". This key sorts after every key TiKV accepts
|
|
19
|
+
# (``storage.max-key-size`` defaults to 8 KiB), so it stands in for +inf.
|
|
20
|
+
MAX_KEY = b"\xff" * (8 * 1024 + 1)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class Region:
|
|
25
|
+
"""A contiguous key range ``[start, end)`` served by one Raft group.
|
|
26
|
+
|
|
27
|
+
An empty ``end`` means the region extends to +inf.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
meta: metapb_pb2.Region
|
|
31
|
+
leader: metapb_pb2.Peer | None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def id(self) -> int:
|
|
35
|
+
return self.meta.id
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def start(self) -> bytes:
|
|
39
|
+
return self.meta.start_key
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def end(self) -> bytes:
|
|
43
|
+
return self.meta.end_key
|
|
44
|
+
|
|
45
|
+
def contains(self, key: bytes) -> bool:
|
|
46
|
+
return self.start <= key and (not self.end or key < self.end)
|
|
47
|
+
|
|
48
|
+
def contains_end(self, key: bytes) -> bool:
|
|
49
|
+
"""Whether *key*, as an exclusive upper bound, falls in this region."""
|
|
50
|
+
return self.start < key and (not self.end or key <= self.end)
|
|
51
|
+
|
|
52
|
+
def context(self) -> kvrpcpb_pb2.Context:
|
|
53
|
+
"""The request context that routes a request to this region's leader."""
|
|
54
|
+
return kvrpcpb_pb2.Context(
|
|
55
|
+
region_id=self.meta.id, region_epoch=self.meta.region_epoch, peer=self.leader
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def __repr__(self) -> str:
|
|
59
|
+
epoch = self.meta.region_epoch
|
|
60
|
+
leader = self.leader.store_id if self.leader else None
|
|
61
|
+
return (
|
|
62
|
+
f"Region(id={self.id}, start={self.start!r}, end={self.end!r}, "
|
|
63
|
+
f"epoch={epoch.conf_ver}:{epoch.version}, leader_store={leader})"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def make_region(meta: metapb_pb2.Region, leader: metapb_pb2.Peer | None) -> Region:
|
|
68
|
+
"""Build a :class:`Region`, treating PD's empty leader (id 0) as unknown."""
|
|
69
|
+
return Region(meta, leader if leader is not None and leader.id else None)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class RegionCache:
|
|
73
|
+
"""Thread-safe cache of regions (sorted by start key) and store addresses."""
|
|
74
|
+
|
|
75
|
+
def __init__(self) -> None:
|
|
76
|
+
self._lock = threading.Lock()
|
|
77
|
+
self._starts: list[bytes] = []
|
|
78
|
+
self._regions: list[Region] = []
|
|
79
|
+
self._stores: dict[int, str] = {}
|
|
80
|
+
|
|
81
|
+
def find(self, key: bytes) -> Region | None:
|
|
82
|
+
"""The cached region containing *key*, if any."""
|
|
83
|
+
with self._lock:
|
|
84
|
+
i = bisect.bisect_right(self._starts, key) - 1
|
|
85
|
+
if i >= 0 and self._regions[i].contains(key):
|
|
86
|
+
return self._regions[i]
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def find_end(self, key: bytes) -> Region | None:
|
|
90
|
+
"""The cached region where *key* is a valid exclusive upper bound."""
|
|
91
|
+
with self._lock:
|
|
92
|
+
i = bisect.bisect_left(self._starts, key) - 1
|
|
93
|
+
if i >= 0 and self._regions[i].contains_end(key):
|
|
94
|
+
return self._regions[i]
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def put(self, region: Region) -> None:
|
|
98
|
+
"""Cache *region*, evicting any cached region it overlaps."""
|
|
99
|
+
with self._lock:
|
|
100
|
+
lo = bisect.bisect_right(self._starts, region.start) - 1
|
|
101
|
+
if lo < 0 or not self._overlaps(self._regions[lo], region):
|
|
102
|
+
lo += 1
|
|
103
|
+
hi = lo
|
|
104
|
+
while hi < len(self._regions) and self._overlaps(self._regions[hi], region):
|
|
105
|
+
hi += 1
|
|
106
|
+
self._starts[lo:hi] = [region.start]
|
|
107
|
+
self._regions[lo:hi] = [region]
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _overlaps(a: Region, b: Region) -> bool:
|
|
111
|
+
return (not a.end or b.start < a.end) and (not b.end or a.start < b.end)
|
|
112
|
+
|
|
113
|
+
def invalidate(self, region: Region) -> None:
|
|
114
|
+
"""Forget *region* so the next lookup asks PD again."""
|
|
115
|
+
with self._lock:
|
|
116
|
+
i = bisect.bisect_left(self._starts, region.start)
|
|
117
|
+
if i < len(self._regions) and self._regions[i].id == region.id:
|
|
118
|
+
del self._starts[i]
|
|
119
|
+
del self._regions[i]
|
|
120
|
+
|
|
121
|
+
def update_leader(self, region: Region, leader: metapb_pb2.Peer) -> bool:
|
|
122
|
+
"""Point *region* at a new *leader*; False if the peer is unknown."""
|
|
123
|
+
for peer in region.meta.peers:
|
|
124
|
+
if peer.store_id == leader.store_id:
|
|
125
|
+
with self._lock:
|
|
126
|
+
i = bisect.bisect_left(self._starts, region.start)
|
|
127
|
+
# Only patch the entry we routed with; if it was replaced
|
|
128
|
+
# meanwhile, the newer entry wins.
|
|
129
|
+
if i < len(self._regions) and self._regions[i].meta == region.meta:
|
|
130
|
+
self._regions[i] = replace(region, leader=peer)
|
|
131
|
+
return True
|
|
132
|
+
self.invalidate(region)
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def store_address(self, store_id: int) -> str | None:
|
|
136
|
+
return self._stores.get(store_id)
|
|
137
|
+
|
|
138
|
+
def set_store_address(self, store_id: int, address: str) -> None:
|
|
139
|
+
self._stores[store_id] = address
|
|
140
|
+
|
|
141
|
+
def forget_store(self, store_id: int) -> None:
|
|
142
|
+
self._stores.pop(store_id, None)
|
|
143
|
+
|
|
144
|
+
def clear(self) -> None:
|
|
145
|
+
with self._lock:
|
|
146
|
+
self._starts.clear()
|
|
147
|
+
self._regions.clear()
|
|
148
|
+
self._stores.clear()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def on_region_error(
|
|
152
|
+
cache: RegionCache,
|
|
153
|
+
region: Region,
|
|
154
|
+
error: errorpb_pb2.Error,
|
|
155
|
+
*,
|
|
156
|
+
idempotent: bool,
|
|
157
|
+
) -> tuple[Wait, float]:
|
|
158
|
+
"""Update *cache* after *region* reported *error*; decide how to retry.
|
|
159
|
+
|
|
160
|
+
Returns the backoff reason and a server-suggested minimum delay. Raises
|
|
161
|
+
when retrying cannot help. The policy follows client-go and client-rust.
|
|
162
|
+
"""
|
|
163
|
+
store_id = region.leader.store_id if region.leader else 0
|
|
164
|
+
kind = _kind(error)
|
|
165
|
+
|
|
166
|
+
if kind == "not_leader":
|
|
167
|
+
if error.not_leader.HasField("leader") and cache.update_leader(region, error.not_leader.leader):
|
|
168
|
+
return Wait.REGION_SCHEDULING, 0.0
|
|
169
|
+
cache.invalidate(region) # leader unknown: an election is in progress
|
|
170
|
+
return Wait.REGION_SCHEDULING, 0.0
|
|
171
|
+
if kind == "server_is_busy":
|
|
172
|
+
return Wait.SERVER_BUSY, error.server_is_busy.backoff_ms / 1000
|
|
173
|
+
if kind == "raft_entry_too_large":
|
|
174
|
+
raise RegionError(f"request too large for region {region.id}: {error.message}")
|
|
175
|
+
if kind in _MAYBE_APPLIED and not idempotent:
|
|
176
|
+
# The write may have been proposed before this error: replaying it
|
|
177
|
+
# could apply it twice, so let the caller decide.
|
|
178
|
+
detail = error.undetermined_result.message
|
|
179
|
+
raise UndeterminedError(describe_region_error(region, error) + (f" ({detail})" if detail else ""))
|
|
180
|
+
if kind in _SCHEDULING:
|
|
181
|
+
return Wait.REGION_SCHEDULING, 0.0
|
|
182
|
+
if kind == "disk_full":
|
|
183
|
+
return Wait.SERVER_BUSY, 0.0
|
|
184
|
+
|
|
185
|
+
# Epoch changes, missing regions, store mismatches and anything unknown:
|
|
186
|
+
# the cached routing is stale, so refetch it from PD.
|
|
187
|
+
cache.invalidate(region)
|
|
188
|
+
if kind not in _REGION_ONLY and store_id:
|
|
189
|
+
cache.forget_store(store_id)
|
|
190
|
+
return Wait.REGION_MISS, 0.0
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Transient conditions that resolve once the region settles.
|
|
194
|
+
_SCHEDULING = frozenset(
|
|
195
|
+
{
|
|
196
|
+
"stale_command",
|
|
197
|
+
"max_timestamp_not_synced",
|
|
198
|
+
"read_index_not_ready",
|
|
199
|
+
"proposal_in_merging_mode",
|
|
200
|
+
"data_is_not_ready",
|
|
201
|
+
"region_not_initialized",
|
|
202
|
+
"RecoveryInProgress",
|
|
203
|
+
"FlashbackInProgress",
|
|
204
|
+
"FlashbackNotPrepared",
|
|
205
|
+
"undetermined_result",
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
# Errors a store may report after proposing a write to Raft, so the write
|
|
209
|
+
# might still be applied: TiKV answers pending proposals with RegionNotFound
|
|
210
|
+
# when the peer is destroyed, and with StaleCommand when leadership changes.
|
|
211
|
+
_MAYBE_APPLIED = frozenset({"undetermined_result", "region_not_found", "stale_command"})
|
|
212
|
+
# Stale routing that does not implicate the store address.
|
|
213
|
+
_REGION_ONLY = frozenset({"epoch_not_match", "region_not_found", "key_not_in_region"})
|
|
214
|
+
|
|
215
|
+
_KINDS = tuple(field.name for field in errorpb_pb2.Error.DESCRIPTOR.fields if field.message_type is not None)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def describe_region_error(region: Region, error: errorpb_pb2.Error) -> str:
|
|
219
|
+
"""A one-line summary such as ``region 12: epoch_not_match: ...``."""
|
|
220
|
+
return f"region {region.id}: {_kind(error) or 'error'}: {error.message}".rstrip(": ")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _kind(error: errorpb_pb2.Error) -> str | None:
|
|
224
|
+
"""The name of the specific error field set on *error*, if any."""
|
|
225
|
+
for name in _KINDS:
|
|
226
|
+
if error.HasField(name):
|
|
227
|
+
return name
|
|
228
|
+
return None
|