Merge commit 'b17c8359ec22892ed4daeaa569f2f5e105477251' into feature-redwood
# Conflicts: # flow/Trace.cpp
This commit is contained in:
commit
b95a2bd6c1
|
@ -136,7 +136,7 @@ static std::pair<std::string, std::string> splitPair(std::string const& input, c
|
||||||
static int abbrevToNID(std::string const& sn) {
|
static int abbrevToNID(std::string const& sn) {
|
||||||
int nid = NID_undef;
|
int nid = NID_undef;
|
||||||
|
|
||||||
if (sn == "C" || sn == "CN" || sn == "L" || sn == "ST" || sn == "O" || sn == "OU")
|
if (sn == "C" || sn == "CN" || sn == "L" || sn == "ST" || sn == "O" || sn == "OU" || sn == "UID" || sn == "DC")
|
||||||
nid = OBJ_sn2nid(sn.c_str());
|
nid = OBJ_sn2nid(sn.c_str());
|
||||||
if (nid == NID_undef)
|
if (nid == NID_undef)
|
||||||
throw std::runtime_error("abbrevToNID");
|
throw std::runtime_error("abbrevToNID");
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -154,7 +154,7 @@ clean: $(CLEAN_TARGETS) docpreview_clean
|
||||||
@rm -rf $(DEPSDIR)
|
@rm -rf $(DEPSDIR)
|
||||||
@rm -rf lib/
|
@rm -rf lib/
|
||||||
@rm -rf bin/coverage.*.xml
|
@rm -rf bin/coverage.*.xml
|
||||||
@rm -f */*.g.cpp */*/*/g.cpp */*.g.h */*/*.g.h
|
@find . -name "*.g.cpp" -exec rm -f {} \; -or -name "*.g.h" -exec rm -f {} \;
|
||||||
|
|
||||||
targets:
|
targets:
|
||||||
@echo "Available targets:"
|
@echo "Available targets:"
|
||||||
|
|
|
@ -28,7 +28,7 @@ from bindingtester import util
|
||||||
from bindingtester.tests import Test, Instruction, InstructionSet, ResultSpecification
|
from bindingtester.tests import Test, Instruction, InstructionSet, ResultSpecification
|
||||||
from bindingtester.tests import test_util, directory_util
|
from bindingtester.tests import test_util, directory_util
|
||||||
|
|
||||||
from bindingtester.tests.directory_util import DirListEntry
|
from bindingtester.tests.directory_state_tree import DirectoryStateTreeNode
|
||||||
|
|
||||||
fdb.api_version(FDB_API_VERSION)
|
fdb.api_version(FDB_API_VERSION)
|
||||||
|
|
||||||
|
@ -48,12 +48,12 @@ class DirectoryTest(Test):
|
||||||
def ensure_default_directory_subspace(self, instructions, path):
|
def ensure_default_directory_subspace(self, instructions, path):
|
||||||
directory_util.create_default_directory_subspace(instructions, path, self.random)
|
directory_util.create_default_directory_subspace(instructions, path, self.random)
|
||||||
|
|
||||||
child = self.root.add_child((path,), path, self.root, DirListEntry(True, True))
|
child = self.root.add_child(path, DirectoryStateTreeNode(True, True, has_known_prefix=True))
|
||||||
self.dir_list.append(child)
|
self.dir_list.append(child)
|
||||||
self.dir_index = directory_util.DEFAULT_DIRECTORY_INDEX
|
self.dir_index = directory_util.DEFAULT_DIRECTORY_INDEX
|
||||||
|
|
||||||
def generate_layer(self):
|
def generate_layer(self):
|
||||||
if random.random < 0.7:
|
if random.random() < 0.7:
|
||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
choice = random.randint(0, 3)
|
choice = random.randint(0, 3)
|
||||||
|
@ -114,29 +114,34 @@ class DirectoryTest(Test):
|
||||||
instructions.push_args(layer)
|
instructions.push_args(layer)
|
||||||
instructions.push_args(*test_util.with_length(path))
|
instructions.push_args(*test_util.with_length(path))
|
||||||
instructions.append('DIRECTORY_OPEN')
|
instructions.append('DIRECTORY_OPEN')
|
||||||
# print '%d. Selected %s, dir=%s, has_known_prefix=%s, dir_list_len=%d' \
|
self.dir_list.append(self.root.add_child(path, DirectoryStateTreeNode(True, True, has_known_prefix=False)))
|
||||||
# % (len(instructions), 'DIRECTORY_OPEN', repr(self.dir_index), False, len(self.dir_list))
|
# print('%d. Selected %s, dir=%s, dir_id=%s, has_known_prefix=%s, dir_list_len=%d' \
|
||||||
self.dir_list.append(self.dir_list[0].add_child(path, default_path, self.root, DirListEntry(True, True, has_known_prefix=False)))
|
# % (len(instructions), 'DIRECTORY_OPEN', repr(self.dir_index), self.dir_list[-1].dir_id, False, len(self.dir_list)-1))
|
||||||
|
|
||||||
instructions.setup_complete()
|
instructions.setup_complete()
|
||||||
|
|
||||||
for i in range(args.num_ops):
|
for i in range(args.num_ops):
|
||||||
if random.random() < 0.5:
|
if random.random() < 0.5:
|
||||||
self.dir_index = random.randrange(0, len(self.dir_list))
|
while True:
|
||||||
|
self.dir_index = random.randrange(0, len(self.dir_list))
|
||||||
|
if not self.dir_list[self.dir_index].state.is_partition or not self.dir_list[self.dir_index].state.deleted:
|
||||||
|
break
|
||||||
|
|
||||||
instructions.push_args(self.dir_index)
|
instructions.push_args(self.dir_index)
|
||||||
instructions.append('DIRECTORY_CHANGE')
|
instructions.append('DIRECTORY_CHANGE')
|
||||||
|
|
||||||
|
dir_entry = self.dir_list[self.dir_index]
|
||||||
|
|
||||||
choices = op_choices[:]
|
choices = op_choices[:]
|
||||||
if self.dir_list[self.dir_index].is_directory:
|
if dir_entry.state.is_directory:
|
||||||
choices += directory
|
choices += directory
|
||||||
if self.dir_list[self.dir_index].is_subspace:
|
if dir_entry.state.is_subspace:
|
||||||
choices += subspace
|
choices += subspace
|
||||||
|
|
||||||
op = random.choice(choices)
|
op = random.choice(choices)
|
||||||
dir_entry = self.dir_list[self.dir_index]
|
|
||||||
|
|
||||||
# print '%d. Selected %s, dir=%s, has_known_prefix=%s, dir_list_len=%d' \
|
# print('%d. Selected %s, dir=%d, dir_id=%d, has_known_prefix=%d, dir_list_len=%d' \
|
||||||
# % (len(instructions), op, repr(self.dir_index), repr(dir_entry.has_known_prefix), len(self.dir_list))
|
# % (len(instructions), op, self.dir_index, dir_entry.dir_id, dir_entry.state.has_known_prefix, len(self.dir_list)))
|
||||||
|
|
||||||
if op.endswith('_DATABASE') or op.endswith('_SNAPSHOT'):
|
if op.endswith('_DATABASE') or op.endswith('_SNAPSHOT'):
|
||||||
root_op = op[0:-9]
|
root_op = op[0:-9]
|
||||||
|
@ -151,24 +156,26 @@ class DirectoryTest(Test):
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_CREATE_SUBSPACE':
|
elif root_op == 'DIRECTORY_CREATE_SUBSPACE':
|
||||||
path = generate_path()
|
path = generate_path()
|
||||||
instructions.push_args(generate_prefix(allow_empty=False, is_partition=True))
|
instructions.push_args(generate_prefix(require_unique=False, is_partition=True))
|
||||||
instructions.push_args(*test_util.with_length(path))
|
instructions.push_args(*test_util.with_length(path))
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
self.dir_list.append(DirListEntry(False, True))
|
self.dir_list.append(DirectoryStateTreeNode(False, True, has_known_prefix=True))
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_CREATE_LAYER':
|
elif root_op == 'DIRECTORY_CREATE_LAYER':
|
||||||
indices = []
|
indices = []
|
||||||
|
|
||||||
|
prefixes = [generate_prefix(require_unique=args.concurrency==1, is_partition=True) for i in range(2)]
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
instructions.push_args(generate_prefix(allow_empty=False, is_partition=True))
|
instructions.push_args(prefixes[i])
|
||||||
instructions.push_args(*test_util.with_length(generate_path()))
|
instructions.push_args(*test_util.with_length(generate_path()))
|
||||||
instructions.append('DIRECTORY_CREATE_SUBSPACE')
|
instructions.append('DIRECTORY_CREATE_SUBSPACE')
|
||||||
indices.append(len(self.dir_list))
|
indices.append(len(self.dir_list))
|
||||||
self.dir_list.append(DirListEntry(False, True))
|
self.dir_list.append(DirectoryStateTreeNode(False, True, has_known_prefix=True))
|
||||||
|
|
||||||
instructions.push_args(random.choice([0, 1]))
|
instructions.push_args(random.choice([0, 1]))
|
||||||
instructions.push_args(*indices)
|
instructions.push_args(*indices)
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
self.dir_list.append(DirListEntry(True, False, False))
|
self.dir_list.append(DirectoryStateTreeNode.get_layer(prefixes[0]))
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_CREATE_OR_OPEN':
|
elif root_op == 'DIRECTORY_CREATE_OR_OPEN':
|
||||||
# Because allocated prefixes are non-deterministic, we cannot have overlapping
|
# Because allocated prefixes are non-deterministic, we cannot have overlapping
|
||||||
|
@ -183,14 +190,18 @@ class DirectoryTest(Test):
|
||||||
if not op.endswith('_DATABASE') and args.concurrency == 1:
|
if not op.endswith('_DATABASE') and args.concurrency == 1:
|
||||||
test_util.blocking_commit(instructions)
|
test_util.blocking_commit(instructions)
|
||||||
|
|
||||||
self.dir_list.append(dir_entry.add_child(path, default_path, self.root, DirListEntry(True, True, False)))
|
child_entry = dir_entry.get_descendent(path)
|
||||||
|
if child_entry is None:
|
||||||
|
child_entry = DirectoryStateTreeNode(True, True)
|
||||||
|
|
||||||
|
child_entry.state.has_known_prefix = False
|
||||||
|
self.dir_list.append(dir_entry.add_child(path, child_entry))
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_CREATE':
|
elif root_op == 'DIRECTORY_CREATE':
|
||||||
layer = self.generate_layer()
|
layer = self.generate_layer()
|
||||||
is_partition = layer == 'partition'
|
is_partition = layer == 'partition'
|
||||||
|
|
||||||
allow_empty_prefix = random.random() < 0.8
|
prefix = generate_prefix(require_unique=is_partition and args.concurrency==1, is_partition=is_partition, min_length=0)
|
||||||
prefix = generate_prefix(allow_empty=allow_empty_prefix, is_partition=is_partition)
|
|
||||||
|
|
||||||
# Because allocated prefixes are non-deterministic, we cannot have overlapping
|
# Because allocated prefixes are non-deterministic, we cannot have overlapping
|
||||||
# transactions that allocate/remove these prefixes in a comparison test
|
# transactions that allocate/remove these prefixes in a comparison test
|
||||||
|
@ -209,40 +220,59 @@ class DirectoryTest(Test):
|
||||||
if not op.endswith('_DATABASE') and args.concurrency == 1: # and allow_empty_prefix:
|
if not op.endswith('_DATABASE') and args.concurrency == 1: # and allow_empty_prefix:
|
||||||
test_util.blocking_commit(instructions)
|
test_util.blocking_commit(instructions)
|
||||||
|
|
||||||
self.dir_list.append(dir_entry.add_child(path, default_path, self.root, DirListEntry(True, True, bool(prefix))))
|
child_entry = dir_entry.get_descendent(path)
|
||||||
|
if child_entry is None:
|
||||||
|
child_entry = DirectoryStateTreeNode(True, True, has_known_prefix=bool(prefix))
|
||||||
|
elif not bool(prefix):
|
||||||
|
child_entry.state.has_known_prefix = False
|
||||||
|
|
||||||
|
if is_partition:
|
||||||
|
child_entry.state.is_partition = True
|
||||||
|
|
||||||
|
self.dir_list.append(dir_entry.add_child(path, child_entry))
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_OPEN':
|
elif root_op == 'DIRECTORY_OPEN':
|
||||||
path = generate_path()
|
path = generate_path()
|
||||||
instructions.push_args(self.generate_layer())
|
instructions.push_args(self.generate_layer())
|
||||||
instructions.push_args(*test_util.with_length(path))
|
instructions.push_args(*test_util.with_length(path))
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
self.dir_list.append(dir_entry.add_child(path, default_path, self.root, DirListEntry(True, True)))
|
|
||||||
|
child_entry = dir_entry.get_descendent(path)
|
||||||
|
if child_entry is None:
|
||||||
|
self.dir_list.append(DirectoryStateTreeNode(False, False, has_known_prefix=False))
|
||||||
|
else:
|
||||||
|
self.dir_list.append(dir_entry.add_child(path, child_entry))
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_MOVE':
|
elif root_op == 'DIRECTORY_MOVE':
|
||||||
old_path = generate_path()
|
old_path = generate_path()
|
||||||
new_path = generate_path()
|
new_path = generate_path()
|
||||||
instructions.push_args(*(test_util.with_length(old_path) + test_util.with_length(new_path)))
|
instructions.push_args(*(test_util.with_length(old_path) + test_util.with_length(new_path)))
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
# This could probably be made to sometimes set has_known_prefix to true
|
|
||||||
self.dir_list.append(dir_entry.add_child(new_path, default_path, self.root, DirListEntry(True, True, False)))
|
child_entry = dir_entry.get_descendent(old_path)
|
||||||
|
if child_entry is None:
|
||||||
|
self.dir_list.append(DirectoryStateTreeNode(False, False, has_known_prefix=False))
|
||||||
|
else:
|
||||||
|
self.dir_list.append(dir_entry.add_child(new_path, child_entry))
|
||||||
|
|
||||||
# Make sure that the default directory subspace still exists after moving the specified directory
|
# Make sure that the default directory subspace still exists after moving the specified directory
|
||||||
if dir_entry.is_directory and not dir_entry.is_subspace and old_path == (u'',):
|
if dir_entry.state.is_directory and not dir_entry.state.is_subspace and old_path == (u'',):
|
||||||
self.ensure_default_directory_subspace(instructions, default_path)
|
self.ensure_default_directory_subspace(instructions, default_path)
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_MOVE_TO':
|
elif root_op == 'DIRECTORY_MOVE_TO':
|
||||||
new_path = generate_path()
|
new_path = generate_path()
|
||||||
instructions.push_args(*test_util.with_length(new_path))
|
instructions.push_args(*test_util.with_length(new_path))
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
self.dir_list.append(dir_entry.root.add_child(new_path, default_path, self.root,
|
|
||||||
DirListEntry(True, True, dir_entry.has_known_prefix)))
|
child_entry = dir_entry.get_descendent(())
|
||||||
|
if child_entry is None:
|
||||||
|
self.dir_list.append(DirectoryStateTreeNode(False, False, has_known_prefix=False))
|
||||||
|
else:
|
||||||
|
self.dir_list.append(dir_entry.add_child(new_path, child_entry))
|
||||||
|
|
||||||
# Make sure that the default directory subspace still exists after moving the current directory
|
# Make sure that the default directory subspace still exists after moving the current directory
|
||||||
self.ensure_default_directory_subspace(instructions, default_path)
|
self.ensure_default_directory_subspace(instructions, default_path)
|
||||||
|
|
||||||
# FIXME: There is currently a problem with removing partitions. In these generated tests, it's possible
|
|
||||||
# for a removed partition to resurrect itself and insert keys into the database using its allocated
|
|
||||||
# prefix. The result is non-deterministic HCA errors.
|
|
||||||
elif root_op == 'DIRECTORY_REMOVE' or root_op == 'DIRECTORY_REMOVE_IF_EXISTS':
|
elif root_op == 'DIRECTORY_REMOVE' or root_op == 'DIRECTORY_REMOVE_IF_EXISTS':
|
||||||
# Because allocated prefixes are non-deterministic, we cannot have overlapping
|
# Because allocated prefixes are non-deterministic, we cannot have overlapping
|
||||||
# transactions that allocate/remove these prefixes in a comparison test
|
# transactions that allocate/remove these prefixes in a comparison test
|
||||||
|
@ -254,12 +284,14 @@ class DirectoryTest(Test):
|
||||||
if count == 1:
|
if count == 1:
|
||||||
path = generate_path()
|
path = generate_path()
|
||||||
instructions.push_args(*test_util.with_length(path))
|
instructions.push_args(*test_util.with_length(path))
|
||||||
instructions.push_args(count)
|
|
||||||
|
|
||||||
|
instructions.push_args(count)
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
|
|
||||||
|
dir_entry.delete(path)
|
||||||
|
|
||||||
# Make sure that the default directory subspace still exists after removing the specified directory
|
# Make sure that the default directory subspace still exists after removing the specified directory
|
||||||
if path == () or (dir_entry.is_directory and not dir_entry.is_subspace and path == (u'',)):
|
if path == () or (dir_entry.state.is_directory and not dir_entry.state.is_subspace and path == (u'',)):
|
||||||
self.ensure_default_directory_subspace(instructions, default_path)
|
self.ensure_default_directory_subspace(instructions, default_path)
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_LIST' or root_op == 'DIRECTORY_EXISTS':
|
elif root_op == 'DIRECTORY_LIST' or root_op == 'DIRECTORY_EXISTS':
|
||||||
|
@ -278,7 +310,7 @@ class DirectoryTest(Test):
|
||||||
instructions.append('DIRECTORY_STRIP_PREFIX')
|
instructions.append('DIRECTORY_STRIP_PREFIX')
|
||||||
|
|
||||||
elif root_op == 'DIRECTORY_UNPACK_KEY' or root_op == 'DIRECTORY_CONTAINS':
|
elif root_op == 'DIRECTORY_UNPACK_KEY' or root_op == 'DIRECTORY_CONTAINS':
|
||||||
if not dir_entry.has_known_prefix or random.random() < 0.2 or root_op == 'DIRECTORY_UNPACK_KEY':
|
if not dir_entry.state.has_known_prefix or random.random() < 0.2 or root_op == 'DIRECTORY_UNPACK_KEY':
|
||||||
t = self.random.random_tuple(5)
|
t = self.random.random_tuple(5)
|
||||||
instructions.push_args(*test_util.with_length(t))
|
instructions.push_args(*test_util.with_length(t))
|
||||||
instructions.append('DIRECTORY_PACK_KEY')
|
instructions.append('DIRECTORY_PACK_KEY')
|
||||||
|
@ -292,7 +324,7 @@ class DirectoryTest(Test):
|
||||||
instructions.push_args(*test_util.with_length(t))
|
instructions.push_args(*test_util.with_length(t))
|
||||||
instructions.append(op)
|
instructions.append(op)
|
||||||
if root_op == 'DIRECTORY_OPEN_SUBSPACE':
|
if root_op == 'DIRECTORY_OPEN_SUBSPACE':
|
||||||
self.dir_list.append(DirListEntry(False, True, dir_entry.has_known_prefix))
|
self.dir_list.append(DirectoryStateTreeNode(False, True, dir_entry.state.has_known_prefix))
|
||||||
else:
|
else:
|
||||||
test_util.to_front(instructions, 1)
|
test_util.to_front(instructions, 1)
|
||||||
instructions.append('DIRECTORY_STRIP_PREFIX')
|
instructions.append('DIRECTORY_STRIP_PREFIX')
|
||||||
|
@ -308,16 +340,18 @@ class DirectoryTest(Test):
|
||||||
for i, dir_entry in enumerate(self.dir_list):
|
for i, dir_entry in enumerate(self.dir_list):
|
||||||
instructions.push_args(i)
|
instructions.push_args(i)
|
||||||
instructions.append('DIRECTORY_CHANGE')
|
instructions.append('DIRECTORY_CHANGE')
|
||||||
if dir_entry.is_directory:
|
if dir_entry.state.is_directory:
|
||||||
instructions.push_args(self.directory_log.key())
|
instructions.push_args(self.directory_log.key())
|
||||||
instructions.append('DIRECTORY_LOG_DIRECTORY')
|
instructions.append('DIRECTORY_LOG_DIRECTORY')
|
||||||
if dir_entry.has_known_prefix and dir_entry.is_subspace:
|
if dir_entry.state.has_known_prefix and dir_entry.state.is_subspace:
|
||||||
# print '%d. Logging subspace: %d' % (i, dir_entry.dir_id)
|
# print('%d. Logging subspace: %d' % (i, dir_entry.dir_id))
|
||||||
instructions.push_args(self.subspace_log.key())
|
instructions.push_args(self.subspace_log.key())
|
||||||
instructions.append('DIRECTORY_LOG_SUBSPACE')
|
instructions.append('DIRECTORY_LOG_SUBSPACE')
|
||||||
if (i + 1) % 100 == 0:
|
if (i + 1) % 100 == 0:
|
||||||
test_util.blocking_commit(instructions)
|
test_util.blocking_commit(instructions)
|
||||||
|
|
||||||
|
test_util.blocking_commit(instructions)
|
||||||
|
|
||||||
instructions.push_args(self.stack_subspace.key())
|
instructions.push_args(self.stack_subspace.key())
|
||||||
instructions.append('LOG_STACK')
|
instructions.append('LOG_STACK')
|
||||||
|
|
||||||
|
@ -365,11 +399,15 @@ def generate_path(min_length=0):
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def generate_prefix(allow_empty=True, is_partition=False):
|
def generate_prefix(require_unique=False, is_partition=False, min_length=1):
|
||||||
if allow_empty and random.random() < 0.8:
|
fixed_prefix = 'abcdefg'
|
||||||
|
if not require_unique and min_length == 0 and random.random() < 0.8:
|
||||||
return None
|
return None
|
||||||
elif is_partition or random.random() < 0.5:
|
elif require_unique or is_partition or min_length > len(fixed_prefix) or random.random() < 0.5:
|
||||||
length = random.randint(0 if allow_empty else 1, 5)
|
if require_unique:
|
||||||
|
min_length = max(min_length, 16)
|
||||||
|
|
||||||
|
length = random.randint(min_length, min_length+5)
|
||||||
if length == 0:
|
if length == 0:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -379,6 +417,6 @@ def generate_prefix(allow_empty=True, is_partition=False):
|
||||||
else:
|
else:
|
||||||
return ''.join(chr(random.randrange(ord('\x02'), ord('\x14'))) for i in range(0, length))
|
return ''.join(chr(random.randrange(ord('\x02'), ord('\x14'))) for i in range(0, length))
|
||||||
else:
|
else:
|
||||||
prefix = 'abcdefg'
|
prefix = fixed_prefix
|
||||||
generated = prefix[0:random.randrange(0 if allow_empty else 1, len(prefix))]
|
generated = prefix[0:random.randrange(min_length, len(prefix))]
|
||||||
return generated
|
return generated
|
||||||
|
|
|
@ -0,0 +1,259 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class TreeNodeState:
|
||||||
|
def __init__(self, node, dir_id, is_directory, is_subspace, has_known_prefix, root, is_partition):
|
||||||
|
self.dir_id = dir_id
|
||||||
|
self.is_directory = is_directory
|
||||||
|
self.is_subspace = is_subspace
|
||||||
|
self.has_known_prefix = has_known_prefix
|
||||||
|
self.root = root
|
||||||
|
self.is_partition = is_partition
|
||||||
|
|
||||||
|
self.parents = { node }
|
||||||
|
self.children = {}
|
||||||
|
self.deleted = False
|
||||||
|
|
||||||
|
# Represents an element of the directory hierarchy. As a result of various operations (e.g. moves) that
|
||||||
|
# may or may not have succeeded, a node can represent multiple possible states.
|
||||||
|
class DirectoryStateTreeNode:
|
||||||
|
# A cache of directory layers. We mustn't have multiple entries for the same layer
|
||||||
|
layers = {}
|
||||||
|
|
||||||
|
# Because our operations may be applied to the default directory in the case that
|
||||||
|
# the current directory failed to open/create, we compute the result of each operation
|
||||||
|
# as if it was performed on the current directory and the default directory.
|
||||||
|
default_directory = None
|
||||||
|
|
||||||
|
# Used for debugging
|
||||||
|
dir_id = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset(cls):
|
||||||
|
cls.dir_id = 0
|
||||||
|
cls.layers = {}
|
||||||
|
cls.default_directory = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_default_directory(cls, default_directory):
|
||||||
|
cls.default_directory = default_directory
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_layer(cls, node_subspace_prefix):
|
||||||
|
if node_subspace_prefix not in DirectoryStateTreeNode.layers:
|
||||||
|
DirectoryStateTreeNode.layers[node_subspace_prefix] = DirectoryStateTreeNode(True, False, has_known_prefix=False)
|
||||||
|
|
||||||
|
return DirectoryStateTreeNode.layers[node_subspace_prefix]
|
||||||
|
|
||||||
|
def __init__(self, is_directory, is_subspace, has_known_prefix=True, root=None, is_partition=False):
|
||||||
|
self.state = TreeNodeState(self, DirectoryStateTreeNode.dir_id + 1, is_directory, is_subspace, has_known_prefix,
|
||||||
|
root or self, is_partition)
|
||||||
|
DirectoryStateTreeNode.dir_id += 1
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '{DirEntry %d: %d}' % (self.state.dir_id, self.state.has_known_prefix)
|
||||||
|
|
||||||
|
def _get_descendent(self, subpath, default):
|
||||||
|
if not subpath:
|
||||||
|
if default is not None:
|
||||||
|
self._merge(default)
|
||||||
|
return self
|
||||||
|
|
||||||
|
default_child = None
|
||||||
|
if default is not None:
|
||||||
|
default_child = default.state.children.get(subpath[0])
|
||||||
|
|
||||||
|
self_child = self.state.children.get(subpath[0])
|
||||||
|
|
||||||
|
if self_child is None:
|
||||||
|
if default_child is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return default_child._get_descendent(subpath[1:], None)
|
||||||
|
|
||||||
|
return self_child._get_descendent(subpath[1:], default_child)
|
||||||
|
|
||||||
|
def get_descendent(self, subpath):
|
||||||
|
return self._get_descendent(subpath, DirectoryStateTreeNode.default_directory)
|
||||||
|
|
||||||
|
def add_child(self, subpath, child):
|
||||||
|
child.state.root = self.state.root
|
||||||
|
if DirectoryStateTreeNode.default_directory:
|
||||||
|
# print('Adding child %r to default directory at %r' % (child, subpath))
|
||||||
|
child = DirectoryStateTreeNode.default_directory._add_child_impl(subpath, child)
|
||||||
|
# print('Added %r' % child)
|
||||||
|
|
||||||
|
# print('Adding child %r to directory at %r' % (child, subpath))
|
||||||
|
c = self._add_child_impl(subpath, child)
|
||||||
|
|
||||||
|
# print('Added %r' % c)
|
||||||
|
return c
|
||||||
|
|
||||||
|
def _add_child_impl(self, subpath, child):
|
||||||
|
# print('%d, %d. Adding child %r (recursive): %r' % (self.state.dir_id, child.state.dir_id, child, subpath))
|
||||||
|
if len(subpath) == 0:
|
||||||
|
# print('%d, %d. Setting child: %d, %d' % (self.state.dir_id, child.state.dir_id, self.state.has_known_prefix, child.state.has_known_prefix))
|
||||||
|
self._merge(child)
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
if not subpath[0] in self.state.children:
|
||||||
|
# print('%d, %d. Path %r was absent from %r (%r)' % (self.state.dir_id, child.state.dir_id, subpath[0:1], self, self.state.children))
|
||||||
|
subdir = DirectoryStateTreeNode(True, True, root=self.state.root)
|
||||||
|
self.state.children[subpath[0]] = subdir
|
||||||
|
else:
|
||||||
|
subdir = self.state.children[subpath[0]]
|
||||||
|
# print('%d, %d. Path was present' % (self.state.dir_id, child.state.dir_id))
|
||||||
|
|
||||||
|
if len(subpath) > 1:
|
||||||
|
subdir.state.has_known_prefix = False
|
||||||
|
|
||||||
|
return subdir._add_child_impl(subpath[1:], child)
|
||||||
|
|
||||||
|
def _merge(self, other):
|
||||||
|
if self.state.dir_id == other.state.dir_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dir_id = other.dir_id
|
||||||
|
self.state.dir_id = min(other.state.dir_id, self.state.dir_id)
|
||||||
|
self.state.is_directory = self.state.is_directory and other.state.is_directory
|
||||||
|
self.state.is_subspace = self.state.is_subspace and other.state.is_subspace
|
||||||
|
self.state.has_known_prefix = self.state.has_known_prefix and other.state.has_known_prefix
|
||||||
|
self.state.deleted = self.state.deleted or other.state.deleted
|
||||||
|
self.state.is_partition = self.state.is_partition or other.state.is_partition
|
||||||
|
|
||||||
|
other_children = other.state.children.copy()
|
||||||
|
other_parents = other.state.parents.copy()
|
||||||
|
|
||||||
|
for node in other_parents:
|
||||||
|
node.state = self.state
|
||||||
|
self.state.parents.add(node)
|
||||||
|
|
||||||
|
for c in other_children:
|
||||||
|
if c not in self.state.children:
|
||||||
|
self.state.children[c] = other_children[c]
|
||||||
|
else:
|
||||||
|
self.state.children[c]._merge(other_children[c])
|
||||||
|
|
||||||
|
def _delete_impl(self):
|
||||||
|
if not self.state.deleted:
|
||||||
|
self.state.deleted = True
|
||||||
|
for c in self.state.children.values():
|
||||||
|
c._delete_impl()
|
||||||
|
|
||||||
|
def delete(self, path):
|
||||||
|
child = self.get_descendent(path)
|
||||||
|
if child:
|
||||||
|
child._delete_impl()
|
||||||
|
|
||||||
|
def validate_dir(dir, root):
|
||||||
|
if dir.state.is_directory:
|
||||||
|
assert dir.state.root == root
|
||||||
|
else:
|
||||||
|
assert dir.state.root == dir
|
||||||
|
|
||||||
|
def run_test():
|
||||||
|
all_entries = []
|
||||||
|
|
||||||
|
root = DirectoryStateTreeNode.get_layer('\xfe')
|
||||||
|
all_entries.append(root)
|
||||||
|
|
||||||
|
default_dir = root.add_child(('default',), DirectoryStateTreeNode(True, True, has_known_prefix=True))
|
||||||
|
DirectoryStateTreeNode.set_default_directory(default_dir)
|
||||||
|
all_entries.append(default_dir)
|
||||||
|
|
||||||
|
all_entries.append(default_dir.add_child(('1',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
all_entries.append(default_dir.add_child(('1', '1'), DirectoryStateTreeNode(True, False, has_known_prefix=True)))
|
||||||
|
all_entries.append(default_dir.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
all_entries.append(default_dir.add_child(('3',), DirectoryStateTreeNode(True, True, has_known_prefix=False)))
|
||||||
|
all_entries.append(default_dir.add_child(('5',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
all_entries.append(default_dir.add_child(('3', '1'), DirectoryStateTreeNode(True, True, has_known_prefix=False)))
|
||||||
|
all_entries.append(default_dir.add_child(('1', '3'), DirectoryStateTreeNode(True, True, has_known_prefix=False)))
|
||||||
|
|
||||||
|
entry = all_entries[-1]
|
||||||
|
child_entries = []
|
||||||
|
child_entries.append(entry.add_child(('1',), DirectoryStateTreeNode(True, False, has_known_prefix=True)))
|
||||||
|
child_entries.append(entry.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
child_entries.append(entry.add_child(('3',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
child_entries.append(entry.add_child(('4',), DirectoryStateTreeNode(True, False, has_known_prefix=False)))
|
||||||
|
child_entries.append(entry.add_child(('5',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
|
||||||
|
all_entries.append(root.add_child(('1', '2'), DirectoryStateTreeNode(True, True, has_known_prefix=False)))
|
||||||
|
all_entries.append(root.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
all_entries.append(root.add_child(('3',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
all_entries.append(root.add_child(('1', '3',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
|
||||||
|
# This directory was merged with the default, but both have readable prefixes
|
||||||
|
entry = root.get_descendent(('2',))
|
||||||
|
assert entry.state.has_known_prefix
|
||||||
|
|
||||||
|
entry = all_entries[-1]
|
||||||
|
all_entries.append(entry.add_child(('1',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
all_entries.append(entry.add_child(('2',), DirectoryStateTreeNode(True, True, has_known_prefix=False)))
|
||||||
|
all_entries.append(entry.add_child(('3',), DirectoryStateTreeNode(True, False, has_known_prefix=True)))
|
||||||
|
|
||||||
|
entry_to_move = all_entries[-1]
|
||||||
|
|
||||||
|
all_entries.append(entry.add_child(('5',), DirectoryStateTreeNode(True, False, has_known_prefix=True)))
|
||||||
|
child_entries.append(entry.add_child(('6',), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
|
||||||
|
all_entries.extend(child_entries)
|
||||||
|
|
||||||
|
# This directory has an unknown prefix
|
||||||
|
entry = root.get_descendent(('1', '2'))
|
||||||
|
assert not entry.state.has_known_prefix
|
||||||
|
|
||||||
|
# This directory was default created and should have an unknown prefix
|
||||||
|
# It will merge with the default directory's child, which is not a subspace
|
||||||
|
entry = root.get_descendent(('1',))
|
||||||
|
assert not entry.state.has_known_prefix
|
||||||
|
assert not entry.state.is_subspace
|
||||||
|
|
||||||
|
# Multiple merges will have made this prefix unreadable
|
||||||
|
entry = root.get_descendent(('2',))
|
||||||
|
assert not entry.state.has_known_prefix
|
||||||
|
|
||||||
|
# Merge with default directory's child that has an unknown prefix
|
||||||
|
entry = root.get_descendent(('3',))
|
||||||
|
assert not entry.state.has_known_prefix
|
||||||
|
|
||||||
|
# Merge with default directory's child that has an unknown prefix and merged children
|
||||||
|
entry = root.get_descendent(('1', '3'))
|
||||||
|
assert set(entry.state.children.keys()) == {'1', '2', '3', '4', '5', '6'}
|
||||||
|
|
||||||
|
# This child entry should be the combination of ['default', '3'], ['default', '1', '3'], and ['1', '3']
|
||||||
|
entry = entry.get_descendent(('3',))
|
||||||
|
assert not entry.state.has_known_prefix
|
||||||
|
assert not entry.state.is_subspace
|
||||||
|
|
||||||
|
# Verify the merge of the children
|
||||||
|
assert not child_entries[0].state.has_known_prefix
|
||||||
|
assert not child_entries[0].state.is_subspace
|
||||||
|
|
||||||
|
assert not child_entries[1].state.has_known_prefix
|
||||||
|
assert child_entries[1].state.is_subspace
|
||||||
|
|
||||||
|
assert not child_entries[2].state.has_known_prefix
|
||||||
|
assert not child_entries[2].state.is_subspace
|
||||||
|
|
||||||
|
assert not child_entries[3].state.has_known_prefix
|
||||||
|
assert not child_entries[3].state.is_subspace
|
||||||
|
|
||||||
|
assert child_entries[4].state.has_known_prefix
|
||||||
|
assert not child_entries[4].state.is_subspace
|
||||||
|
|
||||||
|
assert child_entries[5].state.has_known_prefix
|
||||||
|
assert child_entries[5].state.is_subspace
|
||||||
|
|
||||||
|
entry = root.add_child(('3',), entry_to_move)
|
||||||
|
all_entries.append(entry)
|
||||||
|
|
||||||
|
# Test moving an entry
|
||||||
|
assert not entry.state.has_known_prefix
|
||||||
|
assert not entry.state.is_subspace
|
||||||
|
assert entry.state.children.keys() == ['1']
|
||||||
|
|
||||||
|
for e in all_entries:
|
||||||
|
validate_dir(e, root)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(run_test())
|
||||||
|
|
|
@ -27,6 +27,7 @@ from bindingtester import FDB_API_VERSION
|
||||||
from bindingtester import util
|
from bindingtester import util
|
||||||
|
|
||||||
from bindingtester.tests import test_util
|
from bindingtester.tests import test_util
|
||||||
|
from bindingtester.tests.directory_state_tree import DirectoryStateTreeNode
|
||||||
|
|
||||||
fdb.api_version(FDB_API_VERSION)
|
fdb.api_version(FDB_API_VERSION)
|
||||||
|
|
||||||
|
@ -34,82 +35,26 @@ DEFAULT_DIRECTORY_INDEX = 4
|
||||||
DEFAULT_DIRECTORY_PREFIX = 'default'
|
DEFAULT_DIRECTORY_PREFIX = 'default'
|
||||||
DIRECTORY_ERROR_STRING = 'DIRECTORY_ERROR'
|
DIRECTORY_ERROR_STRING = 'DIRECTORY_ERROR'
|
||||||
|
|
||||||
|
|
||||||
class DirListEntry:
|
|
||||||
dir_id = 0 # Used for debugging
|
|
||||||
|
|
||||||
def __init__(self, is_directory, is_subspace, has_known_prefix=True, path=(), root=None):
|
|
||||||
self.root = root or self
|
|
||||||
self.path = path
|
|
||||||
self.is_directory = is_directory
|
|
||||||
self.is_subspace = is_subspace
|
|
||||||
self.has_known_prefix = has_known_prefix
|
|
||||||
self.children = {}
|
|
||||||
|
|
||||||
self.dir_id = DirListEntry.dir_id + 1
|
|
||||||
DirListEntry.dir_id += 1
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'DirEntry %d %r: %d' % (self.dir_id, self.path, self.has_known_prefix)
|
|
||||||
|
|
||||||
def add_child(self, subpath, default_path, root, child):
|
|
||||||
if default_path in root.children:
|
|
||||||
# print 'Adding child %r to default directory %r at %r' % (child, root.children[DirectoryTest.DEFAULT_DIRECTORY_PATH].path, subpath)
|
|
||||||
c = root.children[default_path]._add_child_impl(subpath, child)
|
|
||||||
child.has_known_prefix = c.has_known_prefix and child.has_known_prefix
|
|
||||||
# print 'Added %r' % c
|
|
||||||
|
|
||||||
# print 'Adding child %r to directory %r at %r' % (child, self.path, subpath)
|
|
||||||
c = self._add_child_impl(subpath, child)
|
|
||||||
# print 'Added %r' % c
|
|
||||||
return c
|
|
||||||
|
|
||||||
def _add_child_impl(self, subpath, child):
|
|
||||||
# print '%d, %d. Adding child (recursive): %s %s' % (self.dir_id, child.dir_id, repr(self.path), repr(subpath))
|
|
||||||
if len(subpath) == 0:
|
|
||||||
self.has_known_prefix = self.has_known_prefix and child.has_known_prefix
|
|
||||||
# print '%d, %d. Setting child: %d' % (self.dir_id, child.dir_id, self.has_known_prefix)
|
|
||||||
self._merge_children(child)
|
|
||||||
|
|
||||||
return self
|
|
||||||
else:
|
|
||||||
if not subpath[0] in self.children:
|
|
||||||
# print '%d, %d. Path %s was absent (%s)' % (self.dir_id, child.dir_id, repr(self.path + subpath[0:1]), repr(self.children))
|
|
||||||
subdir = DirListEntry(True, True, path=self.path + subpath[0:1], root=self.root)
|
|
||||||
subdir.has_known_prefix = len(subpath) == 1
|
|
||||||
self.children[subpath[0]] = subdir
|
|
||||||
else:
|
|
||||||
subdir = self.children[subpath[0]]
|
|
||||||
subdir.has_known_prefix = False
|
|
||||||
# print '%d, %d. Path was present' % (self.dir_id, child.dir_id)
|
|
||||||
|
|
||||||
return subdir._add_child_impl(subpath[1:], child)
|
|
||||||
|
|
||||||
def _merge_children(self, other):
|
|
||||||
for c in other.children:
|
|
||||||
if c not in self.children:
|
|
||||||
self.children[c] = other.children[c]
|
|
||||||
else:
|
|
||||||
self.children[c].has_known_prefix = self.children[c].has_known_prefix and other.children[c].has_known_prefix
|
|
||||||
self.children[c]._merge_children(other.children[c])
|
|
||||||
|
|
||||||
|
|
||||||
def setup_directories(instructions, default_path, random):
|
def setup_directories(instructions, default_path, random):
|
||||||
dir_list = [DirListEntry(True, False, True)]
|
# Clients start with the default directory layer in the directory list
|
||||||
|
DirectoryStateTreeNode.reset()
|
||||||
|
dir_list = [DirectoryStateTreeNode.get_layer('\xfe')]
|
||||||
|
|
||||||
instructions.push_args(0, '\xfe')
|
instructions.push_args(0, '\xfe')
|
||||||
instructions.append('DIRECTORY_CREATE_SUBSPACE')
|
instructions.append('DIRECTORY_CREATE_SUBSPACE')
|
||||||
dir_list.append(DirListEntry(False, True))
|
dir_list.append(DirectoryStateTreeNode(False, True))
|
||||||
|
|
||||||
instructions.push_args(0, '')
|
instructions.push_args(0, '')
|
||||||
instructions.append('DIRECTORY_CREATE_SUBSPACE')
|
instructions.append('DIRECTORY_CREATE_SUBSPACE')
|
||||||
dir_list.append(DirListEntry(False, True))
|
dir_list.append(DirectoryStateTreeNode(False, True))
|
||||||
|
|
||||||
instructions.push_args(1, 2, 1)
|
instructions.push_args(1, 2, 1)
|
||||||
instructions.append('DIRECTORY_CREATE_LAYER')
|
instructions.append('DIRECTORY_CREATE_LAYER')
|
||||||
dir_list.append(DirListEntry(True, False, True))
|
dir_list.append(DirectoryStateTreeNode.get_layer('\xfe'))
|
||||||
|
|
||||||
create_default_directory_subspace(instructions, default_path, random)
|
create_default_directory_subspace(instructions, default_path, random)
|
||||||
dir_list.append(DirListEntry(True, True, True))
|
dir_list.append(dir_list[0].add_child((default_path,), DirectoryStateTreeNode(True, True, has_known_prefix=True)))
|
||||||
|
DirectoryStateTreeNode.set_default_directory(dir_list[-1])
|
||||||
|
|
||||||
instructions.push_args(DEFAULT_DIRECTORY_INDEX)
|
instructions.push_args(DEFAULT_DIRECTORY_INDEX)
|
||||||
instructions.append('DIRECTORY_SET_ERROR_INDEX')
|
instructions.append('DIRECTORY_SET_ERROR_INDEX')
|
||||||
|
|
|
@ -170,7 +170,10 @@ class AsyncDirectoryExtension {
|
||||||
.thenAccept(children -> inst.push(Tuple.fromItems(children).pack()));
|
.thenAccept(children -> inst.push(Tuple.fromItems(children).pack()));
|
||||||
}
|
}
|
||||||
else if(op == DirectoryOperation.DIRECTORY_EXISTS) {
|
else if(op == DirectoryOperation.DIRECTORY_EXISTS) {
|
||||||
return inst.popParam()
|
// In Java, DirectoryLayer.exists can return true without doing any reads.
|
||||||
|
// Other bindings will always do a read, so we get a read version now to be compatible with that behavior.
|
||||||
|
return inst.readTcx.readAsync(tr -> tr.getReadVersion())
|
||||||
|
.thenComposeAsync(v -> inst.popParam())
|
||||||
.thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count)))
|
.thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count)))
|
||||||
.thenComposeAsync(path -> {
|
.thenComposeAsync(path -> {
|
||||||
if(path.size() == 0)
|
if(path.size() == 0)
|
||||||
|
|
|
@ -317,8 +317,8 @@ public class AsyncStackTester {
|
||||||
if(t != null) {
|
if(t != null) {
|
||||||
inst.context.newTransaction(oldTr); // Other bindings allow reuse of non-retryable transactions, so we need to emulate that behavior.
|
inst.context.newTransaction(oldTr); // Other bindings allow reuse of non-retryable transactions, so we need to emulate that behavior.
|
||||||
}
|
}
|
||||||
else {
|
else if(!inst.setTransaction(oldTr, tr)) {
|
||||||
inst.setTransaction(oldTr, tr);
|
tr.close();
|
||||||
}
|
}
|
||||||
}).thenApply(v -> null);
|
}).thenApply(v -> null);
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@ abstract class Context implements Runnable, AutoCloseable {
|
||||||
|
|
||||||
private static synchronized Transaction getTransaction(String trName) {
|
private static synchronized Transaction getTransaction(String trName) {
|
||||||
Transaction tr = transactionMap.get(trName);
|
Transaction tr = transactionMap.get(trName);
|
||||||
|
assert tr != null : "Null transaction";
|
||||||
addTransactionReference(tr);
|
addTransactionReference(tr);
|
||||||
return tr;
|
return tr;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,15 @@ abstract class Context implements Runnable, AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static synchronized boolean updateTransaction(String trName, Transaction oldTr, Transaction newTr) {
|
private static synchronized boolean updateTransaction(String trName, Transaction oldTr, Transaction newTr) {
|
||||||
if(transactionMap.replace(trName, oldTr, newTr)) {
|
boolean added;
|
||||||
|
if(oldTr == null) {
|
||||||
|
added = (transactionMap.putIfAbsent(trName, newTr) == null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
added = transactionMap.replace(trName, oldTr, newTr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(added) {
|
||||||
addTransactionReference(newTr);
|
addTransactionReference(newTr);
|
||||||
releaseTransaction(oldTr);
|
releaseTransaction(oldTr);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -160,6 +160,11 @@ class DirectoryExtension {
|
||||||
int count = StackUtils.getInt(inst.popParam().get());
|
int count = StackUtils.getInt(inst.popParam().get());
|
||||||
List<List<String>> path = DirectoryUtil.popPaths(inst, count).get();
|
List<List<String>> path = DirectoryUtil.popPaths(inst, count).get();
|
||||||
boolean exists;
|
boolean exists;
|
||||||
|
|
||||||
|
// In Java, DirectoryLayer.exists can return true without doing any reads.
|
||||||
|
// Other bindings will always do a read, so we get a read version now to be compatible with that behavior.
|
||||||
|
inst.readTcx.read(tr -> tr.getReadVersion().join());
|
||||||
|
|
||||||
if(path.size() == 0)
|
if(path.size() == 0)
|
||||||
exists = directory().exists(inst.readTcx).get();
|
exists = directory().exists(inst.readTcx).get();
|
||||||
else
|
else
|
||||||
|
|
|
@ -72,16 +72,21 @@ class Instruction extends Stack {
|
||||||
readTcx = isDatabase ? context.db : readTr;
|
readTcx = isDatabase ? context.db : readTr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTransaction(Transaction newTr) {
|
boolean setTransaction(Transaction newTr) {
|
||||||
if(!isDatabase) {
|
if(!isDatabase) {
|
||||||
context.updateCurrentTransaction(newTr);
|
context.updateCurrentTransaction(newTr);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTransaction(Transaction oldTr, Transaction newTr) {
|
boolean setTransaction(Transaction oldTr, Transaction newTr) {
|
||||||
if(!isDatabase) {
|
if(!isDatabase) {
|
||||||
context.updateCurrentTransaction(oldTr, newTr);
|
return context.updateCurrentTransaction(oldTr, newTr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void releaseTransaction() {
|
void releaseTransaction() {
|
||||||
|
|
|
@ -284,7 +284,10 @@ public class StackTester {
|
||||||
FDBException err = new FDBException("Fake testing error", filteredError ? 1020 : errorCode);
|
FDBException err = new FDBException("Fake testing error", filteredError ? 1020 : errorCode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inst.setTransaction(inst.tr.onError(err).join());
|
Transaction tr = inst.tr.onError(err).join();
|
||||||
|
if(!inst.setTransaction(tr)) {
|
||||||
|
tr.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(Throwable t) {
|
catch(Throwable t) {
|
||||||
inst.context.newTransaction(); // Other bindings allow reuse of non-retryable transactions, so we need to emulate that behavior.
|
inst.context.newTransaction(); // Other bindings allow reuse of non-retryable transactions, so we need to emulate that behavior.
|
||||||
|
|
|
@ -102,71 +102,71 @@ class DirectoryExtension():
|
||||||
new_dir = self.dir_list[self.dir_index]
|
new_dir = self.dir_list[self.dir_index]
|
||||||
clazz = new_dir.__class__.__name__
|
clazz = new_dir.__class__.__name__
|
||||||
new_path = repr(new_dir._path) if hasattr(new_dir, '_path') else "<na>"
|
new_path = repr(new_dir._path) if hasattr(new_dir, '_path') else "<na>"
|
||||||
print('changed directory to %d (%s @%s)' % (self.dir_index, clazz, new_path))
|
print('changed directory to %d (%s @%r)' % (self.dir_index, clazz, new_path))
|
||||||
elif inst.op == six.u('DIRECTORY_SET_ERROR_INDEX'):
|
elif inst.op == six.u('DIRECTORY_SET_ERROR_INDEX'):
|
||||||
self.error_index = inst.pop()
|
self.error_index = inst.pop()
|
||||||
elif inst.op == six.u('DIRECTORY_CREATE_OR_OPEN'):
|
elif inst.op == six.u('DIRECTORY_CREATE_OR_OPEN'):
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
layer = inst.pop()
|
layer = inst.pop()
|
||||||
log_op('create_or_open %s: layer=%s' % (repr(directory.get_path() + path), repr(layer)))
|
log_op('create_or_open %r: layer=%r' % (directory.get_path() + path, layer))
|
||||||
d = directory.create_or_open(inst.tr, path, layer or b'')
|
d = directory.create_or_open(inst.tr, path, layer or b'')
|
||||||
self.append_dir(inst, d)
|
self.append_dir(inst, d)
|
||||||
elif inst.op == six.u('DIRECTORY_CREATE'):
|
elif inst.op == six.u('DIRECTORY_CREATE'):
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
layer, prefix = inst.pop(2)
|
layer, prefix = inst.pop(2)
|
||||||
log_op('create %s: layer=%s, prefix=%s' % (repr(directory.get_path() + path), repr(layer), repr(prefix)))
|
log_op('create %r: layer=%r, prefix=%r' % (directory.get_path() + path, layer, prefix))
|
||||||
self.append_dir(inst, directory.create(inst.tr, path, layer or b'', prefix))
|
self.append_dir(inst, directory.create(inst.tr, path, layer or b'', prefix))
|
||||||
elif inst.op == six.u('DIRECTORY_OPEN'):
|
elif inst.op == six.u('DIRECTORY_OPEN'):
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
layer = inst.pop()
|
layer = inst.pop()
|
||||||
log_op('open %s: layer=%s' % (repr(directory.get_path() + path), repr(layer)))
|
log_op('open %r: layer=%r' % (directory.get_path() + path, layer))
|
||||||
self.append_dir(inst, directory.open(inst.tr, path, layer or b''))
|
self.append_dir(inst, directory.open(inst.tr, path, layer or b''))
|
||||||
elif inst.op == six.u('DIRECTORY_MOVE'):
|
elif inst.op == six.u('DIRECTORY_MOVE'):
|
||||||
old_path, new_path = self.pop_tuples(inst.stack, 2)
|
old_path, new_path = self.pop_tuples(inst.stack, 2)
|
||||||
log_op('move %s to %s' % (repr(directory.get_path() + old_path), repr(directory.get_path() + new_path)))
|
log_op('move %r to %r' % (directory.get_path() + old_path, directory.get_path() + new_path))
|
||||||
self.append_dir(inst, directory.move(inst.tr, old_path, new_path))
|
self.append_dir(inst, directory.move(inst.tr, old_path, new_path))
|
||||||
elif inst.op == six.u('DIRECTORY_MOVE_TO'):
|
elif inst.op == six.u('DIRECTORY_MOVE_TO'):
|
||||||
new_absolute_path = self.pop_tuples(inst.stack)
|
new_absolute_path = self.pop_tuples(inst.stack)
|
||||||
log_op('move %s to %s' % (repr(directory.get_path()), repr(new_absolute_path)))
|
log_op('move %r to %r' % (directory.get_path(), new_absolute_path))
|
||||||
self.append_dir(inst, directory.move_to(inst.tr, new_absolute_path))
|
self.append_dir(inst, directory.move_to(inst.tr, new_absolute_path))
|
||||||
elif inst.op == six.u('DIRECTORY_REMOVE'):
|
elif inst.op == six.u('DIRECTORY_REMOVE'):
|
||||||
count = inst.pop()
|
count = inst.pop()
|
||||||
if count == 0:
|
if count == 0:
|
||||||
log_op('remove %s' % repr(directory.get_path()))
|
log_op('remove %r' % (directory.get_path(),))
|
||||||
directory.remove(inst.tr)
|
directory.remove(inst.tr)
|
||||||
else:
|
else:
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
log_op('remove %s' % repr(directory.get_path() + path))
|
log_op('remove %r' % (directory.get_path() + path,))
|
||||||
directory.remove(inst.tr, path)
|
directory.remove(inst.tr, path)
|
||||||
elif inst.op == six.u('DIRECTORY_REMOVE_IF_EXISTS'):
|
elif inst.op == six.u('DIRECTORY_REMOVE_IF_EXISTS'):
|
||||||
count = inst.pop()
|
count = inst.pop()
|
||||||
if count == 0:
|
if count == 0:
|
||||||
log_op('remove_if_exists %s' % repr(directory.get_path()))
|
log_op('remove_if_exists %r' % (directory.get_path(),))
|
||||||
directory.remove_if_exists(inst.tr)
|
directory.remove_if_exists(inst.tr)
|
||||||
else:
|
else:
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
log_op('remove_if_exists %s' % repr(directory.get_path() + path))
|
log_op('remove_if_exists %r' % (directory.get_path() + path,))
|
||||||
directory.remove_if_exists(inst.tr, path)
|
directory.remove_if_exists(inst.tr, path)
|
||||||
elif inst.op == six.u('DIRECTORY_LIST'):
|
elif inst.op == six.u('DIRECTORY_LIST'):
|
||||||
count = inst.pop()
|
count = inst.pop()
|
||||||
if count == 0:
|
if count == 0:
|
||||||
result = directory.list(inst.tr)
|
result = directory.list(inst.tr)
|
||||||
log_op('list %s' % (repr(directory.get_path())))
|
log_op('list %r' % (directory.get_path(),))
|
||||||
else:
|
else:
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
result = directory.list(inst.tr, path)
|
result = directory.list(inst.tr, path)
|
||||||
log_op('list %s' % (repr(directory.get_path() + path)))
|
log_op('list %r' % (directory.get_path() + path,))
|
||||||
|
|
||||||
inst.push(fdb.tuple.pack(tuple(result)))
|
inst.push(fdb.tuple.pack(tuple(result)))
|
||||||
elif inst.op == six.u('DIRECTORY_EXISTS'):
|
elif inst.op == six.u('DIRECTORY_EXISTS'):
|
||||||
count = inst.pop()
|
count = inst.pop()
|
||||||
if count == 0:
|
if count == 0:
|
||||||
result = directory.exists(inst.tr)
|
result = directory.exists(inst.tr)
|
||||||
log_op('exists %s: %d' % (repr(directory.get_path()), result))
|
log_op('exists %r: %d' % (directory.get_path(), result))
|
||||||
else:
|
else:
|
||||||
path = self.pop_tuples(inst.stack)
|
path = self.pop_tuples(inst.stack)
|
||||||
result = directory.exists(inst.tr, path)
|
result = directory.exists(inst.tr, path)
|
||||||
log_op('exists %s: %d' % (repr(directory.get_path() + path), result))
|
log_op('exists %r: %d' % (directory.get_path() + path, result))
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
inst.push(1)
|
inst.push(1)
|
||||||
|
@ -177,7 +177,7 @@ class DirectoryExtension():
|
||||||
inst.push(directory.pack(key_tuple))
|
inst.push(directory.pack(key_tuple))
|
||||||
elif inst.op == six.u('DIRECTORY_UNPACK_KEY'):
|
elif inst.op == six.u('DIRECTORY_UNPACK_KEY'):
|
||||||
key = inst.pop()
|
key = inst.pop()
|
||||||
log_op('unpack %s in subspace with prefix %s' % (repr(key), repr(directory.rawPrefix)))
|
log_op('unpack %r in subspace with prefix %r' % (key, directory.rawPrefix))
|
||||||
tup = directory.unpack(key)
|
tup = directory.unpack(key)
|
||||||
for t in tup:
|
for t in tup:
|
||||||
inst.push(t)
|
inst.push(t)
|
||||||
|
@ -215,7 +215,7 @@ class DirectoryExtension():
|
||||||
elif inst.op == six.u('DIRECTORY_STRIP_PREFIX'):
|
elif inst.op == six.u('DIRECTORY_STRIP_PREFIX'):
|
||||||
s = inst.pop()
|
s = inst.pop()
|
||||||
if not s.startswith(directory.key()):
|
if not s.startswith(directory.key()):
|
||||||
raise Exception('String %s does not start with raw prefix %s' % (s, directory.key()))
|
raise Exception('String %r does not start with raw prefix %r' % (s, directory.key()))
|
||||||
|
|
||||||
inst.push(s[len(directory.key()):])
|
inst.push(s[len(directory.key()):])
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -91,7 +91,7 @@ class Stack:
|
||||||
else:
|
else:
|
||||||
raw[i] = (raw[i][0], val)
|
raw[i] = (raw[i][0], val)
|
||||||
except fdb.FDBError as e:
|
except fdb.FDBError as e:
|
||||||
# print('ERROR: %s' % repr(e))
|
# print('ERROR: %r' % e)
|
||||||
raw[i] = (raw[i][0], fdb.tuple.pack((b'ERROR', str(e.code).encode('ascii'))))
|
raw[i] = (raw[i][0], fdb.tuple.pack((b'ERROR', str(e.code).encode('ascii'))))
|
||||||
|
|
||||||
if count is None:
|
if count is None:
|
||||||
|
@ -543,7 +543,7 @@ class Tester:
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown op %s" % inst.op)
|
raise Exception("Unknown op %s" % inst.op)
|
||||||
except fdb.FDBError as e:
|
except fdb.FDBError as e:
|
||||||
# print('ERROR: %s' % repr(e))
|
# print('ERROR: %r' % e)
|
||||||
inst.stack.push(idx, fdb.tuple.pack((b"ERROR", str(e.code).encode('ascii'))))
|
inst.stack.push(idx, fdb.tuple.pack((b"ERROR", str(e.code).encode('ascii'))))
|
||||||
|
|
||||||
# print(" to %s" % self.stack)
|
# print(" to %s" % self.stack)
|
||||||
|
|
|
@ -62,7 +62,10 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"data_version":12341234,
|
"data_version":12341234,
|
||||||
"data_version_lag":12341234,
|
"data_lag": {
|
||||||
|
"seconds":5.0,
|
||||||
|
"versions":12341234
|
||||||
|
},
|
||||||
"id":"eb84471d68c12d1d26f692a50000003f",
|
"id":"eb84471d68c12d1d26f692a50000003f",
|
||||||
"finished_queries":{
|
"finished_queries":{
|
||||||
"hz":0.0,
|
"hz":0.0,
|
||||||
|
@ -362,6 +365,7 @@
|
||||||
"remote_redundancy_mode":"remote_single",
|
"remote_redundancy_mode":"remote_single",
|
||||||
"remote_log_replicas":3,
|
"remote_log_replicas":3,
|
||||||
"remote_logs":5,
|
"remote_logs":5,
|
||||||
|
"log_routers":10,
|
||||||
"usable_regions":1,
|
"usable_regions":1,
|
||||||
"storage_replicas":1,
|
"storage_replicas":1,
|
||||||
"resolvers":1,
|
"resolvers":1,
|
||||||
|
|
|
@ -31,7 +31,8 @@ extensions = [
|
||||||
'sphinx.ext.todo',
|
'sphinx.ext.todo',
|
||||||
'sphinx.ext.ifconfig',
|
'sphinx.ext.ifconfig',
|
||||||
'brokenrole',
|
'brokenrole',
|
||||||
'relativelink'
|
'relativelink',
|
||||||
|
'sphinxcontrib.rubydomain'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
sphinx==1.5.6
|
sphinx==1.5.6
|
||||||
sphinx-bootstrap-theme==0.4.8
|
sphinx-bootstrap-theme==0.4.8
|
||||||
pygments-style-solarized
|
pygments-style-solarized
|
||||||
|
sphinxcontrib-rubydomain==0.1dev-20100804
|
|
@ -229,46 +229,45 @@ Use the ``status`` command of ``fdbcli`` to determine if the cluster is up and r
|
||||||
The database is available.
|
The database is available.
|
||||||
|
|
||||||
Welcome to the fdbcli. For help, type `help'.
|
Welcome to the fdbcli. For help, type `help'.
|
||||||
fdb> status
|
fdb> status
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
Redundancy mode - triple
|
Redundancy mode - triple
|
||||||
Storage engine - ssd-2
|
Storage engine - ssd-2
|
||||||
Coordinators - 5
|
Coordinators - 5
|
||||||
Desired Proxies - 5
|
Desired Proxies - 5
|
||||||
Desired Logs - 8
|
Desired Logs - 8
|
||||||
|
|
||||||
Cluster:
|
Cluster:
|
||||||
FoundationDB processes - 272
|
FoundationDB processes - 272
|
||||||
Machines - 16
|
Machines - 16
|
||||||
Memory availability - 14.5 GB per process on machine with least available
|
Memory availability - 14.5 GB per process on machine with least available
|
||||||
Retransmissions rate - 20 Hz
|
Retransmissions rate - 20 Hz
|
||||||
Fault Tolerance - 2 machines
|
Fault Tolerance - 2 machines
|
||||||
Server time - 03/19/18 08:51:52
|
Server time - 03/19/18 08:51:52
|
||||||
|
|
||||||
Data:
|
|
||||||
Replication health - Healthy
|
|
||||||
Moving data - 0.000 GB
|
|
||||||
Sum of key-value sizes - 3.298 TB
|
|
||||||
Disk space used - 15.243 TB
|
|
||||||
|
|
||||||
Operating space:
|
|
||||||
Storage server - 1656.2 GB free on most full server
|
|
||||||
Log server - 1794.7 GB free on most full server
|
|
||||||
|
|
||||||
Workload:
|
|
||||||
Read rate - 55990 Hz
|
|
||||||
Write rate - 14946 Hz
|
|
||||||
Transactions started - 6321 Hz
|
|
||||||
Transactions committed - 1132 Hz
|
|
||||||
Conflict rate - 0 Hz
|
|
||||||
|
|
||||||
Backup and DR:
|
|
||||||
Running backups - 1
|
|
||||||
Running DRs - 1 as primary
|
|
||||||
|
|
||||||
Client time: 03/19/18 08:51:51
|
|
||||||
|
|
||||||
|
Data:
|
||||||
|
Replication health - Healthy
|
||||||
|
Moving data - 0.000 GB
|
||||||
|
Sum of key-value sizes - 3.298 TB
|
||||||
|
Disk space used - 15.243 TB
|
||||||
|
|
||||||
|
Operating space:
|
||||||
|
Storage server - 1656.2 GB free on most full server
|
||||||
|
Log server - 1794.7 GB free on most full server
|
||||||
|
|
||||||
|
Workload:
|
||||||
|
Read rate - 55990 Hz
|
||||||
|
Write rate - 14946 Hz
|
||||||
|
Transactions started - 6321 Hz
|
||||||
|
Transactions committed - 1132 Hz
|
||||||
|
Conflict rate - 0 Hz
|
||||||
|
|
||||||
|
Backup and DR:
|
||||||
|
Running backups - 1
|
||||||
|
Running DRs - 1 as primary
|
||||||
|
|
||||||
|
Client time: 03/19/18 08:51:51
|
||||||
|
|
||||||
The summary fields are interpreted as follows:
|
The summary fields are interpreted as follows:
|
||||||
|
|
||||||
|
@ -328,131 +327,132 @@ The ``status`` command can provide detailed statistics about the cluster and the
|
||||||
fdb> status details
|
fdb> status details
|
||||||
|
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
Redundancy mode - triple
|
Redundancy mode - triple
|
||||||
Storage engine - ssd-2
|
Storage engine - ssd-2
|
||||||
Coordinators - 5
|
Coordinators - 5
|
||||||
|
|
||||||
|
Cluster:
|
||||||
|
FoundationDB processes - 85
|
||||||
|
Machines - 5
|
||||||
|
Memory availability - 7.4 GB per process on machine with least available
|
||||||
|
Retransmissions rate - 5 Hz
|
||||||
|
Fault Tolerance - 2 machines
|
||||||
|
Server time - 03/19/18 08:59:37
|
||||||
|
|
||||||
|
Data:
|
||||||
|
Replication health - Healthy
|
||||||
|
Moving data - 0.000 GB
|
||||||
|
Sum of key-value sizes - 87.068 GB
|
||||||
|
Disk space used - 327.819 GB
|
||||||
|
|
||||||
|
Operating space:
|
||||||
|
Storage server - 888.2 GB free on most full server
|
||||||
|
Log server - 897.3 GB free on most full server
|
||||||
|
|
||||||
|
Workload:
|
||||||
|
Read rate - 117 Hz
|
||||||
|
Write rate - 0 Hz
|
||||||
|
Transactions started - 43 Hz
|
||||||
|
Transactions committed - 1 Hz
|
||||||
|
Conflict rate - 0 Hz
|
||||||
|
|
||||||
Cluster:
|
Process performance details:
|
||||||
FoundationDB processes - 85
|
10.0.4.1:4500 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM )
|
||||||
Machines - 5
|
10.0.4.1:4501 ( 1% cpu; 2% machine; 0.010 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
Memory availability - 7.4 GB per process on machine with least available
|
10.0.4.1:4502 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
Retransmissions rate - 5 Hz
|
10.0.4.1:4503 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
Fault Tolerance - 2 machines
|
10.0.4.1:4504 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
Server time - 03/19/18 08:59:37
|
10.0.4.1:4505 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4506 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4507 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4508 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4509 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4510 ( 1% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4511 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4512 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4513 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4514 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4515 ( 12% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.1:4516 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.3 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4500 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4501 ( 15% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4502 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4503 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4504 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4505 ( 18% cpu; 3% machine; 0.124 Gbps; 18% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4506 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4507 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4508 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4509 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4510 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4511 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4512 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4513 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4514 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4515 ( 11% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.2:4516 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4500 ( 14% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 3.0 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4501 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4502 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4503 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4504 ( 7% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4505 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4506 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4507 ( 2% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4508 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4509 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4510 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4511 ( 2% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4512 ( 2% cpu; 3% machine; 0.284 Gbps; 3% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4513 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4514 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4515 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.3:4516 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4500 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4501 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4502 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4503 ( 2% cpu; 4% machine; 0.065 Gbps; 16% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4504 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4505 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4506 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4507 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4508 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4509 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4510 ( 24% cpu; 4% machine; 0.065 Gbps; 15% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4511 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4512 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4513 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4514 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4515 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.4:4516 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4500 ( 6% cpu; 2% machine; 0.076 Gbps; 7% disk IO; 3.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4501 ( 2% cpu; 2% machine; 0.076 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4502 ( 1% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4503 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4504 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4505 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4506 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4507 ( 2% cpu; 2% machine; 0.076 Gbps; 6% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4508 ( 31% cpu; 2% machine; 0.076 Gbps; 8% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4509 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4510 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4511 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4512 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4513 ( 0% cpu; 2% machine; 0.076 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4514 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4515 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
||||||
|
10.0.4.5:4516 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM )
|
||||||
|
|
||||||
Data:
|
Coordination servers:
|
||||||
Replication health - Healthy
|
10.0.4.1:4500 (reachable)
|
||||||
Moving data - 0.000 GB
|
10.0.4.2:4500 (reachable)
|
||||||
Sum of key-value sizes - 87.068 GB
|
10.0.4.3:4500 (reachable)
|
||||||
Disk space used - 327.819 GB
|
10.0.4.4:4500 (reachable)
|
||||||
|
10.0.4.5:4500 (reachable)
|
||||||
|
|
||||||
|
Client time: 03/19/18 08:59:37
|
||||||
|
|
||||||
Operating space:
|
|
||||||
Storage server - 888.2 GB free on most full server
|
|
||||||
Log server - 897.3 GB free on most full server
|
|
||||||
|
|
||||||
Workload:
|
|
||||||
Read rate - 117 Hz
|
|
||||||
Write rate - 0 Hz
|
|
||||||
Transactions started - 43 Hz
|
|
||||||
Transactions committed - 1 Hz
|
|
||||||
Conflict rate - 0 Hz
|
|
||||||
|
|
||||||
Process performance details:
|
|
||||||
10.0.4.1:4500 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4501 ( 1% cpu; 2% machine; 0.010 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4502 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4503 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4504 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4505 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4506 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4507 ( 2% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4508 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4509 ( 2% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4510 ( 1% cpu; 2% machine; 0.010 Gbps; 1% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4511 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4512 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4513 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4514 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4515 ( 12% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.1:4516 ( 0% cpu; 2% machine; 0.010 Gbps; 0% disk IO; 0.3 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4500 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4501 ( 15% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4502 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4503 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4504 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4505 ( 18% cpu; 3% machine; 0.124 Gbps; 18% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4506 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4507 ( 2% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4508 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4509 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4510 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4511 ( 2% cpu; 3% machine; 0.124 Gbps; 1% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4512 ( 2% cpu; 3% machine; 0.124 Gbps; 19% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4513 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4514 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4515 ( 11% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.2:4516 ( 0% cpu; 3% machine; 0.124 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4500 ( 14% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 3.0 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4501 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4502 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4503 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4504 ( 7% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4505 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4506 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4507 ( 2% cpu; 3% machine; 0.284 Gbps; 26% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4508 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4509 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4510 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4511 ( 2% cpu; 3% machine; 0.284 Gbps; 12% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4512 ( 2% cpu; 3% machine; 0.284 Gbps; 3% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4513 ( 2% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4514 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4515 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.3:4516 ( 0% cpu; 3% machine; 0.284 Gbps; 0% disk IO; 0.1 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4500 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 3.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4501 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4502 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4503 ( 2% cpu; 4% machine; 0.065 Gbps; 16% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4504 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4505 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4506 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4507 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4508 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4509 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4510 ( 24% cpu; 4% machine; 0.065 Gbps; 15% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4511 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.8 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4512 ( 2% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4513 ( 0% cpu; 4% machine; 0.065 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4514 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4515 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.4:4516 ( 0% cpu; 4% machine; 0.065 Gbps; 1% disk IO; 0.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4500 ( 6% cpu; 2% machine; 0.076 Gbps; 7% disk IO; 3.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4501 ( 2% cpu; 2% machine; 0.076 Gbps; 19% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4502 ( 1% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4503 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4504 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4505 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4506 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4507 ( 2% cpu; 2% machine; 0.076 Gbps; 6% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4508 ( 31% cpu; 2% machine; 0.076 Gbps; 8% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4509 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4510 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.7 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4511 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4512 ( 2% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4513 ( 0% cpu; 2% machine; 0.076 Gbps; 3% disk IO; 2.6 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4514 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4515 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.2 GB / 7.4 GB RAM )
|
|
||||||
10.0.4.5:4516 ( 0% cpu; 2% machine; 0.076 Gbps; 0% disk IO; 0.6 GB / 7.4 GB RAM )
|
|
||||||
|
|
||||||
Coordination servers:
|
|
||||||
10.0.4.1:4500 (reachable)
|
|
||||||
10.0.4.2:4500 (reachable)
|
|
||||||
10.0.4.3:4500 (reachable)
|
|
||||||
10.0.4.4:4500 (reachable)
|
|
||||||
10.0.4.5:4500 (reachable)
|
|
||||||
|
|
||||||
Client time: 03/19/18 08:59:37
|
|
||||||
Several details about individual FoundationDB processes are displayed in a list format in parenthesis after the IP address and port:
|
Several details about individual FoundationDB processes are displayed in a list format in parenthesis after the IP address and port:
|
||||||
|
|
||||||
======= =========================================================================
|
======= =========================================================================
|
||||||
|
|
|
@ -10,38 +10,38 @@ macOS
|
||||||
|
|
||||||
The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server.
|
The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server.
|
||||||
|
|
||||||
* `FoundationDB-5.2.4.pkg <https://www.foundationdb.org/downloads/5.2.4/macOS/installers/FoundationDB-5.2.4.pkg>`_
|
* `FoundationDB-5.2.5.pkg <https://www.foundationdb.org/downloads/5.2.5/macOS/installers/FoundationDB-5.2.5.pkg>`_
|
||||||
|
|
||||||
Ubuntu
|
Ubuntu
|
||||||
------
|
------
|
||||||
|
|
||||||
The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x.
|
The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x.
|
||||||
|
|
||||||
* `foundationdb-clients-5.2.4-1_amd64.deb <https://www.foundationdb.org/downloads/5.2.4/ubuntu/installers/foundationdb-clients_5.2.4-1_amd64.deb>`_
|
* `foundationdb-clients-5.2.5-1_amd64.deb <https://www.foundationdb.org/downloads/5.2.5/ubuntu/installers/foundationdb-clients_5.2.5-1_amd64.deb>`_
|
||||||
* `foundationdb-server-5.2.4-1_amd64.deb <https://www.foundationdb.org/downloads/5.2.4/ubuntu/installers/foundationdb-server_5.2.4-1_amd64.deb>`_ (depends on the clients package)
|
* `foundationdb-server-5.2.5-1_amd64.deb <https://www.foundationdb.org/downloads/5.2.5/ubuntu/installers/foundationdb-server_5.2.5-1_amd64.deb>`_ (depends on the clients package)
|
||||||
|
|
||||||
RHEL/CentOS EL6
|
RHEL/CentOS EL6
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x.
|
The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x.
|
||||||
|
|
||||||
* `foundationdb-clients-5.2.4-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.4/rhel6/installers/foundationdb-clients-5.2.4-1.el6.x86_64.rpm>`_
|
* `foundationdb-clients-5.2.5-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.5/rhel6/installers/foundationdb-clients-5.2.5-1.el6.x86_64.rpm>`_
|
||||||
* `foundationdb-server-5.2.4-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.4/rhel6/installers/foundationdb-server-5.2.4-1.el6.x86_64.rpm>`_ (depends on the clients package)
|
* `foundationdb-server-5.2.5-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.5/rhel6/installers/foundationdb-server-5.2.5-1.el6.x86_64.rpm>`_ (depends on the clients package)
|
||||||
|
|
||||||
RHEL/CentOS EL7
|
RHEL/CentOS EL7
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x.
|
The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x.
|
||||||
|
|
||||||
* `foundationdb-clients-5.2.4-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.4/rhel7/installers/foundationdb-clients-5.2.4-1.el7.x86_64.rpm>`_
|
* `foundationdb-clients-5.2.5-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.5/rhel7/installers/foundationdb-clients-5.2.5-1.el7.x86_64.rpm>`_
|
||||||
* `foundationdb-server-5.2.4-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.4/rhel7/installers/foundationdb-server-5.2.4-1.el7.x86_64.rpm>`_ (depends on the clients package)
|
* `foundationdb-server-5.2.5-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/5.2.5/rhel7/installers/foundationdb-server-5.2.5-1.el7.x86_64.rpm>`_ (depends on the clients package)
|
||||||
|
|
||||||
Windows
|
Windows
|
||||||
-------
|
-------
|
||||||
|
|
||||||
The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server.
|
The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server.
|
||||||
|
|
||||||
* `foundationdb-5.2.4-x64.msi <https://www.foundationdb.org/downloads/5.2.4/windows/installers/foundationdb-5.2.4-x64.msi>`_
|
* `foundationdb-5.2.5-x64.msi <https://www.foundationdb.org/downloads/5.2.5/windows/installers/foundationdb-5.2.5-x64.msi>`_
|
||||||
|
|
||||||
API Language Bindings
|
API Language Bindings
|
||||||
=====================
|
=====================
|
||||||
|
@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part
|
||||||
|
|
||||||
If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package:
|
If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package:
|
||||||
|
|
||||||
* `foundationdb-5.2.4.tar.gz <https://www.foundationdb.org/downloads/5.2.4/bindings/python/foundationdb-5.2.4.tar.gz>`_
|
* `foundationdb-5.2.5.tar.gz <https://www.foundationdb.org/downloads/5.2.5/bindings/python/foundationdb-5.2.5.tar.gz>`_
|
||||||
|
|
||||||
Ruby 1.9.3/2.0.0+
|
Ruby 1.9.3/2.0.0+
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
* `fdb-5.2.4.gem <https://www.foundationdb.org/downloads/5.2.4/bindings/ruby/fdb-5.2.4.gem>`_
|
* `fdb-5.2.5.gem <https://www.foundationdb.org/downloads/5.2.5/bindings/ruby/fdb-5.2.5.gem>`_
|
||||||
|
|
||||||
Java 8+
|
Java 8+
|
||||||
-------
|
-------
|
||||||
|
|
||||||
* `fdb-java-5.2.4.jar <https://www.foundationdb.org/downloads/5.2.4/bindings/java/fdb-java-5.2.4.jar>`_
|
* `fdb-java-5.2.5.jar <https://www.foundationdb.org/downloads/5.2.5/bindings/java/fdb-java-5.2.5.jar>`_
|
||||||
* `fdb-java-5.2.4-javadoc.jar <https://www.foundationdb.org/downloads/5.2.4/bindings/java/fdb-java-5.2.4-javadoc.jar>`_
|
* `fdb-java-5.2.5-javadoc.jar <https://www.foundationdb.org/downloads/5.2.5/bindings/java/fdb-java-5.2.5-javadoc.jar>`_
|
||||||
|
|
||||||
Go 1.1+
|
Go 1.1+
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -81,19 +81,18 @@ The following is a simple implementation of the basic pattern:
|
||||||
|
|
||||||
// Remove the top element from the queue.
|
// Remove the top element from the queue.
|
||||||
public static Object dequeue(TransactionContext tcx){
|
public static Object dequeue(TransactionContext tcx){
|
||||||
final KeyValue item = firstItem(tcx);
|
|
||||||
if(item == null){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from the top of the queue.
|
// Remove from the top of the queue.
|
||||||
tcx.run((Transaction tr) -> {
|
return tcx.run((Transaction tr) -> {
|
||||||
|
final KeyValue item = firstItem(tr);
|
||||||
|
if(item == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
tr.clear(item.getKey());
|
tr.clear(item.getKey());
|
||||||
return null;
|
// Return the old value.
|
||||||
|
return Tuple.fromBytes(item.getValue()).get(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return the old value.
|
|
||||||
return Tuple.fromBytes(item.getValue()).get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an element to the queue.
|
// Add an element to the queue.
|
||||||
|
|
|
@ -101,7 +101,7 @@ The default behavior when the certificate or key file is not specified is to loo
|
||||||
Default Peer Verification
|
Default Peer Verification
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The default peer verification is ``Check.Valid=0``.
|
The default peer verification is ``Check.Valid=1``.
|
||||||
|
|
||||||
Default Password
|
Default Password
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -884,6 +884,12 @@ void printStatus(StatusObjectReader statusObj, StatusClient::StatusLevel level,
|
||||||
|
|
||||||
if (statusObjConfig.get("logs", intVal))
|
if (statusObjConfig.get("logs", intVal))
|
||||||
outputString += format("\n Desired Logs - %d", intVal);
|
outputString += format("\n Desired Logs - %d", intVal);
|
||||||
|
|
||||||
|
if (statusObjConfig.get("remote_logs", intVal))
|
||||||
|
outputString += format("\n Desired Remote Logs - %d", intVal);
|
||||||
|
|
||||||
|
if (statusObjConfig.get("log_routers", intVal))
|
||||||
|
outputString += format("\n Desired Log Routers - %d", intVal);
|
||||||
}
|
}
|
||||||
catch (std::runtime_error& e) {
|
catch (std::runtime_error& e) {
|
||||||
outputString = outputStringCache;
|
outputString = outputStringCache;
|
||||||
|
|
|
@ -29,7 +29,7 @@ DatabaseConfiguration::DatabaseConfiguration()
|
||||||
void DatabaseConfiguration::resetInternal() {
|
void DatabaseConfiguration::resetInternal() {
|
||||||
// does NOT reset rawConfiguration
|
// does NOT reset rawConfiguration
|
||||||
initialized = false;
|
initialized = false;
|
||||||
masterProxyCount = resolverCount = desiredTLogCount = tLogWriteAntiQuorum = tLogReplicationFactor = storageTeamSize = -1;
|
masterProxyCount = resolverCount = desiredTLogCount = tLogWriteAntiQuorum = tLogReplicationFactor = storageTeamSize = desiredLogRouterCount = -1;
|
||||||
tLogDataStoreType = storageServerStoreType = KeyValueStoreType::END;
|
tLogDataStoreType = storageServerStoreType = KeyValueStoreType::END;
|
||||||
autoMasterProxyCount = CLIENT_KNOBS->DEFAULT_AUTO_PROXIES;
|
autoMasterProxyCount = CLIENT_KNOBS->DEFAULT_AUTO_PROXIES;
|
||||||
autoResolverCount = CLIENT_KNOBS->DEFAULT_AUTO_RESOLVERS;
|
autoResolverCount = CLIENT_KNOBS->DEFAULT_AUTO_RESOLVERS;
|
||||||
|
@ -102,11 +102,19 @@ void parse( std::vector<RegionInfo>* regions, ValueRef const& v ) {
|
||||||
info.satelliteTLogUsableDcs = 2;
|
info.satelliteTLogUsableDcs = 2;
|
||||||
info.satelliteTLogWriteAntiQuorum = 0;
|
info.satelliteTLogWriteAntiQuorum = 0;
|
||||||
info.satelliteTLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid", IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))));
|
info.satelliteTLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid", IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))));
|
||||||
|
info.satelliteTLogReplicationFactorFallback = 2;
|
||||||
|
info.satelliteTLogUsableDcsFallback = 1;
|
||||||
|
info.satelliteTLogWriteAntiQuorumFallback = 0;
|
||||||
|
info.satelliteTLogPolicyFallback = IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())));
|
||||||
} else if(satelliteReplication == "two_satellite_fast") {
|
} else if(satelliteReplication == "two_satellite_fast") {
|
||||||
info.satelliteTLogReplicationFactor = 4;
|
info.satelliteTLogReplicationFactor = 4;
|
||||||
info.satelliteTLogUsableDcs = 2;
|
info.satelliteTLogUsableDcs = 2;
|
||||||
info.satelliteTLogWriteAntiQuorum = 2;
|
info.satelliteTLogWriteAntiQuorum = 2;
|
||||||
info.satelliteTLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid", IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))));
|
info.satelliteTLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid", IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))));
|
||||||
|
info.satelliteTLogReplicationFactorFallback = 2;
|
||||||
|
info.satelliteTLogUsableDcsFallback = 1;
|
||||||
|
info.satelliteTLogWriteAntiQuorumFallback = 0;
|
||||||
|
info.satelliteTLogPolicyFallback = IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())));
|
||||||
} else {
|
} else {
|
||||||
throw invalid_option();
|
throw invalid_option();
|
||||||
}
|
}
|
||||||
|
@ -114,6 +122,9 @@ void parse( std::vector<RegionInfo>* regions, ValueRef const& v ) {
|
||||||
dc.tryGet("satellite_log_replicas", info.satelliteTLogReplicationFactor);
|
dc.tryGet("satellite_log_replicas", info.satelliteTLogReplicationFactor);
|
||||||
dc.tryGet("satellite_usable_dcs", info.satelliteTLogUsableDcs);
|
dc.tryGet("satellite_usable_dcs", info.satelliteTLogUsableDcs);
|
||||||
dc.tryGet("satellite_anti_quorum", info.satelliteTLogWriteAntiQuorum);
|
dc.tryGet("satellite_anti_quorum", info.satelliteTLogWriteAntiQuorum);
|
||||||
|
dc.tryGet("satellite_log_replicas_fallback", info.satelliteTLogReplicationFactorFallback);
|
||||||
|
dc.tryGet("satellite_usable_dcs_fallback", info.satelliteTLogUsableDcsFallback);
|
||||||
|
dc.tryGet("satellite_anti_quorum_fallback", info.satelliteTLogWriteAntiQuorumFallback);
|
||||||
regions->push_back(info);
|
regions->push_back(info);
|
||||||
}
|
}
|
||||||
std::sort(regions->begin(), regions->end(), RegionInfo::sort_by_priority() );
|
std::sort(regions->begin(), regions->end(), RegionInfo::sort_by_priority() );
|
||||||
|
@ -137,6 +148,9 @@ void DatabaseConfiguration::setDefaultReplicationPolicy() {
|
||||||
if(r.satelliteTLogReplicationFactor > 0 && !r.satelliteTLogPolicy) {
|
if(r.satelliteTLogReplicationFactor > 0 && !r.satelliteTLogPolicy) {
|
||||||
r.satelliteTLogPolicy = IRepPolicyRef(new PolicyAcross(r.satelliteTLogReplicationFactor, "zoneid", IRepPolicyRef(new PolicyOne())));
|
r.satelliteTLogPolicy = IRepPolicyRef(new PolicyAcross(r.satelliteTLogReplicationFactor, "zoneid", IRepPolicyRef(new PolicyOne())));
|
||||||
}
|
}
|
||||||
|
if(r.satelliteTLogReplicationFactorFallback > 0 && !r.satelliteTLogPolicyFallback) {
|
||||||
|
r.satelliteTLogPolicyFallback = IRepPolicyRef(new PolicyAcross(r.satelliteTLogReplicationFactorFallback, "zoneid", IRepPolicyRef(new PolicyOne())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +186,8 @@ bool DatabaseConfiguration::isValid() const {
|
||||||
r.satelliteTLogReplicationFactor >= 0 &&
|
r.satelliteTLogReplicationFactor >= 0 &&
|
||||||
r.satelliteTLogWriteAntiQuorum >= 0 &&
|
r.satelliteTLogWriteAntiQuorum >= 0 &&
|
||||||
r.satelliteTLogUsableDcs >= 1 &&
|
r.satelliteTLogUsableDcs >= 1 &&
|
||||||
( r.satelliteTLogReplicationFactor == 0 || ( r.satelliteTLogPolicy && r.satellites.size() ) ) ) ) {
|
( r.satelliteTLogReplicationFactor == 0 || ( r.satelliteTLogPolicy && r.satellites.size() ) ) &&
|
||||||
|
( r.satelliteTLogUsableDcsFallback == 0 || ( r.satelliteTLogReplicationFactor > 0 && r.satelliteTLogReplicationFactorFallback > 0 ) ) ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
dcIds.insert(r.dcId);
|
dcIds.insert(r.dcId);
|
||||||
|
@ -201,6 +216,8 @@ StatusObject DatabaseConfiguration::toJSON(bool noPolicies) const {
|
||||||
result["redundancy_mode"] = "double";
|
result["redundancy_mode"] = "double";
|
||||||
} else if( tLogReplicationFactor == 4 && storageTeamSize == 6 && tlogInfo == "dcid^2 x zoneid^2 x 1" && storageInfo == "dcid^3 x zoneid^2 x 1" ) {
|
} else if( tLogReplicationFactor == 4 && storageTeamSize == 6 && tlogInfo == "dcid^2 x zoneid^2 x 1" && storageInfo == "dcid^3 x zoneid^2 x 1" ) {
|
||||||
result["redundancy_mode"] = "three_datacenter";
|
result["redundancy_mode"] = "three_datacenter";
|
||||||
|
} else if( tLogReplicationFactor == 4 && storageTeamSize == 4 && tlogInfo == "dcid^2 x zoneid^2 x 1" && storageInfo == "dcid^2 x zoneid^2 x 1" ) {
|
||||||
|
result["redundancy_mode"] = "three_datacenter_fallback";
|
||||||
} else if( tLogReplicationFactor == 3 && storageTeamSize == 3 ) {
|
} else if( tLogReplicationFactor == 3 && storageTeamSize == 3 ) {
|
||||||
result["redundancy_mode"] = "triple";
|
result["redundancy_mode"] = "triple";
|
||||||
} else if( tLogReplicationFactor == 4 && storageTeamSize == 3 && tlogInfo == "data_hall^2 x zoneid^2 x 1" && storageInfo == "data_hall^3 x 1" ) {
|
} else if( tLogReplicationFactor == 4 && storageTeamSize == 3 && tlogInfo == "data_hall^2 x zoneid^2 x 1" && storageInfo == "data_hall^3 x 1" ) {
|
||||||
|
@ -254,21 +271,25 @@ StatusObject DatabaseConfiguration::toJSON(bool noPolicies) const {
|
||||||
dcObj["priority"] = r.priority;
|
dcObj["priority"] = r.priority;
|
||||||
dcArr.push_back(dcObj);
|
dcArr.push_back(dcObj);
|
||||||
|
|
||||||
if(r.satelliteTLogReplicationFactor == 1 && r.satelliteTLogUsableDcs == 1 && r.satelliteTLogWriteAntiQuorum == 0) {
|
if(r.satelliteTLogReplicationFactor == 1 && r.satelliteTLogUsableDcs == 1 && r.satelliteTLogWriteAntiQuorum == 0 && r.satelliteTLogUsableDcsFallback == 0) {
|
||||||
regionObj["satellite_redundancy_mode"] = "one_satellite_single";
|
regionObj["satellite_redundancy_mode"] = "one_satellite_single";
|
||||||
} else if(r.satelliteTLogReplicationFactor == 2 && r.satelliteTLogUsableDcs == 1 && r.satelliteTLogWriteAntiQuorum == 0) {
|
} else if(r.satelliteTLogReplicationFactor == 2 && r.satelliteTLogUsableDcs == 1 && r.satelliteTLogWriteAntiQuorum == 0 && r.satelliteTLogUsableDcsFallback == 0) {
|
||||||
regionObj["satellite_redundancy_mode"] = "one_satellite_double";
|
regionObj["satellite_redundancy_mode"] = "one_satellite_double";
|
||||||
} else if(r.satelliteTLogReplicationFactor == 3 && r.satelliteTLogUsableDcs == 1 && r.satelliteTLogWriteAntiQuorum == 0) {
|
} else if(r.satelliteTLogReplicationFactor == 3 && r.satelliteTLogUsableDcs == 1 && r.satelliteTLogWriteAntiQuorum == 0 && r.satelliteTLogUsableDcsFallback == 0) {
|
||||||
regionObj["satellite_redundancy_mode"] = "one_satellite_triple";
|
regionObj["satellite_redundancy_mode"] = "one_satellite_triple";
|
||||||
} else if(r.satelliteTLogReplicationFactor == 4 && r.satelliteTLogUsableDcs == 2 && r.satelliteTLogWriteAntiQuorum == 0) {
|
} else if(r.satelliteTLogReplicationFactor == 4 && r.satelliteTLogUsableDcs == 2 && r.satelliteTLogWriteAntiQuorum == 0 && r.satelliteTLogUsableDcsFallback == 1 && r.satelliteTLogReplicationFactorFallback == 2 && r.satelliteTLogWriteAntiQuorumFallback == 0) {
|
||||||
regionObj["satellite_redundancy_mode"] = "two_satellite_safe";
|
regionObj["satellite_redundancy_mode"] = "two_satellite_safe";
|
||||||
} else if(r.satelliteTLogReplicationFactor == 4 && r.satelliteTLogUsableDcs == 2 && r.satelliteTLogWriteAntiQuorum == 2) {
|
} else if(r.satelliteTLogReplicationFactor == 4 && r.satelliteTLogUsableDcs == 2 && r.satelliteTLogWriteAntiQuorum == 2 && r.satelliteTLogUsableDcsFallback == 1 && r.satelliteTLogReplicationFactorFallback == 2 && r.satelliteTLogWriteAntiQuorumFallback == 0) {
|
||||||
regionObj["satellite_redundancy_mode"] = "two_satellite_fast";
|
regionObj["satellite_redundancy_mode"] = "two_satellite_fast";
|
||||||
} else if(r.satelliteTLogReplicationFactor != 0) {
|
} else if(r.satelliteTLogReplicationFactor != 0) {
|
||||||
regionObj["satellite_log_replicas"] = r.satelliteTLogReplicationFactor;
|
regionObj["satellite_log_replicas"] = r.satelliteTLogReplicationFactor;
|
||||||
regionObj["satellite_usable_dcs"] = r.satelliteTLogUsableDcs;
|
regionObj["satellite_usable_dcs"] = r.satelliteTLogUsableDcs;
|
||||||
regionObj["satellite_anti_quorum"] = r.satelliteTLogWriteAntiQuorum;
|
regionObj["satellite_anti_quorum"] = r.satelliteTLogWriteAntiQuorum;
|
||||||
if(r.satelliteTLogPolicy) regionObj["satellite_log_policy"] = r.satelliteTLogPolicy->info();
|
if(r.satelliteTLogPolicy) regionObj["satellite_log_policy"] = r.satelliteTLogPolicy->info();
|
||||||
|
regionObj["satellite_log_replicas_fallback"] = r.satelliteTLogReplicationFactorFallback;
|
||||||
|
regionObj["satellite_usable_dcs_fallback"] = r.satelliteTLogUsableDcsFallback;
|
||||||
|
regionObj["satellite_anti_quorum_fallback"] = r.satelliteTLogWriteAntiQuorumFallback;
|
||||||
|
if(r.satelliteTLogPolicyFallback) regionObj["satellite_log_policy_fallback"] = r.satelliteTLogPolicyFallback->info();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( r.satelliteDesiredTLogCount != -1 ) {
|
if( r.satelliteDesiredTLogCount != -1 ) {
|
||||||
|
@ -301,6 +322,9 @@ StatusObject DatabaseConfiguration::toJSON(bool noPolicies) const {
|
||||||
if( resolverCount != -1 ) {
|
if( resolverCount != -1 ) {
|
||||||
result["resolvers"] = resolverCount;
|
result["resolvers"] = resolverCount;
|
||||||
}
|
}
|
||||||
|
if( desiredLogRouterCount != -1 ) {
|
||||||
|
result["log_routers"] = desiredLogRouterCount;
|
||||||
|
}
|
||||||
if( remoteDesiredTLogCount != -1 ) {
|
if( remoteDesiredTLogCount != -1 ) {
|
||||||
result["remote_logs"] = remoteDesiredTLogCount;
|
result["remote_logs"] = remoteDesiredTLogCount;
|
||||||
}
|
}
|
||||||
|
@ -344,6 +368,7 @@ bool DatabaseConfiguration::setInternal(KeyRef key, ValueRef value) {
|
||||||
else if (ck == LiteralStringRef("auto_logs")) parse(&autoDesiredTLogCount, value);
|
else if (ck == LiteralStringRef("auto_logs")) parse(&autoDesiredTLogCount, value);
|
||||||
else if (ck == LiteralStringRef("storage_replication_policy")) parseReplicationPolicy(&storagePolicy, value);
|
else if (ck == LiteralStringRef("storage_replication_policy")) parseReplicationPolicy(&storagePolicy, value);
|
||||||
else if (ck == LiteralStringRef("log_replication_policy")) parseReplicationPolicy(&tLogPolicy, value);
|
else if (ck == LiteralStringRef("log_replication_policy")) parseReplicationPolicy(&tLogPolicy, value);
|
||||||
|
else if (ck == LiteralStringRef("log_routers")) parse(&desiredLogRouterCount, value);
|
||||||
else if (ck == LiteralStringRef("remote_logs")) parse(&remoteDesiredTLogCount, value);
|
else if (ck == LiteralStringRef("remote_logs")) parse(&remoteDesiredTLogCount, value);
|
||||||
else if (ck == LiteralStringRef("remote_log_replicas")) parse(&remoteTLogReplicationFactor, value);
|
else if (ck == LiteralStringRef("remote_log_replicas")) parse(&remoteTLogReplicationFactor, value);
|
||||||
else if (ck == LiteralStringRef("remote_log_policy")) parseReplicationPolicy(&remoteTLogPolicy, value);
|
else if (ck == LiteralStringRef("remote_log_policy")) parseReplicationPolicy(&remoteTLogPolicy, value);
|
||||||
|
|
|
@ -55,9 +55,15 @@ struct RegionInfo {
|
||||||
int32_t satelliteTLogWriteAntiQuorum;
|
int32_t satelliteTLogWriteAntiQuorum;
|
||||||
int32_t satelliteTLogUsableDcs;
|
int32_t satelliteTLogUsableDcs;
|
||||||
|
|
||||||
|
IRepPolicyRef satelliteTLogPolicyFallback;
|
||||||
|
int32_t satelliteTLogReplicationFactorFallback;
|
||||||
|
int32_t satelliteTLogWriteAntiQuorumFallback;
|
||||||
|
int32_t satelliteTLogUsableDcsFallback;
|
||||||
|
|
||||||
std::vector<SatelliteInfo> satellites;
|
std::vector<SatelliteInfo> satellites;
|
||||||
|
|
||||||
RegionInfo() : priority(0), satelliteDesiredTLogCount(-1), satelliteTLogReplicationFactor(0), satelliteTLogWriteAntiQuorum(0), satelliteTLogUsableDcs(1) {}
|
RegionInfo() : priority(0), satelliteDesiredTLogCount(-1), satelliteTLogReplicationFactor(0), satelliteTLogWriteAntiQuorum(0), satelliteTLogUsableDcs(1),
|
||||||
|
satelliteTLogReplicationFactorFallback(0), satelliteTLogWriteAntiQuorumFallback(0), satelliteTLogUsableDcsFallback(0) {}
|
||||||
|
|
||||||
struct sort_by_priority {
|
struct sort_by_priority {
|
||||||
bool operator ()(RegionInfo const&a, RegionInfo const& b) const { return a.priority > b.priority; }
|
bool operator ()(RegionInfo const&a, RegionInfo const& b) const { return a.priority > b.priority; }
|
||||||
|
@ -65,7 +71,8 @@ struct RegionInfo {
|
||||||
|
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize(Ar& ar) {
|
void serialize(Ar& ar) {
|
||||||
ar & dcId & priority & satelliteTLogPolicy & satelliteDesiredTLogCount & satelliteTLogReplicationFactor & satelliteTLogWriteAntiQuorum & satelliteTLogUsableDcs & satellites;
|
ar & dcId & priority & satelliteTLogPolicy & satelliteDesiredTLogCount & satelliteTLogReplicationFactor & satelliteTLogWriteAntiQuorum & satelliteTLogUsableDcs &
|
||||||
|
satelliteTLogPolicyFallback & satelliteTLogReplicationFactorFallback & satelliteTLogWriteAntiQuorumFallback & satelliteTLogUsableDcsFallback & satellites;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,12 +122,12 @@ struct DatabaseConfiguration {
|
||||||
}
|
}
|
||||||
return minRequired;
|
return minRequired;
|
||||||
}
|
}
|
||||||
int32_t minMachinesRequiredPerDatacenter() const {
|
int32_t minMachinesRequiredPerDatacenter() const {
|
||||||
int minRequired = std::max( remoteTLogReplicationFactor, std::max(tLogReplicationFactor, storageTeamSize) );
|
int minRequired = std::max( remoteTLogReplicationFactor, std::max(tLogReplicationFactor, storageTeamSize) );
|
||||||
for(auto& r : regions) {
|
for(auto& r : regions) {
|
||||||
minRequired = std::max( minRequired, r.satelliteTLogReplicationFactor/std::max(1, r.satelliteTLogUsableDcs) );
|
minRequired = std::max( minRequired, r.satelliteTLogReplicationFactor/std::max(1, r.satelliteTLogUsableDcs) );
|
||||||
}
|
}
|
||||||
return minRequired;
|
return minRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Killing an entire datacenter counts as killing one machine in modes that support it
|
//Killing an entire datacenter counts as killing one machine in modes that support it
|
||||||
|
@ -128,6 +135,9 @@ struct DatabaseConfiguration {
|
||||||
int worstSatellite = regions.size() ? std::numeric_limits<int>::max() : 0;
|
int worstSatellite = regions.size() ? std::numeric_limits<int>::max() : 0;
|
||||||
for(auto& r : regions) {
|
for(auto& r : regions) {
|
||||||
worstSatellite = std::min(worstSatellite, r.satelliteTLogReplicationFactor - r.satelliteTLogWriteAntiQuorum);
|
worstSatellite = std::min(worstSatellite, r.satelliteTLogReplicationFactor - r.satelliteTLogWriteAntiQuorum);
|
||||||
|
if(r.satelliteTLogUsableDcsFallback > 0) {
|
||||||
|
worstSatellite = std::min(worstSatellite, r.satelliteTLogReplicationFactorFallback - r.satelliteTLogWriteAntiQuorumFallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(usableRegions > 1 && worstSatellite > 0) {
|
if(usableRegions > 1 && worstSatellite > 0) {
|
||||||
return 1 + std::min(std::max(tLogReplicationFactor - 1 - tLogWriteAntiQuorum, worstSatellite - 1), storageTeamSize - 1);
|
return 1 + std::min(std::max(tLogReplicationFactor - 1 - tLogWriteAntiQuorum, worstSatellite - 1), storageTeamSize - 1);
|
||||||
|
@ -159,6 +169,7 @@ struct DatabaseConfiguration {
|
||||||
KeyValueStoreType storageServerStoreType;
|
KeyValueStoreType storageServerStoreType;
|
||||||
|
|
||||||
// Remote TLogs
|
// Remote TLogs
|
||||||
|
int32_t desiredLogRouterCount;
|
||||||
int32_t remoteDesiredTLogCount;
|
int32_t remoteDesiredTLogCount;
|
||||||
int32_t remoteTLogReplicationFactor;
|
int32_t remoteTLogReplicationFactor;
|
||||||
IRepPolicyRef remoteTLogPolicy;
|
IRepPolicyRef remoteTLogPolicy;
|
||||||
|
|
|
@ -604,7 +604,7 @@ static bool addressExcluded( std::set<AddressExclusion> const& exclusions, Netwo
|
||||||
struct ClusterControllerPriorityInfo {
|
struct ClusterControllerPriorityInfo {
|
||||||
enum DCFitness { FitnessPrimary, FitnessRemote, FitnessPreferred, FitnessUnknown, FitnessBad }; //cannot be larger than 7 because of leader election mask
|
enum DCFitness { FitnessPrimary, FitnessRemote, FitnessPreferred, FitnessUnknown, FitnessBad }; //cannot be larger than 7 because of leader election mask
|
||||||
|
|
||||||
static DCFitness calculateDCFitness(Optional<Key> dcId, vector<Optional<Key>> dcPriority) {
|
static DCFitness calculateDCFitness(Optional<Key> const& dcId, vector<Optional<Key>> const& dcPriority) {
|
||||||
if(!dcPriority.size()) {
|
if(!dcPriority.size()) {
|
||||||
return FitnessUnknown;
|
return FitnessUnknown;
|
||||||
} else if(dcPriority.size() == 1) {
|
} else if(dcPriority.size() == 1) {
|
||||||
|
|
|
@ -35,8 +35,8 @@ ClientKnobs::ClientKnobs(bool randomize) {
|
||||||
|
|
||||||
init( SYSTEM_MONITOR_INTERVAL, 5.0 );
|
init( SYSTEM_MONITOR_INTERVAL, 5.0 );
|
||||||
|
|
||||||
init( FAILURE_MAX_DELAY, 10.0 ); if( randomize && BUGGIFY ) FAILURE_MAX_DELAY = 5.0;
|
init( FAILURE_MAX_DELAY, 5.0 );
|
||||||
init( FAILURE_MIN_DELAY, 5.0 ); if( randomize && BUGGIFY ) FAILURE_MIN_DELAY = 2.0;
|
init( FAILURE_MIN_DELAY, 4.0 ); if( randomize && BUGGIFY ) FAILURE_MIN_DELAY = 1.0;
|
||||||
init( FAILURE_TIMEOUT_DELAY, FAILURE_MIN_DELAY );
|
init( FAILURE_TIMEOUT_DELAY, FAILURE_MIN_DELAY );
|
||||||
init( CLIENT_FAILURE_TIMEOUT_DELAY, FAILURE_MIN_DELAY );
|
init( CLIENT_FAILURE_TIMEOUT_DELAY, FAILURE_MIN_DELAY );
|
||||||
|
|
||||||
|
@ -168,6 +168,11 @@ ClientKnobs::ClientKnobs(bool randomize) {
|
||||||
init( BLOBSTORE_MAX_SEND_BYTES_PER_SECOND, 1e9 );
|
init( BLOBSTORE_MAX_SEND_BYTES_PER_SECOND, 1e9 );
|
||||||
init( BLOBSTORE_MAX_RECV_BYTES_PER_SECOND, 1e9 );
|
init( BLOBSTORE_MAX_RECV_BYTES_PER_SECOND, 1e9 );
|
||||||
|
|
||||||
|
init( BLOBSTORE_LIST_REQUESTS_PER_SECOND, 25 );
|
||||||
|
init( BLOBSTORE_WRITE_REQUESTS_PER_SECOND, 50 );
|
||||||
|
init( BLOBSTORE_READ_REQUESTS_PER_SECOND, 100 );
|
||||||
|
init( BLOBSTORE_DELETE_REQUESTS_PER_SECOND, 200 );
|
||||||
|
|
||||||
// Client Status Info
|
// Client Status Info
|
||||||
init(CSI_SAMPLING_PROBABILITY, -1.0);
|
init(CSI_SAMPLING_PROBABILITY, -1.0);
|
||||||
init(CSI_SIZE_LIMIT, std::numeric_limits<int64_t>::max());
|
init(CSI_SIZE_LIMIT, std::numeric_limits<int64_t>::max());
|
||||||
|
|
|
@ -155,6 +155,10 @@ public:
|
||||||
int BLOBSTORE_REQUEST_TRIES;
|
int BLOBSTORE_REQUEST_TRIES;
|
||||||
int BLOBSTORE_REQUEST_TIMEOUT;
|
int BLOBSTORE_REQUEST_TIMEOUT;
|
||||||
int BLOBSTORE_REQUESTS_PER_SECOND;
|
int BLOBSTORE_REQUESTS_PER_SECOND;
|
||||||
|
int BLOBSTORE_LIST_REQUESTS_PER_SECOND;
|
||||||
|
int BLOBSTORE_WRITE_REQUESTS_PER_SECOND;
|
||||||
|
int BLOBSTORE_READ_REQUESTS_PER_SECOND;
|
||||||
|
int BLOBSTORE_DELETE_REQUESTS_PER_SECOND;
|
||||||
int BLOBSTORE_CONCURRENT_REQUESTS;
|
int BLOBSTORE_CONCURRENT_REQUESTS;
|
||||||
int BLOBSTORE_MULTIPART_MAX_PART_SIZE;
|
int BLOBSTORE_MULTIPART_MAX_PART_SIZE;
|
||||||
int BLOBSTORE_MULTIPART_MIN_PART_SIZE;
|
int BLOBSTORE_MULTIPART_MIN_PART_SIZE;
|
||||||
|
|
|
@ -65,14 +65,14 @@ std::map<std::string, std::string> configForToken( std::string const& mode ) {
|
||||||
std::string key = mode.substr(0, pos);
|
std::string key = mode.substr(0, pos);
|
||||||
std::string value = mode.substr(pos+1);
|
std::string value = mode.substr(pos+1);
|
||||||
|
|
||||||
if( (key == "logs" || key == "proxies" || key == "resolvers" || key == "remote_logs" || key == "satellite_logs" || key == "usable_regions") && isInteger(value) ) {
|
if( (key == "logs" || key == "proxies" || key == "resolvers" || key == "remote_logs" || key == "log_routers" || key == "satellite_logs" || key == "usable_regions") && isInteger(value) ) {
|
||||||
out[p+key] = value;
|
out[p+key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( key == "regions" ) {
|
if( key == "regions" ) {
|
||||||
json_spirit::mValue mv;
|
json_spirit::mValue mv;
|
||||||
json_spirit::read_string( value, mv );
|
json_spirit::read_string( value, mv );
|
||||||
|
|
||||||
StatusObject regionObj;
|
StatusObject regionObj;
|
||||||
regionObj["regions"] = mv;
|
regionObj["regions"] = mv;
|
||||||
out[p+key] = BinaryWriter::toValue(regionObj, IncludeVersion()).toString();
|
out[p+key] = BinaryWriter::toValue(regionObj, IncludeVersion()).toString();
|
||||||
|
@ -125,6 +125,10 @@ std::map<std::string, std::string> configForToken( std::string const& mode ) {
|
||||||
tLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid",
|
tLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid",
|
||||||
IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))
|
IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))
|
||||||
));
|
));
|
||||||
|
} else if(mode == "three_datacenter_fallback") {
|
||||||
|
redundancy="4";
|
||||||
|
log_replicas="4";
|
||||||
|
storagePolicy = tLogPolicy = IRepPolicyRef(new PolicyAcross(2, "dcid", IRepPolicyRef(new PolicyAcross(2, "zoneid", IRepPolicyRef(new PolicyOne())))));
|
||||||
} else if(mode == "three_data_hall") {
|
} else if(mode == "three_data_hall") {
|
||||||
redundancy="3";
|
redundancy="3";
|
||||||
log_replicas="4";
|
log_replicas="4";
|
||||||
|
@ -340,6 +344,9 @@ ConfigureAutoResult parseConfig( StatusObject const& status ) {
|
||||||
} else if( result.old_replication == "three_datacenter" ) {
|
} else if( result.old_replication == "three_datacenter" ) {
|
||||||
storage_replication = 6;
|
storage_replication = 6;
|
||||||
log_replication = 4;
|
log_replication = 4;
|
||||||
|
} else if( result.old_replication == "three_datacenter_fallback" ) {
|
||||||
|
storage_replication = 4;
|
||||||
|
log_replication = 4;
|
||||||
} else
|
} else
|
||||||
return ConfigureAutoResult();
|
return ConfigureAutoResult();
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "flow/Knobs.h"
|
#include "flow/Knobs.h"
|
||||||
#include "fdbclient/Knobs.h"
|
#include "fdbclient/Knobs.h"
|
||||||
#include "fdbrpc/Net2FileSystem.h"
|
#include "fdbrpc/Net2FileSystem.h"
|
||||||
|
#include "fdbrpc/simulator.h"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
|
@ -3013,7 +3014,7 @@ void Transaction::checkDeferredError() { cx->checkDeferredError(); }
|
||||||
|
|
||||||
Reference<TransactionLogInfo> Transaction::createTrLogInfoProbabilistically(const Database &cx) {
|
Reference<TransactionLogInfo> Transaction::createTrLogInfoProbabilistically(const Database &cx) {
|
||||||
double clientSamplingProbability = std::isinf(cx->clientInfo->get().clientTxnInfoSampleRate) ? CLIENT_KNOBS->CSI_SAMPLING_PROBABILITY : cx->clientInfo->get().clientTxnInfoSampleRate;
|
double clientSamplingProbability = std::isinf(cx->clientInfo->get().clientTxnInfoSampleRate) ? CLIENT_KNOBS->CSI_SAMPLING_PROBABILITY : cx->clientInfo->get().clientTxnInfoSampleRate;
|
||||||
if (((networkOptions.logClientInfo.present() && networkOptions.logClientInfo.get()) || BUGGIFY) && g_random->random01() < clientSamplingProbability)
|
if (((networkOptions.logClientInfo.present() && networkOptions.logClientInfo.get()) || BUGGIFY) && g_random->random01() < clientSamplingProbability && (!g_network->isSimulated() || !g_simulator.speedUpSimulation))
|
||||||
return Reference<TransactionLogInfo>(new TransactionLogInfo());
|
return Reference<TransactionLogInfo>(new TransactionLogInfo());
|
||||||
else
|
else
|
||||||
return Reference<TransactionLogInfo>();
|
return Reference<TransactionLogInfo>();
|
||||||
|
|
|
@ -57,6 +57,10 @@ BlobStoreEndpoint::BlobKnobs::BlobKnobs() {
|
||||||
request_timeout = CLIENT_KNOBS->BLOBSTORE_REQUEST_TIMEOUT;
|
request_timeout = CLIENT_KNOBS->BLOBSTORE_REQUEST_TIMEOUT;
|
||||||
requests_per_second = CLIENT_KNOBS->BLOBSTORE_REQUESTS_PER_SECOND;
|
requests_per_second = CLIENT_KNOBS->BLOBSTORE_REQUESTS_PER_SECOND;
|
||||||
concurrent_requests = CLIENT_KNOBS->BLOBSTORE_CONCURRENT_REQUESTS;
|
concurrent_requests = CLIENT_KNOBS->BLOBSTORE_CONCURRENT_REQUESTS;
|
||||||
|
list_requests_per_second = CLIENT_KNOBS->BLOBSTORE_LIST_REQUESTS_PER_SECOND;
|
||||||
|
write_requests_per_second = CLIENT_KNOBS->BLOBSTORE_WRITE_REQUESTS_PER_SECOND;
|
||||||
|
read_requests_per_second = CLIENT_KNOBS->BLOBSTORE_READ_REQUESTS_PER_SECOND;
|
||||||
|
delete_requests_per_second = CLIENT_KNOBS->BLOBSTORE_DELETE_REQUESTS_PER_SECOND;
|
||||||
multipart_max_part_size = CLIENT_KNOBS->BLOBSTORE_MULTIPART_MAX_PART_SIZE;
|
multipart_max_part_size = CLIENT_KNOBS->BLOBSTORE_MULTIPART_MAX_PART_SIZE;
|
||||||
multipart_min_part_size = CLIENT_KNOBS->BLOBSTORE_MULTIPART_MIN_PART_SIZE;
|
multipart_min_part_size = CLIENT_KNOBS->BLOBSTORE_MULTIPART_MIN_PART_SIZE;
|
||||||
concurrent_uploads = CLIENT_KNOBS->BLOBSTORE_CONCURRENT_UPLOADS;
|
concurrent_uploads = CLIENT_KNOBS->BLOBSTORE_CONCURRENT_UPLOADS;
|
||||||
|
@ -79,6 +83,10 @@ bool BlobStoreEndpoint::BlobKnobs::set(StringRef name, int value) {
|
||||||
TRY_PARAM(request_tries, rt);
|
TRY_PARAM(request_tries, rt);
|
||||||
TRY_PARAM(request_timeout, rto);
|
TRY_PARAM(request_timeout, rto);
|
||||||
TRY_PARAM(requests_per_second, rps);
|
TRY_PARAM(requests_per_second, rps);
|
||||||
|
TRY_PARAM(list_requests_per_second, lrps);
|
||||||
|
TRY_PARAM(write_requests_per_second, wrps);
|
||||||
|
TRY_PARAM(read_requests_per_second, rrps);
|
||||||
|
TRY_PARAM(delete_requests_per_second, drps);
|
||||||
TRY_PARAM(concurrent_requests, cr);
|
TRY_PARAM(concurrent_requests, cr);
|
||||||
TRY_PARAM(multipart_max_part_size, maxps);
|
TRY_PARAM(multipart_max_part_size, maxps);
|
||||||
TRY_PARAM(multipart_min_part_size, minps);
|
TRY_PARAM(multipart_min_part_size, minps);
|
||||||
|
@ -107,6 +115,10 @@ std::string BlobStoreEndpoint::BlobKnobs::getURLParameters() const {
|
||||||
_CHECK_PARAM(request_tries, rt);
|
_CHECK_PARAM(request_tries, rt);
|
||||||
_CHECK_PARAM(request_timeout, rto);
|
_CHECK_PARAM(request_timeout, rto);
|
||||||
_CHECK_PARAM(requests_per_second, rps);
|
_CHECK_PARAM(requests_per_second, rps);
|
||||||
|
_CHECK_PARAM(list_requests_per_second, lrps);
|
||||||
|
_CHECK_PARAM(write_requests_per_second, wrps);
|
||||||
|
_CHECK_PARAM(read_requests_per_second, rrps);
|
||||||
|
_CHECK_PARAM(delete_requests_per_second, drps);
|
||||||
_CHECK_PARAM(concurrent_requests, cr);
|
_CHECK_PARAM(concurrent_requests, cr);
|
||||||
_CHECK_PARAM(multipart_max_part_size, maxps);
|
_CHECK_PARAM(multipart_max_part_size, maxps);
|
||||||
_CHECK_PARAM(multipart_min_part_size, minps);
|
_CHECK_PARAM(multipart_min_part_size, minps);
|
||||||
|
@ -195,6 +207,8 @@ std::string BlobStoreEndpoint::getResourceURL(std::string resource) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<bool> objectExists_impl(Reference<BlobStoreEndpoint> b, std::string bucket, std::string object) {
|
ACTOR Future<bool> objectExists_impl(Reference<BlobStoreEndpoint> b, std::string bucket, std::string object) {
|
||||||
|
Void _ = wait(b->requestRateRead->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket + "/" + object;
|
std::string resource = std::string("/") + bucket + "/" + object;
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
|
|
||||||
|
@ -207,6 +221,8 @@ Future<bool> BlobStoreEndpoint::objectExists(std::string const &bucket, std::str
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> deleteObject_impl(Reference<BlobStoreEndpoint> b, std::string bucket, std::string object) {
|
ACTOR Future<Void> deleteObject_impl(Reference<BlobStoreEndpoint> b, std::string bucket, std::string object) {
|
||||||
|
Void _ = wait(b->requestRateDelete->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket + "/" + object;
|
std::string resource = std::string("/") + bucket + "/" + object;
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
Reference<HTTP::Response> r = wait(b->doRequest("DELETE", resource, headers, NULL, 0, {200, 204, 404}));
|
Reference<HTTP::Response> r = wait(b->doRequest("DELETE", resource, headers, NULL, 0, {200, 204, 404}));
|
||||||
|
@ -273,9 +289,10 @@ Future<Void> BlobStoreEndpoint::deleteRecursively(std::string const &bucket, std
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> createBucket_impl(Reference<BlobStoreEndpoint> b, std::string bucket) {
|
ACTOR Future<Void> createBucket_impl(Reference<BlobStoreEndpoint> b, std::string bucket) {
|
||||||
|
Void _ = wait(b->requestRateWrite->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket;
|
std::string resource = std::string("/") + bucket;
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
|
|
||||||
Reference<HTTP::Response> r = wait(b->doRequest("PUT", resource, headers, NULL, 0, {200, 409}));
|
Reference<HTTP::Response> r = wait(b->doRequest("PUT", resource, headers, NULL, 0, {200, 409}));
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
@ -285,6 +302,8 @@ Future<Void> BlobStoreEndpoint::createBucket(std::string const &bucket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<int64_t> objectSize_impl(Reference<BlobStoreEndpoint> b, std::string bucket, std::string object) {
|
ACTOR Future<int64_t> objectSize_impl(Reference<BlobStoreEndpoint> b, std::string bucket, std::string object) {
|
||||||
|
Void _ = wait(b->requestRateRead->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket + "/" + object;
|
std::string resource = std::string("/") + bucket + "/" + object;
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
|
|
||||||
|
@ -789,6 +808,8 @@ void BlobStoreEndpoint::setAuthHeaders(std::string const &verb, std::string cons
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<std::string> readEntireFile_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object) {
|
ACTOR Future<std::string> readEntireFile_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object) {
|
||||||
|
Void _ = wait(bstore->requestRateRead->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket + "/" + object;
|
std::string resource = std::string("/") + bucket + "/" + object;
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
Reference<HTTP::Response> r = wait(bstore->doRequest("GET", resource, headers, NULL, 0, {200, 404}));
|
Reference<HTTP::Response> r = wait(bstore->doRequest("GET", resource, headers, NULL, 0, {200, 404}));
|
||||||
|
@ -805,6 +826,7 @@ ACTOR Future<Void> writeEntireFileFromBuffer_impl(Reference<BlobStoreEndpoint> b
|
||||||
if(contentLen > bstore->knobs.multipart_max_part_size)
|
if(contentLen > bstore->knobs.multipart_max_part_size)
|
||||||
throw file_too_large();
|
throw file_too_large();
|
||||||
|
|
||||||
|
Void _ = wait(bstore->requestRateWrite->getAllowance(1));
|
||||||
Void _ = wait(bstore->concurrentUploads.take());
|
Void _ = wait(bstore->concurrentUploads.take());
|
||||||
state FlowLock::Releaser uploadReleaser(bstore->concurrentUploads, 1);
|
state FlowLock::Releaser uploadReleaser(bstore->concurrentUploads, 1);
|
||||||
|
|
||||||
|
@ -856,6 +878,8 @@ Future<Void> BlobStoreEndpoint::writeEntireFileFromBuffer(std::string const &buc
|
||||||
ACTOR Future<int> readObject_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object, void *data, int length, int64_t offset) {
|
ACTOR Future<int> readObject_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object, void *data, int length, int64_t offset) {
|
||||||
if(length <= 0)
|
if(length <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
Void _ = wait(bstore->requestRateRead->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket + "/" + object;
|
std::string resource = std::string("/") + bucket + "/" + object;
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
headers["Range"] = format("bytes=%lld-%lld", offset, offset + length - 1);
|
headers["Range"] = format("bytes=%lld-%lld", offset, offset + length - 1);
|
||||||
|
@ -874,6 +898,8 @@ Future<int> BlobStoreEndpoint::readObject(std::string const &bucket, std::string
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR static Future<std::string> beginMultiPartUpload_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object) {
|
ACTOR static Future<std::string> beginMultiPartUpload_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object) {
|
||||||
|
Void _ = wait(bstore->requestRateWrite->getAllowance(1));
|
||||||
|
|
||||||
std::string resource = std::string("/") + bucket + "/" + object + "?uploads";
|
std::string resource = std::string("/") + bucket + "/" + object + "?uploads";
|
||||||
HTTP::Headers headers;
|
HTTP::Headers headers;
|
||||||
Reference<HTTP::Response> r = wait(bstore->doRequest("POST", resource, headers, NULL, 0, {200}));
|
Reference<HTTP::Response> r = wait(bstore->doRequest("POST", resource, headers, NULL, 0, {200}));
|
||||||
|
@ -892,6 +918,7 @@ Future<std::string> BlobStoreEndpoint::beginMultiPartUpload(std::string const &b
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<std::string> uploadPart_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object, std::string uploadID, unsigned int partNumber, UnsentPacketQueue *pContent, int contentLen, std::string contentMD5) {
|
ACTOR Future<std::string> uploadPart_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object, std::string uploadID, unsigned int partNumber, UnsentPacketQueue *pContent, int contentLen, std::string contentMD5) {
|
||||||
|
Void _ = wait(bstore->requestRateWrite->getAllowance(1));
|
||||||
Void _ = wait(bstore->concurrentUploads.take());
|
Void _ = wait(bstore->concurrentUploads.take());
|
||||||
state FlowLock::Releaser uploadReleaser(bstore->concurrentUploads, 1);
|
state FlowLock::Releaser uploadReleaser(bstore->concurrentUploads, 1);
|
||||||
|
|
||||||
|
@ -921,6 +948,7 @@ Future<std::string> BlobStoreEndpoint::uploadPart(std::string const &bucket, std
|
||||||
|
|
||||||
ACTOR Future<Void> finishMultiPartUpload_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object, std::string uploadID, BlobStoreEndpoint::MultiPartSetT parts) {
|
ACTOR Future<Void> finishMultiPartUpload_impl(Reference<BlobStoreEndpoint> bstore, std::string bucket, std::string object, std::string uploadID, BlobStoreEndpoint::MultiPartSetT parts) {
|
||||||
state UnsentPacketQueue part_list(); // NonCopyable state var so must be declared at top of actor
|
state UnsentPacketQueue part_list(); // NonCopyable state var so must be declared at top of actor
|
||||||
|
Void _ = wait(bstore->requestRateWrite->getAllowance(1));
|
||||||
|
|
||||||
std::string manifest = "<CompleteMultipartUpload>";
|
std::string manifest = "<CompleteMultipartUpload>";
|
||||||
for(auto &p : parts)
|
for(auto &p : parts)
|
||||||
|
|
|
@ -55,6 +55,10 @@ public:
|
||||||
request_tries,
|
request_tries,
|
||||||
request_timeout,
|
request_timeout,
|
||||||
requests_per_second,
|
requests_per_second,
|
||||||
|
list_requests_per_second,
|
||||||
|
write_requests_per_second,
|
||||||
|
read_requests_per_second,
|
||||||
|
delete_requests_per_second,
|
||||||
multipart_max_part_size,
|
multipart_max_part_size,
|
||||||
multipart_min_part_size,
|
multipart_min_part_size,
|
||||||
concurrent_requests,
|
concurrent_requests,
|
||||||
|
@ -78,6 +82,10 @@ public:
|
||||||
"request_tries (or rt) Number of times to try each request until a parseable HTTP response other than 429 is received.",
|
"request_tries (or rt) Number of times to try each request until a parseable HTTP response other than 429 is received.",
|
||||||
"request_timeout (or rto) Number of seconds to wait for a request to succeed after a connection is established.",
|
"request_timeout (or rto) Number of seconds to wait for a request to succeed after a connection is established.",
|
||||||
"requests_per_second (or rps) Max number of requests to start per second.",
|
"requests_per_second (or rps) Max number of requests to start per second.",
|
||||||
|
"list_requests_per_second (or lrps) Max number of list requests to start per second.",
|
||||||
|
"write_requests_per_second (or wrps) Max number of write requests to start per second.",
|
||||||
|
"read_requests_per_second (or rrps) Max number of read requests to start per second.",
|
||||||
|
"delete_requests_per_second (or drps) Max number of delete requests to start per second.",
|
||||||
"multipart_max_part_size (or maxps) Max part size for multipart uploads.",
|
"multipart_max_part_size (or maxps) Max part size for multipart uploads.",
|
||||||
"multipart_min_part_size (or minps) Min part size for multipart uploads.",
|
"multipart_min_part_size (or minps) Min part size for multipart uploads.",
|
||||||
"concurrent_requests (or cr) Max number of total requests in progress at once, regardless of operation-specific concurrency limits.",
|
"concurrent_requests (or cr) Max number of total requests in progress at once, regardless of operation-specific concurrency limits.",
|
||||||
|
@ -97,6 +105,10 @@ public:
|
||||||
BlobStoreEndpoint(std::string const &host, std::string service, std::string const &key, std::string const &secret, BlobKnobs const &knobs = BlobKnobs())
|
BlobStoreEndpoint(std::string const &host, std::string service, std::string const &key, std::string const &secret, BlobKnobs const &knobs = BlobKnobs())
|
||||||
: host(host), service(service), key(key), secret(secret), lookupSecret(secret.empty()), knobs(knobs),
|
: host(host), service(service), key(key), secret(secret), lookupSecret(secret.empty()), knobs(knobs),
|
||||||
requestRate(new SpeedLimit(knobs.requests_per_second, 1)),
|
requestRate(new SpeedLimit(knobs.requests_per_second, 1)),
|
||||||
|
requestRateList(new SpeedLimit(knobs.list_requests_per_second, 1)),
|
||||||
|
requestRateWrite(new SpeedLimit(knobs.write_requests_per_second, 1)),
|
||||||
|
requestRateRead(new SpeedLimit(knobs.read_requests_per_second, 1)),
|
||||||
|
requestRateDelete(new SpeedLimit(knobs.delete_requests_per_second, 1)),
|
||||||
sendRate(new SpeedLimit(knobs.max_send_bytes_per_second, 1)),
|
sendRate(new SpeedLimit(knobs.max_send_bytes_per_second, 1)),
|
||||||
recvRate(new SpeedLimit(knobs.max_recv_bytes_per_second, 1)),
|
recvRate(new SpeedLimit(knobs.max_recv_bytes_per_second, 1)),
|
||||||
concurrentRequests(knobs.concurrent_requests),
|
concurrentRequests(knobs.concurrent_requests),
|
||||||
|
@ -135,6 +147,10 @@ public:
|
||||||
|
|
||||||
// Speed and concurrency limits
|
// Speed and concurrency limits
|
||||||
Reference<IRateControl> requestRate;
|
Reference<IRateControl> requestRate;
|
||||||
|
Reference<IRateControl> requestRateList;
|
||||||
|
Reference<IRateControl> requestRateWrite;
|
||||||
|
Reference<IRateControl> requestRateRead;
|
||||||
|
Reference<IRateControl> requestRateDelete;
|
||||||
Reference<IRateControl> sendRate;
|
Reference<IRateControl> sendRate;
|
||||||
Reference<IRateControl> recvRate;
|
Reference<IRateControl> recvRate;
|
||||||
FlowLock concurrentRequests;
|
FlowLock concurrentRequests;
|
||||||
|
|
|
@ -137,6 +137,8 @@ ProcessClass::Fitness ProcessClass::machineClassFitness( ClusterRole role ) cons
|
||||||
return ProcessClass::OkayFit;
|
return ProcessClass::OkayFit;
|
||||||
case ProcessClass::ProxyClass:
|
case ProcessClass::ProxyClass:
|
||||||
return ProcessClass::OkayFit;
|
return ProcessClass::OkayFit;
|
||||||
|
case ProcessClass::LogRouterClass:
|
||||||
|
return ProcessClass::OkayFit;
|
||||||
case ProcessClass::UnsetClass:
|
case ProcessClass::UnsetClass:
|
||||||
return ProcessClass::UnsetFit;
|
return ProcessClass::UnsetFit;
|
||||||
case ProcessClass::TesterClass:
|
case ProcessClass::TesterClass:
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
struct ProcessClass {
|
struct ProcessClass {
|
||||||
// This enum is stored in restartInfo.ini for upgrade tests, so be very careful about changing the existing items!
|
// This enum is stored in restartInfo.ini for upgrade tests, so be very careful about changing the existing items!
|
||||||
enum ClassType { UnsetClass, StorageClass, TransactionClass, ResolutionClass, TesterClass, ProxyClass, MasterClass, StatelessClass, LogClass, ClusterControllerClass, LogRouterClass, InvalidClass = -1 };
|
enum ClassType { UnsetClass, StorageClass, TransactionClass, ResolutionClass, TesterClass, ProxyClass, MasterClass, StatelessClass, LogClass, ClusterControllerClass, LogRouterClass, InvalidClass = -1 };
|
||||||
enum Fitness { BestFit, GoodFit, OkayFit, UnsetFit, WorstFit, ExcludeFit, NeverAssign }; //cannot be larger than 7 because of leader election mask
|
enum Fitness { BestFit, GoodFit, UnsetFit, OkayFit, WorstFit, ExcludeFit, NeverAssign }; //cannot be larger than 7 because of leader election mask
|
||||||
enum ClusterRole { Storage, TLog, Proxy, Master, Resolver, LogRouter, ClusterController };
|
enum ClusterRole { Storage, TLog, Proxy, Master, Resolver, LogRouter, ClusterController };
|
||||||
enum ClassSource { CommandLineSource, AutoSource, DBSource, InvalidSource = -1 };
|
enum ClassSource { CommandLineSource, AutoSource, DBSource, InvalidSource = -1 };
|
||||||
int16_t _class;
|
int16_t _class;
|
||||||
|
|
|
@ -91,7 +91,7 @@ TLSConnection::TLSConnection( Reference<IConnection> const& conn, Reference<ITLS
|
||||||
// If session is NULL, we're trusting policy->create_session
|
// If session is NULL, we're trusting policy->create_session
|
||||||
// to have used its provided logging function to have logged
|
// to have used its provided logging function to have logged
|
||||||
// the error
|
// the error
|
||||||
throw internal_error();
|
throw tls_error();
|
||||||
}
|
}
|
||||||
handshook = handshake(this);
|
handshook = handshake(this);
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ Reference<ITLSPolicy> TLSOptions::get_policy(PolicyType type) {
|
||||||
if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", verifyPeerString))
|
if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", verifyPeerString))
|
||||||
set_verify_peers({ verifyPeerString });
|
set_verify_peers({ verifyPeerString });
|
||||||
else
|
else
|
||||||
set_verify_peers({ std::string("Check.Valid=0")});
|
set_verify_peers({ std::string("Check.Valid=1")});
|
||||||
}
|
}
|
||||||
if (!ca_set) {
|
if (!ca_set) {
|
||||||
std::string caFile;
|
std::string caFile;
|
||||||
|
|
|
@ -339,6 +339,43 @@ public:
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FIXME: This logic will fallback unnecessarily when usable dcs > 1 because it does not check all combinations of potential satellite locations
|
||||||
|
std::vector<std::pair<WorkerInterface, ProcessClass>> getWorkersForSatelliteLogs( const DatabaseConfiguration& conf, const RegionInfo& region, std::map< Optional<Standalone<StringRef>>, int>& id_used, bool& satelliteFallback, bool checkStable = false ) {
|
||||||
|
int startDC = 0;
|
||||||
|
loop {
|
||||||
|
if(startDC > 0 && startDC >= region.satellites.size() + 1 - (satelliteFallback ? region.satelliteTLogUsableDcsFallback : region.satelliteTLogUsableDcs)) {
|
||||||
|
if(satelliteFallback || region.satelliteTLogUsableDcsFallback == 0) {
|
||||||
|
throw no_more_servers();
|
||||||
|
} else {
|
||||||
|
if(now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY) {
|
||||||
|
throw operation_failed();
|
||||||
|
}
|
||||||
|
satelliteFallback = true;
|
||||||
|
startDC = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::set<Optional<Key>> satelliteDCs;
|
||||||
|
for(int s = startDC; s < std::min<int>(startDC + (satelliteFallback ? region.satelliteTLogUsableDcsFallback : region.satelliteTLogUsableDcs), region.satellites.size()); s++) {
|
||||||
|
satelliteDCs.insert(region.satellites[s].dcId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(satelliteFallback) {
|
||||||
|
return getWorkersForTlogs( conf, region.satelliteTLogReplicationFactorFallback, conf.getDesiredSatelliteLogs(region.dcId)*region.satelliteTLogUsableDcsFallback/region.satelliteTLogUsableDcs, region.satelliteTLogPolicyFallback, id_used, checkStable, satelliteDCs );
|
||||||
|
} else {
|
||||||
|
return getWorkersForTlogs( conf, region.satelliteTLogReplicationFactor, conf.getDesiredSatelliteLogs(region.dcId), region.satelliteTLogPolicy, id_used, checkStable, satelliteDCs );
|
||||||
|
}
|
||||||
|
} catch (Error &e) {
|
||||||
|
if(e.code() != error_code_no_more_servers) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startDC++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WorkerFitnessInfo getWorkerForRoleInDatacenter(Optional<Standalone<StringRef>> const& dcId, ProcessClass::ClusterRole role, ProcessClass::Fitness unacceptableFitness, DatabaseConfiguration const& conf, std::map< Optional<Standalone<StringRef>>, int>& id_used, bool checkStable = false ) {
|
WorkerFitnessInfo getWorkerForRoleInDatacenter(Optional<Standalone<StringRef>> const& dcId, ProcessClass::ClusterRole role, ProcessClass::Fitness unacceptableFitness, DatabaseConfiguration const& conf, std::map< Optional<Standalone<StringRef>>, int>& id_used, bool checkStable = false ) {
|
||||||
std::map<std::pair<ProcessClass::Fitness,int>, vector<std::pair<WorkerInterface, ProcessClass>>> fitness_workers;
|
std::map<std::pair<ProcessClass::Fitness,int>, vector<std::pair<WorkerInterface, ProcessClass>>> fitness_workers;
|
||||||
|
|
||||||
|
@ -439,7 +476,14 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool betterCount (RoleFitness const& r) const {
|
||||||
|
if(count > r.count) return true;
|
||||||
|
return worstFit < r.worstFit;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator == (RoleFitness const& r) const { return worstFit == r.worstFit && bestFit == r.bestFit && count == r.count; }
|
bool operator == (RoleFitness const& r) const { return worstFit == r.worstFit && bestFit == r.bestFit && count == r.count; }
|
||||||
|
|
||||||
|
std::string toString() const { return format("%d %d &d", bestFit, worstFit, count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::set<Optional<Standalone<StringRef>>> getDatacenters( DatabaseConfiguration const& conf, bool checkStable = false ) {
|
std::set<Optional<Standalone<StringRef>>> getDatacenters( DatabaseConfiguration const& conf, bool checkStable = false ) {
|
||||||
|
@ -459,7 +503,6 @@ public:
|
||||||
|
|
||||||
std::set<Optional<Key>> remoteDC;
|
std::set<Optional<Key>> remoteDC;
|
||||||
remoteDC.insert(req.dcId);
|
remoteDC.insert(req.dcId);
|
||||||
|
|
||||||
|
|
||||||
auto remoteLogs = getWorkersForTlogs( req.configuration, req.configuration.getRemoteTLogReplicationFactor(), req.configuration.getDesiredRemoteLogs(), req.configuration.getRemoteTLogPolicy(), id_used, false, remoteDC );
|
auto remoteLogs = getWorkersForTlogs( req.configuration, req.configuration.getRemoteTLogReplicationFactor(), req.configuration.getDesiredRemoteLogs(), req.configuration.getRemoteTLogPolicy(), id_used, false, remoteDC );
|
||||||
for(int i = 0; i < remoteLogs.size(); i++) {
|
for(int i = 0; i < remoteLogs.size(); i++) {
|
||||||
|
@ -471,9 +514,13 @@ public:
|
||||||
result.logRouters.push_back(logRouters[i].first);
|
result.logRouters.push_back(logRouters[i].first);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY &&
|
if(!remoteStartTime.present()) {
|
||||||
( ( RoleFitness(remoteLogs, ProcessClass::TLog) > RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredLogs()) ) ||
|
remoteStartTime = now();
|
||||||
( RoleFitness(logRouters, ProcessClass::LogRouter) > RoleFitness(SERVER_KNOBS->EXPECTED_LOG_ROUTER_FITNESS, req.logRouterCount) ) ) ) {
|
}
|
||||||
|
|
||||||
|
if( now() - remoteStartTime.get() < SERVER_KNOBS->WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY &&
|
||||||
|
( ( RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredRemoteLogs()).betterCount(RoleFitness(remoteLogs, ProcessClass::TLog)) ) ||
|
||||||
|
( RoleFitness(SERVER_KNOBS->EXPECTED_LOG_ROUTER_FITNESS, req.logRouterCount).betterCount(RoleFitness(logRouters, ProcessClass::LogRouter)) ) ) ) {
|
||||||
throw operation_failed();
|
throw operation_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,11 +534,11 @@ public:
|
||||||
id_used[clusterControllerProcessId]++;
|
id_used[clusterControllerProcessId]++;
|
||||||
|
|
||||||
ASSERT(dcId.present());
|
ASSERT(dcId.present());
|
||||||
|
|
||||||
std::set<Optional<Key>> primaryDC;
|
std::set<Optional<Key>> primaryDC;
|
||||||
primaryDC.insert(dcId);
|
primaryDC.insert(dcId);
|
||||||
result.dcId = dcId;
|
result.dcId = dcId;
|
||||||
|
|
||||||
RegionInfo region;
|
RegionInfo region;
|
||||||
for(auto& r : req.configuration.regions) {
|
for(auto& r : req.configuration.regions) {
|
||||||
if(r.dcId == dcId.get()) {
|
if(r.dcId == dcId.get()) {
|
||||||
|
@ -499,14 +546,14 @@ public:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req.recruitSeedServers) {
|
if(req.recruitSeedServers) {
|
||||||
auto primaryStorageServers = getWorkersForSeedServers( req.configuration, req.configuration.storagePolicy, dcId );
|
auto primaryStorageServers = getWorkersForSeedServers( req.configuration, req.configuration.storagePolicy, dcId );
|
||||||
for(int i = 0; i < primaryStorageServers.size(); i++) {
|
for(int i = 0; i < primaryStorageServers.size(); i++) {
|
||||||
result.storageServers.push_back(primaryStorageServers[i].first);
|
result.storageServers.push_back(primaryStorageServers[i].first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tlogs = getWorkersForTlogs( req.configuration, req.configuration.tLogReplicationFactor, req.configuration.getDesiredLogs(), req.configuration.tLogPolicy, id_used, false, primaryDC );
|
auto tlogs = getWorkersForTlogs( req.configuration, req.configuration.tLogReplicationFactor, req.configuration.getDesiredLogs(), req.configuration.tLogPolicy, id_used, false, primaryDC );
|
||||||
for(int i = 0; i < tlogs.size(); i++) {
|
for(int i = 0; i < tlogs.size(); i++) {
|
||||||
result.tLogs.push_back(tlogs[i].first);
|
result.tLogs.push_back(tlogs[i].first);
|
||||||
|
@ -514,31 +561,9 @@ public:
|
||||||
|
|
||||||
std::vector<std::pair<WorkerInterface, ProcessClass>> satelliteLogs;
|
std::vector<std::pair<WorkerInterface, ProcessClass>> satelliteLogs;
|
||||||
if(region.satelliteTLogReplicationFactor > 0) {
|
if(region.satelliteTLogReplicationFactor > 0) {
|
||||||
int startDC = 0;
|
satelliteLogs = getWorkersForSatelliteLogs( req.configuration, region, id_used, result.satelliteFallback );
|
||||||
loop {
|
for(int i = 0; i < satelliteLogs.size(); i++) {
|
||||||
if(startDC > 0 && startDC >= region.satellites.size() + 1 - region.satelliteTLogUsableDcs) {
|
result.satelliteTLogs.push_back(satelliteLogs[i].first);
|
||||||
throw no_more_servers();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::set<Optional<Key>> satelliteDCs;
|
|
||||||
for(int s = startDC; s < std::min<int>(startDC + region.satelliteTLogUsableDcs, region.satellites.size()); s++) {
|
|
||||||
satelliteDCs.insert(region.satellites[s].dcId);
|
|
||||||
}
|
|
||||||
|
|
||||||
satelliteLogs = getWorkersForTlogs( req.configuration, region.satelliteTLogReplicationFactor, req.configuration.getDesiredSatelliteLogs(dcId), region.satelliteTLogPolicy, id_used, false, satelliteDCs );
|
|
||||||
|
|
||||||
for(int i = 0; i < satelliteLogs.size(); i++) {
|
|
||||||
result.satelliteTLogs.push_back(satelliteLogs[i].first);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} catch (Error &e) {
|
|
||||||
if(e.code() != error_code_no_more_servers) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
startDC++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,10 +587,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if( now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY &&
|
if( now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY &&
|
||||||
( RoleFitness(tlogs, ProcessClass::TLog) > RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredLogs()) ||
|
( RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredLogs()).betterCount(RoleFitness(tlogs, ProcessClass::TLog)) ||
|
||||||
( region.satelliteTLogReplicationFactor > 0 && RoleFitness(satelliteLogs, ProcessClass::TLog) > RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredSatelliteLogs(dcId)) ) ||
|
( region.satelliteTLogReplicationFactor > 0 && RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredSatelliteLogs(dcId)).betterCount(RoleFitness(satelliteLogs, ProcessClass::TLog)) ) ||
|
||||||
RoleFitness(proxies, ProcessClass::Proxy) > RoleFitness(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, req.configuration.getDesiredProxies()) ||
|
RoleFitness(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, req.configuration.getDesiredProxies()).betterCount(RoleFitness(proxies, ProcessClass::Proxy)) ||
|
||||||
RoleFitness(resolvers, ProcessClass::Resolver) > RoleFitness(SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS, req.configuration.getDesiredResolvers()) ) ) {
|
RoleFitness(SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS, req.configuration.getDesiredResolvers()).betterCount(RoleFitness(resolvers, ProcessClass::Resolver)) ) ) {
|
||||||
return operation_failed();
|
return operation_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +618,11 @@ public:
|
||||||
}
|
}
|
||||||
throw no_more_servers();
|
throw no_more_servers();
|
||||||
} catch( Error& e ) {
|
} catch( Error& e ) {
|
||||||
if (e.code() != error_code_no_more_servers || regions[1].priority < 0 || now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY) {
|
if(now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY && (!clusterControllerDcId.present() || regions[1].dcId != clusterControllerDcId.get())) {
|
||||||
|
throw operation_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.code() != error_code_no_more_servers || regions[1].priority < 0) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
TraceEvent(SevWarn, "AttemptingRecruitmentInRemoteDC", id).error(e);
|
TraceEvent(SevWarn, "AttemptingRecruitmentInRemoteDC", id).error(e);
|
||||||
|
@ -703,8 +732,8 @@ public:
|
||||||
.detail("DesiredResolvers", req.configuration.getDesiredResolvers()).detail("ActualResolvers", result.resolvers.size());
|
.detail("DesiredResolvers", req.configuration.getDesiredResolvers()).detail("ActualResolvers", result.resolvers.size());
|
||||||
|
|
||||||
if( now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY &&
|
if( now() - startTime < SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY &&
|
||||||
( RoleFitness(tlogs, ProcessClass::TLog) > RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredLogs()) ||
|
( RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredLogs()).betterCount(RoleFitness(tlogs, ProcessClass::TLog)) ||
|
||||||
bestFitness > RoleFitness(std::min(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS), std::max(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS), req.configuration.getDesiredProxies()+req.configuration.getDesiredResolvers()) ) ) {
|
RoleFitness(std::min(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS), std::max(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS), req.configuration.getDesiredProxies()+req.configuration.getDesiredResolvers()).betterCount(bestFitness) ) ) {
|
||||||
throw operation_failed();
|
throw operation_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,22 +745,19 @@ public:
|
||||||
if(desiredDcIds.get().present() && desiredDcIds.get().get().size() == 2 && desiredDcIds.get().get()[0].get() == regions[0].dcId && desiredDcIds.get().get()[1].get() == regions[1].dcId) {
|
if(desiredDcIds.get().present() && desiredDcIds.get().get().size() == 2 && desiredDcIds.get().get()[0].get() == regions[0].dcId && desiredDcIds.get().get()[1].get() == regions[1].dcId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::map< Optional<Standalone<StringRef>>, int> id_used;
|
std::map< Optional<Standalone<StringRef>>, int> id_used;
|
||||||
getWorkerForRoleInDatacenter(regions[0].dcId, ProcessClass::ClusterController, ProcessClass::ExcludeFit, db.config, id_used, true);
|
getWorkerForRoleInDatacenter(regions[0].dcId, ProcessClass::ClusterController, ProcessClass::ExcludeFit, db.config, id_used, true);
|
||||||
getWorkerForRoleInDatacenter(regions[0].dcId, ProcessClass::Master, ProcessClass::ExcludeFit, db.config, id_used, true);
|
getWorkerForRoleInDatacenter(regions[0].dcId, ProcessClass::Master, ProcessClass::ExcludeFit, db.config, id_used, true);
|
||||||
|
|
||||||
std::set<Optional<Key>> primaryDC;
|
std::set<Optional<Key>> primaryDC;
|
||||||
primaryDC.insert(regions[0].dcId);
|
primaryDC.insert(regions[0].dcId);
|
||||||
getWorkersForTlogs(db.config, db.config.tLogReplicationFactor, db.config.desiredTLogCount, db.config.tLogPolicy, id_used, true, primaryDC);
|
getWorkersForTlogs(db.config, db.config.tLogReplicationFactor, db.config.desiredTLogCount, db.config.tLogPolicy, id_used, true, primaryDC);
|
||||||
|
|
||||||
if(regions[0].satelliteTLogReplicationFactor > 0) {
|
if(regions[0].satelliteTLogReplicationFactor > 0) {
|
||||||
std::set<Optional<Key>> satelliteDCs;
|
bool satelliteFallback = false;
|
||||||
for(auto &s : regions[0].satellites) {
|
getWorkersForSatelliteLogs(db.config, regions[0], id_used, satelliteFallback, true);
|
||||||
satelliteDCs.insert(s.dcId);
|
|
||||||
}
|
|
||||||
getWorkersForTlogs(db.config, regions[0].satelliteTLogReplicationFactor, db.config.getDesiredSatelliteLogs(regions[0].dcId), regions[0].satelliteTLogPolicy, id_used, true, satelliteDCs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getWorkerForRoleInDatacenter( regions[0].dcId, ProcessClass::Resolver, ProcessClass::ExcludeFit, db.config, id_used, true );
|
getWorkerForRoleInDatacenter( regions[0].dcId, ProcessClass::Resolver, ProcessClass::ExcludeFit, db.config, id_used, true );
|
||||||
|
@ -856,7 +882,6 @@ public:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
std::set<Optional<Key>> primaryDC;
|
std::set<Optional<Key>> primaryDC;
|
||||||
std::set<Optional<Key>> satelliteDCs;
|
|
||||||
std::set<Optional<Key>> remoteDC;
|
std::set<Optional<Key>> remoteDC;
|
||||||
|
|
||||||
RegionInfo region;
|
RegionInfo region;
|
||||||
|
@ -871,12 +896,6 @@ public:
|
||||||
region = r;
|
region = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(region.satelliteTLogReplicationFactor > 0) {
|
|
||||||
for(auto &s : region.satellites) {
|
|
||||||
satelliteDCs.insert(s.dcId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check tLog fitness
|
// Check tLog fitness
|
||||||
|
@ -885,23 +904,38 @@ public:
|
||||||
|
|
||||||
if(oldTLogFit < newTLogFit) return false;
|
if(oldTLogFit < newTLogFit) return false;
|
||||||
|
|
||||||
RoleFitness oldSatelliteTLogFit(satellite_tlogs, ProcessClass::TLog);
|
bool oldSatelliteFallback = false;
|
||||||
RoleFitness newSatelliteTLogFit(region.satelliteTLogReplicationFactor > 0 ? getWorkersForTlogs(db.config, region.satelliteTLogReplicationFactor, db.config.getDesiredSatelliteLogs(clusterControllerDcId), region.satelliteTLogPolicy, id_used, true, satelliteDCs) : satellite_tlogs, ProcessClass::TLog);
|
for(auto& logSet : dbi.logSystemConfig.tLogs) {
|
||||||
|
if(logSet.isLocal && logSet.locality == tagLocalitySatellite) {
|
||||||
|
oldSatelliteFallback = logSet.tLogPolicy->info() != region.satelliteTLogPolicy->info();
|
||||||
|
ASSERT(!oldSatelliteFallback || logSet.tLogPolicy->info() == region.satelliteTLogPolicyFallback->info());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(oldSatelliteTLogFit < newSatelliteTLogFit) return false;
|
RoleFitness oldSatelliteTLogFit(satellite_tlogs, ProcessClass::TLog);
|
||||||
|
bool newSatelliteFallback = false;
|
||||||
|
RoleFitness newSatelliteTLogFit(region.satelliteTLogReplicationFactor > 0 ? getWorkersForSatelliteLogs(db.config, region, id_used, newSatelliteFallback, true) : satellite_tlogs, ProcessClass::TLog);
|
||||||
|
|
||||||
|
if(oldSatelliteTLogFit < newSatelliteTLogFit)
|
||||||
|
return false;
|
||||||
|
if(!oldSatelliteFallback && newSatelliteFallback)
|
||||||
|
return false;
|
||||||
|
|
||||||
RoleFitness oldRemoteTLogFit(remote_tlogs, ProcessClass::TLog);
|
RoleFitness oldRemoteTLogFit(remote_tlogs, ProcessClass::TLog);
|
||||||
RoleFitness newRemoteTLogFit((db.config.usableRegions > 1 && dbi.recoveryState == RecoveryState::REMOTE_RECOVERED) ? getWorkersForTlogs(db.config, db.config.getRemoteTLogReplicationFactor(), db.config.getDesiredRemoteLogs(), db.config.getRemoteTLogPolicy(), id_used, true, remoteDC) : remote_tlogs, ProcessClass::TLog);
|
RoleFitness newRemoteTLogFit((db.config.usableRegions > 1 && dbi.recoveryState == RecoveryState::REMOTE_RECOVERED) ? getWorkersForTlogs(db.config, db.config.getRemoteTLogReplicationFactor(), db.config.getDesiredRemoteLogs(), db.config.getRemoteTLogPolicy(), id_used, true, remoteDC) : remote_tlogs, ProcessClass::TLog);
|
||||||
|
|
||||||
if(oldRemoteTLogFit < newRemoteTLogFit) return false;
|
if(oldRemoteTLogFit < newRemoteTLogFit) return false;
|
||||||
|
|
||||||
|
int oldRouterCount = oldTLogFit.count * std::max<int>(1, db.config.desiredLogRouterCount / std::max(1,oldTLogFit.count));
|
||||||
|
int newRouterCount = newTLogFit.count * std::max<int>(1, db.config.desiredLogRouterCount / std::max(1,newTLogFit.count));
|
||||||
RoleFitness oldLogRoutersFit(log_routers, ProcessClass::LogRouter);
|
RoleFitness oldLogRoutersFit(log_routers, ProcessClass::LogRouter);
|
||||||
RoleFitness newLogRoutersFit((db.config.usableRegions > 1 && dbi.recoveryState == RecoveryState::REMOTE_RECOVERED) ? getWorkersForRoleInDatacenter( *remoteDC.begin(), ProcessClass::LogRouter, newTLogFit.count, db.config, id_used, Optional<WorkerFitnessInfo>(), true ) : log_routers, ProcessClass::LogRouter);
|
RoleFitness newLogRoutersFit((db.config.usableRegions > 1 && dbi.recoveryState == RecoveryState::REMOTE_RECOVERED) ? getWorkersForRoleInDatacenter( *remoteDC.begin(), ProcessClass::LogRouter, newRouterCount, db.config, id_used, Optional<WorkerFitnessInfo>(), true ) : log_routers, ProcessClass::LogRouter);
|
||||||
|
|
||||||
if(oldLogRoutersFit.count < oldTLogFit.count) {
|
if(oldLogRoutersFit.count < oldRouterCount) {
|
||||||
oldLogRoutersFit.worstFit = ProcessClass::NeverAssign;
|
oldLogRoutersFit.worstFit = ProcessClass::NeverAssign;
|
||||||
}
|
}
|
||||||
if(newLogRoutersFit.count < newTLogFit.count) {
|
if(newLogRoutersFit.count < newRouterCount) {
|
||||||
newLogRoutersFit.worstFit = ProcessClass::NeverAssign;
|
newLogRoutersFit.worstFit = ProcessClass::NeverAssign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,14 +956,14 @@ public:
|
||||||
|
|
||||||
if(oldInFit.betterFitness(newInFit)) return false;
|
if(oldInFit.betterFitness(newInFit)) return false;
|
||||||
|
|
||||||
if(oldTLogFit > newTLogFit || oldInFit > newInFit || oldSatelliteTLogFit > newSatelliteTLogFit || oldRemoteTLogFit > newRemoteTLogFit || oldLogRoutersFit > newLogRoutersFit) {
|
if(oldTLogFit > newTLogFit || oldInFit > newInFit || (oldSatelliteFallback && !newSatelliteFallback) || oldSatelliteTLogFit > newSatelliteTLogFit || oldRemoteTLogFit > newRemoteTLogFit || oldLogRoutersFit > newLogRoutersFit) {
|
||||||
TraceEvent("BetterMasterExists", id).detail("OldMasterFit", oldMasterFit).detail("NewMasterFit", mworker.fitness)
|
TraceEvent("BetterMasterExists", id).detail("OldMasterFit", oldMasterFit).detail("NewMasterFit", mworker.fitness)
|
||||||
.detail("OldTLogFitC", oldTLogFit.count).detail("NewTLogFitC", newTLogFit.count)
|
.detail("OldTLogFit", oldTLogFit.toString()).detail("NewTLogFit", newTLogFit.toString())
|
||||||
.detail("OldTLogWorstFitT", oldTLogFit.worstFit).detail("NewTLogWorstFitT", newTLogFit.worstFit)
|
.detail("OldInFit", oldInFit.toString()).detail("NewInFit", newInFit.toString())
|
||||||
.detail("OldTLogBestFitT", oldTLogFit.bestFit).detail("NewTLogBestFitT", newTLogFit.bestFit)
|
.detail("OldSatelliteFit", oldSatelliteTLogFit.toString()).detail("NewSatelliteFit", newSatelliteTLogFit.toString())
|
||||||
.detail("OldInFitW", oldInFit.worstFit).detail("NewInFitW", newInFit.worstFit)
|
.detail("OldRemoteFit", oldRemoteTLogFit.toString()).detail("NewRemoteFit", newRemoteTLogFit.toString())
|
||||||
.detail("OldInFitB", oldInFit.bestFit).detail("NewInFitB", newInFit.bestFit)
|
.detail("OldRouterFit", oldLogRoutersFit.toString()).detail("NewRouterFit", newLogRoutersFit.toString())
|
||||||
.detail("OldInFitC", oldInFit.count).detail("NewInFitC", newInFit.count);
|
.detail("OldSatelliteFallback", oldSatelliteFallback).detail("NewSatelliteFallback", newSatelliteFallback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -945,24 +979,25 @@ public:
|
||||||
Optional<Standalone<StringRef>> clusterControllerProcessId;
|
Optional<Standalone<StringRef>> clusterControllerProcessId;
|
||||||
Optional<Standalone<StringRef>> clusterControllerDcId;
|
Optional<Standalone<StringRef>> clusterControllerDcId;
|
||||||
AsyncVar<Optional<vector<Optional<Key>>>> desiredDcIds; //desired DC priorities
|
AsyncVar<Optional<vector<Optional<Key>>>> desiredDcIds; //desired DC priorities
|
||||||
AsyncVar<Optional<vector<Optional<Key>>>> changingDcIds; //current DC priorities for everyone other than the cluster controller process
|
AsyncVar<std::pair<bool,Optional<vector<Optional<Key>>>>> changingDcIds; //current DC priorities to change first, and whether that is the cluster controller
|
||||||
AsyncVar<Optional<vector<Optional<Key>>>> changedDcIds; //current DC priority for the cluster controller process
|
AsyncVar<std::pair<bool,Optional<vector<Optional<Key>>>>> changedDcIds; //current DC priorities to change second, and whether the cluster controller has been changed
|
||||||
UID id;
|
UID id;
|
||||||
std::vector<RecruitFromConfigurationRequest> outstandingRecruitmentRequests;
|
std::vector<RecruitFromConfigurationRequest> outstandingRecruitmentRequests;
|
||||||
std::vector<RecruitRemoteFromConfigurationRequest> outstandingRemoteRecruitmentRequests;
|
std::vector<RecruitRemoteFromConfigurationRequest> outstandingRemoteRecruitmentRequests;
|
||||||
std::vector<std::pair<RecruitStorageRequest, double>> outstandingStorageRequests;
|
std::vector<std::pair<RecruitStorageRequest, double>> outstandingStorageRequests;
|
||||||
ActorCollection ac;
|
ActorCollection ac;
|
||||||
UpdateWorkerList updateWorkerList;
|
UpdateWorkerList updateWorkerList;
|
||||||
Future<Void> betterMasterExistsChecker;
|
Future<Void> outstandingRequestChecker;
|
||||||
|
|
||||||
DBInfo db;
|
DBInfo db;
|
||||||
Database cx;
|
Database cx;
|
||||||
double startTime;
|
double startTime;
|
||||||
|
Optional<double> remoteStartTime;
|
||||||
Version datacenterVersionDifference;
|
Version datacenterVersionDifference;
|
||||||
bool versionDifferenceUpdated;
|
bool versionDifferenceUpdated;
|
||||||
|
|
||||||
explicit ClusterControllerData( ClusterControllerFullInterface ccInterface )
|
explicit ClusterControllerData( ClusterControllerFullInterface ccInterface )
|
||||||
: id(ccInterface.id()), ac(false), betterMasterExistsChecker(Void()), gotProcessClasses(false), gotFullyRecoveredConfig(false), startTime(now()), datacenterVersionDifference(0), versionDifferenceUpdated(false)
|
: id(ccInterface.id()), ac(false), outstandingRequestChecker(Void()), gotProcessClasses(false), gotFullyRecoveredConfig(false), startTime(now()), datacenterVersionDifference(0), versionDifferenceUpdated(false)
|
||||||
{
|
{
|
||||||
auto serverInfo = db.serverInfo->get();
|
auto serverInfo = db.serverInfo->get();
|
||||||
serverInfo.id = g_random->randomUniqueID();
|
serverInfo.id = g_random->randomUniqueID();
|
||||||
|
@ -1214,31 +1249,34 @@ void checkOutstandingStorageRequests( ClusterControllerData* self ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> doCheckOutstandingMasterRequests( ClusterControllerData* self ) {
|
ACTOR Future<Void> doCheckOutstandingRequests( ClusterControllerData* self ) {
|
||||||
Void _ = wait( delay(SERVER_KNOBS->CHECK_BETTER_MASTER_INTERVAL) );
|
try {
|
||||||
if (self->betterMasterExists()) {
|
Void _ = wait( delay(SERVER_KNOBS->CHECK_OUTSTANDING_INTERVAL) );
|
||||||
if (!self->db.forceMasterFailure.isSet()) {
|
|
||||||
self->db.forceMasterFailure.send( Void() );
|
checkOutstandingRecruitmentRequests( self );
|
||||||
TraceEvent("MasterRegistrationKill", self->id).detail("MasterId", self->db.serverInfo->get().master.id());
|
checkOutstandingRemoteRecruitmentRequests( self );
|
||||||
|
checkOutstandingStorageRequests( self );
|
||||||
|
|
||||||
|
self->checkRecoveryStalled();
|
||||||
|
if (self->betterMasterExists()) {
|
||||||
|
if (!self->db.forceMasterFailure.isSet()) {
|
||||||
|
self->db.forceMasterFailure.send( Void() );
|
||||||
|
TraceEvent("MasterRegistrationKill", self->id).detail("MasterId", self->db.serverInfo->get().master.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch( Error &e ) {
|
||||||
|
if(e.code() != error_code_operation_failed && e.code() != error_code_no_more_servers) {
|
||||||
|
TraceEvent(SevError, "CheckOutstandingError").error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkOutstandingMasterRequests( ClusterControllerData* self ) {
|
void checkOutstandingRequests( ClusterControllerData* self ) {
|
||||||
self->checkRecoveryStalled();
|
if( !self->outstandingRequestChecker.isReady() )
|
||||||
|
|
||||||
if( !self->betterMasterExistsChecker.isReady() )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self->betterMasterExistsChecker = doCheckOutstandingMasterRequests(self);
|
self->outstandingRequestChecker = doCheckOutstandingRequests(self);
|
||||||
}
|
|
||||||
|
|
||||||
void checkOutstandingRequests( ClusterControllerData* self ) {
|
|
||||||
checkOutstandingRecruitmentRequests( self );
|
|
||||||
checkOutstandingRemoteRecruitmentRequests( self );
|
|
||||||
checkOutstandingStorageRequests( self );
|
|
||||||
checkOutstandingMasterRequests( self );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> rebootAndCheck( ClusterControllerData* cluster, Optional<Standalone<StringRef>> processID ) {
|
ACTOR Future<Void> rebootAndCheck( ClusterControllerData* cluster, Optional<Standalone<StringRef>> processID ) {
|
||||||
|
@ -1252,7 +1290,7 @@ ACTOR Future<Void> rebootAndCheck( ClusterControllerData* cluster, Optional<Stan
|
||||||
if(watcher != cluster->id_worker.end()) {
|
if(watcher != cluster->id_worker.end()) {
|
||||||
watcher->second.reboots--;
|
watcher->second.reboots--;
|
||||||
if( watcher->second.reboots < 2 )
|
if( watcher->second.reboots < 2 )
|
||||||
checkOutstandingMasterRequests( cluster );
|
checkOutstandingRequests( cluster );
|
||||||
}
|
}
|
||||||
|
|
||||||
return Void();
|
return Void();
|
||||||
|
@ -1481,7 +1519,7 @@ ACTOR Future<Void> clusterRecruitRemoteFromConfiguration( ClusterControllerData*
|
||||||
req.reply.send( self->findRemoteWorkersForConfiguration( req ) );
|
req.reply.send( self->findRemoteWorkersForConfiguration( req ) );
|
||||||
return Void();
|
return Void();
|
||||||
} catch (Error& e) {
|
} catch (Error& e) {
|
||||||
if (e.code() == error_code_no_more_servers && now() - self->startTime >= SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY) {
|
if (e.code() == error_code_no_more_servers && self->remoteStartTime.present() && now() - self->remoteStartTime.get() >= SERVER_KNOBS->WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY) {
|
||||||
self->outstandingRemoteRecruitmentRequests.push_back( req );
|
self->outstandingRemoteRecruitmentRequests.push_back( req );
|
||||||
TraceEvent(SevWarn, "RecruitRemoteFromConfigurationNotAvailable", self->id).error(e);
|
TraceEvent(SevWarn, "RecruitRemoteFromConfigurationNotAvailable", self->id).error(e);
|
||||||
return Void();
|
return Void();
|
||||||
|
@ -1575,7 +1613,7 @@ void clusterRegisterMaster( ClusterControllerData* self, RegisterMasterRequest c
|
||||||
self->db.serverInfo->set( dbInfo );
|
self->db.serverInfo->set( dbInfo );
|
||||||
}
|
}
|
||||||
|
|
||||||
checkOutstandingMasterRequests(self);
|
checkOutstandingRequests(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) {
|
void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) {
|
||||||
|
@ -1589,11 +1627,21 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) {
|
||||||
if ( w.address() == g_network->getLocalAddress() ) {
|
if ( w.address() == g_network->getLocalAddress() ) {
|
||||||
self->clusterControllerProcessId = w.locality.processId();
|
self->clusterControllerProcessId = w.locality.processId();
|
||||||
self->clusterControllerDcId = w.locality.dcId();
|
self->clusterControllerDcId = w.locality.dcId();
|
||||||
if(self->changedDcIds.get().present()) {
|
if(self->changingDcIds.get().first) {
|
||||||
newPriorityInfo.dcFitness = ClusterControllerPriorityInfo::calculateDCFitness( w.locality.dcId(), self->changedDcIds.get().get() );
|
if(self->changingDcIds.get().second.present()) {
|
||||||
|
newPriorityInfo.dcFitness = ClusterControllerPriorityInfo::calculateDCFitness( w.locality.dcId(), self->changingDcIds.get().second.get() );
|
||||||
|
}
|
||||||
|
} else if(self->changedDcIds.get().second.present()) {
|
||||||
|
newPriorityInfo.dcFitness = ClusterControllerPriorityInfo::calculateDCFitness( w.locality.dcId(), self->changedDcIds.get().second.get() );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!self->changingDcIds.get().first) {
|
||||||
|
if(self->changingDcIds.get().second.present()) {
|
||||||
|
newPriorityInfo.dcFitness = ClusterControllerPriorityInfo::calculateDCFitness( w.locality.dcId(), self->changingDcIds.get().second.get() );
|
||||||
|
}
|
||||||
|
} else if(self->changedDcIds.get().second.present()) {
|
||||||
|
newPriorityInfo.dcFitness = ClusterControllerPriorityInfo::calculateDCFitness( w.locality.dcId(), self->changedDcIds.get().second.get() );
|
||||||
}
|
}
|
||||||
} else if(self->changingDcIds.get().present()) {
|
|
||||||
newPriorityInfo.dcFitness = ClusterControllerPriorityInfo::calculateDCFitness( w.locality.dcId(), self->changingDcIds.get().get() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check process class and exclusive property
|
// Check process class and exclusive property
|
||||||
|
@ -1900,38 +1948,91 @@ ACTOR Future<Void> updatedChangingDatacenters(ClusterControllerData *self) {
|
||||||
//do not change the cluster controller until all the processes have had a chance to register
|
//do not change the cluster controller until all the processes have had a chance to register
|
||||||
Void _ = wait( delay(SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY) );
|
Void _ = wait( delay(SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY) );
|
||||||
loop {
|
loop {
|
||||||
self->changingDcIds.set(self->desiredDcIds.get());
|
state Future<Void> onChange = self->desiredDcIds.onChange();
|
||||||
if(self->changingDcIds.get().present()) {
|
if(!self->desiredDcIds.get().present()) {
|
||||||
for ( auto& it : self->id_worker ) {
|
self->changingDcIds.set(std::make_pair(false,self->desiredDcIds.get()));
|
||||||
uint8_t fitness = ClusterControllerPriorityInfo::calculateDCFitness( it.second.interf.locality.dcId(), self->changingDcIds.get().get() );
|
} else {
|
||||||
if ( it.first != self->clusterControllerProcessId && it.second.priorityInfo.dcFitness != fitness ) {
|
auto& worker = self->id_worker[self->clusterControllerProcessId];
|
||||||
it.second.priorityInfo.dcFitness = fitness;
|
uint8_t newFitness = ClusterControllerPriorityInfo::calculateDCFitness( worker.interf.locality.dcId(), self->desiredDcIds.get().get() );
|
||||||
if(!it.second.reply.isSet()) {
|
self->changingDcIds.set(std::make_pair(worker.priorityInfo.dcFitness > newFitness,self->desiredDcIds.get()));
|
||||||
it.second.reply.send( RegisterWorkerReply( it.second.processClass, it.second.priorityInfo ) );
|
|
||||||
|
if ( worker.priorityInfo.dcFitness > newFitness ) {
|
||||||
|
worker.priorityInfo.dcFitness = newFitness;
|
||||||
|
if(!worker.reply.isSet()) {
|
||||||
|
worker.reply.send( RegisterWorkerReply( worker.processClass, worker.priorityInfo ) );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state int currentFit = ProcessClass::BestFit;
|
||||||
|
while(currentFit <= ProcessClass::NeverAssign) {
|
||||||
|
bool updated = false;
|
||||||
|
for ( auto& it : self->id_worker ) {
|
||||||
|
if( ( !it.second.priorityInfo.isExcluded && it.second.priorityInfo.processClassFitness == currentFit ) || currentFit == ProcessClass::NeverAssign ) {
|
||||||
|
uint8_t fitness = ClusterControllerPriorityInfo::calculateDCFitness( it.second.interf.locality.dcId(), self->changingDcIds.get().second.get() );
|
||||||
|
if ( it.first != self->clusterControllerProcessId && it.second.priorityInfo.dcFitness != fitness ) {
|
||||||
|
updated = true;
|
||||||
|
it.second.priorityInfo.dcFitness = fitness;
|
||||||
|
if(!it.second.reply.isSet()) {
|
||||||
|
it.second.reply.send( RegisterWorkerReply( it.second.processClass, it.second.priorityInfo ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(updated && currentFit < ProcessClass::NeverAssign) {
|
||||||
|
Void _ = wait( delay(SERVER_KNOBS->CC_CLASS_DELAY) );
|
||||||
|
}
|
||||||
|
currentFit++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Void _ = wait(self->desiredDcIds.onChange());
|
Void _ = wait(onChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> updatedChangedDatacenters(ClusterControllerData *self) {
|
ACTOR Future<Void> updatedChangedDatacenters(ClusterControllerData *self) {
|
||||||
state Future<Void> changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
|
state Future<Void> changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
|
||||||
|
state Future<Void> onChange = self->changingDcIds.onChange();
|
||||||
loop {
|
loop {
|
||||||
choose {
|
choose {
|
||||||
when( Void _ = wait(self->changingDcIds.onChange()) ) { changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY); }
|
when( Void _ = wait(onChange) ) {
|
||||||
|
changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
|
||||||
|
onChange = self->changingDcIds.onChange();
|
||||||
|
}
|
||||||
when( Void _ = wait(changeDelay) ) {
|
when( Void _ = wait(changeDelay) ) {
|
||||||
changeDelay = Never();
|
changeDelay = Never();
|
||||||
self->changedDcIds.set(self->changingDcIds.get());
|
onChange = self->changingDcIds.onChange();
|
||||||
if(self->changedDcIds.get().present()) {
|
|
||||||
auto& worker = self->id_worker[self->clusterControllerProcessId];
|
self->changedDcIds.set(self->changingDcIds.get());
|
||||||
uint8_t fitness = ClusterControllerPriorityInfo::calculateDCFitness( worker.interf.locality.dcId(), self->changedDcIds.get().get() );
|
if(self->changedDcIds.get().second.present()) {
|
||||||
if ( worker.priorityInfo.dcFitness != fitness ) {
|
if( !self->changedDcIds.get().first ) {
|
||||||
worker.priorityInfo.dcFitness = fitness;
|
auto& worker = self->id_worker[self->clusterControllerProcessId];
|
||||||
if(!worker.reply.isSet()) {
|
uint8_t newFitness = ClusterControllerPriorityInfo::calculateDCFitness( worker.interf.locality.dcId(), self->changedDcIds.get().second.get() );
|
||||||
worker.reply.send( RegisterWorkerReply( worker.processClass, worker.priorityInfo ) );
|
if( worker.priorityInfo.dcFitness != newFitness ) {
|
||||||
|
worker.priorityInfo.dcFitness = newFitness;
|
||||||
|
if(!worker.reply.isSet()) {
|
||||||
|
worker.reply.send( RegisterWorkerReply( worker.processClass, worker.priorityInfo ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state int currentFit = ProcessClass::BestFit;
|
||||||
|
while(currentFit <= ProcessClass::NeverAssign) {
|
||||||
|
bool updated = false;
|
||||||
|
for ( auto& it : self->id_worker ) {
|
||||||
|
if( ( !it.second.priorityInfo.isExcluded && it.second.priorityInfo.processClassFitness == currentFit ) || currentFit == ProcessClass::NeverAssign ) {
|
||||||
|
uint8_t fitness = ClusterControllerPriorityInfo::calculateDCFitness( it.second.interf.locality.dcId(), self->changedDcIds.get().second.get() );
|
||||||
|
if ( it.first != self->clusterControllerProcessId && it.second.priorityInfo.dcFitness != fitness ) {
|
||||||
|
updated = true;
|
||||||
|
it.second.priorityInfo.dcFitness = fitness;
|
||||||
|
if(!it.second.reply.isSet()) {
|
||||||
|
it.second.reply.send( RegisterWorkerReply( it.second.processClass, it.second.priorityInfo ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(updated && currentFit < ProcessClass::NeverAssign) {
|
||||||
|
Void _ = wait( delay(SERVER_KNOBS->CC_CLASS_DELAY) );
|
||||||
|
}
|
||||||
|
currentFit++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1941,6 +2042,7 @@ ACTOR Future<Void> updatedChangedDatacenters(ClusterControllerData *self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> updateDatacenterVersionDifference( ClusterControllerData *self ) {
|
ACTOR Future<Void> updateDatacenterVersionDifference( ClusterControllerData *self ) {
|
||||||
|
state double lastLogTime = 0;
|
||||||
loop {
|
loop {
|
||||||
self->versionDifferenceUpdated = false;
|
self->versionDifferenceUpdated = false;
|
||||||
if(self->db.serverInfo->get().recoveryState >= RecoveryState::FULLY_RECOVERED && self->db.config.usableRegions == 1) {
|
if(self->db.serverInfo->get().recoveryState >= RecoveryState::FULLY_RECOVERED && self->db.config.usableRegions == 1) {
|
||||||
|
@ -1977,12 +2079,12 @@ ACTOR Future<Void> updateDatacenterVersionDifference( ClusterControllerData *sel
|
||||||
Void _ = wait(self->db.serverInfo->onChange());
|
Void _ = wait(self->db.serverInfo->onChange());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
state Future<Void> onChange = self->db.serverInfo->onChange();
|
state Future<Void> onChange = self->db.serverInfo->onChange();
|
||||||
loop {
|
loop {
|
||||||
state Future<TLogQueuingMetricsReply> primaryMetrics = primaryLog.get().getQueuingMetrics.getReply( TLogQueuingMetricsRequest() );
|
state Future<TLogQueuingMetricsReply> primaryMetrics = primaryLog.get().getQueuingMetrics.getReply( TLogQueuingMetricsRequest() );
|
||||||
state Future<TLogQueuingMetricsReply> remoteMetrics = remoteLog.get().getQueuingMetrics.getReply( TLogQueuingMetricsRequest() );
|
state Future<TLogQueuingMetricsReply> remoteMetrics = remoteLog.get().getQueuingMetrics.getReply( TLogQueuingMetricsRequest() );
|
||||||
|
|
||||||
Void _ = wait( ( success(primaryMetrics) && success(remoteMetrics) ) || onChange );
|
Void _ = wait( ( success(primaryMetrics) && success(remoteMetrics) ) || onChange );
|
||||||
if(onChange.isReady()) {
|
if(onChange.isReady()) {
|
||||||
break;
|
break;
|
||||||
|
@ -1990,6 +2092,10 @@ ACTOR Future<Void> updateDatacenterVersionDifference( ClusterControllerData *sel
|
||||||
|
|
||||||
self->versionDifferenceUpdated = true;
|
self->versionDifferenceUpdated = true;
|
||||||
self->datacenterVersionDifference = primaryMetrics.get().v - remoteMetrics.get().v;
|
self->datacenterVersionDifference = primaryMetrics.get().v - remoteMetrics.get().v;
|
||||||
|
if(now() - lastLogTime > SERVER_KNOBS->CLUSTER_CONTROLLER_LOGGING_DELAY) {
|
||||||
|
lastLogTime = now();
|
||||||
|
TraceEvent("DatacenterVersionDifference", self->id).detail("Difference", self->datacenterVersionDifference);
|
||||||
|
}
|
||||||
|
|
||||||
Void _ = wait( delay(SERVER_KNOBS->VERSION_LAG_METRIC_INTERVAL) || onChange );
|
Void _ = wait( delay(SERVER_KNOBS->VERSION_LAG_METRIC_INTERVAL) || onChange );
|
||||||
if(onChange.isReady()) {
|
if(onChange.isReady()) {
|
||||||
|
|
|
@ -89,10 +89,13 @@ struct RecruitFromConfigurationReply {
|
||||||
vector<WorkerInterface> storageServers;
|
vector<WorkerInterface> storageServers;
|
||||||
vector<WorkerInterface> oldLogRouters;
|
vector<WorkerInterface> oldLogRouters;
|
||||||
Optional<Key> dcId;
|
Optional<Key> dcId;
|
||||||
|
bool satelliteFallback;
|
||||||
|
|
||||||
|
RecruitFromConfigurationReply() : satelliteFallback(false) {}
|
||||||
|
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize( Ar& ar ) {
|
void serialize( Ar& ar ) {
|
||||||
ar & tLogs & satelliteTLogs & proxies & resolvers & storageServers & oldLogRouters & dcId;
|
ar & tLogs & satelliteTLogs & proxies & resolvers & storageServers & oldLogRouters & dcId & satelliteFallback;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -404,7 +404,7 @@ ACTOR Future<Reference<InitialDataDistribution>> getInitialDataDistribution( Dat
|
||||||
|
|
||||||
// for each range
|
// for each range
|
||||||
for(int i = 0; i < keyServers.size() - 1; i++) {
|
for(int i = 0; i < keyServers.size() - 1; i++) {
|
||||||
ShardInfo info( keyServers[i].key );
|
DDShardInfo info( keyServers[i].key );
|
||||||
decodeKeyServersValue( keyServers[i].value, src, dest );
|
decodeKeyServersValue( keyServers[i].value, src, dest );
|
||||||
if(remoteDcIds.size()) {
|
if(remoteDcIds.size()) {
|
||||||
auto srcIter = team_cache.find(src);
|
auto srcIter = team_cache.find(src);
|
||||||
|
@ -471,7 +471,7 @@ ACTOR Future<Reference<InitialDataDistribution>> getInitialDataDistribution( Dat
|
||||||
}
|
}
|
||||||
|
|
||||||
// a dummy shard at the end with no keys or servers makes life easier for trackInitialShards()
|
// a dummy shard at the end with no keys or servers makes life easier for trackInitialShards()
|
||||||
result->shards.push_back( ShardInfo(allKeys.end) );
|
result->shards.push_back( DDShardInfo(allKeys.end) );
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,8 @@ private:
|
||||||
void insert(Team team, KeyRange const& range);
|
void insert(Team team, KeyRange const& range);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShardInfo {
|
// DDShardInfo is so named to avoid link-time name collision with ShardInfo within the StorageServer
|
||||||
|
struct DDShardInfo {
|
||||||
Key key;
|
Key key;
|
||||||
vector<UID> primarySrc;
|
vector<UID> primarySrc;
|
||||||
vector<UID> remoteSrc;
|
vector<UID> remoteSrc;
|
||||||
|
@ -183,7 +184,7 @@ struct ShardInfo {
|
||||||
vector<UID> remoteDest;
|
vector<UID> remoteDest;
|
||||||
bool hasDest;
|
bool hasDest;
|
||||||
|
|
||||||
explicit ShardInfo(Key key) : key(key), hasDest(false) {}
|
explicit DDShardInfo(Key key) : key(key), hasDest(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InitialDataDistribution : ReferenceCounted<InitialDataDistribution> {
|
struct InitialDataDistribution : ReferenceCounted<InitialDataDistribution> {
|
||||||
|
@ -191,7 +192,7 @@ struct InitialDataDistribution : ReferenceCounted<InitialDataDistribution> {
|
||||||
vector<std::pair<StorageServerInterface, ProcessClass>> allServers;
|
vector<std::pair<StorageServerInterface, ProcessClass>> allServers;
|
||||||
std::set<vector<UID>> primaryTeams;
|
std::set<vector<UID>> primaryTeams;
|
||||||
std::set<vector<UID>> remoteTeams;
|
std::set<vector<UID>> remoteTeams;
|
||||||
vector<ShardInfo> shards;
|
vector<DDShardInfo> shards;
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<Void> dataDistribution(
|
Future<Void> dataDistribution(
|
||||||
|
|
|
@ -248,7 +248,6 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs) {
|
||||||
init( COMMIT_BATCHES_MEM_TO_TOTAL_MEM_SCALE_FACTOR, 10.0 );
|
init( COMMIT_BATCHES_MEM_TO_TOTAL_MEM_SCALE_FACTOR, 10.0 );
|
||||||
|
|
||||||
// Master Server
|
// Master Server
|
||||||
init( MASTER_LOGGING_DELAY, 1.0 );
|
|
||||||
// masterCommitter() in the master server will allow lower priority tasks (e.g. DataDistibution)
|
// masterCommitter() in the master server will allow lower priority tasks (e.g. DataDistibution)
|
||||||
// by delay()ing for this amount of time between accepted batches of TransactionRequests.
|
// by delay()ing for this amount of time between accepted batches of TransactionRequests.
|
||||||
init( COMMIT_SLEEP_TIME, 0.0001 ); if( randomize && BUGGIFY ) COMMIT_SLEEP_TIME = 0;
|
init( COMMIT_SLEEP_TIME, 0.0001 ); if( randomize && BUGGIFY ) COMMIT_SLEEP_TIME = 0;
|
||||||
|
@ -267,6 +266,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs) {
|
||||||
init( LAST_LIMITED_RATIO, 0.6 );
|
init( LAST_LIMITED_RATIO, 0.6 );
|
||||||
|
|
||||||
//Cluster Controller
|
//Cluster Controller
|
||||||
|
init( CLUSTER_CONTROLLER_LOGGING_DELAY, 5.0 );
|
||||||
init( MASTER_FAILURE_REACTION_TIME, 0.4 ); if( randomize && BUGGIFY ) MASTER_FAILURE_REACTION_TIME = 10.0;
|
init( MASTER_FAILURE_REACTION_TIME, 0.4 ); if( randomize && BUGGIFY ) MASTER_FAILURE_REACTION_TIME = 10.0;
|
||||||
init( MASTER_FAILURE_SLOPE_DURING_RECOVERY, 0.1 );
|
init( MASTER_FAILURE_SLOPE_DURING_RECOVERY, 0.1 );
|
||||||
init( WORKER_COORDINATION_PING_DELAY, 60 );
|
init( WORKER_COORDINATION_PING_DELAY, 60 );
|
||||||
|
@ -274,19 +274,21 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs) {
|
||||||
init( SHUTDOWN_TIMEOUT, 600 ); if( randomize && BUGGIFY ) SHUTDOWN_TIMEOUT = 60.0;
|
init( SHUTDOWN_TIMEOUT, 600 ); if( randomize && BUGGIFY ) SHUTDOWN_TIMEOUT = 60.0;
|
||||||
init( MASTER_SPIN_DELAY, 1.0 ); if( randomize && BUGGIFY ) MASTER_SPIN_DELAY = 10.0;
|
init( MASTER_SPIN_DELAY, 1.0 ); if( randomize && BUGGIFY ) MASTER_SPIN_DELAY = 10.0;
|
||||||
init( CC_CHANGE_DELAY, 0.1 );
|
init( CC_CHANGE_DELAY, 0.1 );
|
||||||
init( WAIT_FOR_GOOD_RECRUITMENT_DELAY, 0.1 );
|
init( CC_CLASS_DELAY, 0.01 );
|
||||||
|
init( WAIT_FOR_GOOD_RECRUITMENT_DELAY, 1.0 );
|
||||||
|
init( WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY, 5.0 );
|
||||||
init( ATTEMPT_RECRUITMENT_DELAY, 0.035 );
|
init( ATTEMPT_RECRUITMENT_DELAY, 0.035 );
|
||||||
init( WORKER_FAILURE_TIME, 1.0 ); if( randomize && BUGGIFY ) WORKER_FAILURE_TIME = 10.0;
|
init( WORKER_FAILURE_TIME, 1.0 ); if( randomize && BUGGIFY ) WORKER_FAILURE_TIME = 10.0;
|
||||||
init( CHECK_BETTER_MASTER_INTERVAL, 1.0 ); if( randomize && BUGGIFY ) CHECK_BETTER_MASTER_INTERVAL = 0.001;
|
init( CHECK_OUTSTANDING_INTERVAL, 0.5 ); if( randomize && BUGGIFY ) CHECK_OUTSTANDING_INTERVAL = 0.001;
|
||||||
init( VERSION_LAG_METRIC_INTERVAL, 0.5 ); if( randomize && BUGGIFY ) VERSION_LAG_METRIC_INTERVAL = 10.0;
|
init( VERSION_LAG_METRIC_INTERVAL, 0.5 ); if( randomize && BUGGIFY ) VERSION_LAG_METRIC_INTERVAL = 10.0;
|
||||||
init( MAX_VERSION_DIFFERENCE, 20 * VERSIONS_PER_SECOND );
|
init( MAX_VERSION_DIFFERENCE, 20 * VERSIONS_PER_SECOND );
|
||||||
|
|
||||||
init( INCOMPATIBLE_PEERS_LOGGING_INTERVAL, 600 ); if( randomize && BUGGIFY ) INCOMPATIBLE_PEERS_LOGGING_INTERVAL = 60.0;
|
init( INCOMPATIBLE_PEERS_LOGGING_INTERVAL, 600 ); if( randomize && BUGGIFY ) INCOMPATIBLE_PEERS_LOGGING_INTERVAL = 60.0;
|
||||||
init( EXPECTED_MASTER_FITNESS, ProcessClass::GoodFit );
|
init( EXPECTED_MASTER_FITNESS, ProcessClass::UnsetFit );
|
||||||
init( EXPECTED_TLOG_FITNESS, ProcessClass::GoodFit );
|
init( EXPECTED_TLOG_FITNESS, ProcessClass::UnsetFit );
|
||||||
init( EXPECTED_LOG_ROUTER_FITNESS, ProcessClass::GoodFit );
|
init( EXPECTED_LOG_ROUTER_FITNESS, ProcessClass::UnsetFit );
|
||||||
init( EXPECTED_PROXY_FITNESS, ProcessClass::GoodFit );
|
init( EXPECTED_PROXY_FITNESS, ProcessClass::UnsetFit );
|
||||||
init( EXPECTED_RESOLVER_FITNESS, ProcessClass::GoodFit );
|
init( EXPECTED_RESOLVER_FITNESS, ProcessClass::UnsetFit );
|
||||||
init( RECRUITMENT_TIMEOUT, 600 ); if( randomize && BUGGIFY ) RECRUITMENT_TIMEOUT = g_random->coinflip() ? 60.0 : 1.0;
|
init( RECRUITMENT_TIMEOUT, 600 ); if( randomize && BUGGIFY ) RECRUITMENT_TIMEOUT = g_random->coinflip() ? 60.0 : 1.0;
|
||||||
|
|
||||||
init( POLICY_RATING_TESTS, 200 ); if( randomize && BUGGIFY ) POLICY_RATING_TESTS = 20;
|
init( POLICY_RATING_TESTS, 200 ); if( randomize && BUGGIFY ) POLICY_RATING_TESTS = 20;
|
||||||
|
@ -343,7 +345,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs) {
|
||||||
//Storage Server
|
//Storage Server
|
||||||
init( STORAGE_LOGGING_DELAY, 5.0 );
|
init( STORAGE_LOGGING_DELAY, 5.0 );
|
||||||
init( STORAGE_SERVER_POLL_METRICS_DELAY, 1.0 );
|
init( STORAGE_SERVER_POLL_METRICS_DELAY, 1.0 );
|
||||||
init( FUTURE_VERSION_DELAY, 1.0 ); if( randomize && BUGGIFY ) FUTURE_VERSION_DELAY = 0.001;
|
init( FUTURE_VERSION_DELAY, 1.0 );
|
||||||
init( STORAGE_LIMIT_BYTES, 500000 );
|
init( STORAGE_LIMIT_BYTES, 500000 );
|
||||||
init( BUGGIFY_LIMIT_BYTES, 1000 );
|
init( BUGGIFY_LIMIT_BYTES, 1000 );
|
||||||
init( FETCH_BLOCK_BYTES, 2e6 );
|
init( FETCH_BLOCK_BYTES, 2e6 );
|
||||||
|
|
|
@ -192,7 +192,6 @@ public:
|
||||||
double PROXY_SPIN_DELAY;
|
double PROXY_SPIN_DELAY;
|
||||||
|
|
||||||
// Master Server
|
// Master Server
|
||||||
double MASTER_LOGGING_DELAY;
|
|
||||||
double COMMIT_SLEEP_TIME;
|
double COMMIT_SLEEP_TIME;
|
||||||
double MIN_BALANCE_TIME;
|
double MIN_BALANCE_TIME;
|
||||||
int64_t MIN_BALANCE_DIFFERENCE;
|
int64_t MIN_BALANCE_DIFFERENCE;
|
||||||
|
@ -208,6 +207,7 @@ public:
|
||||||
int64_t RESOLVER_STATE_MEMORY_LIMIT;
|
int64_t RESOLVER_STATE_MEMORY_LIMIT;
|
||||||
|
|
||||||
//Cluster Controller
|
//Cluster Controller
|
||||||
|
double CLUSTER_CONTROLLER_LOGGING_DELAY;
|
||||||
double MASTER_FAILURE_REACTION_TIME;
|
double MASTER_FAILURE_REACTION_TIME;
|
||||||
double MASTER_FAILURE_SLOPE_DURING_RECOVERY;
|
double MASTER_FAILURE_SLOPE_DURING_RECOVERY;
|
||||||
int WORKER_COORDINATION_PING_DELAY;
|
int WORKER_COORDINATION_PING_DELAY;
|
||||||
|
@ -215,10 +215,12 @@ public:
|
||||||
double SHUTDOWN_TIMEOUT;
|
double SHUTDOWN_TIMEOUT;
|
||||||
double MASTER_SPIN_DELAY;
|
double MASTER_SPIN_DELAY;
|
||||||
double CC_CHANGE_DELAY;
|
double CC_CHANGE_DELAY;
|
||||||
|
double CC_CLASS_DELAY;
|
||||||
double WAIT_FOR_GOOD_RECRUITMENT_DELAY;
|
double WAIT_FOR_GOOD_RECRUITMENT_DELAY;
|
||||||
|
double WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY;
|
||||||
double ATTEMPT_RECRUITMENT_DELAY;
|
double ATTEMPT_RECRUITMENT_DELAY;
|
||||||
double WORKER_FAILURE_TIME;
|
double WORKER_FAILURE_TIME;
|
||||||
double CHECK_BETTER_MASTER_INTERVAL;
|
double CHECK_OUTSTANDING_INTERVAL;
|
||||||
double INCOMPATIBLE_PEERS_LOGGING_INTERVAL;
|
double INCOMPATIBLE_PEERS_LOGGING_INTERVAL;
|
||||||
double VERSION_LAG_METRIC_INTERVAL;
|
double VERSION_LAG_METRIC_INTERVAL;
|
||||||
int64_t MAX_VERSION_DIFFERENCE;
|
int64_t MAX_VERSION_DIFFERENCE;
|
||||||
|
|
|
@ -82,7 +82,10 @@ ACTOR Future<Void> tryBecomeLeaderInternal( ServerCoordinators coordinators, Val
|
||||||
state bool iAmLeader = false;
|
state bool iAmLeader = false;
|
||||||
state UID prevChangeID;
|
state UID prevChangeID;
|
||||||
|
|
||||||
if( asyncPriorityInfo->get().processClassFitness > ProcessClass::UnsetFit || asyncPriorityInfo->get().dcFitness == ClusterControllerPriorityInfo::FitnessBad || asyncPriorityInfo->get().isExcluded ) {
|
|
||||||
|
if(asyncPriorityInfo->get().dcFitness == ClusterControllerPriorityInfo::FitnessBad || asyncPriorityInfo->get().dcFitness == ClusterControllerPriorityInfo::FitnessRemote || asyncPriorityInfo->get().isExcluded) {
|
||||||
|
Void _ = wait( delay(SERVER_KNOBS->WAIT_FOR_GOOD_REMOTE_RECRUITMENT_DELAY) );
|
||||||
|
} else if( asyncPriorityInfo->get().processClassFitness > ProcessClass::UnsetFit ) {
|
||||||
Void _ = wait( delay(SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY) );
|
Void _ = wait( delay(SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,17 +37,17 @@ struct LogRouterData {
|
||||||
struct TagData : NonCopyable, public ReferenceCounted<TagData> {
|
struct TagData : NonCopyable, public ReferenceCounted<TagData> {
|
||||||
std::deque<std::pair<Version, LengthPrefixedStringRef>> version_messages;
|
std::deque<std::pair<Version, LengthPrefixedStringRef>> version_messages;
|
||||||
Version popped;
|
Version popped;
|
||||||
Version knownCommittedVersion;
|
Version durableKnownCommittedVersion;
|
||||||
Tag tag;
|
Tag tag;
|
||||||
|
|
||||||
TagData( Tag tag, Version popped, Version knownCommittedVersion ) : tag(tag), popped(popped), knownCommittedVersion(knownCommittedVersion) {}
|
TagData( Tag tag, Version popped, Version durableKnownCommittedVersion ) : tag(tag), popped(popped), durableKnownCommittedVersion(durableKnownCommittedVersion) {}
|
||||||
|
|
||||||
TagData(TagData&& r) noexcept(true) : version_messages(std::move(r.version_messages)), tag(r.tag), popped(r.popped), knownCommittedVersion(r.knownCommittedVersion) {}
|
TagData(TagData&& r) noexcept(true) : version_messages(std::move(r.version_messages)), tag(r.tag), popped(r.popped), durableKnownCommittedVersion(r.durableKnownCommittedVersion) {}
|
||||||
void operator= (TagData&& r) noexcept(true) {
|
void operator= (TagData&& r) noexcept(true) {
|
||||||
version_messages = std::move(r.version_messages);
|
version_messages = std::move(r.version_messages);
|
||||||
tag = r.tag;
|
tag = r.tag;
|
||||||
popped = r.popped;
|
popped = r.popped;
|
||||||
knownCommittedVersion = r.knownCommittedVersion;
|
durableKnownCommittedVersion = r.durableKnownCommittedVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erase messages not needed to update *from* versions >= before (thus, messages with toversion <= before)
|
// Erase messages not needed to update *from* versions >= before (thus, messages with toversion <= before)
|
||||||
|
@ -79,6 +79,7 @@ struct LogRouterData {
|
||||||
NotifiedVersion version;
|
NotifiedVersion version;
|
||||||
NotifiedVersion minPopped;
|
NotifiedVersion minPopped;
|
||||||
Version startVersion;
|
Version startVersion;
|
||||||
|
Version minKnownCommittedVersion;
|
||||||
Deque<std::pair<Version, Standalone<VectorRef<uint8_t>>>> messageBlocks;
|
Deque<std::pair<Version, Standalone<VectorRef<uint8_t>>>> messageBlocks;
|
||||||
Tag routerTag;
|
Tag routerTag;
|
||||||
bool allowPops;
|
bool allowPops;
|
||||||
|
@ -101,7 +102,7 @@ struct LogRouterData {
|
||||||
return newTagData;
|
return newTagData;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogRouterData(UID dbgid, InitializeLogRouterRequest req) : dbgid(dbgid), routerTag(req.routerTag), logSystem(new AsyncVar<Reference<ILogSystem>>()), version(req.startVersion-1), minPopped(req.startVersion-1), startVersion(req.startVersion), allowPops(false) {
|
LogRouterData(UID dbgid, InitializeLogRouterRequest req) : dbgid(dbgid), routerTag(req.routerTag), logSystem(new AsyncVar<Reference<ILogSystem>>()), version(req.startVersion-1), minPopped(req.startVersion-1), startVersion(req.startVersion), allowPops(false), minKnownCommittedVersion(0) {
|
||||||
//setup just enough of a logSet to be able to call getPushLocations
|
//setup just enough of a logSet to be able to call getPushLocations
|
||||||
logSet.logServers.resize(req.tLogLocalities.size());
|
logSet.logServers.resize(req.tLogLocalities.size());
|
||||||
logSet.tLogPolicy = req.tLogPolicy;
|
logSet.tLogPolicy = req.tLogPolicy;
|
||||||
|
@ -162,7 +163,7 @@ void commitMessages( LogRouterData* self, Version version, const std::vector<Tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msgSize -= msg.message.size();
|
msgSize -= msg.message.size();
|
||||||
}
|
}
|
||||||
self->messageBlocks.push_back( std::make_pair(version, block) );
|
self->messageBlocks.push_back( std::make_pair(version, block) );
|
||||||
|
@ -193,6 +194,8 @@ ACTOR Future<Void> pullAsyncData( LogRouterData *self ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self->minKnownCommittedVersion = std::max(self->minKnownCommittedVersion, r->getMinKnownCommittedVersion());
|
||||||
|
|
||||||
state Version ver = 0;
|
state Version ver = 0;
|
||||||
state std::vector<TagsAndMessage> messages;
|
state std::vector<TagsAndMessage> messages;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -306,6 +309,7 @@ ACTOR Future<Void> logRouterPeekMessages( LogRouterData* self, TLogPeekRequest r
|
||||||
|
|
||||||
TLogPeekReply reply;
|
TLogPeekReply reply;
|
||||||
reply.maxKnownVersion = self->version.get();
|
reply.maxKnownVersion = self->version.get();
|
||||||
|
reply.minKnownCommittedVersion = self->minKnownCommittedVersion;
|
||||||
reply.messages = messages.toStringRef();
|
reply.messages = messages.toStringRef();
|
||||||
reply.popped = self->minPopped.get() >= self->startVersion ? self->minPopped.get() : 0;
|
reply.popped = self->minPopped.get() >= self->startVersion ? self->minPopped.get() : 0;
|
||||||
reply.end = endVersion;
|
reply.end = endVersion;
|
||||||
|
@ -318,10 +322,10 @@ ACTOR Future<Void> logRouterPeekMessages( LogRouterData* self, TLogPeekRequest r
|
||||||
ACTOR Future<Void> logRouterPop( LogRouterData* self, TLogPopRequest req ) {
|
ACTOR Future<Void> logRouterPop( LogRouterData* self, TLogPopRequest req ) {
|
||||||
auto tagData = self->getTagData(req.tag);
|
auto tagData = self->getTagData(req.tag);
|
||||||
if (!tagData) {
|
if (!tagData) {
|
||||||
tagData = self->createTagData(req.tag, req.to, req.knownCommittedVersion);
|
tagData = self->createTagData(req.tag, req.to, req.durableKnownCommittedVersion);
|
||||||
} else if (req.to > tagData->popped) {
|
} else if (req.to > tagData->popped) {
|
||||||
tagData->popped = req.to;
|
tagData->popped = req.to;
|
||||||
tagData->knownCommittedVersion = req.knownCommittedVersion;
|
tagData->durableKnownCommittedVersion = req.durableKnownCommittedVersion;
|
||||||
Void _ = wait(tagData->eraseMessagesBefore( req.to, self, TaskTLogPop ));
|
Void _ = wait(tagData->eraseMessagesBefore( req.to, self, TaskTLogPop ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +334,7 @@ ACTOR Future<Void> logRouterPop( LogRouterData* self, TLogPopRequest req ) {
|
||||||
for( auto it : self->tag_data ) {
|
for( auto it : self->tag_data ) {
|
||||||
if(it) {
|
if(it) {
|
||||||
minPopped = std::min( it->popped, minPopped );
|
minPopped = std::min( it->popped, minPopped );
|
||||||
minKnownCommittedVersion = std::min( it->knownCommittedVersion, minKnownCommittedVersion );
|
minKnownCommittedVersion = std::min( it->durableKnownCommittedVersion, minKnownCommittedVersion );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,10 +344,7 @@ ACTOR Future<Void> logRouterPop( LogRouterData* self, TLogPopRequest req ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(self->logSystem->get() && self->allowPops) {
|
if(self->logSystem->get() && self->allowPops) {
|
||||||
//The knownCommittedVersion might not be committed on the primary logs, so subtracting max_read_transaction_life_versions will ensure it is committed.
|
self->logSystem->get()->pop(std::min(minKnownCommittedVersion, self->minKnownCommittedVersion), self->routerTag);
|
||||||
//We then need to subtract max_read_transaction_life_versions again ensure we do not pop below the knownCommittedVersion of the primary logs.
|
|
||||||
//FIXME: if we get the true knownCommittedVersion when peeking from the primary logs we only need to subtract max_read_transaction_life_versions once.
|
|
||||||
self->logSystem->get()->pop(minKnownCommittedVersion - 2*SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS, self->routerTag);
|
|
||||||
}
|
}
|
||||||
req.reply.send(Void());
|
req.reply.send(Void());
|
||||||
self->minPopped.set(std::max(minPopped, self->minPopped.get()));
|
self->minPopped.set(std::max(minPopped, self->minPopped.get()));
|
||||||
|
|
|
@ -76,7 +76,8 @@ public:
|
||||||
void populateSatelliteTagLocations(int logRouterTags, int oldLogRouterTags) {
|
void populateSatelliteTagLocations(int logRouterTags, int oldLogRouterTags) {
|
||||||
satelliteTagLocations.clear();
|
satelliteTagLocations.clear();
|
||||||
satelliteTagLocations.resize(std::max(logRouterTags,oldLogRouterTags) + 1);
|
satelliteTagLocations.resize(std::max(logRouterTags,oldLogRouterTags) + 1);
|
||||||
|
|
||||||
|
std::map<int,int> server_usedBest;
|
||||||
std::set<std::pair<int,int>> used_servers;
|
std::set<std::pair<int,int>> used_servers;
|
||||||
for(int i = 0; i < tLogLocalities.size(); i++) {
|
for(int i = 0; i < tLogLocalities.size(); i++) {
|
||||||
used_servers.insert(std::make_pair(0,i));
|
used_servers.insert(std::make_pair(0,i));
|
||||||
|
@ -109,6 +110,16 @@ public:
|
||||||
for(auto& entry : resultEntries) {
|
for(auto& entry : resultEntries) {
|
||||||
resultPairs.push_back(*serverMap->getObject(entry));
|
resultPairs.push_back(*serverMap->getObject(entry));
|
||||||
}
|
}
|
||||||
|
int firstBestUsed = server_usedBest[resultPairs[0].second];
|
||||||
|
for(int i = 1; i < resultPairs.size(); i++) {
|
||||||
|
int thisBestUsed = server_usedBest[resultPairs[i].second];
|
||||||
|
if(thisBestUsed < firstBestUsed) {
|
||||||
|
std::swap(resultPairs[0], resultPairs[i]);
|
||||||
|
firstBestUsed = thisBestUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server_usedBest[resultPairs[0].second]++;
|
||||||
|
|
||||||
for(auto& res : resultPairs) {
|
for(auto& res : resultPairs) {
|
||||||
satelliteTagLocations[team].push_back(res.second);
|
satelliteTagLocations[team].push_back(res.second);
|
||||||
used_servers.erase(res);
|
used_servers.erase(res);
|
||||||
|
@ -126,20 +137,31 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkSatelliteTagLocations() {
|
void checkSatelliteTagLocations() {
|
||||||
|
std::vector<int> usedBest;
|
||||||
std::vector<int> used;
|
std::vector<int> used;
|
||||||
|
usedBest.resize(tLogLocalities.size());
|
||||||
used.resize(tLogLocalities.size());
|
used.resize(tLogLocalities.size());
|
||||||
for(auto team : satelliteTagLocations) {
|
for(auto team : satelliteTagLocations) {
|
||||||
|
usedBest[team[0]]++;
|
||||||
for(auto loc : team) {
|
for(auto loc : team) {
|
||||||
used[loc]++;
|
used[loc]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int minUsedBest = satelliteTagLocations.size();
|
||||||
|
int maxUsedBest = 0;
|
||||||
|
for(auto i : usedBest) {
|
||||||
|
minUsedBest = std::min(minUsedBest, i);
|
||||||
|
maxUsedBest = std::max(maxUsedBest, i);
|
||||||
|
}
|
||||||
|
|
||||||
int minUsed = satelliteTagLocations.size();
|
int minUsed = satelliteTagLocations.size();
|
||||||
int maxUsed = 0;
|
int maxUsed = 0;
|
||||||
for(auto i : used) {
|
for(auto i : used) {
|
||||||
minUsed = std::min(minUsed, i);
|
minUsed = std::min(minUsed, i);
|
||||||
maxUsed = std::max(maxUsed, i);
|
maxUsed = std::max(maxUsed, i);
|
||||||
}
|
}
|
||||||
TraceEvent(maxUsed - minUsed > 1 ? (g_network->isSimulated() ? SevError : SevWarnAlways) : SevInfo, "CheckSatelliteTagLocations").detail("MinUsed", minUsed).detail("MaxUsed", maxUsed);
|
TraceEvent(((maxUsed - minUsed > 1) || (maxUsedBest - minUsedBest > 1)) ? (g_network->isSimulated() ? SevError : SevWarnAlways) : SevInfo, "CheckSatelliteTagLocations").detail("MinUsed", minUsed).detail("MaxUsed", maxUsed).detail("MinUsedBest", minUsedBest).detail("MaxUsedBest", maxUsedBest);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bestLocationFor( Tag tag ) {
|
int bestLocationFor( Tag tag ) {
|
||||||
|
@ -314,6 +336,8 @@ struct ILogSystem {
|
||||||
// Returns the maximum version known to have been pushed (not necessarily durably) into the log system (0 is always a possible result!)
|
// Returns the maximum version known to have been pushed (not necessarily durably) into the log system (0 is always a possible result!)
|
||||||
virtual Version getMaxKnownVersion() { return 0; }
|
virtual Version getMaxKnownVersion() { return 0; }
|
||||||
|
|
||||||
|
virtual Version getMinKnownCommittedVersion() = 0;
|
||||||
|
|
||||||
virtual void addref() = 0;
|
virtual void addref() = 0;
|
||||||
|
|
||||||
virtual void delref() = 0;
|
virtual void delref() = 0;
|
||||||
|
@ -358,6 +382,7 @@ struct ILogSystem {
|
||||||
virtual bool isExhausted();
|
virtual bool isExhausted();
|
||||||
virtual LogMessageVersion version();
|
virtual LogMessageVersion version();
|
||||||
virtual Version popped();
|
virtual Version popped();
|
||||||
|
virtual Version getMinKnownCommittedVersion();
|
||||||
|
|
||||||
virtual void addref() {
|
virtual void addref() {
|
||||||
ReferenceCounted<ServerPeekCursor>::addref();
|
ReferenceCounted<ServerPeekCursor>::addref();
|
||||||
|
@ -411,6 +436,7 @@ struct ILogSystem {
|
||||||
virtual bool isExhausted();
|
virtual bool isExhausted();
|
||||||
virtual LogMessageVersion version();
|
virtual LogMessageVersion version();
|
||||||
virtual Version popped();
|
virtual Version popped();
|
||||||
|
virtual Version getMinKnownCommittedVersion();
|
||||||
|
|
||||||
virtual void addref() {
|
virtual void addref() {
|
||||||
ReferenceCounted<MergedPeekCursor>::addref();
|
ReferenceCounted<MergedPeekCursor>::addref();
|
||||||
|
@ -455,6 +481,7 @@ struct ILogSystem {
|
||||||
virtual bool isExhausted();
|
virtual bool isExhausted();
|
||||||
virtual LogMessageVersion version();
|
virtual LogMessageVersion version();
|
||||||
virtual Version popped();
|
virtual Version popped();
|
||||||
|
virtual Version getMinKnownCommittedVersion();
|
||||||
|
|
||||||
virtual void addref() {
|
virtual void addref() {
|
||||||
ReferenceCounted<SetPeekCursor>::addref();
|
ReferenceCounted<SetPeekCursor>::addref();
|
||||||
|
@ -488,6 +515,7 @@ struct ILogSystem {
|
||||||
virtual bool isExhausted();
|
virtual bool isExhausted();
|
||||||
virtual LogMessageVersion version();
|
virtual LogMessageVersion version();
|
||||||
virtual Version popped();
|
virtual Version popped();
|
||||||
|
virtual Version getMinKnownCommittedVersion();
|
||||||
|
|
||||||
virtual void addref() {
|
virtual void addref() {
|
||||||
ReferenceCounted<MultiCursor>::addref();
|
ReferenceCounted<MultiCursor>::addref();
|
||||||
|
@ -516,7 +544,7 @@ struct ILogSystem {
|
||||||
// Never returns normally, but throws an error if the subsystem stops working
|
// Never returns normally, but throws an error if the subsystem stops working
|
||||||
|
|
||||||
//Future<Void> push( UID bundle, int64_t seq, VectorRef<TaggedMessageRef> messages );
|
//Future<Void> push( UID bundle, int64_t seq, VectorRef<TaggedMessageRef> messages );
|
||||||
virtual Future<Void> push( Version prevVersion, Version version, Version knownCommittedVersion, struct LogPushData& data, Optional<UID> debugID = Optional<UID>() ) = 0;
|
virtual Future<Version> push( Version prevVersion, Version version, Version knownCommittedVersion, Version minKnownCommittedVersion, struct LogPushData& data, Optional<UID> debugID = Optional<UID>() ) = 0;
|
||||||
// Waits for the version number of the bundle (in this epoch) to be prevVersion (i.e. for all pushes ordered earlier)
|
// Waits for the version number of the bundle (in this epoch) to be prevVersion (i.e. for all pushes ordered earlier)
|
||||||
// Puts the given messages into the bundle, each with the given tags, and with message versions (version, 0) - (version, N)
|
// Puts the given messages into the bundle, each with the given tags, and with message versions (version, 0) - (version, N)
|
||||||
// Changes the version number of the bundle to be version (unblocking the next push)
|
// Changes the version number of the bundle to be version (unblocking the next push)
|
||||||
|
@ -535,7 +563,7 @@ struct ILogSystem {
|
||||||
// Same contract as peek(), but blocks until the preferred log server(s) for the given tag are available (and is correspondingly less expensive)
|
// Same contract as peek(), but blocks until the preferred log server(s) for the given tag are available (and is correspondingly less expensive)
|
||||||
|
|
||||||
virtual Reference<IPeekCursor> peekLogRouter( UID dbgid, Version begin, Tag tag ) = 0;
|
virtual Reference<IPeekCursor> peekLogRouter( UID dbgid, Version begin, Tag tag ) = 0;
|
||||||
// Same contract as peek(), but can only peek from the logs elected in the same generation.
|
// Same contract as peek(), but can only peek from the logs elected in the same generation.
|
||||||
// If the preferred log server is down, a different log from the same generation will merge results locally before sending them to the log router.
|
// If the preferred log server is down, a different log from the same generation will merge results locally before sending them to the log router.
|
||||||
|
|
||||||
virtual void pop( Version upTo, Tag tag, Version knownCommittedVersion = 0, int8_t popLocality = tagLocalityInvalid ) = 0;
|
virtual void pop( Version upTo, Tag tag, Version knownCommittedVersion = 0, int8_t popLocality = tagLocalityInvalid ) = 0;
|
||||||
|
@ -548,8 +576,8 @@ struct ILogSystem {
|
||||||
virtual Future<Void> endEpoch() = 0;
|
virtual Future<Void> endEpoch() = 0;
|
||||||
// Ends the current epoch without starting a new one
|
// Ends the current epoch without starting a new one
|
||||||
|
|
||||||
static Reference<ILogSystem> fromServerDBInfo( UID const& dbgid, struct ServerDBInfo const& db, bool usePreviousEpochEnd = false, Optional<PromiseStream<Future<Void>>> addActor = Optional<PromiseStream<Future<Void>>>() );
|
static Reference<ILogSystem> fromServerDBInfo( UID const& dbgid, struct ServerDBInfo const& db, bool useRecoveredAt = false, Optional<PromiseStream<Future<Void>>> addActor = Optional<PromiseStream<Future<Void>>>() );
|
||||||
static Reference<ILogSystem> fromLogSystemConfig( UID const& dbgid, struct LocalityData const&, struct LogSystemConfig const&, bool excludeRemote = false, bool usePreviousEpochEnd = false, Optional<PromiseStream<Future<Void>>> addActor = Optional<PromiseStream<Future<Void>>>() );
|
static Reference<ILogSystem> fromLogSystemConfig( UID const& dbgid, struct LocalityData const&, struct LogSystemConfig const&, bool excludeRemote = false, bool useRecoveredAt = false, Optional<PromiseStream<Future<Void>>> addActor = Optional<PromiseStream<Future<Void>>>() );
|
||||||
// Constructs a new ILogSystem implementation from the given ServerDBInfo/LogSystemConfig. Might return a null reference if there isn't a fully recovered log system available.
|
// Constructs a new ILogSystem implementation from the given ServerDBInfo/LogSystemConfig. Might return a null reference if there isn't a fully recovered log system available.
|
||||||
// The caller can peek() the returned log system and can push() if it has version numbers reserved for it and prevVersions
|
// The caller can peek() the returned log system and can push() if it has version numbers reserved for it and prevVersions
|
||||||
|
|
||||||
|
|
|
@ -156,11 +156,11 @@ struct LogSystemConfig {
|
||||||
int32_t expectedLogSets;
|
int32_t expectedLogSets;
|
||||||
UID recruitmentID;
|
UID recruitmentID;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
Optional<Version> previousEpochEndVersion;
|
Optional<Version> recoveredAt;
|
||||||
|
|
||||||
LogSystemConfig() : logSystemType(0), logRouterTags(0), expectedLogSets(0), stopped(false) {}
|
LogSystemConfig() : logSystemType(0), logRouterTags(0), expectedLogSets(0), stopped(false) {}
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
return format("type: %d oldGenerations: %d tags: %d %s", logSystemType, oldTLogs.size(), logRouterTags, describe(tLogs).c_str());
|
return format("type: %d oldGenerations: %d tags: %d %s", logSystemType, oldTLogs.size(), logRouterTags, describe(tLogs).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ struct LogSystemConfig {
|
||||||
bool operator == ( const LogSystemConfig& rhs ) const { return isEqual(rhs); }
|
bool operator == ( const LogSystemConfig& rhs ) const { return isEqual(rhs); }
|
||||||
|
|
||||||
bool isEqual(LogSystemConfig const& r) const {
|
bool isEqual(LogSystemConfig const& r) const {
|
||||||
return logSystemType == r.logSystemType && tLogs == r.tLogs && oldTLogs == r.oldTLogs && expectedLogSets == r.expectedLogSets && logRouterTags == r.logRouterTags && recruitmentID == r.recruitmentID && stopped == r.stopped && previousEpochEndVersion == r.previousEpochEndVersion;
|
return logSystemType == r.logSystemType && tLogs == r.tLogs && oldTLogs == r.oldTLogs && expectedLogSets == r.expectedLogSets && logRouterTags == r.logRouterTags && recruitmentID == r.recruitmentID && stopped == r.stopped && recoveredAt == r.recoveredAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEqualIds(LogSystemConfig const& r) const {
|
bool isEqualIds(LogSystemConfig const& r) const {
|
||||||
|
@ -248,7 +248,7 @@ struct LogSystemConfig {
|
||||||
|
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize( Ar& ar ) {
|
void serialize( Ar& ar ) {
|
||||||
ar & logSystemType & tLogs & logRouterTags & oldTLogs & expectedLogSets & recruitmentID & stopped & previousEpochEndVersion;
|
ar & logSystemType & tLogs & logRouterTags & oldTLogs & expectedLogSets & recruitmentID & stopped & recoveredAt;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
ILogSystem::ServerPeekCursor::ServerPeekCursor( Reference<AsyncVar<OptionalInterface<TLogInterface>>> const& interf, Tag tag, Version begin, Version end, bool returnIfBlocked, bool parallelGetMore )
|
ILogSystem::ServerPeekCursor::ServerPeekCursor( Reference<AsyncVar<OptionalInterface<TLogInterface>>> const& interf, Tag tag, Version begin, Version end, bool returnIfBlocked, bool parallelGetMore )
|
||||||
: interf(interf), tag(tag), messageVersion(begin), end(end), hasMsg(false), rd(results.arena, results.messages, Unversioned()), randomID(g_random->randomUniqueID()), poppedVersion(0), returnIfBlocked(returnIfBlocked), sequence(0), parallelGetMore(parallelGetMore) {
|
: interf(interf), tag(tag), messageVersion(begin), end(end), hasMsg(false), rd(results.arena, results.messages, Unversioned()), randomID(g_random->randomUniqueID()), poppedVersion(0), returnIfBlocked(returnIfBlocked), sequence(0), parallelGetMore(parallelGetMore) {
|
||||||
this->results.maxKnownVersion = 0;
|
this->results.maxKnownVersion = 0;
|
||||||
|
this->results.minKnownCommittedVersion = 0;
|
||||||
//TraceEvent("SPC_Starting", randomID).detail("Tag", tag.toString()).detail("Begin", begin).detail("End", end).backtrace();
|
//TraceEvent("SPC_Starting", randomID).detail("Tag", tag.toString()).detail("Begin", begin).detail("End", end).backtrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ ILogSystem::ServerPeekCursor::ServerPeekCursor( TLogPeekReply const& results, Lo
|
||||||
{
|
{
|
||||||
//TraceEvent("SPC_Clone", randomID);
|
//TraceEvent("SPC_Clone", randomID);
|
||||||
this->results.maxKnownVersion = 0;
|
this->results.maxKnownVersion = 0;
|
||||||
|
this->results.minKnownCommittedVersion = 0;
|
||||||
if(hasMsg)
|
if(hasMsg)
|
||||||
nextMessage();
|
nextMessage();
|
||||||
|
|
||||||
|
@ -141,6 +143,7 @@ ACTOR Future<Void> serverPeekParallelGetMore( ILogSystem::ServerPeekCursor* self
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
state Version expectedBegin = self->messageVersion.version;
|
||||||
try {
|
try {
|
||||||
while(self->futureResults.size() < SERVER_KNOBS->PARALLEL_GET_MORE_REQUESTS && self->interf->get().present()) {
|
while(self->futureResults.size() < SERVER_KNOBS->PARALLEL_GET_MORE_REQUESTS && self->interf->get().present()) {
|
||||||
self->futureResults.push_back( brokenPromiseToNever( self->interf->get().interf().peekMessages.getReply(TLogPeekRequest(self->messageVersion.version,self->tag,self->returnIfBlocked, std::make_pair(self->randomID, self->sequence++)), taskID) ) );
|
self->futureResults.push_back( brokenPromiseToNever( self->interf->get().interf().peekMessages.getReply(TLogPeekRequest(self->messageVersion.version,self->tag,self->returnIfBlocked, std::make_pair(self->randomID, self->sequence++)), taskID) ) );
|
||||||
|
@ -148,6 +151,10 @@ ACTOR Future<Void> serverPeekParallelGetMore( ILogSystem::ServerPeekCursor* self
|
||||||
|
|
||||||
choose {
|
choose {
|
||||||
when( TLogPeekReply res = wait( self->interf->get().present() ? self->futureResults.front() : Never() ) ) {
|
when( TLogPeekReply res = wait( self->interf->get().present() ? self->futureResults.front() : Never() ) ) {
|
||||||
|
if(res.begin.get() != expectedBegin) {
|
||||||
|
throw timed_out();
|
||||||
|
}
|
||||||
|
expectedBegin = res.end;
|
||||||
self->futureResults.pop_front();
|
self->futureResults.pop_front();
|
||||||
self->results = res;
|
self->results = res;
|
||||||
if(res.popped.present())
|
if(res.popped.present())
|
||||||
|
@ -254,6 +261,8 @@ bool ILogSystem::ServerPeekCursor::isExhausted() {
|
||||||
|
|
||||||
LogMessageVersion ILogSystem::ServerPeekCursor::version() { return messageVersion; } // Call only after nextMessage(). The sequence of the current message, or results.end if nextMessage() has returned false.
|
LogMessageVersion ILogSystem::ServerPeekCursor::version() { return messageVersion; } // Call only after nextMessage(). The sequence of the current message, or results.end if nextMessage() has returned false.
|
||||||
|
|
||||||
|
Version ILogSystem::ServerPeekCursor::getMinKnownCommittedVersion() { return results.minKnownCommittedVersion; }
|
||||||
|
|
||||||
Version ILogSystem::ServerPeekCursor::popped() { return poppedVersion; }
|
Version ILogSystem::ServerPeekCursor::popped() { return poppedVersion; }
|
||||||
|
|
||||||
ILogSystem::MergedPeekCursor::MergedPeekCursor( vector< Reference<ILogSystem::IPeekCursor> > const& serverCursors, Version begin, bool collectTags )
|
ILogSystem::MergedPeekCursor::MergedPeekCursor( vector< Reference<ILogSystem::IPeekCursor> > const& serverCursors, Version begin, bool collectTags )
|
||||||
|
@ -485,6 +494,10 @@ bool ILogSystem::MergedPeekCursor::isExhausted() {
|
||||||
|
|
||||||
LogMessageVersion ILogSystem::MergedPeekCursor::version() { return messageVersion; }
|
LogMessageVersion ILogSystem::MergedPeekCursor::version() { return messageVersion; }
|
||||||
|
|
||||||
|
Version ILogSystem::MergedPeekCursor::getMinKnownCommittedVersion() {
|
||||||
|
return serverCursors[currentCursor]->getMinKnownCommittedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
Version ILogSystem::MergedPeekCursor::popped() {
|
Version ILogSystem::MergedPeekCursor::popped() {
|
||||||
Version poppedVersion = 0;
|
Version poppedVersion = 0;
|
||||||
for (auto& c : serverCursors)
|
for (auto& c : serverCursors)
|
||||||
|
@ -776,6 +789,10 @@ bool ILogSystem::SetPeekCursor::isExhausted() {
|
||||||
|
|
||||||
LogMessageVersion ILogSystem::SetPeekCursor::version() { return messageVersion; }
|
LogMessageVersion ILogSystem::SetPeekCursor::version() { return messageVersion; }
|
||||||
|
|
||||||
|
Version ILogSystem::SetPeekCursor::getMinKnownCommittedVersion() {
|
||||||
|
return serverCursors[currentSet][currentCursor]->getMinKnownCommittedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
Version ILogSystem::SetPeekCursor::popped() {
|
Version ILogSystem::SetPeekCursor::popped() {
|
||||||
Version poppedVersion = 0;
|
Version poppedVersion = 0;
|
||||||
for (auto& cursors : serverCursors) {
|
for (auto& cursors : serverCursors) {
|
||||||
|
@ -858,6 +875,10 @@ LogMessageVersion ILogSystem::MultiCursor::version() {
|
||||||
return cursors.back()->version();
|
return cursors.back()->version();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Version ILogSystem::MultiCursor::getMinKnownCommittedVersion() {
|
||||||
|
return cursors.back()->getMinKnownCommittedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
Version ILogSystem::MultiCursor::popped() {
|
Version ILogSystem::MultiCursor::popped() {
|
||||||
return std::max(poppedVersion, cursors.back()->popped());
|
return std::max(poppedVersion, cursors.back()->popped());
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,8 @@ struct ProxyCommitData {
|
||||||
LogSystemDiskQueueAdapter* logAdapter;
|
LogSystemDiskQueueAdapter* logAdapter;
|
||||||
Reference<ILogSystem> logSystem;
|
Reference<ILogSystem> logSystem;
|
||||||
IKeyValueStore* txnStateStore;
|
IKeyValueStore* txnStateStore;
|
||||||
NotifiedVersion committedVersion; // Provided that this recovery has succeeded or will succeed, this version is fully committed (durable)
|
NotifiedVersion committedVersion; // Provided that this recovery has succeeded or will succeed, this version is fully committed (durable)
|
||||||
|
Version minKnownCommittedVersion; // No version smaller than this one will be used as the known committed version during recovery
|
||||||
Version version; // The version at which txnStateStore is up to date
|
Version version; // The version at which txnStateStore is up to date
|
||||||
Promise<Void> validState; // Set once txnStateStore and version are valid
|
Promise<Void> validState; // Set once txnStateStore and version are valid
|
||||||
double lastVersionTime;
|
double lastVersionTime;
|
||||||
|
@ -223,9 +224,9 @@ struct ProxyCommitData {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyCommitData(UID dbgid, MasterInterface master, RequestStream<GetReadVersionRequest> getConsistentReadVersion, Version recoveryTransactionVersion, RequestStream<CommitTransactionRequest> commit, Reference<AsyncVar<ServerDBInfo>> db, bool firstProxy)
|
ProxyCommitData(UID dbgid, MasterInterface master, RequestStream<GetReadVersionRequest> getConsistentReadVersion, Version recoveryTransactionVersion, RequestStream<CommitTransactionRequest> commit, Reference<AsyncVar<ServerDBInfo>> db, bool firstProxy)
|
||||||
: dbgid(dbgid), stats(dbgid, &version, &committedVersion, &commitBatchesMemBytesCount), master(master),
|
: dbgid(dbgid), stats(dbgid, &version, &committedVersion, &commitBatchesMemBytesCount), master(master),
|
||||||
logAdapter(NULL), txnStateStore(NULL),
|
logAdapter(NULL), txnStateStore(NULL),
|
||||||
committedVersion(recoveryTransactionVersion), version(0),
|
committedVersion(recoveryTransactionVersion), version(0), minKnownCommittedVersion(0),
|
||||||
lastVersionTime(0), commitVersionRequestNumber(1), mostRecentProcessedRequestNumber(0),
|
lastVersionTime(0), commitVersionRequestNumber(1), mostRecentProcessedRequestNumber(0),
|
||||||
getConsistentReadVersion(getConsistentReadVersion), commit(commit), lastCoalesceTime(0),
|
getConsistentReadVersion(getConsistentReadVersion), commit(commit), lastCoalesceTime(0),
|
||||||
localCommitBatchesStarted(0), locked(false), firstProxy(firstProxy),
|
localCommitBatchesStarted(0), locked(false), firstProxy(firstProxy),
|
||||||
|
@ -794,8 +795,7 @@ ACTOR Future<Void> commitBatch(
|
||||||
if ( prevVersion && commitVersion - prevVersion < SERVER_KNOBS->MAX_VERSIONS_IN_FLIGHT/2 )
|
if ( prevVersion && commitVersion - prevVersion < SERVER_KNOBS->MAX_VERSIONS_IN_FLIGHT/2 )
|
||||||
debug_advanceMaxCommittedVersion(UID(), commitVersion);
|
debug_advanceMaxCommittedVersion(UID(), commitVersion);
|
||||||
|
|
||||||
Future<Void> loggingComplete = self->logSystem->push( prevVersion, commitVersion, self->committedVersion.get(), toCommit, debugID )
|
Future<Version> loggingComplete = self->logSystem->push( prevVersion, commitVersion, self->committedVersion.get(), self->minKnownCommittedVersion, toCommit, debugID );
|
||||||
|| self->committedVersion.whenAtLeast( commitVersion+1 );
|
|
||||||
|
|
||||||
if (!forceRecovery) {
|
if (!forceRecovery) {
|
||||||
ASSERT(self->latestLocalCommitBatchLogging.get() == localBatchNumber-1);
|
ASSERT(self->latestLocalCommitBatchLogging.get() == localBatchNumber-1);
|
||||||
|
@ -803,12 +803,25 @@ ACTOR Future<Void> commitBatch(
|
||||||
}
|
}
|
||||||
|
|
||||||
/////// Phase 4: Logging (network bound; pipelined up to MAX_READ_TRANSACTION_LIFE_VERSIONS (limited by loop above))
|
/////// Phase 4: Logging (network bound; pipelined up to MAX_READ_TRANSACTION_LIFE_VERSIONS (limited by loop above))
|
||||||
Void _ = wait(loggingComplete);
|
|
||||||
|
try {
|
||||||
|
choose {
|
||||||
|
when(Version ver = wait(loggingComplete)) {
|
||||||
|
self->minKnownCommittedVersion = std::max(self->minKnownCommittedVersion, ver);
|
||||||
|
}
|
||||||
|
when(Void _ = wait(self->committedVersion.whenAtLeast( commitVersion+1 ))) {}
|
||||||
|
}
|
||||||
|
} catch(Error &e) {
|
||||||
|
if(e.code() == error_code_broken_promise) {
|
||||||
|
throw master_tlog_failed();
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
Void _ = wait(yield());
|
Void _ = wait(yield());
|
||||||
|
|
||||||
self->logSystem->pop(msg.popTo, txsTag);
|
self->logSystem->pop(msg.popTo, txsTag);
|
||||||
|
|
||||||
/////// Phase 5: Replies (CPU bound; no particular order required, though ordered execution would be best for latency)
|
/////// Phase 5: Replies (CPU bound; no particular order required, though ordered execution would be best for latency)
|
||||||
if ( prevVersion && commitVersion - prevVersion < SERVER_KNOBS->MAX_VERSIONS_IN_FLIGHT/2 )
|
if ( prevVersion && commitVersion - prevVersion < SERVER_KNOBS->MAX_VERSIONS_IN_FLIGHT/2 )
|
||||||
debug_advanceMinCommittedVersion(UID(), commitVersion);
|
debug_advanceMinCommittedVersion(UID(), commitVersion);
|
||||||
|
|
||||||
|
@ -826,7 +839,7 @@ ACTOR Future<Void> commitBatch(
|
||||||
if( commitVersion > self->committedVersion.get() ) {
|
if( commitVersion > self->committedVersion.get() ) {
|
||||||
self->locked = lockedAfter;
|
self->locked = lockedAfter;
|
||||||
self->committedVersion.set(commitVersion);
|
self->committedVersion.set(commitVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceRecovery) {
|
if (forceRecovery) {
|
||||||
TraceEvent(SevWarn, "RestartingTxnSubsystem", self->dbgid).detail("Stage", "ProxyShutdown");
|
TraceEvent(SevWarn, "RestartingTxnSubsystem", self->dbgid).detail("Stage", "ProxyShutdown");
|
||||||
|
@ -1170,7 +1183,7 @@ ACTOR Future<Void> masterProxyServerCore(
|
||||||
state Future<Void> lastCommitComplete = Void();
|
state Future<Void> lastCommitComplete = Void();
|
||||||
|
|
||||||
state PromiseStream<Future<Void>> addActor;
|
state PromiseStream<Future<Void>> addActor;
|
||||||
state Future<Void> onError = actorCollection(addActor.getFuture());
|
state Future<Void> onError = transformError( actorCollection(addActor.getFuture()), broken_promise(), master_tlog_failed() );
|
||||||
state double lastCommit = 0;
|
state double lastCommit = 0;
|
||||||
state std::set<Sequence> txnSequences;
|
state std::set<Sequence> txnSequences;
|
||||||
state Sequence maxSequence = std::numeric_limits<Sequence>::max();
|
state Sequence maxSequence = std::numeric_limits<Sequence>::max();
|
||||||
|
|
|
@ -952,6 +952,7 @@ namespace oldTLog {
|
||||||
|
|
||||||
TLogPeekReply reply;
|
TLogPeekReply reply;
|
||||||
reply.maxKnownVersion = logData->version.get();
|
reply.maxKnownVersion = logData->version.get();
|
||||||
|
reply.minKnownCommittedVersion = 0;
|
||||||
if(poppedVer > req.begin) {
|
if(poppedVer > req.begin) {
|
||||||
reply.popped = poppedVer;
|
reply.popped = poppedVer;
|
||||||
reply.end = poppedVer;
|
reply.end = poppedVer;
|
||||||
|
@ -974,6 +975,7 @@ namespace oldTLog {
|
||||||
} else {
|
} else {
|
||||||
sequenceData.send(reply.end);
|
sequenceData.send(reply.end);
|
||||||
}
|
}
|
||||||
|
reply.begin = req.begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.reply.send( reply );
|
req.reply.send( reply );
|
||||||
|
|
|
@ -68,13 +68,13 @@ ACTOR Future<WorkerInterface> getMasterWorker( Database cx, Reference<AsyncVar<S
|
||||||
ACTOR Future<int64_t> getDataInFlight( Database cx, WorkerInterface masterWorker ) {
|
ACTOR Future<int64_t> getDataInFlight( Database cx, WorkerInterface masterWorker ) {
|
||||||
try {
|
try {
|
||||||
TraceEvent("DataInFlight").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
TraceEvent("DataInFlight").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
||||||
Standalone<StringRef> md = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
TraceEventFields md = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
||||||
EventLogRequest( StringRef( cx->dbName.toString() + "/TotalDataInFlight" ) ) ), 1.0 ) );
|
EventLogRequest( StringRef( cx->dbName.toString() + "/TotalDataInFlight" ) ) ), 1.0 ) );
|
||||||
int64_t dataInFlight;
|
int64_t dataInFlight;
|
||||||
sscanf(extractAttribute(md.toString(), "TotalBytes").c_str(), "%lld", &dataInFlight);
|
sscanf(md.getValue("TotalBytes").c_str(), "%lld", &dataInFlight);
|
||||||
return dataInFlight;
|
return dataInFlight;
|
||||||
} catch( Error &e ) {
|
} catch( Error &e ) {
|
||||||
TraceEvent("QuietDatabaseFailure", masterWorker.id()).detail("Reason", "Failed to extract DataInFlight");
|
TraceEvent("QuietDatabaseFailure", masterWorker.id()).detail("Reason", "Failed to extract DataInFlight").error(e);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +89,13 @@ ACTOR Future<int64_t> getDataInFlight( Database cx, Reference<AsyncVar<ServerDBI
|
||||||
}
|
}
|
||||||
|
|
||||||
//Computes the queue size for storage servers and tlogs using the bytesInput and bytesDurable attributes
|
//Computes the queue size for storage servers and tlogs using the bytesInput and bytesDurable attributes
|
||||||
int64_t getQueueSize( Standalone<StringRef> md ) {
|
int64_t getQueueSize( TraceEventFields md ) {
|
||||||
double inputRate, durableRate;
|
double inputRate, durableRate;
|
||||||
double inputRoughness, durableRoughness;
|
double inputRoughness, durableRoughness;
|
||||||
int64_t inputBytes, durableBytes;
|
int64_t inputBytes, durableBytes;
|
||||||
|
|
||||||
sscanf(extractAttribute(md.toString(), "BytesInput").c_str(), "%lf %lf %lld", &inputRate, &inputRoughness, &inputBytes);
|
sscanf(md.getValue("BytesInput").c_str(), "%lf %lf %lld", &inputRate, &inputRoughness, &inputBytes);
|
||||||
sscanf(extractAttribute(md.toString(), "BytesDurable").c_str(), "%lf %lf %lld", &durableRate, &durableRoughness, &durableBytes);
|
sscanf(md.getValue("BytesDurable").c_str(), "%lf %lf %lld", &durableRate, &durableRoughness, &durableBytes);
|
||||||
|
|
||||||
return inputBytes - durableBytes;
|
return inputBytes - durableBytes;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ ACTOR Future<int64_t> getMaxTLogQueueSize( Database cx, Reference<AsyncVar<Serve
|
||||||
workersMap[worker.first.address()] = worker.first;
|
workersMap[worker.first.address()] = worker.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
state std::vector<Future<Standalone<StringRef>>> messages;
|
state std::vector<Future<TraceEventFields>> messages;
|
||||||
state std::vector<TLogInterface> tlogs = dbInfo->get().logSystemConfig.allPresentLogs();
|
state std::vector<TLogInterface> tlogs = dbInfo->get().logSystemConfig.allPresentLogs();
|
||||||
for(int i = 0; i < tlogs.size(); i++) {
|
for(int i = 0; i < tlogs.size(); i++) {
|
||||||
auto itr = workersMap.find(tlogs[i].address());
|
auto itr = workersMap.find(tlogs[i].address());
|
||||||
|
@ -182,7 +182,7 @@ ACTOR Future<int64_t> getMaxStorageServerQueueSize( Database cx, Reference<Async
|
||||||
workersMap[worker.first.address()] = worker.first;
|
workersMap[worker.first.address()] = worker.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
state std::vector<Future<Standalone<StringRef>>> messages;
|
state std::vector<Future<TraceEventFields>> messages;
|
||||||
for(int i = 0; i < servers.size(); i++) {
|
for(int i = 0; i < servers.size(); i++) {
|
||||||
auto itr = workersMap.find(servers[i].address());
|
auto itr = workersMap.find(servers[i].address());
|
||||||
if(itr == workersMap.end()) {
|
if(itr == workersMap.end()) {
|
||||||
|
@ -224,17 +224,17 @@ ACTOR Future<int64_t> getDataDistributionQueueSize( Database cx, WorkerInterface
|
||||||
try {
|
try {
|
||||||
TraceEvent("DataDistributionQueueSize").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
TraceEvent("DataDistributionQueueSize").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
||||||
|
|
||||||
Standalone<StringRef> movingDataMessage = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
TraceEventFields movingDataMessage = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
||||||
EventLogRequest( StringRef( cx->dbName.toString() + "/MovingData") ) ), 1.0 ) );
|
EventLogRequest( StringRef( cx->dbName.toString() + "/MovingData") ) ), 1.0 ) );
|
||||||
|
|
||||||
TraceEvent("DataDistributionQueueSize").detail("Database", printable(cx->dbName)).detail("Stage", "GotString").detail("Result", printable(movingDataMessage)).detail("TrackLatest", printable( StringRef( cx->dbName.toString() + "/MovingData") ) );
|
TraceEvent("DataDistributionQueueSize").detail("Database", printable(cx->dbName)).detail("Stage", "GotString")/*.detail("Result", printable(movingDataMessage))*/.detail("TrackLatest", printable( StringRef( cx->dbName.toString() + "/MovingData") ) );
|
||||||
|
|
||||||
int64_t inQueue;
|
int64_t inQueue;
|
||||||
sscanf(extractAttribute(movingDataMessage.toString(), "InQueue").c_str(), "%lld", &inQueue);
|
sscanf(movingDataMessage.getValue("InQueue").c_str(), "%lld", &inQueue);
|
||||||
|
|
||||||
if(reportInFlight) {
|
if(reportInFlight) {
|
||||||
int64_t inFlight;
|
int64_t inFlight;
|
||||||
sscanf(extractAttribute(movingDataMessage.toString(), "InFlight").c_str(), "%lld", &inFlight);
|
sscanf(movingDataMessage.getValue("InFlight").c_str(), "%lld", &inFlight);
|
||||||
inQueue += inFlight;
|
inQueue += inFlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,10 +258,10 @@ ACTOR Future<bool> getDataDistributionActive( Database cx, WorkerInterface maste
|
||||||
try {
|
try {
|
||||||
TraceEvent("DataDistributionActive").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
TraceEvent("DataDistributionActive").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
||||||
|
|
||||||
Standalone<StringRef> activeMessage = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
TraceEventFields activeMessage = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
||||||
EventLogRequest( StringRef( cx->dbName.toString() + "/DDTrackerStarting") ) ), 1.0 ) );
|
EventLogRequest( StringRef( cx->dbName.toString() + "/DDTrackerStarting") ) ), 1.0 ) );
|
||||||
|
|
||||||
return extractAttribute(activeMessage.toString(), "State") == "Active";
|
return activeMessage.getValue("State") == "Active";
|
||||||
} catch( Error &e ) {
|
} catch( Error &e ) {
|
||||||
TraceEvent("QuietDatabaseFailure", masterWorker.id()).detail("Reason", "Failed to extract DataDistributionActive");
|
TraceEvent("QuietDatabaseFailure", masterWorker.id()).detail("Reason", "Failed to extract DataDistributionActive");
|
||||||
throw;
|
throw;
|
||||||
|
@ -273,10 +273,10 @@ ACTOR Future<bool> getStorageServersRecruiting( Database cx, Reference<AsyncVar<
|
||||||
try {
|
try {
|
||||||
TraceEvent("StorageServersRecruiting").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
TraceEvent("StorageServersRecruiting").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
||||||
|
|
||||||
Standalone<StringRef> recruitingMessage = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
TraceEventFields recruitingMessage = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
||||||
EventLogRequest( StringRef( cx->dbName.toString() + "/StorageServerRecruitment_" + dbInfo->get().master.id().toString()) ) ), 1.0 ) );
|
EventLogRequest( StringRef( cx->dbName.toString() + "/StorageServerRecruitment_" + dbInfo->get().master.id().toString()) ) ), 1.0 ) );
|
||||||
|
|
||||||
return extractAttribute(recruitingMessage.toString(), "State") == "Recruiting";
|
return recruitingMessage.getValue("State") == "Recruiting";
|
||||||
} catch( Error &e ) {
|
} catch( Error &e ) {
|
||||||
TraceEvent("QuietDatabaseFailure", masterWorker.id()).detail("Reason", "Failed to extract StorageServersRecruiting").detail("MasterID", dbInfo->get().master.id());
|
TraceEvent("QuietDatabaseFailure", masterWorker.id()).detail("Reason", "Failed to extract StorageServersRecruiting").detail("MasterID", dbInfo->get().master.id());
|
||||||
throw;
|
throw;
|
||||||
|
|
|
@ -119,6 +119,7 @@ T simulate( const T& in ) {
|
||||||
static void simInitTLS(Reference<TLSOptions> tlsOptions) {
|
static void simInitTLS(Reference<TLSOptions> tlsOptions) {
|
||||||
tlsOptions->set_cert_data( certBytes );
|
tlsOptions->set_cert_data( certBytes );
|
||||||
tlsOptions->set_key_data( certBytes );
|
tlsOptions->set_key_data( certBytes );
|
||||||
|
tlsOptions->set_verify_peers(std::vector<std::string>(1, "Check.Valid=0"));
|
||||||
tlsOptions->register_network();
|
tlsOptions->register_network();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,6 +865,7 @@ void SimulationConfig::generateNormalConfig(int minimumReplication) {
|
||||||
ASSERT(false); // Programmer forgot to adjust cases.
|
ASSERT(false); // Programmer forgot to adjust cases.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_random->random01() < 0.25) db.desiredLogRouterCount = g_random->randomInt(1,7);
|
||||||
if (g_random->random01() < 0.25) db.remoteDesiredTLogCount = g_random->randomInt(1,7);
|
if (g_random->random01() < 0.25) db.remoteDesiredTLogCount = g_random->randomInt(1,7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,25 +937,16 @@ void setupSimulatedSystem( vector<Future<Void>> *systemActors, std::string baseF
|
||||||
g_simulator.remoteTLogPolicy = simconfig.db.getRemoteTLogPolicy();
|
g_simulator.remoteTLogPolicy = simconfig.db.getRemoteTLogPolicy();
|
||||||
g_simulator.usableRegions = simconfig.db.usableRegions;
|
g_simulator.usableRegions = simconfig.db.usableRegions;
|
||||||
|
|
||||||
if(simconfig.db.regions.size() == 2) {
|
if(simconfig.db.regions.size() > 0) {
|
||||||
g_simulator.primaryDcId = simconfig.db.regions[0].dcId;
|
|
||||||
g_simulator.remoteDcId = simconfig.db.regions[1].dcId;
|
|
||||||
g_simulator.hasSatelliteReplication = simconfig.db.regions[0].satelliteTLogReplicationFactor > 0;
|
|
||||||
ASSERT((!simconfig.db.regions[0].satelliteTLogPolicy && !simconfig.db.regions[1].satelliteTLogPolicy) || simconfig.db.regions[0].satelliteTLogPolicy->info() == simconfig.db.regions[1].satelliteTLogPolicy->info());
|
|
||||||
g_simulator.satelliteTLogPolicy = simconfig.db.regions[0].satelliteTLogPolicy;
|
|
||||||
g_simulator.satelliteTLogWriteAntiQuorum = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum;
|
|
||||||
|
|
||||||
for(auto s : simconfig.db.regions[0].satellites) {
|
|
||||||
g_simulator.primarySatelliteDcIds.push_back(s.dcId);
|
|
||||||
}
|
|
||||||
for(auto s : simconfig.db.regions[1].satellites) {
|
|
||||||
g_simulator.remoteSatelliteDcIds.push_back(s.dcId);
|
|
||||||
}
|
|
||||||
} else if(simconfig.db.regions.size() == 1) {
|
|
||||||
g_simulator.primaryDcId = simconfig.db.regions[0].dcId;
|
g_simulator.primaryDcId = simconfig.db.regions[0].dcId;
|
||||||
g_simulator.hasSatelliteReplication = simconfig.db.regions[0].satelliteTLogReplicationFactor > 0;
|
g_simulator.hasSatelliteReplication = simconfig.db.regions[0].satelliteTLogReplicationFactor > 0;
|
||||||
g_simulator.satelliteTLogPolicy = simconfig.db.regions[0].satelliteTLogPolicy;
|
if(simconfig.db.regions[0].satelliteTLogUsableDcsFallback > 0) {
|
||||||
g_simulator.satelliteTLogWriteAntiQuorum = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum;
|
g_simulator.satelliteTLogPolicy = simconfig.db.regions[0].satelliteTLogPolicyFallback;
|
||||||
|
g_simulator.satelliteTLogWriteAntiQuorum = simconfig.db.regions[0].satelliteTLogWriteAntiQuorumFallback;
|
||||||
|
} else {
|
||||||
|
g_simulator.satelliteTLogPolicy = simconfig.db.regions[0].satelliteTLogPolicy;
|
||||||
|
g_simulator.satelliteTLogWriteAntiQuorum = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto s : simconfig.db.regions[0].satellites) {
|
for(auto s : simconfig.db.regions[0].satellites) {
|
||||||
g_simulator.primarySatelliteDcIds.push_back(s.dcId);
|
g_simulator.primarySatelliteDcIds.push_back(s.dcId);
|
||||||
|
@ -962,7 +955,16 @@ void setupSimulatedSystem( vector<Future<Void>> *systemActors, std::string baseF
|
||||||
g_simulator.hasSatelliteReplication = false;
|
g_simulator.hasSatelliteReplication = false;
|
||||||
g_simulator.satelliteTLogWriteAntiQuorum = 0;
|
g_simulator.satelliteTLogWriteAntiQuorum = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(simconfig.db.regions.size() == 2) {
|
||||||
|
g_simulator.remoteDcId = simconfig.db.regions[1].dcId;
|
||||||
|
ASSERT((!simconfig.db.regions[0].satelliteTLogPolicy && !simconfig.db.regions[1].satelliteTLogPolicy) || simconfig.db.regions[0].satelliteTLogPolicy->info() == simconfig.db.regions[1].satelliteTLogPolicy->info());
|
||||||
|
|
||||||
|
for(auto s : simconfig.db.regions[1].satellites) {
|
||||||
|
g_simulator.remoteSatelliteDcIds.push_back(s.dcId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT(g_simulator.storagePolicy && g_simulator.tLogPolicy);
|
ASSERT(g_simulator.storagePolicy && g_simulator.tLogPolicy);
|
||||||
ASSERT(!g_simulator.hasSatelliteReplication || g_simulator.satelliteTLogPolicy);
|
ASSERT(!g_simulator.hasSatelliteReplication || g_simulator.satelliteTLogPolicy);
|
||||||
TraceEvent("SimulatorConfig").detail("ConfigString", printable(StringRef(startingConfigString)));
|
TraceEvent("SimulatorConfig").detail("ConfigString", printable(StringRef(startingConfigString)));
|
||||||
|
|
|
@ -75,137 +75,28 @@ extern int limitReasonEnd;
|
||||||
extern const char* limitReasonName[];
|
extern const char* limitReasonName[];
|
||||||
extern const char* limitReasonDesc[];
|
extern const char* limitReasonDesc[];
|
||||||
|
|
||||||
// Returns -1 if it fails to find a quoted string at the start of xml; returns the position beyond the close quote
|
struct WorkerEvents : std::map<NetworkAddress, TraceEventFields> {};
|
||||||
// If decoded is not NULL, writes the decoded attribute value there
|
|
||||||
int decodeQuotedAttributeValue( StringRef xml, std::string* decoded ) {
|
|
||||||
if (decoded) decoded->clear();
|
|
||||||
if (!xml.size() || xml[0] != '"') return -1;
|
|
||||||
int pos = 1;
|
|
||||||
|
|
||||||
loop {
|
ACTOR static Future< Optional<TraceEventFields> > latestEventOnWorker(WorkerInterface worker, std::string eventName) {
|
||||||
if (pos == xml.size()) return -1; // No closing quote
|
|
||||||
if (xml[pos]=='"') { pos++; break; } // Success
|
|
||||||
|
|
||||||
uint8_t out = xml[pos];
|
|
||||||
if (xml[pos] == '&') {
|
|
||||||
if (xml.substr(pos).startsWith(LiteralStringRef("&"))) { out = '&'; pos += 5; }
|
|
||||||
else if (xml.substr(pos).startsWith(LiteralStringRef("<"))) { out = '<'; pos += 4; }
|
|
||||||
else if (xml.substr(pos).startsWith(LiteralStringRef("""))) { out = '"'; pos += 6; }
|
|
||||||
else return -1;
|
|
||||||
} else
|
|
||||||
pos++;
|
|
||||||
if (decoded) decoded->push_back(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return false on failure; outputs decoded attribute value to `ret`
|
|
||||||
bool tryExtractAttribute( StringRef expanded, StringRef attributeToExtract, std::string& ret ) {
|
|
||||||
// This is only expected to parse the XML that Trace.cpp actually generates; we haven't looked at the standard to even find out what it doesn't try to do
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
// Consume '<'
|
|
||||||
if (pos == expanded.size() || expanded[pos] != '<') return false;
|
|
||||||
pos++;
|
|
||||||
// Consume tag name
|
|
||||||
while (pos != expanded.size() && expanded[pos] != ' ' && expanded[pos] != '/' && expanded[pos] != '>') pos++;
|
|
||||||
|
|
||||||
while (pos != expanded.size() && expanded[pos] != '>' && expanded[pos] != '/') {
|
|
||||||
// Consume whitespace
|
|
||||||
while (pos != expanded.size() && expanded[pos] == ' ') pos++;
|
|
||||||
|
|
||||||
// We should be looking at an attribute or the end of the string; find '=' at the end of the attribute, if any
|
|
||||||
int eq_or_end = pos;
|
|
||||||
while (eq_or_end != expanded.size() && expanded[eq_or_end]!='=' && expanded[eq_or_end]!='>') eq_or_end++;
|
|
||||||
|
|
||||||
if ( expanded.substr(pos, eq_or_end-pos) == attributeToExtract ) {
|
|
||||||
// Found the attribute we want; decode the value
|
|
||||||
int end = decodeQuotedAttributeValue(expanded.substr(eq_or_end+1), &ret);
|
|
||||||
if (end<0) { ret.clear(); return false; }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want this attribute, but we need to skip over its value
|
|
||||||
// It looks like this *could* just be a scan for '"' characters
|
|
||||||
int end = decodeQuotedAttributeValue(expanded.substr(eq_or_end+1), NULL);
|
|
||||||
if (end<0) return false;
|
|
||||||
pos = (eq_or_end+1)+end;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Throws attribute_not_found if the key is not found
|
|
||||||
std::string extractAttribute( StringRef expanded, StringRef attributeToExtract ) {
|
|
||||||
std::string ret;
|
|
||||||
if (!tryExtractAttribute(expanded, attributeToExtract, ret))
|
|
||||||
throw attribute_not_found();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
std::string extractAttribute( std::string const& expanded, std::string const& attributeToExtract ) {
|
|
||||||
return extractAttribute(StringRef(expanded), StringRef(attributeToExtract));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("fdbserver/Status/extractAttribute/basic") {
|
|
||||||
std::string a;
|
|
||||||
|
|
||||||
ASSERT( tryExtractAttribute(
|
|
||||||
LiteralStringRef("<Foo A=\""a"\" B=\"\" />"),
|
|
||||||
LiteralStringRef("A"),
|
|
||||||
a) && a == LiteralStringRef("\"a\""));
|
|
||||||
|
|
||||||
ASSERT( tryExtractAttribute(
|
|
||||||
LiteralStringRef("<Foo A=\""a"\" B=\"\\\" />"),
|
|
||||||
LiteralStringRef("B"),
|
|
||||||
a) && a == LiteralStringRef("\\") );
|
|
||||||
|
|
||||||
ASSERT( tryExtractAttribute(
|
|
||||||
LiteralStringRef("<Event Severity=\"10\" Time=\"1415124565.129695\" Type=\"ProgramStart\" Machine=\"10.0.0.85:6863\" ID=\"0000000000000000\" RandomSeed=\"-2044671207\" SourceVersion=\"675cd9579467+ tip\" Version=\"3.0.0-PRERELEASE\" PackageName=\"3.0\" DataFolder=\"\" ConnectionString=\"circus:81060aa85f0a5b5b@10.0.0.5:4000,10.0.0.17:4000,10.0.0.78:4000,10.0.0.162:4000,10.0.0.182:4000\" ActualTime=\"1415124565\" CommandLine=\"fdbserver -r multitest -p auto:6863 -f /tmp/circus/testspec.txt --num_testers 24 --logdir /tmp/circus/multitest\" BuggifyEnabled=\"0\"/>"),
|
|
||||||
LiteralStringRef("Version"),
|
|
||||||
a) && a == LiteralStringRef("3.0.0-PRERELEASE") );
|
|
||||||
|
|
||||||
ASSERT( !tryExtractAttribute(
|
|
||||||
LiteralStringRef("<Event Severity=\"10\" Time=\"1415124565.129695\" Type=\"ProgramStart\" Machine=\"10.0.0.85:6863\" ID=\"0000000000000000\" RandomSeed=\"-2044671207\" SourceVersion=\"675cd9579467+ tip\" Version=\"3.0.0-PRERELEASE\" PackageName=\"3.0\" DataFolder=\"\" ConnectionString=\"circus:81060aa85f0a5b5b@10.0.0.5:4000,10.0.0.17:4000,10.0.0.78:4000,10.0.0.162:4000,10.0.0.182:4000\" ActualTime=\"1415124565\" CommandLine=\"fdbserver -r multitest -p auto:6863 -f /tmp/circus/testspec.txt --num_testers 24 --logdir /tmp/circus/multitest\" BuggifyEnabled=\"0\"/>"),
|
|
||||||
LiteralStringRef("ersion"),
|
|
||||||
a) );
|
|
||||||
|
|
||||||
return Void();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("fdbserver/Status/extractAttribute/fuzz") {
|
|
||||||
// This is just looking for anything that crashes or infinite loops
|
|
||||||
std::string out;
|
|
||||||
for(int i=0; i<100000; i++)
|
|
||||||
{
|
|
||||||
std::string s = "<Event Severity=\"10\" Time=\"1415124565.129695\" Type=\"Program "Start"\" Machine=\"10.0.0.85:6863\" ID=\"0000000000000000\" RandomSeed=\"-2044671207\" SourceVersion=\"675cd9579467+ tip\" Version=\"3.0.0-PRERELEASE\" PackageName=\"3.0\" DataFolder=\"\" ConnectionString=\"circus:81060aa85f0a5b5b@10.0.0.5:4000,10.0.0.17:4000,10.0.0.78:4000,10.0.0.162:4000,10.0.0.182:4000\" ActualTime=\"1415124565\" CommandLine=\"fdbserver -r multitest -p auto:6863 -f /tmp/circus/testspec.txt --num_testers 24 --logdir /tmp/circus/multitest\" BuggifyEnabled=\"0\"/>";
|
|
||||||
s[ g_random->randomInt(0, s.size()) ] = g_random->randomChoice(LiteralStringRef("\" =q0\\&"));
|
|
||||||
tryExtractAttribute(s, LiteralStringRef("Version"), out);
|
|
||||||
}
|
|
||||||
return Void();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WorkerEvents : std::map<NetworkAddress, std::string> {};
|
|
||||||
|
|
||||||
ACTOR static Future< Optional<std::string> > latestEventOnWorker(WorkerInterface worker, std::string eventName) {
|
|
||||||
try {
|
try {
|
||||||
EventLogRequest req = eventName.size() > 0 ? EventLogRequest(Standalone<StringRef>(eventName)) : EventLogRequest();
|
EventLogRequest req = eventName.size() > 0 ? EventLogRequest(Standalone<StringRef>(eventName)) : EventLogRequest();
|
||||||
ErrorOr<Standalone<StringRef>> eventTrace = wait( errorOr(timeoutError(worker.eventLogRequest.getReply(req), 2.0)));
|
ErrorOr<TraceEventFields> eventTrace = wait( errorOr(timeoutError(worker.eventLogRequest.getReply(req), 2.0)));
|
||||||
|
|
||||||
if (eventTrace.isError()){
|
if (eventTrace.isError()){
|
||||||
return Optional<std::string>();
|
return Optional<TraceEventFields>();
|
||||||
}
|
}
|
||||||
return eventTrace.get().toString();
|
return eventTrace.get();
|
||||||
}
|
}
|
||||||
catch (Error &e){
|
catch (Error &e){
|
||||||
if (e.code() == error_code_actor_cancelled)
|
if (e.code() == error_code_actor_cancelled)
|
||||||
throw;
|
throw;
|
||||||
return Optional<std::string>();
|
return Optional<TraceEventFields>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR static Future< Optional< std::pair<WorkerEvents, std::set<std::string>> > > latestEventOnWorkers(std::vector<std::pair<WorkerInterface, ProcessClass>> workers, std::string eventName) {
|
ACTOR static Future< Optional< std::pair<WorkerEvents, std::set<std::string>> > > latestEventOnWorkers(std::vector<std::pair<WorkerInterface, ProcessClass>> workers, std::string eventName) {
|
||||||
try {
|
try {
|
||||||
state vector<Future<ErrorOr<Standalone<StringRef>>>> eventTraces;
|
state vector<Future<ErrorOr<TraceEventFields>>> eventTraces;
|
||||||
for (int c = 0; c < workers.size(); c++) {
|
for (int c = 0; c < workers.size(); c++) {
|
||||||
EventLogRequest req = eventName.size() > 0 ? EventLogRequest(Standalone<StringRef>(eventName)) : EventLogRequest();
|
EventLogRequest req = eventName.size() > 0 ? EventLogRequest(Standalone<StringRef>(eventName)) : EventLogRequest();
|
||||||
eventTraces.push_back(errorOr(timeoutError(workers[c].first.eventLogRequest.getReply(req), 2.0)));
|
eventTraces.push_back(errorOr(timeoutError(workers[c].first.eventLogRequest.getReply(req), 2.0)));
|
||||||
|
@ -217,13 +108,13 @@ ACTOR static Future< Optional< std::pair<WorkerEvents, std::set<std::string>> >
|
||||||
WorkerEvents results;
|
WorkerEvents results;
|
||||||
|
|
||||||
for (int i = 0; i < eventTraces.size(); i++) {
|
for (int i = 0; i < eventTraces.size(); i++) {
|
||||||
ErrorOr<Standalone<StringRef>> v = eventTraces[i].get();
|
const ErrorOr<TraceEventFields>& v = eventTraces[i].get();
|
||||||
if (v.isError()){
|
if (v.isError()){
|
||||||
failed.insert(workers[i].first.address().toString());
|
failed.insert(workers[i].first.address().toString());
|
||||||
results[workers[i].first.address()] = "";
|
results[workers[i].first.address()] = TraceEventFields();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
results[workers[i].first.address()] = v.get().toString();
|
results[workers[i].first.address()] = v.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,21 +233,21 @@ static StatusObject getLocalityInfo(const LocalityData& locality) {
|
||||||
return localityObj;
|
return localityObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static StatusObject getError(std::string error) {
|
static StatusObject getError(const TraceEventFields& errorFields) {
|
||||||
StatusObject statusObj;
|
StatusObject statusObj;
|
||||||
try {
|
try {
|
||||||
if (error.size()) {
|
if (errorFields.size()) {
|
||||||
double time = atof(extractAttribute(error, "Time").c_str());
|
double time = atof(errorFields.getValue("Time").c_str());
|
||||||
statusObj["time"] = time;
|
statusObj["time"] = time;
|
||||||
|
|
||||||
statusObj["raw_log_message"] = error;
|
statusObj["raw_log_message"] = errorFields.toString();
|
||||||
|
|
||||||
std::string type = extractAttribute(error, "Type");
|
std::string type = errorFields.getValue("Type");
|
||||||
statusObj["type"] = type;
|
statusObj["type"] = type;
|
||||||
|
|
||||||
std::string description = type;
|
std::string description = type;
|
||||||
std::string errorName;
|
std::string errorName;
|
||||||
if (tryExtractAttribute(error, LiteralStringRef("Error"), errorName)) {
|
if(errorFields.tryGetValue("Error", errorName)) {
|
||||||
statusObj["name"] = errorName;
|
statusObj["name"] = errorName;
|
||||||
description += ": " + errorName;
|
description += ": " + errorName;
|
||||||
}
|
}
|
||||||
|
@ -374,7 +265,7 @@ static StatusObject getError(std::string error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Error &e){
|
catch (Error &e){
|
||||||
TraceEvent(SevError, "StatusGetErrorError").error(e).detail("RawError", error);
|
TraceEvent(SevError, "StatusGetErrorError").error(e).detail("RawError", errorFields.toString());
|
||||||
}
|
}
|
||||||
return statusObj;
|
return statusObj;
|
||||||
}
|
}
|
||||||
|
@ -385,7 +276,7 @@ static StatusObject machineStatusFetcher(WorkerEvents mMetrics, vector<std::pair
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
|
|
||||||
// map from machine networkAddress to datacenter ID
|
// map from machine networkAddress to datacenter ID
|
||||||
WorkerEvents dcIds;
|
std::map<NetworkAddress, std::string> dcIds;
|
||||||
std::map<NetworkAddress, LocalityData> locality;
|
std::map<NetworkAddress, LocalityData> locality;
|
||||||
|
|
||||||
for (auto worker : workers){
|
for (auto worker : workers){
|
||||||
|
@ -401,12 +292,12 @@ static StatusObject machineStatusFetcher(WorkerEvents mMetrics, vector<std::pair
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusObject statusObj; // Represents the status for a machine
|
StatusObject statusObj; // Represents the status for a machine
|
||||||
std::string event = it->second;
|
const TraceEventFields& event = it->second;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::string address = toIPString(it->first.ip);
|
std::string address = toIPString(it->first.ip);
|
||||||
// We will use the "physical" caluculated machine ID here to limit exposure to machineID repurposing
|
// We will use the "physical" caluculated machine ID here to limit exposure to machineID repurposing
|
||||||
std::string machineId = extractAttribute(event, "MachineID");
|
std::string machineId = event.getValue("MachineID");
|
||||||
|
|
||||||
// If this machine ID does not already exist in the machineMap, add it
|
// If this machine ID does not already exist in the machineMap, add it
|
||||||
if (!machineMap.count(machineId)) {
|
if (!machineMap.count(machineId)) {
|
||||||
|
@ -424,23 +315,23 @@ static StatusObject machineStatusFetcher(WorkerEvents mMetrics, vector<std::pair
|
||||||
|
|
||||||
StatusObject memoryObj;
|
StatusObject memoryObj;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "TotalMemory"));
|
metric = parseDouble(event.getValue("TotalMemory"));
|
||||||
memoryObj["total_bytes"] = metric;
|
memoryObj["total_bytes"] = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "CommittedMemory"));
|
metric = parseDouble(event.getValue("CommittedMemory"));
|
||||||
memoryObj["committed_bytes"] = metric;
|
memoryObj["committed_bytes"] = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "AvailableMemory"));
|
metric = parseDouble(event.getValue("AvailableMemory"));
|
||||||
memoryObj["free_bytes"] = metric;
|
memoryObj["free_bytes"] = metric;
|
||||||
|
|
||||||
statusObj["memory"] = memoryObj;
|
statusObj["memory"] = memoryObj;
|
||||||
|
|
||||||
StatusObject cpuObj;
|
StatusObject cpuObj;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "CPUSeconds"));
|
metric = parseDouble(event.getValue("CPUSeconds"));
|
||||||
double cpu_seconds = metric;
|
double cpu_seconds = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "Elapsed"));
|
metric = parseDouble(event.getValue("Elapsed"));
|
||||||
double elapsed = metric;
|
double elapsed = metric;
|
||||||
|
|
||||||
if (elapsed > 0){
|
if (elapsed > 0){
|
||||||
|
@ -451,17 +342,17 @@ static StatusObject machineStatusFetcher(WorkerEvents mMetrics, vector<std::pair
|
||||||
|
|
||||||
StatusObject networkObj;
|
StatusObject networkObj;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "MbpsSent"));
|
metric = parseDouble(event.getValue("MbpsSent"));
|
||||||
StatusObject megabits_sent;
|
StatusObject megabits_sent;
|
||||||
megabits_sent["hz"] = metric;
|
megabits_sent["hz"] = metric;
|
||||||
networkObj["megabits_sent"] = megabits_sent;
|
networkObj["megabits_sent"] = megabits_sent;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "MbpsReceived"));
|
metric = parseDouble(event.getValue("MbpsReceived"));
|
||||||
StatusObject megabits_received;
|
StatusObject megabits_received;
|
||||||
megabits_received["hz"] = metric;
|
megabits_received["hz"] = metric;
|
||||||
networkObj["megabits_received"] = megabits_received;
|
networkObj["megabits_received"] = megabits_received;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "RetransSegs"));
|
metric = parseDouble(event.getValue("RetransSegs"));
|
||||||
StatusObject retransSegsObj;
|
StatusObject retransSegsObj;
|
||||||
if (elapsed > 0){
|
if (elapsed > 0){
|
||||||
retransSegsObj["hz"] = metric / elapsed;
|
retransSegsObj["hz"] = metric / elapsed;
|
||||||
|
@ -512,50 +403,62 @@ struct RolesInfo {
|
||||||
obj["role"] = role;
|
obj["role"] = role;
|
||||||
return roles.insert( make_pair(address, obj ))->second;
|
return roles.insert( make_pair(address, obj ))->second;
|
||||||
}
|
}
|
||||||
StatusObject& addRole(std::string const& role, StorageServerInterface& iface, std::string const& metrics, Version maxTLogVersion) {
|
StatusObject& addRole(std::string const& role, StorageServerInterface& iface, TraceEventFields const& metrics, Version maxTLogVersion) {
|
||||||
StatusObject obj;
|
StatusObject obj;
|
||||||
obj["id"] = iface.id().shortString();
|
obj["id"] = iface.id().shortString();
|
||||||
obj["role"] = role;
|
obj["role"] = role;
|
||||||
try {
|
try {
|
||||||
obj["stored_bytes"] = parseInt64(extractAttribute(metrics, "BytesStored"));
|
obj["stored_bytes"] = parseInt64(metrics.getValue("BytesStored"));
|
||||||
obj["kvstore_used_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesUsed"));
|
obj["kvstore_used_bytes"] = parseInt64(metrics.getValue("KvstoreBytesUsed"));
|
||||||
obj["kvstore_free_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesFree"));
|
obj["kvstore_free_bytes"] = parseInt64(metrics.getValue("KvstoreBytesFree"));
|
||||||
obj["kvstore_available_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesAvailable"));
|
obj["kvstore_available_bytes"] = parseInt64(metrics.getValue("KvstoreBytesAvailable"));
|
||||||
obj["kvstore_total_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesTotal"));
|
obj["kvstore_total_bytes"] = parseInt64(metrics.getValue("KvstoreBytesTotal"));
|
||||||
obj["input_bytes"] = parseCounter(extractAttribute(metrics, "BytesInput"));
|
obj["input_bytes"] = parseCounter(metrics.getValue("BytesInput"));
|
||||||
obj["durable_bytes"] = parseCounter(extractAttribute(metrics, "BytesDurable"));
|
obj["durable_bytes"] = parseCounter(metrics.getValue("BytesDurable"));
|
||||||
obj["query_queue_max"] = parseInt(extractAttribute(metrics, "QueryQueueMax"));
|
obj["query_queue_max"] = parseInt(metrics.getValue("QueryQueueMax"));
|
||||||
obj["finished_queries"] = parseCounter(extractAttribute(metrics, "FinishedQueries"));
|
obj["finished_queries"] = parseCounter(metrics.getValue("FinishedQueries"));
|
||||||
|
|
||||||
Version version = parseInt64(extractAttribute(metrics, "Version"));
|
Version version = parseInt64(metrics.getValue("Version"));
|
||||||
obj["data_version"] = version;
|
obj["data_version"] = version;
|
||||||
|
|
||||||
|
int64_t versionLag = parseInt64(metrics.getValue("VersionLag"));
|
||||||
if(maxTLogVersion > 0) {
|
if(maxTLogVersion > 0) {
|
||||||
obj["data_version_lag"] = std::max<Version>(0, maxTLogVersion - version);
|
// It's possible that the storage server hasn't talked to the logs recently, in which case it may not be aware of how far behind it is.
|
||||||
|
// To account for that, we also compute the version difference between each storage server and the tlog with the largest version.
|
||||||
|
//
|
||||||
|
// Because this data is only logged periodically, this difference will likely be an overestimate for the lag. We subtract off the logging interval
|
||||||
|
// in order to make this estimate a bounded underestimate instead.
|
||||||
|
versionLag = std::max<int64_t>(versionLag, maxTLogVersion - version - SERVER_KNOBS->STORAGE_LOGGING_DELAY * SERVER_KNOBS->VERSIONS_PER_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusObject dataLag;
|
||||||
|
dataLag["versions"] = versionLag;
|
||||||
|
dataLag["seconds"] = versionLag / (double)SERVER_KNOBS->VERSIONS_PER_SECOND;
|
||||||
|
|
||||||
|
obj["data_lag"] = dataLag;
|
||||||
|
|
||||||
} catch (Error& e) {
|
} catch (Error& e) {
|
||||||
if(e.code() != error_code_attribute_not_found)
|
if(e.code() != error_code_attribute_not_found)
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
return roles.insert( make_pair(iface.address(), obj ))->second;
|
return roles.insert( make_pair(iface.address(), obj ))->second;
|
||||||
}
|
}
|
||||||
StatusObject& addRole(std::string const& role, TLogInterface& iface, std::string const& metrics) {
|
StatusObject& addRole(std::string const& role, TLogInterface& iface, TraceEventFields const& metrics) {
|
||||||
StatusObject obj;
|
StatusObject obj;
|
||||||
obj["id"] = iface.id().shortString();
|
obj["id"] = iface.id().shortString();
|
||||||
obj["role"] = role;
|
obj["role"] = role;
|
||||||
try {
|
try {
|
||||||
obj["kvstore_used_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesUsed"));
|
obj["kvstore_used_bytes"] = parseInt64(metrics.getValue("KvstoreBytesUsed"));
|
||||||
obj["kvstore_free_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesFree"));
|
obj["kvstore_free_bytes"] = parseInt64(metrics.getValue("KvstoreBytesFree"));
|
||||||
obj["kvstore_available_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesAvailable"));
|
obj["kvstore_available_bytes"] = parseInt64(metrics.getValue("KvstoreBytesAvailable"));
|
||||||
obj["kvstore_total_bytes"] = parseInt64(extractAttribute(metrics, "KvstoreBytesTotal"));
|
obj["kvstore_total_bytes"] = parseInt64(metrics.getValue("KvstoreBytesTotal"));
|
||||||
obj["queue_disk_used_bytes"] = parseInt64(extractAttribute(metrics, "QueueDiskBytesUsed"));
|
obj["queue_disk_used_bytes"] = parseInt64(metrics.getValue("QueueDiskBytesUsed"));
|
||||||
obj["queue_disk_free_bytes"] = parseInt64(extractAttribute(metrics, "QueueDiskBytesFree"));
|
obj["queue_disk_free_bytes"] = parseInt64(metrics.getValue("QueueDiskBytesFree"));
|
||||||
obj["queue_disk_available_bytes"] = parseInt64(extractAttribute(metrics, "QueueDiskBytesAvailable"));
|
obj["queue_disk_available_bytes"] = parseInt64(metrics.getValue("QueueDiskBytesAvailable"));
|
||||||
obj["queue_disk_total_bytes"] = parseInt64(extractAttribute(metrics, "QueueDiskBytesTotal"));
|
obj["queue_disk_total_bytes"] = parseInt64(metrics.getValue("QueueDiskBytesTotal"));
|
||||||
obj["input_bytes"] = parseCounter(extractAttribute(metrics, "BytesInput"));
|
obj["input_bytes"] = parseCounter(metrics.getValue("BytesInput"));
|
||||||
obj["durable_bytes"] = parseCounter(extractAttribute(metrics, "BytesDurable"));
|
obj["durable_bytes"] = parseCounter(metrics.getValue("BytesDurable"));
|
||||||
obj["data_version"] = parseInt64(extractAttribute(metrics, "Version"));
|
obj["data_version"] = parseInt64(metrics.getValue("Version"));
|
||||||
} catch (Error& e) {
|
} catch (Error& e) {
|
||||||
if(e.code() != error_code_attribute_not_found)
|
if(e.code() != error_code_attribute_not_found)
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -586,8 +489,8 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
WorkerEvents traceFileOpenErrors,
|
WorkerEvents traceFileOpenErrors,
|
||||||
WorkerEvents programStarts,
|
WorkerEvents programStarts,
|
||||||
std::map<std::string, StatusObject> processIssues,
|
std::map<std::string, StatusObject> processIssues,
|
||||||
vector<std::pair<StorageServerInterface, std::string>> storageServers,
|
vector<std::pair<StorageServerInterface, TraceEventFields>> storageServers,
|
||||||
vector<std::pair<TLogInterface, std::string>> tLogs,
|
vector<std::pair<TLogInterface, TraceEventFields>> tLogs,
|
||||||
Database cx,
|
Database cx,
|
||||||
Optional<DatabaseConfiguration> configuration,
|
Optional<DatabaseConfiguration> configuration,
|
||||||
std::set<std::string> *incomplete_reasons) {
|
std::set<std::string> *incomplete_reasons) {
|
||||||
|
@ -604,10 +507,10 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
Void _ = wait(yield());
|
Void _ = wait(yield());
|
||||||
if (traceFileErrorsItr->second.size()){
|
if (traceFileErrorsItr->second.size()){
|
||||||
try {
|
try {
|
||||||
// Have event string, parse it and turn it into a message object describing the trace file opening error
|
// Have event fields, parse it and turn it into a message object describing the trace file opening error
|
||||||
std::string event = traceFileErrorsItr->second;
|
const TraceEventFields& event = traceFileErrorsItr->second;
|
||||||
std::string fileName = extractAttribute(event, "Filename");
|
std::string fileName = event.getValue("Filename");
|
||||||
StatusObject msgObj = makeMessage("file_open_error", format("Could not open file '%s' (%s).", fileName.c_str(), extractAttribute(event, "Error").c_str()).c_str());
|
StatusObject msgObj = makeMessage("file_open_error", format("Could not open file '%s' (%s).", fileName.c_str(), event.getValue("Error").c_str()).c_str());
|
||||||
msgObj["file_name"] = fileName;
|
msgObj["file_name"] = fileName;
|
||||||
|
|
||||||
// Map the address of the worker to the error message object
|
// Map the address of the worker to the error message object
|
||||||
|
@ -626,11 +529,11 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
state std::map<Optional<Standalone<StringRef>>, MachineMemoryInfo>::iterator memInfo = machineMemoryUsage.insert(std::make_pair(workerItr->first.locality.machineId(), MachineMemoryInfo())).first;
|
state std::map<Optional<Standalone<StringRef>>, MachineMemoryInfo>::iterator memInfo = machineMemoryUsage.insert(std::make_pair(workerItr->first.locality.machineId(), MachineMemoryInfo())).first;
|
||||||
try {
|
try {
|
||||||
ASSERT(pMetrics.count(workerItr->first.address()));
|
ASSERT(pMetrics.count(workerItr->first.address()));
|
||||||
std::string processMetrics = pMetrics[workerItr->first.address()];
|
const TraceEventFields& processMetrics = pMetrics[workerItr->first.address()];
|
||||||
|
|
||||||
if(memInfo->second.valid()) {
|
if(memInfo->second.valid()) {
|
||||||
if(processMetrics.size() > 0) {
|
if(processMetrics.size() > 0) {
|
||||||
memInfo->second.memoryUsage += parseDouble(extractAttribute(processMetrics, "Memory"));
|
memInfo->second.memoryUsage += parseDouble(processMetrics.getValue("Memory"));
|
||||||
++memInfo->second.numProcesses;
|
++memInfo->second.numProcesses;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -656,7 +559,7 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state std::vector<std::pair<TLogInterface, std::string>>::iterator log;
|
state std::vector<std::pair<TLogInterface, TraceEventFields>>::iterator log;
|
||||||
state Version maxTLogVersion = 0;
|
state Version maxTLogVersion = 0;
|
||||||
for(log = tLogs.begin(); log != tLogs.end(); ++log) {
|
for(log = tLogs.begin(); log != tLogs.end(); ++log) {
|
||||||
StatusObject const& roleStatus = roles.addRole( "log", log->first, log->second );
|
StatusObject const& roleStatus = roles.addRole( "log", log->first, log->second );
|
||||||
|
@ -666,7 +569,7 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
Void _ = wait(yield());
|
Void _ = wait(yield());
|
||||||
}
|
}
|
||||||
|
|
||||||
state std::vector<std::pair<StorageServerInterface, std::string>>::iterator ss;
|
state std::vector<std::pair<StorageServerInterface, TraceEventFields>>::iterator ss;
|
||||||
state std::map<NetworkAddress, int64_t> ssLag;
|
state std::map<NetworkAddress, int64_t> ssLag;
|
||||||
for(ss = storageServers.begin(); ss != storageServers.end(); ++ss) {
|
for(ss = storageServers.begin(); ss != storageServers.end(); ++ss) {
|
||||||
StatusObject const& roleStatus = roles.addRole( "storage", ss->first, ss->second, maxTLogVersion );
|
StatusObject const& roleStatus = roles.addRole( "storage", ss->first, ss->second, maxTLogVersion );
|
||||||
|
@ -692,45 +595,45 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
processMap[printable(workerItr->first.locality.processId())] = StatusObject();
|
processMap[printable(workerItr->first.locality.processId())] = StatusObject();
|
||||||
|
|
||||||
NetworkAddress address = workerItr->first.address();
|
NetworkAddress address = workerItr->first.address();
|
||||||
std::string event = pMetrics[workerItr->first.address()];
|
const TraceEventFields& event = pMetrics[workerItr->first.address()];
|
||||||
statusObj["address"] = address.toString();
|
statusObj["address"] = address.toString();
|
||||||
StatusObject memoryObj;
|
StatusObject memoryObj;
|
||||||
|
|
||||||
if (event.size() > 0) {
|
if (event.size() > 0) {
|
||||||
std::string zoneID = extractAttribute(event, "ZoneID");
|
std::string zoneID = event.getValue("ZoneID");
|
||||||
statusObj["fault_domain"] = zoneID;
|
statusObj["fault_domain"] = zoneID;
|
||||||
|
|
||||||
std::string MachineID = extractAttribute(event, "MachineID");
|
std::string MachineID = event.getValue("MachineID");
|
||||||
statusObj["machine_id"] = MachineID;
|
statusObj["machine_id"] = MachineID;
|
||||||
|
|
||||||
statusObj["locality"] = getLocalityInfo(workerItr->first.locality);
|
statusObj["locality"] = getLocalityInfo(workerItr->first.locality);
|
||||||
|
|
||||||
statusObj["uptime_seconds"] = parseDouble(extractAttribute(event, "UptimeSeconds"));
|
statusObj["uptime_seconds"] = parseDouble(event.getValue("UptimeSeconds"));
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "CPUSeconds"));
|
metric = parseDouble(event.getValue("CPUSeconds"));
|
||||||
double cpu_seconds = metric;
|
double cpu_seconds = metric;
|
||||||
|
|
||||||
// rates are calculated over the last elapsed seconds
|
// rates are calculated over the last elapsed seconds
|
||||||
metric = parseDouble(extractAttribute(event, "Elapsed"));
|
metric = parseDouble(event.getValue("Elapsed"));
|
||||||
double elapsed = metric;
|
double elapsed = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "DiskIdleSeconds"));
|
metric = parseDouble(event.getValue("DiskIdleSeconds"));
|
||||||
double diskIdleSeconds = metric;
|
double diskIdleSeconds = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "DiskReads"));
|
metric = parseDouble(event.getValue("DiskReads"));
|
||||||
double diskReads = metric;
|
double diskReads = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "DiskWrites"));
|
metric = parseDouble(event.getValue("DiskWrites"));
|
||||||
double diskWrites = metric;
|
double diskWrites = metric;
|
||||||
|
|
||||||
uint64_t diskReadsCount = parseInt64(extractAttribute(event, "DiskReadsCount"));
|
uint64_t diskReadsCount = parseInt64(event.getValue("DiskReadsCount"));
|
||||||
|
|
||||||
uint64_t diskWritesCount = parseInt64(extractAttribute(event, "DiskWritesCount"));
|
uint64_t diskWritesCount = parseInt64(event.getValue("DiskWritesCount"));
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "DiskWriteSectors"));
|
metric = parseDouble(event.getValue("DiskWriteSectors"));
|
||||||
double diskWriteSectors = metric;
|
double diskWriteSectors = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "DiskReadSectors"));
|
metric = parseDouble(event.getValue("DiskReadSectors"));
|
||||||
double diskReadSectors = metric;
|
double diskReadSectors = metric;
|
||||||
|
|
||||||
StatusObject diskObj;
|
StatusObject diskObj;
|
||||||
|
@ -757,39 +660,39 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
diskObj["writes"] = writesObj;
|
diskObj["writes"] = writesObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
diskObj["total_bytes"] = parseInt64(extractAttribute(event, "DiskTotalBytes"));
|
diskObj["total_bytes"] = parseInt64(event.getValue("DiskTotalBytes"));
|
||||||
diskObj["free_bytes"] = parseInt64(extractAttribute(event, "DiskFreeBytes"));
|
diskObj["free_bytes"] = parseInt64(event.getValue("DiskFreeBytes"));
|
||||||
statusObj["disk"] = diskObj;
|
statusObj["disk"] = diskObj;
|
||||||
|
|
||||||
StatusObject networkObj;
|
StatusObject networkObj;
|
||||||
|
|
||||||
networkObj["current_connections"] = parseInt64(extractAttribute(event, "CurrentConnections"));
|
networkObj["current_connections"] = parseInt64(event.getValue("CurrentConnections"));
|
||||||
StatusObject connections_established;
|
StatusObject connections_established;
|
||||||
connections_established["hz"] = parseDouble(extractAttribute(event, "ConnectionsEstablished"));
|
connections_established["hz"] = parseDouble(event.getValue("ConnectionsEstablished"));
|
||||||
networkObj["connections_established"] = connections_established;
|
networkObj["connections_established"] = connections_established;
|
||||||
StatusObject connections_closed;
|
StatusObject connections_closed;
|
||||||
connections_closed["hz"] = parseDouble(extractAttribute(event, "ConnectionsClosed"));
|
connections_closed["hz"] = parseDouble(event.getValue("ConnectionsClosed"));
|
||||||
networkObj["connections_closed"] = connections_closed;
|
networkObj["connections_closed"] = connections_closed;
|
||||||
StatusObject connection_errors;
|
StatusObject connection_errors;
|
||||||
connection_errors["hz"] = parseDouble(extractAttribute(event, "ConnectionErrors"));
|
connection_errors["hz"] = parseDouble(event.getValue("ConnectionErrors"));
|
||||||
networkObj["connection_errors"] = connection_errors;
|
networkObj["connection_errors"] = connection_errors;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "MbpsSent"));
|
metric = parseDouble(event.getValue("MbpsSent"));
|
||||||
StatusObject megabits_sent;
|
StatusObject megabits_sent;
|
||||||
megabits_sent["hz"] = metric;
|
megabits_sent["hz"] = metric;
|
||||||
networkObj["megabits_sent"] = megabits_sent;
|
networkObj["megabits_sent"] = megabits_sent;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "MbpsReceived"));
|
metric = parseDouble(event.getValue("MbpsReceived"));
|
||||||
StatusObject megabits_received;
|
StatusObject megabits_received;
|
||||||
megabits_received["hz"] = metric;
|
megabits_received["hz"] = metric;
|
||||||
networkObj["megabits_received"] = megabits_received;
|
networkObj["megabits_received"] = megabits_received;
|
||||||
|
|
||||||
statusObj["network"] = networkObj;
|
statusObj["network"] = networkObj;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "Memory"));
|
metric = parseDouble(event.getValue("Memory"));
|
||||||
memoryObj["used_bytes"] = metric;
|
memoryObj["used_bytes"] = metric;
|
||||||
|
|
||||||
metric = parseDouble(extractAttribute(event, "UnusedAllocatedMemory"));
|
metric = parseDouble(event.getValue("UnusedAllocatedMemory"));
|
||||||
memoryObj["unused_allocated_memory"] = metric;
|
memoryObj["unused_allocated_memory"] = metric;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,16 +700,16 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
auto const& psxml = programStarts.at(address);
|
auto const& psxml = programStarts.at(address);
|
||||||
|
|
||||||
if(psxml.size() > 0) {
|
if(psxml.size() > 0) {
|
||||||
int64_t memLimit = parseInt64(extractAttribute(psxml, "MemoryLimit"));
|
int64_t memLimit = parseInt64(psxml.getValue("MemoryLimit"));
|
||||||
memoryObj["limit_bytes"] = memLimit;
|
memoryObj["limit_bytes"] = memLimit;
|
||||||
|
|
||||||
std::string version;
|
std::string version;
|
||||||
if (tryExtractAttribute(psxml, LiteralStringRef("Version"), version)) {
|
if (psxml.tryGetValue("Version", version)) {
|
||||||
statusObj["version"] = version;
|
statusObj["version"] = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string commandLine;
|
std::string commandLine;
|
||||||
if (tryExtractAttribute(psxml, LiteralStringRef("CommandLine"), commandLine)) {
|
if (psxml.tryGetValue("CommandLine", commandLine)) {
|
||||||
statusObj["command_line"] = commandLine;
|
statusObj["command_line"] = commandLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,7 +718,7 @@ ACTOR static Future<StatusObject> processStatusFetcher(
|
||||||
// if this process address is in the machine metrics
|
// if this process address is in the machine metrics
|
||||||
if (mMetrics.count(address) && mMetrics[address].size()){
|
if (mMetrics.count(address) && mMetrics[address].size()){
|
||||||
double availableMemory;
|
double availableMemory;
|
||||||
availableMemory = parseDouble(extractAttribute(mMetrics[address], "AvailableMemory"));
|
availableMemory = parseDouble(mMetrics[address].getValue("AvailableMemory"));
|
||||||
|
|
||||||
auto machineMemInfo = machineMemoryUsage[workerItr->first.locality.machineId()];
|
auto machineMemInfo = machineMemoryUsage[workerItr->first.locality.machineId()];
|
||||||
if (machineMemInfo.valid()) {
|
if (machineMemInfo.valid()) {
|
||||||
|
@ -918,8 +821,8 @@ ACTOR static Future<StatusObject> recoveryStateStatusFetcher(std::pair<WorkerInt
|
||||||
state StatusObject message;
|
state StatusObject message;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Standalone<StringRef> md = wait( timeoutError(mWorker.first.eventLogRequest.getReply( EventLogRequest( LiteralStringRef("MasterRecoveryState") ) ), 1.0) );
|
TraceEventFields md = wait( timeoutError(mWorker.first.eventLogRequest.getReply( EventLogRequest( LiteralStringRef("MasterRecoveryState") ) ), 1.0) );
|
||||||
state int mStatusCode = parseInt( extractAttribute(md, LiteralStringRef("StatusCode")) );
|
state int mStatusCode = parseInt( md.getValue("StatusCode") );
|
||||||
if (mStatusCode < 0 || mStatusCode >= RecoveryStatus::END)
|
if (mStatusCode < 0 || mStatusCode >= RecoveryStatus::END)
|
||||||
throw attribute_not_found();
|
throw attribute_not_found();
|
||||||
|
|
||||||
|
@ -927,9 +830,9 @@ ACTOR static Future<StatusObject> recoveryStateStatusFetcher(std::pair<WorkerInt
|
||||||
|
|
||||||
// Add additional metadata for certain statuses
|
// Add additional metadata for certain statuses
|
||||||
if (mStatusCode == RecoveryStatus::recruiting_transaction_servers) {
|
if (mStatusCode == RecoveryStatus::recruiting_transaction_servers) {
|
||||||
int requiredLogs = atoi( extractAttribute(md, LiteralStringRef("RequiredTLogs")).c_str() );
|
int requiredLogs = atoi( md.getValue("RequiredTLogs").c_str() );
|
||||||
int requiredProxies = atoi( extractAttribute(md, LiteralStringRef("RequiredProxies")).c_str() );
|
int requiredProxies = atoi( md.getValue("RequiredProxies").c_str() );
|
||||||
int requiredResolvers = atoi( extractAttribute(md, LiteralStringRef("RequiredResolvers")).c_str() );
|
int requiredResolvers = atoi( md.getValue("RequiredResolvers").c_str() );
|
||||||
//int requiredProcesses = std::max(requiredLogs, std::max(requiredResolvers, requiredProxies));
|
//int requiredProcesses = std::max(requiredLogs, std::max(requiredResolvers, requiredProxies));
|
||||||
//int requiredMachines = std::max(requiredLogs, 1);
|
//int requiredMachines = std::max(requiredLogs, 1);
|
||||||
|
|
||||||
|
@ -937,7 +840,7 @@ ACTOR static Future<StatusObject> recoveryStateStatusFetcher(std::pair<WorkerInt
|
||||||
message["required_proxies"] = requiredProxies;
|
message["required_proxies"] = requiredProxies;
|
||||||
message["required_resolvers"] = requiredResolvers;
|
message["required_resolvers"] = requiredResolvers;
|
||||||
} else if (mStatusCode == RecoveryStatus::locking_old_transaction_servers) {
|
} else if (mStatusCode == RecoveryStatus::locking_old_transaction_servers) {
|
||||||
message["missing_logs"] = extractAttribute(md, LiteralStringRef("MissingIDs")).c_str();
|
message["missing_logs"] = md.getValue("MissingIDs").c_str();
|
||||||
}
|
}
|
||||||
// TODO: time_in_recovery: 0.5
|
// TODO: time_in_recovery: 0.5
|
||||||
// time_in_state: 0.1
|
// time_in_state: 0.1
|
||||||
|
@ -1165,32 +1068,32 @@ ACTOR static Future<StatusObject> dataStatusFetcher(std::pair<WorkerInterface, P
|
||||||
state StatusObject statusObjData;
|
state StatusObject statusObjData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::vector<Future<Standalone<StringRef>>> futures;
|
std::vector<Future<TraceEventFields>> futures;
|
||||||
|
|
||||||
// TODO: Should this be serial?
|
// TODO: Should this be serial?
|
||||||
futures.push_back(timeoutError(mWorker.first.eventLogRequest.getReply(EventLogRequest(StringRef(dbName + "/DDTrackerStarting"))), 1.0));
|
futures.push_back(timeoutError(mWorker.first.eventLogRequest.getReply(EventLogRequest(StringRef(dbName + "/DDTrackerStarting"))), 1.0));
|
||||||
futures.push_back(timeoutError(mWorker.first.eventLogRequest.getReply(EventLogRequest(StringRef(dbName + "/DDTrackerStats"))), 1.0));
|
futures.push_back(timeoutError(mWorker.first.eventLogRequest.getReply(EventLogRequest(StringRef(dbName + "/DDTrackerStats"))), 1.0));
|
||||||
|
|
||||||
std::vector<Standalone<StringRef>> dataInfo = wait(getAll(futures));
|
std::vector<TraceEventFields> dataInfo = wait(getAll(futures));
|
||||||
|
|
||||||
Standalone<StringRef> startingStats = dataInfo[0];
|
TraceEventFields startingStats = dataInfo[0];
|
||||||
state Standalone<StringRef> dataStats = dataInfo[1];
|
state TraceEventFields dataStats = dataInfo[1];
|
||||||
|
|
||||||
if (startingStats.size() && extractAttribute(startingStats, LiteralStringRef("State")) != "Active") {
|
if (startingStats.size() && startingStats.getValue("State") != "Active") {
|
||||||
stateSectionObj["name"] = "initializing";
|
stateSectionObj["name"] = "initializing";
|
||||||
stateSectionObj["description"] = "(Re)initializing automatic data distribution";
|
stateSectionObj["description"] = "(Re)initializing automatic data distribution";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
state Standalone<StringRef> md = wait(timeoutError(mWorker.first.eventLogRequest.getReply(EventLogRequest(StringRef(dbName + "/MovingData"))), 1.0));
|
state TraceEventFields md = wait(timeoutError(mWorker.first.eventLogRequest.getReply(EventLogRequest(StringRef(dbName + "/MovingData"))), 1.0));
|
||||||
|
|
||||||
// If we have a MovingData message, parse it.
|
// If we have a MovingData message, parse it.
|
||||||
if (md.size())
|
if (md.size())
|
||||||
{
|
{
|
||||||
int64_t partitionsInQueue = parseInt64(extractAttribute(md, LiteralStringRef("InQueue")));
|
int64_t partitionsInQueue = parseInt64(md.getValue("InQueue"));
|
||||||
int64_t partitionsInFlight = parseInt64(extractAttribute(md, LiteralStringRef("InFlight")));
|
int64_t partitionsInFlight = parseInt64(md.getValue("InFlight"));
|
||||||
int64_t averagePartitionSize = parseInt64(extractAttribute(md, LiteralStringRef("AverageShardSize")));
|
int64_t averagePartitionSize = parseInt64(md.getValue("AverageShardSize"));
|
||||||
int64_t totalBytesWritten = parseInt64(extractAttribute(md, LiteralStringRef("BytesWritten")));
|
int64_t totalBytesWritten = parseInt64(md.getValue("BytesWritten"));
|
||||||
int highestPriority = parseInt(extractAttribute(md, LiteralStringRef("HighestPriority")));
|
int highestPriority = parseInt(md.getValue("HighestPriority"));
|
||||||
|
|
||||||
if( averagePartitionSize >= 0 ) {
|
if( averagePartitionSize >= 0 ) {
|
||||||
StatusObject moving_data;
|
StatusObject moving_data;
|
||||||
|
@ -1253,9 +1156,9 @@ ACTOR static Future<StatusObject> dataStatusFetcher(std::pair<WorkerInterface, P
|
||||||
|
|
||||||
if (dataStats.size())
|
if (dataStats.size())
|
||||||
{
|
{
|
||||||
int64_t totalDBBytes = parseInt64(extractAttribute(dataStats, LiteralStringRef("TotalSizeBytes")));
|
int64_t totalDBBytes = parseInt64(dataStats.getValue("TotalSizeBytes"));
|
||||||
statusObjData["total_kv_size_bytes"] = totalDBBytes;
|
statusObjData["total_kv_size_bytes"] = totalDBBytes;
|
||||||
int shards = parseInt(extractAttribute(dataStats, LiteralStringRef("Shards")));
|
int shards = parseInt(dataStats.getValue("Shards"));
|
||||||
statusObjData["partitions_count"] = shards;
|
statusObjData["partitions_count"] = shards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,30 +1190,30 @@ namespace std
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR template <class iface>
|
ACTOR template <class iface>
|
||||||
static Future<vector<std::pair<iface, std::string>>> getServerMetrics(vector<iface> servers, std::unordered_map<NetworkAddress, WorkerInterface> address_workers, std::string suffix) {
|
static Future<vector<std::pair<iface, TraceEventFields>>> getServerMetrics(vector<iface> servers, std::unordered_map<NetworkAddress, WorkerInterface> address_workers, std::string suffix) {
|
||||||
state vector<Future<Optional<std::string>>> futures;
|
state vector<Future<Optional<TraceEventFields>>> futures;
|
||||||
for (auto s : servers) {
|
for (auto s : servers) {
|
||||||
futures.push_back(latestEventOnWorker(address_workers[s.address()], s.id().toString() + suffix));
|
futures.push_back(latestEventOnWorker(address_workers[s.address()], s.id().toString() + suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
Void _ = wait(waitForAll(futures));
|
Void _ = wait(waitForAll(futures));
|
||||||
|
|
||||||
vector<std::pair<iface, std::string>> results;
|
vector<std::pair<iface, TraceEventFields>> results;
|
||||||
for (int i = 0; i < servers.size(); i++) {
|
for (int i = 0; i < servers.size(); i++) {
|
||||||
results.push_back(std::make_pair(servers[i], futures[i].get().present() ? futures[i].get().get() : ""));
|
results.push_back(std::make_pair(servers[i], futures[i].get().present() ? futures[i].get().get() : TraceEventFields()));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR static Future<vector<std::pair<StorageServerInterface, std::string>>> getStorageServersAndMetrics(Database cx, std::unordered_map<NetworkAddress, WorkerInterface> address_workers) {
|
ACTOR static Future<vector<std::pair<StorageServerInterface, TraceEventFields>>> getStorageServersAndMetrics(Database cx, std::unordered_map<NetworkAddress, WorkerInterface> address_workers) {
|
||||||
vector<StorageServerInterface> servers = wait(timeoutError(getStorageServers(cx, true), 5.0));
|
vector<StorageServerInterface> servers = wait(timeoutError(getStorageServers(cx, true), 5.0));
|
||||||
vector<std::pair<StorageServerInterface, std::string>> results = wait(getServerMetrics(servers, address_workers, "/StorageMetrics"));
|
vector<std::pair<StorageServerInterface, TraceEventFields>> results = wait(getServerMetrics(servers, address_workers, "/StorageMetrics"));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR static Future<vector<std::pair<TLogInterface, std::string>>> getTLogsAndMetrics(Reference<AsyncVar<struct ServerDBInfo>> db, std::unordered_map<NetworkAddress, WorkerInterface> address_workers) {
|
ACTOR static Future<vector<std::pair<TLogInterface, TraceEventFields>>> getTLogsAndMetrics(Reference<AsyncVar<struct ServerDBInfo>> db, std::unordered_map<NetworkAddress, WorkerInterface> address_workers) {
|
||||||
vector<TLogInterface> servers = db->get().logSystemConfig.allPresentLogs();
|
vector<TLogInterface> servers = db->get().logSystemConfig.allPresentLogs();
|
||||||
vector<std::pair<TLogInterface, std::string>> results = wait(getServerMetrics(servers, address_workers, "/TLogMetrics"));
|
vector<std::pair<TLogInterface, TraceEventFields>> results = wait(getServerMetrics(servers, address_workers, "/TLogMetrics"));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1334,6 +1237,7 @@ static int getExtraTLogEligibleMachines(vector<std::pair<WorkerInterface, Proces
|
||||||
int extraTlogEligibleMachines = std::numeric_limits<int>::max();
|
int extraTlogEligibleMachines = std::numeric_limits<int>::max();
|
||||||
for(auto& region : configuration.regions) {
|
for(auto& region : configuration.regions) {
|
||||||
extraTlogEligibleMachines = std::min<int>( extraTlogEligibleMachines, dcId_machine[region.dcId].size() - std::max(configuration.remoteTLogReplicationFactor, std::max(configuration.tLogReplicationFactor, configuration.storageTeamSize) ) );
|
extraTlogEligibleMachines = std::min<int>( extraTlogEligibleMachines, dcId_machine[region.dcId].size() - std::max(configuration.remoteTLogReplicationFactor, std::max(configuration.tLogReplicationFactor, configuration.storageTeamSize) ) );
|
||||||
|
//FIXME: does not take into account fallback satellite policies
|
||||||
if(region.satelliteTLogReplicationFactor > 0) {
|
if(region.satelliteTLogReplicationFactor > 0) {
|
||||||
int totalSatelliteEligible = 0;
|
int totalSatelliteEligible = 0;
|
||||||
for(auto& sat : region.satellites) {
|
for(auto& sat : region.satellites) {
|
||||||
|
@ -1346,7 +1250,7 @@ static int getExtraTLogEligibleMachines(vector<std::pair<WorkerInterface, Proces
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struct ServerDBInfo>> db, vector<std::pair<WorkerInterface, ProcessClass>> workers, std::pair<WorkerInterface, ProcessClass> mWorker,
|
ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struct ServerDBInfo>> db, vector<std::pair<WorkerInterface, ProcessClass>> workers, std::pair<WorkerInterface, ProcessClass> mWorker,
|
||||||
std::string dbName, StatusObject *qos, StatusObject *data_overlay, std::set<std::string> *incomplete_reasons, Future<ErrorOr<vector<std::pair<StorageServerInterface, std::string>>>> storageServerFuture)
|
std::string dbName, StatusObject *qos, StatusObject *data_overlay, std::set<std::string> *incomplete_reasons, Future<ErrorOr<vector<std::pair<StorageServerInterface, TraceEventFields>>>> storageServerFuture)
|
||||||
{
|
{
|
||||||
state StatusObject statusObj;
|
state StatusObject statusObj;
|
||||||
state StatusObject operationsObj;
|
state StatusObject operationsObj;
|
||||||
|
@ -1355,7 +1259,7 @@ ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struc
|
||||||
|
|
||||||
// Writes and conflicts
|
// Writes and conflicts
|
||||||
try {
|
try {
|
||||||
vector<Future<Standalone<StringRef>>> proxyStatFutures;
|
vector<Future<TraceEventFields>> proxyStatFutures;
|
||||||
std::map<NetworkAddress, std::pair<WorkerInterface, ProcessClass>> workersMap;
|
std::map<NetworkAddress, std::pair<WorkerInterface, ProcessClass>> workersMap;
|
||||||
for (auto w : workers) {
|
for (auto w : workers) {
|
||||||
workersMap[w.first.address()] = w;
|
workersMap[w.first.address()] = w;
|
||||||
|
@ -1367,16 +1271,16 @@ ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struc
|
||||||
else
|
else
|
||||||
throw all_alternatives_failed(); // We need data from all proxies for this result to be trustworthy
|
throw all_alternatives_failed(); // We need data from all proxies for this result to be trustworthy
|
||||||
}
|
}
|
||||||
vector<Standalone<StringRef>> proxyStats = wait(getAll(proxyStatFutures));
|
vector<TraceEventFields> proxyStats = wait(getAll(proxyStatFutures));
|
||||||
|
|
||||||
StatusObject mutations=makeCounter(), mutationBytes=makeCounter(), txnConflicts=makeCounter(), txnStartOut=makeCounter(), txnCommitOutSuccess=makeCounter();
|
StatusObject mutations=makeCounter(), mutationBytes=makeCounter(), txnConflicts=makeCounter(), txnStartOut=makeCounter(), txnCommitOutSuccess=makeCounter();
|
||||||
|
|
||||||
for (auto &ps : proxyStats) {
|
for (auto &ps : proxyStats) {
|
||||||
mutations = addCounters( mutations, parseCounter(extractAttribute(ps, LiteralStringRef("Mutations"))) );
|
mutations = addCounters( mutations, parseCounter(ps.getValue("Mutations")) );
|
||||||
mutationBytes = addCounters( mutationBytes, parseCounter(extractAttribute(ps, LiteralStringRef("MutationBytes"))) );
|
mutationBytes = addCounters( mutationBytes, parseCounter(ps.getValue("MutationBytes")) );
|
||||||
txnConflicts = addCounters( txnConflicts, parseCounter(extractAttribute(ps, LiteralStringRef("TxnConflicts"))) );
|
txnConflicts = addCounters( txnConflicts, parseCounter(ps.getValue("TxnConflicts")) );
|
||||||
txnStartOut = addCounters( txnStartOut, parseCounter(extractAttribute(ps, LiteralStringRef("TxnStartOut"))) );
|
txnStartOut = addCounters( txnStartOut, parseCounter(ps.getValue("TxnStartOut")) );
|
||||||
txnCommitOutSuccess = addCounters( txnCommitOutSuccess, parseCounter(extractAttribute(ps, LiteralStringRef("TxnCommitOutSuccess"))) );
|
txnCommitOutSuccess = addCounters( txnCommitOutSuccess, parseCounter(ps.getValue("TxnCommitOutSuccess")) );
|
||||||
}
|
}
|
||||||
|
|
||||||
operationsObj["writes"] = mutations;
|
operationsObj["writes"] = mutations;
|
||||||
|
@ -1397,19 +1301,19 @@ ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struc
|
||||||
|
|
||||||
// Transactions
|
// Transactions
|
||||||
try {
|
try {
|
||||||
Standalone<StringRef> md = wait( timeoutError(mWorker.first.eventLogRequest.getReply( EventLogRequest(StringRef(dbName+"/RkUpdate") ) ), 1.0) );
|
TraceEventFields md = wait( timeoutError(mWorker.first.eventLogRequest.getReply( EventLogRequest(StringRef(dbName+"/RkUpdate") ) ), 1.0) );
|
||||||
double tpsLimit = parseDouble(extractAttribute(md, LiteralStringRef("TPSLimit")));
|
double tpsLimit = parseDouble(md.getValue("TPSLimit"));
|
||||||
double transPerSec = parseDouble(extractAttribute(md, LiteralStringRef("ReleasedTPS")));
|
double transPerSec = parseDouble(md.getValue("ReleasedTPS"));
|
||||||
int ssCount = parseInt(extractAttribute(md, LiteralStringRef("StorageServers")));
|
int ssCount = parseInt(md.getValue("StorageServers"));
|
||||||
int tlogCount = parseInt(extractAttribute(md, LiteralStringRef("TLogs")));
|
int tlogCount = parseInt(md.getValue("TLogs"));
|
||||||
int64_t worstFreeSpaceStorageServer = parseInt64(extractAttribute(md, LiteralStringRef("WorstFreeSpaceStorageServer")));
|
int64_t worstFreeSpaceStorageServer = parseInt64(md.getValue("WorstFreeSpaceStorageServer"));
|
||||||
int64_t worstFreeSpaceTLog = parseInt64(extractAttribute(md, LiteralStringRef("WorstFreeSpaceTLog")));
|
int64_t worstFreeSpaceTLog = parseInt64(md.getValue("WorstFreeSpaceTLog"));
|
||||||
int64_t worstStorageServerQueue = parseInt64(extractAttribute(md, LiteralStringRef("WorstStorageServerQueue")));
|
int64_t worstStorageServerQueue = parseInt64(md.getValue("WorstStorageServerQueue"));
|
||||||
int64_t limitingStorageServerQueue = parseInt64(extractAttribute(md, LiteralStringRef("LimitingStorageServerQueue")));
|
int64_t limitingStorageServerQueue = parseInt64(md.getValue("LimitingStorageServerQueue"));
|
||||||
int64_t worstTLogQueue = parseInt64(extractAttribute(md, LiteralStringRef("WorstTLogQueue")));
|
int64_t worstTLogQueue = parseInt64(md.getValue("WorstTLogQueue"));
|
||||||
int64_t totalDiskUsageBytes = parseInt64(extractAttribute(md, LiteralStringRef("TotalDiskUsageBytes")));
|
int64_t totalDiskUsageBytes = parseInt64(md.getValue("TotalDiskUsageBytes"));
|
||||||
int64_t worstVersionLag = parseInt64(extractAttribute(md, LiteralStringRef("WorstStorageServerVersionLag")));
|
int64_t worstVersionLag = parseInt64(md.getValue("WorstStorageServerVersionLag"));
|
||||||
int64_t limitingVersionLag = parseInt64(extractAttribute(md, LiteralStringRef("LimitingStorageServerVersionLag")));
|
int64_t limitingVersionLag = parseInt64(md.getValue("LimitingStorageServerVersionLag"));
|
||||||
(*data_overlay)["total_disk_used_bytes"] = totalDiskUsageBytes;
|
(*data_overlay)["total_disk_used_bytes"] = totalDiskUsageBytes;
|
||||||
|
|
||||||
if(ssCount > 0) {
|
if(ssCount > 0) {
|
||||||
|
@ -1428,13 +1332,13 @@ ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struc
|
||||||
(*qos)["transactions_per_second_limit"] = tpsLimit;
|
(*qos)["transactions_per_second_limit"] = tpsLimit;
|
||||||
(*qos)["released_transactions_per_second"] = transPerSec;
|
(*qos)["released_transactions_per_second"] = transPerSec;
|
||||||
|
|
||||||
int reason = parseInt(extractAttribute(md, LiteralStringRef("Reason")));
|
int reason = parseInt(md.getValue("Reason"));
|
||||||
StatusObject perfLimit;
|
StatusObject perfLimit;
|
||||||
if (transPerSec > tpsLimit * 0.8) {
|
if (transPerSec > tpsLimit * 0.8) {
|
||||||
// If reason is known, set qos.performance_limited_by, otherwise omit
|
// If reason is known, set qos.performance_limited_by, otherwise omit
|
||||||
if (reason >= 0 && reason < limitReasonEnd) {
|
if (reason >= 0 && reason < limitReasonEnd) {
|
||||||
perfLimit = makeMessage(limitReasonName[reason], limitReasonDesc[reason]);
|
perfLimit = makeMessage(limitReasonName[reason], limitReasonDesc[reason]);
|
||||||
std::string reason_server_id = extractAttribute(md, LiteralStringRef("ReasonServerID"));
|
std::string reason_server_id = md.getValue("ReasonServerID");
|
||||||
if (!reason_server_id.empty())
|
if (!reason_server_id.empty())
|
||||||
perfLimit["reason_server_id"] = reason_server_id;
|
perfLimit["reason_server_id"] = reason_server_id;
|
||||||
}
|
}
|
||||||
|
@ -1455,7 +1359,7 @@ ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struc
|
||||||
|
|
||||||
// Reads
|
// Reads
|
||||||
try {
|
try {
|
||||||
ErrorOr<vector<std::pair<StorageServerInterface, std::string>>> storageServers = wait(storageServerFuture);
|
ErrorOr<vector<std::pair<StorageServerInterface, TraceEventFields>>> storageServers = wait(storageServerFuture);
|
||||||
if(!storageServers.present()) {
|
if(!storageServers.present()) {
|
||||||
throw storageServers.getError();
|
throw storageServers.getError();
|
||||||
}
|
}
|
||||||
|
@ -1465,9 +1369,9 @@ ACTOR static Future<StatusObject> workloadStatusFetcher(Reference<AsyncVar<struc
|
||||||
StatusObject readBytes = makeCounter();
|
StatusObject readBytes = makeCounter();
|
||||||
|
|
||||||
for(auto &ss : storageServers.get()) {
|
for(auto &ss : storageServers.get()) {
|
||||||
reads = addCounters(reads, parseCounter(extractAttribute(ss.second, LiteralStringRef("FinishedQueries"))));
|
reads = addCounters(reads, parseCounter(ss.second.getValue("FinishedQueries")));
|
||||||
readKeys = addCounters(readKeys, parseCounter(extractAttribute(ss.second, LiteralStringRef("RowsQueried"))));
|
readKeys = addCounters(readKeys, parseCounter(ss.second.getValue("RowsQueried")));
|
||||||
readBytes = addCounters(readBytes, parseCounter(extractAttribute(ss.second, LiteralStringRef("BytesQueried"))));
|
readBytes = addCounters(readBytes, parseCounter(ss.second.getValue("BytesQueried")));
|
||||||
}
|
}
|
||||||
|
|
||||||
operationsObj["reads"] = reads;
|
operationsObj["reads"] = reads;
|
||||||
|
@ -1827,8 +1731,8 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
||||||
}
|
}
|
||||||
|
|
||||||
state std::map<std::string, StatusObject> processIssues = getProcessIssuesAsMessages(workerIssues);
|
state std::map<std::string, StatusObject> processIssues = getProcessIssuesAsMessages(workerIssues);
|
||||||
state vector<std::pair<StorageServerInterface, std::string>> storageServers;
|
state vector<std::pair<StorageServerInterface, TraceEventFields>> storageServers;
|
||||||
state vector<std::pair<TLogInterface, std::string>> tLogs;
|
state vector<std::pair<TLogInterface, TraceEventFields>> tLogs;
|
||||||
state StatusObject qos;
|
state StatusObject qos;
|
||||||
state StatusObject data_overlay;
|
state StatusObject data_overlay;
|
||||||
|
|
||||||
|
@ -1863,8 +1767,8 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
||||||
state std::unordered_map<NetworkAddress, WorkerInterface> address_workers;
|
state std::unordered_map<NetworkAddress, WorkerInterface> address_workers;
|
||||||
for (auto worker : workers)
|
for (auto worker : workers)
|
||||||
address_workers[worker.first.address()] = worker.first;
|
address_workers[worker.first.address()] = worker.first;
|
||||||
state Future<ErrorOr<vector<std::pair<StorageServerInterface, std::string>>>> storageServerFuture = errorOr(getStorageServersAndMetrics(cx, address_workers));
|
state Future<ErrorOr<vector<std::pair<StorageServerInterface, TraceEventFields>>>> storageServerFuture = errorOr(getStorageServersAndMetrics(cx, address_workers));
|
||||||
state Future<ErrorOr<vector<std::pair<TLogInterface, std::string>>>> tLogFuture = errorOr(getTLogsAndMetrics(db, address_workers));
|
state Future<ErrorOr<vector<std::pair<TLogInterface, TraceEventFields>>>> tLogFuture = errorOr(getTLogsAndMetrics(db, address_workers));
|
||||||
|
|
||||||
state int minReplicasRemaining = -1;
|
state int minReplicasRemaining = -1;
|
||||||
std::vector<Future<StatusObject>> futures2;
|
std::vector<Future<StatusObject>> futures2;
|
||||||
|
@ -1915,7 +1819,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need storage servers now for processStatusFetcher() below.
|
// Need storage servers now for processStatusFetcher() below.
|
||||||
ErrorOr<vector<std::pair<StorageServerInterface, std::string>>> _storageServers = wait(storageServerFuture);
|
ErrorOr<vector<std::pair<StorageServerInterface, TraceEventFields>>> _storageServers = wait(storageServerFuture);
|
||||||
if (_storageServers.present()) {
|
if (_storageServers.present()) {
|
||||||
storageServers = _storageServers.get();
|
storageServers = _storageServers.get();
|
||||||
}
|
}
|
||||||
|
@ -1923,7 +1827,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
||||||
messages.push_back(makeMessage("storage_servers_error", "Timed out trying to retrieve storage servers."));
|
messages.push_back(makeMessage("storage_servers_error", "Timed out trying to retrieve storage servers."));
|
||||||
|
|
||||||
// ...also tlogs
|
// ...also tlogs
|
||||||
ErrorOr<vector<std::pair<TLogInterface, std::string>>> _tLogs = wait(tLogFuture);
|
ErrorOr<vector<std::pair<TLogInterface, TraceEventFields>>> _tLogs = wait(tLogFuture);
|
||||||
if (_tLogs.present()) {
|
if (_tLogs.present()) {
|
||||||
tLogs = _tLogs.get();
|
tLogs = _tLogs.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
typedef std::map< NetworkAddress, std::pair<std::string,UID> > ProcessIssuesMap;
|
typedef std::map< NetworkAddress, std::pair<std::string,UID> > ProcessIssuesMap;
|
||||||
typedef std::map< NetworkAddress, Standalone<VectorRef<ClientVersionRef>> > ClientVersionMap;
|
typedef std::map< NetworkAddress, Standalone<VectorRef<ClientVersionRef>> > ClientVersionMap;
|
||||||
|
|
||||||
std::string extractAttribute( std::string const& expanded, std::string const& attributeToExtract );
|
|
||||||
Future<StatusReply> clusterGetStatus( Reference<AsyncVar<struct ServerDBInfo>> const& db, Database const& cx, vector<std::pair<WorkerInterface, ProcessClass>> const& workers,
|
Future<StatusReply> clusterGetStatus( Reference<AsyncVar<struct ServerDBInfo>> const& db, Database const& cx, vector<std::pair<WorkerInterface, ProcessClass>> const& workers,
|
||||||
ProcessIssuesMap const& workerIssues, ProcessIssuesMap const& clientIssues, ClientVersionMap const& clientVersionMap, std::map<NetworkAddress, std::string> const& traceLogGroupMap,
|
ProcessIssuesMap const& workerIssues, ProcessIssuesMap const& clientIssues, ClientVersionMap const& clientVersionMap, std::map<NetworkAddress, std::string> const& traceLogGroupMap,
|
||||||
ServerCoordinators const& coordinators, std::vector<NetworkAddress> const& incompatibleConnections, Version const& datacenterVersionDifference );
|
ServerCoordinators const& coordinators, std::vector<NetworkAddress> const& incompatibleConnections, Version const& datacenterVersionDifference );
|
||||||
|
|
|
@ -127,7 +127,7 @@ struct VerUpdateRef {
|
||||||
VerUpdateRef( Arena& to, const VerUpdateRef& from ) : version(from.version), mutations( to, from.mutations ), isPrivateData( from.isPrivateData ) {}
|
VerUpdateRef( Arena& to, const VerUpdateRef& from ) : version(from.version), mutations( to, from.mutations ), isPrivateData( from.isPrivateData ) {}
|
||||||
int expectedSize() const { return mutations.expectedSize(); }
|
int expectedSize() const { return mutations.expectedSize(); }
|
||||||
|
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize( Ar& ar ) {
|
void serialize( Ar& ar ) {
|
||||||
ar & version & mutations & isPrivateData;
|
ar & version & mutations & isPrivateData;
|
||||||
}
|
}
|
||||||
|
@ -139,10 +139,12 @@ struct TLogPeekReply {
|
||||||
Version end;
|
Version end;
|
||||||
Optional<Version> popped;
|
Optional<Version> popped;
|
||||||
Version maxKnownVersion;
|
Version maxKnownVersion;
|
||||||
|
Version minKnownCommittedVersion;
|
||||||
|
Optional<Version> begin;
|
||||||
|
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize(Ar& ar) {
|
void serialize(Ar& ar) {
|
||||||
ar & arena & messages & end & popped & maxKnownVersion;
|
ar & arena & messages & end & popped & maxKnownVersion & minKnownCommittedVersion & begin;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,16 +168,16 @@ struct TLogPeekRequest {
|
||||||
struct TLogPopRequest {
|
struct TLogPopRequest {
|
||||||
Arena arena;
|
Arena arena;
|
||||||
Version to;
|
Version to;
|
||||||
Version knownCommittedVersion;
|
Version durableKnownCommittedVersion;
|
||||||
Tag tag;
|
Tag tag;
|
||||||
ReplyPromise<Void> reply;
|
ReplyPromise<Void> reply;
|
||||||
|
|
||||||
TLogPopRequest( Version to, Version knownCommittedVersion, Tag tag ) : to(to), knownCommittedVersion(knownCommittedVersion), tag(tag) {}
|
TLogPopRequest( Version to, Version durableKnownCommittedVersion, Tag tag ) : to(to), durableKnownCommittedVersion(durableKnownCommittedVersion), tag(tag) {}
|
||||||
TLogPopRequest() {}
|
TLogPopRequest() {}
|
||||||
|
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize(Ar& ar) {
|
void serialize(Ar& ar) {
|
||||||
ar & arena & to & knownCommittedVersion & tag & reply;
|
ar & arena & to & durableKnownCommittedVersion & tag & reply;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -198,19 +200,19 @@ struct TagMessagesRef {
|
||||||
|
|
||||||
struct TLogCommitRequest {
|
struct TLogCommitRequest {
|
||||||
Arena arena;
|
Arena arena;
|
||||||
Version prevVersion, version, knownCommittedVersion;
|
Version prevVersion, version, knownCommittedVersion, minKnownCommittedVersion;
|
||||||
|
|
||||||
StringRef messages;// Each message prefixed by a 4-byte length
|
StringRef messages;// Each message prefixed by a 4-byte length
|
||||||
|
|
||||||
ReplyPromise<Void> reply;
|
ReplyPromise<Version> reply;
|
||||||
Optional<UID> debugID;
|
Optional<UID> debugID;
|
||||||
|
|
||||||
TLogCommitRequest() {}
|
TLogCommitRequest() {}
|
||||||
TLogCommitRequest( const Arena& a, Version prevVersion, Version version, Version knownCommittedVersion, StringRef messages, Optional<UID> debugID )
|
TLogCommitRequest( const Arena& a, Version prevVersion, Version version, Version knownCommittedVersion, Version minKnownCommittedVersion, StringRef messages, Optional<UID> debugID )
|
||||||
: arena(a), prevVersion(prevVersion), version(version), knownCommittedVersion(knownCommittedVersion), messages(messages), debugID(debugID) {}
|
: arena(a), prevVersion(prevVersion), version(version), knownCommittedVersion(knownCommittedVersion), minKnownCommittedVersion(minKnownCommittedVersion), messages(messages), debugID(debugID) {}
|
||||||
template <class Ar>
|
template <class Ar>
|
||||||
void serialize( Ar& ar ) {
|
void serialize( Ar& ar ) {
|
||||||
ar & prevVersion & version & knownCommittedVersion & messages & reply & arena & debugID;
|
ar & prevVersion & version & knownCommittedVersion & minKnownCommittedVersion & messages & reply & arena & debugID;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,7 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
||||||
VersionMetricHandle persistentDataVersion, persistentDataDurableVersion; // The last version number in the portion of the log (written|durable) to persistentData
|
VersionMetricHandle persistentDataVersion, persistentDataDurableVersion; // The last version number in the portion of the log (written|durable) to persistentData
|
||||||
NotifiedVersion version, queueCommittedVersion;
|
NotifiedVersion version, queueCommittedVersion;
|
||||||
Version queueCommittingVersion;
|
Version queueCommittingVersion;
|
||||||
Version knownCommittedVersion, durableKnownCommittedVersion;
|
Version knownCommittedVersion, durableKnownCommittedVersion, minKnownCommittedVersion;
|
||||||
|
|
||||||
Deque<std::pair<Version, Standalone<VectorRef<uint8_t>>>> messageBlocks;
|
Deque<std::pair<Version, Standalone<VectorRef<uint8_t>>>> messageBlocks;
|
||||||
std::vector<std::vector<Reference<TagData>>> tag_data; //tag.locality | tag.id
|
std::vector<std::vector<Reference<TagData>>> tag_data; //tag.locality | tag.id
|
||||||
|
@ -409,7 +409,7 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
||||||
|
|
||||||
explicit LogData(TLogData* tLogData, TLogInterface interf, Tag remoteTag, bool isPrimary, int logRouterTags, UID recruitmentID) : tLogData(tLogData), knownCommittedVersion(1), logId(interf.id()),
|
explicit LogData(TLogData* tLogData, TLogInterface interf, Tag remoteTag, bool isPrimary, int logRouterTags, UID recruitmentID) : tLogData(tLogData), knownCommittedVersion(1), logId(interf.id()),
|
||||||
cc("TLog", interf.id().toString()), bytesInput("BytesInput", cc), bytesDurable("BytesDurable", cc), remoteTag(remoteTag), isPrimary(isPrimary), logRouterTags(logRouterTags), recruitmentID(recruitmentID),
|
cc("TLog", interf.id().toString()), bytesInput("BytesInput", cc), bytesDurable("BytesDurable", cc), remoteTag(remoteTag), isPrimary(isPrimary), logRouterTags(logRouterTags), recruitmentID(recruitmentID),
|
||||||
logSystem(new AsyncVar<Reference<ILogSystem>>()), logRouterPoppedVersion(0), durableKnownCommittedVersion(0),
|
logSystem(new AsyncVar<Reference<ILogSystem>>()), logRouterPoppedVersion(0), durableKnownCommittedVersion(0), minKnownCommittedVersion(0),
|
||||||
// These are initialized differently on init() or recovery
|
// These are initialized differently on init() or recovery
|
||||||
recoveryCount(), stopped(false), initialized(false), queueCommittingVersion(0), newPersistentDataVersion(invalidVersion), unrecoveredBefore(1), recoveredAt(1), unpoppedRecoveredTags(0),
|
recoveryCount(), stopped(false), initialized(false), queueCommittingVersion(0), newPersistentDataVersion(invalidVersion), unrecoveredBefore(1), recoveredAt(1), unpoppedRecoveredTags(0),
|
||||||
logRouterPopToVersion(0), locality(tagLocalityInvalid)
|
logRouterPopToVersion(0), locality(tagLocalityInvalid)
|
||||||
|
@ -990,6 +990,7 @@ ACTOR Future<Void> tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere
|
||||||
if(poppedVer > req.begin) {
|
if(poppedVer > req.begin) {
|
||||||
TLogPeekReply rep;
|
TLogPeekReply rep;
|
||||||
rep.maxKnownVersion = logData->version.get();
|
rep.maxKnownVersion = logData->version.get();
|
||||||
|
rep.minKnownCommittedVersion = logData->minKnownCommittedVersion;
|
||||||
rep.popped = poppedVer;
|
rep.popped = poppedVer;
|
||||||
rep.end = poppedVer;
|
rep.end = poppedVer;
|
||||||
|
|
||||||
|
@ -1006,6 +1007,7 @@ ACTOR Future<Void> tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere
|
||||||
} else {
|
} else {
|
||||||
sequenceData.send(rep.end);
|
sequenceData.send(rep.end);
|
||||||
}
|
}
|
||||||
|
rep.begin = req.begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.reply.send( rep );
|
req.reply.send( rep );
|
||||||
|
@ -1048,6 +1050,7 @@ ACTOR Future<Void> tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere
|
||||||
|
|
||||||
TLogPeekReply reply;
|
TLogPeekReply reply;
|
||||||
reply.maxKnownVersion = logData->version.get();
|
reply.maxKnownVersion = logData->version.get();
|
||||||
|
reply.minKnownCommittedVersion = logData->minKnownCommittedVersion;
|
||||||
reply.messages = messages.toStringRef();
|
reply.messages = messages.toStringRef();
|
||||||
reply.end = endVersion;
|
reply.end = endVersion;
|
||||||
|
|
||||||
|
@ -1066,6 +1069,7 @@ ACTOR Future<Void> tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere
|
||||||
} else {
|
} else {
|
||||||
sequenceData.send(reply.end);
|
sequenceData.send(reply.end);
|
||||||
}
|
}
|
||||||
|
reply.begin = req.begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.reply.send( reply );
|
req.reply.send( reply );
|
||||||
|
@ -1166,7 +1170,7 @@ ACTOR Future<Void> tLogCommit(
|
||||||
g_traceBatch.addEvent("CommitDebug", tlogDebugID.get().first(), "TLog.tLogCommit.BeforeWaitForVersion");
|
g_traceBatch.addEvent("CommitDebug", tlogDebugID.get().first(), "TLog.tLogCommit.BeforeWaitForVersion");
|
||||||
}
|
}
|
||||||
|
|
||||||
logData->knownCommittedVersion = std::max(logData->knownCommittedVersion, req.knownCommittedVersion);
|
logData->minKnownCommittedVersion = std::max(logData->minKnownCommittedVersion, req.minKnownCommittedVersion);
|
||||||
|
|
||||||
Void _ = wait( logData->version.whenAtLeast( req.prevVersion ) );
|
Void _ = wait( logData->version.whenAtLeast( req.prevVersion ) );
|
||||||
|
|
||||||
|
@ -1199,10 +1203,12 @@ ACTOR Future<Void> tLogCommit(
|
||||||
TraceEvent("TLogCommit", logData->logId).detail("Version", req.version);
|
TraceEvent("TLogCommit", logData->logId).detail("Version", req.version);
|
||||||
commitMessages(logData, req.version, req.arena, req.messages, self->bytesInput);
|
commitMessages(logData, req.version, req.arena, req.messages, self->bytesInput);
|
||||||
|
|
||||||
|
logData->knownCommittedVersion = std::max(logData->knownCommittedVersion, req.knownCommittedVersion);
|
||||||
|
|
||||||
// Log the changes to the persistent queue, to be committed by commitQueue()
|
// Log the changes to the persistent queue, to be committed by commitQueue()
|
||||||
TLogQueueEntryRef qe;
|
TLogQueueEntryRef qe;
|
||||||
qe.version = req.version;
|
qe.version = req.version;
|
||||||
qe.knownCommittedVersion = req.knownCommittedVersion;
|
qe.knownCommittedVersion = logData->knownCommittedVersion;
|
||||||
qe.messages = req.messages;
|
qe.messages = req.messages;
|
||||||
qe.id = logData->logId;
|
qe.id = logData->logId;
|
||||||
self->persistentQueue->push( qe, logData );
|
self->persistentQueue->push( qe, logData );
|
||||||
|
@ -1232,7 +1238,7 @@ ACTOR Future<Void> tLogCommit(
|
||||||
if(req.debugID.present())
|
if(req.debugID.present())
|
||||||
g_traceBatch.addEvent("CommitDebug", tlogDebugID.get().first(), "TLog.tLogCommit.After");
|
g_traceBatch.addEvent("CommitDebug", tlogDebugID.get().first(), "TLog.tLogCommit.After");
|
||||||
|
|
||||||
req.reply.send( Void() );
|
req.reply.send( logData->durableKnownCommittedVersion );
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1452,7 +1458,7 @@ void removeLog( TLogData* self, Reference<LogData> logData ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTOR Future<Void> pullAsyncData( TLogData* self, Reference<LogData> logData, std::vector<Tag> tags, Version beginVersion, Optional<Version> endVersion, bool poppedIsKnownCommitted ) {
|
ACTOR Future<Void> pullAsyncData( TLogData* self, Reference<LogData> logData, std::vector<Tag> tags, Version beginVersion, Optional<Version> endVersion, bool poppedIsKnownCommitted, bool parallelGetMore ) {
|
||||||
state Future<Void> dbInfoChange = Void();
|
state Future<Void> dbInfoChange = Void();
|
||||||
state Reference<ILogSystem::IPeekCursor> r;
|
state Reference<ILogSystem::IPeekCursor> r;
|
||||||
state Version tagAt = beginVersion;
|
state Version tagAt = beginVersion;
|
||||||
|
@ -1462,14 +1468,11 @@ ACTOR Future<Void> pullAsyncData( TLogData* self, Reference<LogData> logData, st
|
||||||
loop {
|
loop {
|
||||||
choose {
|
choose {
|
||||||
when(Void _ = wait( r ? r->getMore(TaskTLogCommit) : Never() ) ) {
|
when(Void _ = wait( r ? r->getMore(TaskTLogCommit) : Never() ) ) {
|
||||||
if(poppedIsKnownCommitted) {
|
|
||||||
logData->knownCommittedVersion = std::max(logData->knownCommittedVersion, r->popped());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
when( Void _ = wait( dbInfoChange ) ) {
|
when( Void _ = wait( dbInfoChange ) ) {
|
||||||
if( logData->logSystem->get() ) {
|
if( logData->logSystem->get() ) {
|
||||||
r = logData->logSystem->get()->peek( logData->logId, tagAt, tags );
|
r = logData->logSystem->get()->peek( logData->logId, tagAt, tags, parallelGetMore );
|
||||||
} else {
|
} else {
|
||||||
r = Reference<ILogSystem::IPeekCursor>();
|
r = Reference<ILogSystem::IPeekCursor>();
|
||||||
}
|
}
|
||||||
|
@ -1504,6 +1507,11 @@ ACTOR Future<Void> pullAsyncData( TLogData* self, Reference<LogData> logData, st
|
||||||
if(endVersion.present() && ver > endVersion.get()) {
|
if(endVersion.present() && ver > endVersion.get()) {
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(poppedIsKnownCommitted) {
|
||||||
|
logData->knownCommittedVersion = std::max(logData->knownCommittedVersion, r->popped());
|
||||||
|
}
|
||||||
|
|
||||||
commitMessages(logData, ver, messages, self->bytesInput);
|
commitMessages(logData, ver, messages, self->bytesInput);
|
||||||
|
|
||||||
// Log the changes to the persistent queue, to be committed by commitQueue()
|
// Log the changes to the persistent queue, to be committed by commitQueue()
|
||||||
|
@ -1534,6 +1542,11 @@ ACTOR Future<Void> pullAsyncData( TLogData* self, Reference<LogData> logData, st
|
||||||
if(endVersion.present() && ver > endVersion.get()) {
|
if(endVersion.present() && ver > endVersion.get()) {
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(poppedIsKnownCommitted) {
|
||||||
|
logData->knownCommittedVersion = std::max(logData->knownCommittedVersion, r->popped());
|
||||||
|
}
|
||||||
|
|
||||||
// Log the changes to the persistent queue, to be committed by commitQueue()
|
// Log the changes to the persistent queue, to be committed by commitQueue()
|
||||||
TLogQueueEntryRef qe;
|
TLogQueueEntryRef qe;
|
||||||
qe.version = ver;
|
qe.version = ver;
|
||||||
|
@ -1591,7 +1604,7 @@ ACTOR Future<Void> tLogCore( TLogData* self, Reference<LogData> logData, TLogInt
|
||||||
if(!logData->isPrimary) {
|
if(!logData->isPrimary) {
|
||||||
std::vector<Tag> tags;
|
std::vector<Tag> tags;
|
||||||
tags.push_back(logData->remoteTag);
|
tags.push_back(logData->remoteTag);
|
||||||
logData->addActor.send( pullAsyncData(self, logData, tags, logData->unrecoveredBefore, Optional<Version>(), true) );
|
logData->addActor.send( pullAsyncData(self, logData, tags, logData->unrecoveredBefore, Optional<Version>(), true, false) );
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1949,10 +1962,10 @@ ACTOR Future<Void> tLogStart( TLogData* self, InitializeTLogRequest req, Localit
|
||||||
logData->logRouterPopToVersion = req.recoverAt;
|
logData->logRouterPopToVersion = req.recoverAt;
|
||||||
std::vector<Tag> tags;
|
std::vector<Tag> tags;
|
||||||
tags.push_back(logData->remoteTag);
|
tags.push_back(logData->remoteTag);
|
||||||
Void _ = wait(pullAsyncData(self, logData, tags, logData->unrecoveredBefore, req.recoverAt, true) || logData->removed);
|
Void _ = wait(pullAsyncData(self, logData, tags, logData->unrecoveredBefore, req.recoverAt, true, false) || logData->removed);
|
||||||
} else if(!req.recoverTags.empty()) {
|
} else if(!req.recoverTags.empty()) {
|
||||||
ASSERT(logData->unrecoveredBefore > req.knownCommittedVersion);
|
ASSERT(logData->unrecoveredBefore > req.knownCommittedVersion);
|
||||||
Void _ = wait(pullAsyncData(self, logData, req.recoverTags, req.knownCommittedVersion + 1, req.recoverAt, false) || logData->removed);
|
Void _ = wait(pullAsyncData(self, logData, req.recoverTags, req.knownCommittedVersion + 1, req.recoverAt, false, true) || logData->removed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,15 @@
|
||||||
#include "fdbrpc/ReplicationUtils.h"
|
#include "fdbrpc/ReplicationUtils.h"
|
||||||
#include "RecoveryState.h"
|
#include "RecoveryState.h"
|
||||||
|
|
||||||
ACTOR static Future<Void> reportTLogCommitErrors( Future<Void> commitReply, UID debugID ) {
|
ACTOR Future<Version> minVersionWhenReady( Future<Void> f, std::vector<Future<Version>> replies) {
|
||||||
try {
|
Void _ = wait(f);
|
||||||
Void _ = wait(commitReply);
|
Version minVersion = std::numeric_limits<Version>::max();
|
||||||
return Void();
|
for(auto& reply : replies) {
|
||||||
} catch (Error& e) {
|
if(reply.isReady() && !reply.isError()) {
|
||||||
if (e.code() == error_code_broken_promise)
|
minVersion = std::min(minVersion, reply.get());
|
||||||
throw master_tlog_failed();
|
}
|
||||||
else if (e.code() != error_code_actor_cancelled && e.code() != error_code_tlog_stopped)
|
|
||||||
TraceEvent(SevError, "MasterTLogCommitRequestError", debugID).error(e);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
return minVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OldLogData {
|
struct OldLogData {
|
||||||
|
@ -79,8 +77,8 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
bool remoteLogsWrittenToCoreState;
|
bool remoteLogsWrittenToCoreState;
|
||||||
bool hasRemoteServers;
|
bool hasRemoteServers;
|
||||||
|
|
||||||
Optional<Version> epochEndVersion;
|
Optional<Version> recoverAt;
|
||||||
Optional<Version> previousEpochEndVersion;
|
Optional<Version> recoveredAt;
|
||||||
Version knownCommittedVersion;
|
Version knownCommittedVersion;
|
||||||
LocalityData locality;
|
LocalityData locality;
|
||||||
std::map< std::pair<UID, Tag>, std::pair<Version, Version> > outstandingPops; // For each currently running popFromLog actor, (log server #, tag)->popped version
|
std::map< std::pair<UID, Tag>, std::pair<Version, Version> > outstandingPops; // For each currently running popFromLog actor, (log server #, tag)->popped version
|
||||||
|
@ -122,7 +120,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
return epochEnd( outLogSystem, dbgid, oldState, rejoins, locality );
|
return epochEnd( outLogSystem, dbgid, oldState, rejoins, locality );
|
||||||
}
|
}
|
||||||
|
|
||||||
static Reference<ILogSystem> fromLogSystemConfig( UID const& dbgid, LocalityData const& locality, LogSystemConfig const& lsConf, bool excludeRemote, bool usePreviousEpochEnd, Optional<PromiseStream<Future<Void>>> addActor ) {
|
static Reference<ILogSystem> fromLogSystemConfig( UID const& dbgid, LocalityData const& locality, LogSystemConfig const& lsConf, bool excludeRemote, bool useRecoveredAt, Optional<PromiseStream<Future<Void>>> addActor ) {
|
||||||
ASSERT( lsConf.logSystemType == 2 || (lsConf.logSystemType == 0 && !lsConf.tLogs.size()) );
|
ASSERT( lsConf.logSystemType == 2 || (lsConf.logSystemType == 0 && !lsConf.tLogs.size()) );
|
||||||
//ASSERT(lsConf.epoch == epoch); //< FIXME
|
//ASSERT(lsConf.epoch == epoch); //< FIXME
|
||||||
Reference<TagPartitionedLogSystem> logSystem( new TagPartitionedLogSystem(dbgid, locality, addActor) );
|
Reference<TagPartitionedLogSystem> logSystem( new TagPartitionedLogSystem(dbgid, locality, addActor) );
|
||||||
|
@ -132,8 +130,8 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
logSystem->logRouterTags = lsConf.logRouterTags;
|
logSystem->logRouterTags = lsConf.logRouterTags;
|
||||||
logSystem->recruitmentID = lsConf.recruitmentID;
|
logSystem->recruitmentID = lsConf.recruitmentID;
|
||||||
logSystem->stopped = lsConf.stopped;
|
logSystem->stopped = lsConf.stopped;
|
||||||
if(usePreviousEpochEnd) {
|
if(useRecoveredAt) {
|
||||||
logSystem->previousEpochEndVersion = lsConf.previousEpochEndVersion;
|
logSystem->recoveredAt = lsConf.recoveredAt;
|
||||||
}
|
}
|
||||||
for( int i = 0; i < lsConf.tLogs.size(); i++ ) {
|
for( int i = 0; i < lsConf.tLogs.size(); i++ ) {
|
||||||
TLogSet const& tLogSet = lsConf.tLogs[i];
|
TLogSet const& tLogSet = lsConf.tLogs[i];
|
||||||
|
@ -391,27 +389,26 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Future<Void> push( Version prevVersion, Version version, Version knownCommittedVersion, LogPushData& data, Optional<UID> debugID ) {
|
virtual Future<Version> push( Version prevVersion, Version version, Version knownCommittedVersion, Version minKnownCommittedVersion, LogPushData& data, Optional<UID> debugID ) {
|
||||||
// FIXME: Randomize request order as in LegacyLogSystem?
|
// FIXME: Randomize request order as in LegacyLogSystem?
|
||||||
vector<Future<Void>> quorumResults;
|
vector<Future<Void>> quorumResults;
|
||||||
|
vector<Future<Version>> allReplies;
|
||||||
int location = 0;
|
int location = 0;
|
||||||
for(auto& it : tLogs) {
|
for(auto& it : tLogs) {
|
||||||
if(it->isLocal && it->logServers.size()) {
|
if(it->isLocal && it->logServers.size()) {
|
||||||
vector<Future<Void>> tLogCommitResults;
|
vector<Future<Void>> tLogCommitResults;
|
||||||
for(int loc=0; loc< it->logServers.size(); loc++) {
|
for(int loc=0; loc< it->logServers.size(); loc++) {
|
||||||
Future<Void> commitMessage = reportTLogCommitErrors(
|
allReplies.push_back( it->logServers[loc]->get().interf().commit.getReply( TLogCommitRequest( data.getArena(), prevVersion, version, knownCommittedVersion, minKnownCommittedVersion, data.getMessages(location), debugID ), TaskTLogCommitReply ) );
|
||||||
it->logServers[loc]->get().interf().commit.getReply(
|
Future<Void> commitSuccess = success(allReplies.back());
|
||||||
TLogCommitRequest( data.getArena(), prevVersion, version, knownCommittedVersion, data.getMessages(location), debugID ), TaskTLogCommitReply ),
|
addActor.get().send(commitSuccess);
|
||||||
getDebugID());
|
tLogCommitResults.push_back(commitSuccess);
|
||||||
addActor.get().send(commitMessage);
|
|
||||||
tLogCommitResults.push_back(commitMessage);
|
|
||||||
location++;
|
location++;
|
||||||
}
|
}
|
||||||
quorumResults.push_back( quorum( tLogCommitResults, tLogCommitResults.size() - it->tLogWriteAntiQuorum ) );
|
quorumResults.push_back( quorum( tLogCommitResults, tLogCommitResults.size() - it->tLogWriteAntiQuorum ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitForAll(quorumResults);
|
return minVersionWhenReady( waitForAll(quorumResults), allReplies);
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference<IPeekCursor> peekAll( UID dbgid, Version begin, Version end, Tag tag, bool parallelGetMore, bool throwIfDead ) {
|
Reference<IPeekCursor> peekAll( UID dbgid, Version begin, Version end, Tag tag, bool parallelGetMore, bool throwIfDead ) {
|
||||||
|
@ -498,7 +495,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
|
|
||||||
Reference<IPeekCursor> peekRemote( UID dbgid, Version begin, Tag tag, bool parallelGetMore ) {
|
Reference<IPeekCursor> peekRemote( UID dbgid, Version begin, Tag tag, bool parallelGetMore ) {
|
||||||
int bestSet = -1;
|
int bestSet = -1;
|
||||||
Version lastBegin = previousEpochEndVersion.present() ? previousEpochEndVersion.get() + 1 : 0;
|
Version lastBegin = recoveredAt.present() ? recoveredAt.get() + 1 : 0;
|
||||||
for(int t = 0; t < tLogs.size(); t++) {
|
for(int t = 0; t < tLogs.size(); t++) {
|
||||||
if(tLogs[t]->isLocal) {
|
if(tLogs[t]->isLocal) {
|
||||||
lastBegin = std::max(lastBegin, tLogs[t]->startVersion);
|
lastBegin = std::max(lastBegin, tLogs[t]->startVersion);
|
||||||
|
@ -757,23 +754,23 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceEvent("TLogPeekLogRouterOldSets", dbgid).detail("Tag", tag.toString()).detail("Begin", begin).detail("OldEpoch", old.epochEnd).detail("PreviousEpochEndVersion", previousEpochEndVersion.present() ? previousEpochEndVersion.get() : -1).detail("FirstOld", firstOld);
|
TraceEvent("TLogPeekLogRouterOldSets", dbgid).detail("Tag", tag.toString()).detail("Begin", begin).detail("OldEpoch", old.epochEnd).detail("RecoveredAt", recoveredAt.present() ? recoveredAt.get() : -1).detail("FirstOld", firstOld);
|
||||||
//FIXME: do this merge on one of the logs in the other data center to avoid sending multiple copies across the WAN
|
//FIXME: do this merge on one of the logs in the other data center to avoid sending multiple copies across the WAN
|
||||||
return Reference<ILogSystem::SetPeekCursor>( new ILogSystem::SetPeekCursor( localSets, bestSet, localSets[bestSet]->bestLocationFor( tag ), tag, begin, firstOld && previousEpochEndVersion.present() ? previousEpochEndVersion.get() + 1 : old.epochEnd, true ) );
|
return Reference<ILogSystem::SetPeekCursor>( new ILogSystem::SetPeekCursor( localSets, bestSet, localSets[bestSet]->bestLocationFor( tag ), tag, begin, firstOld && recoveredAt.present() ? recoveredAt.get() + 1 : old.epochEnd, true ) );
|
||||||
}
|
}
|
||||||
firstOld = false;
|
firstOld = false;
|
||||||
}
|
}
|
||||||
return Reference<ILogSystem::ServerPeekCursor>( new ILogSystem::ServerPeekCursor( Reference<AsyncVar<OptionalInterface<TLogInterface>>>(), tag, begin, getPeekEnd(), false, false ) );
|
return Reference<ILogSystem::ServerPeekCursor>( new ILogSystem::ServerPeekCursor( Reference<AsyncVar<OptionalInterface<TLogInterface>>>(), tag, begin, getPeekEnd(), false, false ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void popLogRouter( Version upTo, Tag tag, Version knownCommittedVersion, int8_t popLocality ) { //FIXME: do not need to pop all generations of old logs
|
void popLogRouter( Version upTo, Tag tag, Version durableKnownCommittedVersion, int8_t popLocality ) { //FIXME: do not need to pop all generations of old logs
|
||||||
if (!upTo) return;
|
if (!upTo) return;
|
||||||
for(auto& t : tLogs) {
|
for(auto& t : tLogs) {
|
||||||
if(t->locality == popLocality) {
|
if(t->locality == popLocality) {
|
||||||
for(auto& log : t->logRouters) {
|
for(auto& log : t->logRouters) {
|
||||||
Version prev = outstandingPops[std::make_pair(log->get().id(),tag)].first;
|
Version prev = outstandingPops[std::make_pair(log->get().id(),tag)].first;
|
||||||
if (prev < upTo)
|
if (prev < upTo)
|
||||||
outstandingPops[std::make_pair(log->get().id(),tag)] = std::make_pair(upTo, knownCommittedVersion);
|
outstandingPops[std::make_pair(log->get().id(),tag)] = std::make_pair(upTo, durableKnownCommittedVersion);
|
||||||
if (prev == 0) {
|
if (prev == 0) {
|
||||||
popActors.add( popFromLog( this, log, tag, 0.0 ) ); //Fast pop time because log routers can only hold 5 seconds of data.
|
popActors.add( popFromLog( this, log, tag, 0.0 ) ); //Fast pop time because log routers can only hold 5 seconds of data.
|
||||||
}
|
}
|
||||||
|
@ -787,7 +784,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
for(auto& log : t->logRouters) {
|
for(auto& log : t->logRouters) {
|
||||||
Version prev = outstandingPops[std::make_pair(log->get().id(),tag)].first;
|
Version prev = outstandingPops[std::make_pair(log->get().id(),tag)].first;
|
||||||
if (prev < upTo)
|
if (prev < upTo)
|
||||||
outstandingPops[std::make_pair(log->get().id(),tag)] = std::make_pair(upTo, knownCommittedVersion);
|
outstandingPops[std::make_pair(log->get().id(),tag)] = std::make_pair(upTo, durableKnownCommittedVersion);
|
||||||
if (prev == 0)
|
if (prev == 0)
|
||||||
popActors.add( popFromLog( this, log, tag, 0.0 ) );
|
popActors.add( popFromLog( this, log, tag, 0.0 ) );
|
||||||
}
|
}
|
||||||
|
@ -796,10 +793,10 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void pop( Version upTo, Tag tag, Version knownCommittedVersion, int8_t popLocality ) {
|
virtual void pop( Version upTo, Tag tag, Version durableKnownCommittedVersion, int8_t popLocality ) {
|
||||||
if (upTo <= 0) return;
|
if (upTo <= 0) return;
|
||||||
if( tag.locality == tagLocalityRemoteLog) {
|
if( tag.locality == tagLocalityRemoteLog) {
|
||||||
popLogRouter(upTo, tag, knownCommittedVersion, popLocality);
|
popLogRouter(upTo, tag, durableKnownCommittedVersion, popLocality);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASSERT(popLocality == tagLocalityInvalid);
|
ASSERT(popLocality == tagLocalityInvalid);
|
||||||
|
@ -808,7 +805,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
for(auto& log : t->logServers) {
|
for(auto& log : t->logServers) {
|
||||||
Version prev = outstandingPops[std::make_pair(log->get().id(),tag)].first;
|
Version prev = outstandingPops[std::make_pair(log->get().id(),tag)].first;
|
||||||
if (prev < upTo)
|
if (prev < upTo)
|
||||||
outstandingPops[std::make_pair(log->get().id(),tag)] = std::make_pair(upTo, knownCommittedVersion);
|
outstandingPops[std::make_pair(log->get().id(),tag)] = std::make_pair(upTo, durableKnownCommittedVersion);
|
||||||
if (prev == 0)
|
if (prev == 0)
|
||||||
popActors.add( popFromLog( this, log, tag, 1.0 ) ); //< FIXME: knob
|
popActors.add( popFromLog( this, log, tag, 1.0 ) ); //< FIXME: knob
|
||||||
}
|
}
|
||||||
|
@ -931,7 +928,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
logSystemConfig.logRouterTags = logRouterTags;
|
logSystemConfig.logRouterTags = logRouterTags;
|
||||||
logSystemConfig.recruitmentID = recruitmentID;
|
logSystemConfig.recruitmentID = recruitmentID;
|
||||||
logSystemConfig.stopped = stopped;
|
logSystemConfig.stopped = stopped;
|
||||||
logSystemConfig.previousEpochEndVersion = previousEpochEndVersion;
|
logSystemConfig.recoveredAt = recoveredAt;
|
||||||
for( int i = 0; i < tLogs.size(); i++ ) {
|
for( int i = 0; i < tLogs.size(); i++ ) {
|
||||||
Reference<LogSet> logSet = tLogs[i];
|
Reference<LogSet> logSet = tLogs[i];
|
||||||
if(logSet->isLocal || remoteLogsWrittenToCoreState) {
|
if(logSet->isLocal || remoteLogsWrittenToCoreState) {
|
||||||
|
@ -1034,12 +1031,12 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Version getEnd() {
|
virtual Version getEnd() {
|
||||||
ASSERT( epochEndVersion.present() );
|
ASSERT( recoverAt.present() );
|
||||||
return epochEndVersion.get() + 1;
|
return recoverAt.get() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Version getPeekEnd() {
|
Version getPeekEnd() {
|
||||||
if (epochEndVersion.present())
|
if (recoverAt.present())
|
||||||
return getEnd();
|
return getEnd();
|
||||||
else
|
else
|
||||||
return std::numeric_limits<Version>::max();
|
return std::numeric_limits<Version>::max();
|
||||||
|
@ -1169,7 +1166,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
// This is a brand new database
|
// This is a brand new database
|
||||||
Reference<TagPartitionedLogSystem> logSystem( new TagPartitionedLogSystem(dbgid, locality) );
|
Reference<TagPartitionedLogSystem> logSystem( new TagPartitionedLogSystem(dbgid, locality) );
|
||||||
logSystem->logSystemType = prevState.logSystemType;
|
logSystem->logSystemType = prevState.logSystemType;
|
||||||
logSystem->epochEndVersion = 0;
|
logSystem->recoverAt = 0;
|
||||||
logSystem->knownCommittedVersion = 0;
|
logSystem->knownCommittedVersion = 0;
|
||||||
logSystem->stopped = true;
|
logSystem->stopped = true;
|
||||||
outLogSystem->set(logSystem);
|
outLogSystem->set(logSystem);
|
||||||
|
@ -1308,7 +1305,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
logSystem->logSystemType = prevState.logSystemType;
|
logSystem->logSystemType = prevState.logSystemType;
|
||||||
logSystem->rejoins = rejoins;
|
logSystem->rejoins = rejoins;
|
||||||
logSystem->lockResults = lockResults;
|
logSystem->lockResults = lockResults;
|
||||||
logSystem->epochEndVersion = minEnd;
|
logSystem->recoverAt = minEnd;
|
||||||
logSystem->knownCommittedVersion = knownCommittedVersion;
|
logSystem->knownCommittedVersion = knownCommittedVersion;
|
||||||
logSystem->remoteLogsWrittenToCoreState = true;
|
logSystem->remoteLogsWrittenToCoreState = true;
|
||||||
logSystem->stopped = true;
|
logSystem->stopped = true;
|
||||||
|
@ -1552,7 +1549,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
req.recruitmentID = self->recruitmentID;
|
req.recruitmentID = self->recruitmentID;
|
||||||
req.storeType = configuration.tLogDataStoreType;
|
req.storeType = configuration.tLogDataStoreType;
|
||||||
req.recoverFrom = oldLogSystem->getLogSystemConfig();
|
req.recoverFrom = oldLogSystem->getLogSystemConfig();
|
||||||
req.recoverAt = oldLogSystem->epochEndVersion.get();
|
req.recoverAt = oldLogSystem->recoverAt.get();
|
||||||
req.knownCommittedVersion = oldLogSystem->knownCommittedVersion;
|
req.knownCommittedVersion = oldLogSystem->knownCommittedVersion;
|
||||||
req.epoch = recoveryCount;
|
req.epoch = recoveryCount;
|
||||||
req.remoteTag = Tag(tagLocalityRemoteLog, i);
|
req.remoteTag = Tag(tagLocalityRemoteLog, i);
|
||||||
|
@ -1598,12 +1595,12 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
state Reference<TagPartitionedLogSystem> logSystem( new TagPartitionedLogSystem(oldLogSystem->getDebugID(), oldLogSystem->locality) );
|
state Reference<TagPartitionedLogSystem> logSystem( new TagPartitionedLogSystem(oldLogSystem->getDebugID(), oldLogSystem->locality) );
|
||||||
logSystem->logSystemType = 2;
|
logSystem->logSystemType = 2;
|
||||||
logSystem->expectedLogSets = 1;
|
logSystem->expectedLogSets = 1;
|
||||||
logSystem->previousEpochEndVersion = oldLogSystem->epochEndVersion;
|
logSystem->recoveredAt = oldLogSystem->recoverAt;
|
||||||
logSystem->recruitmentID = g_random->randomUniqueID();
|
logSystem->recruitmentID = g_random->randomUniqueID();
|
||||||
oldLogSystem->recruitmentID = logSystem->recruitmentID;
|
oldLogSystem->recruitmentID = logSystem->recruitmentID;
|
||||||
|
|
||||||
if(configuration.usableRegions > 1) {
|
if(configuration.usableRegions > 1) {
|
||||||
logSystem->logRouterTags = recr.tLogs.size();
|
logSystem->logRouterTags = recr.tLogs.size() * std::max<int>(1, configuration.desiredLogRouterCount / std::max<int>(1,recr.tLogs.size()));
|
||||||
logSystem->expectedLogSets++;
|
logSystem->expectedLogSets++;
|
||||||
} else {
|
} else {
|
||||||
logSystem->logRouterTags = 0;
|
logSystem->logRouterTags = 0;
|
||||||
|
@ -1620,9 +1617,15 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
|
|
||||||
if(region.satelliteTLogReplicationFactor > 0) {
|
if(region.satelliteTLogReplicationFactor > 0) {
|
||||||
logSystem->tLogs.push_back( Reference<LogSet>( new LogSet() ) );
|
logSystem->tLogs.push_back( Reference<LogSet>( new LogSet() ) );
|
||||||
logSystem->tLogs[1]->tLogWriteAntiQuorum = region.satelliteTLogWriteAntiQuorum;
|
if(recr.satelliteFallback) {
|
||||||
logSystem->tLogs[1]->tLogReplicationFactor = region.satelliteTLogReplicationFactor;
|
logSystem->tLogs[1]->tLogWriteAntiQuorum = region.satelliteTLogWriteAntiQuorumFallback;
|
||||||
logSystem->tLogs[1]->tLogPolicy = region.satelliteTLogPolicy;
|
logSystem->tLogs[1]->tLogReplicationFactor = region.satelliteTLogReplicationFactorFallback;
|
||||||
|
logSystem->tLogs[1]->tLogPolicy = region.satelliteTLogPolicyFallback;
|
||||||
|
} else {
|
||||||
|
logSystem->tLogs[1]->tLogWriteAntiQuorum = region.satelliteTLogWriteAntiQuorum;
|
||||||
|
logSystem->tLogs[1]->tLogReplicationFactor = region.satelliteTLogReplicationFactor;
|
||||||
|
logSystem->tLogs[1]->tLogPolicy = region.satelliteTLogPolicy;
|
||||||
|
}
|
||||||
logSystem->tLogs[1]->isLocal = true;
|
logSystem->tLogs[1]->isLocal = true;
|
||||||
logSystem->tLogs[1]->locality = tagLocalitySatellite;
|
logSystem->tLogs[1]->locality = tagLocalitySatellite;
|
||||||
logSystem->tLogs[1]->startVersion = oldLogSystem->knownCommittedVersion + 1;
|
logSystem->tLogs[1]->startVersion = oldLogSystem->knownCommittedVersion + 1;
|
||||||
|
@ -1710,7 +1713,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
req.recruitmentID = logSystem->recruitmentID;
|
req.recruitmentID = logSystem->recruitmentID;
|
||||||
req.storeType = configuration.tLogDataStoreType;
|
req.storeType = configuration.tLogDataStoreType;
|
||||||
req.recoverFrom = oldLogSystem->getLogSystemConfig();
|
req.recoverFrom = oldLogSystem->getLogSystemConfig();
|
||||||
req.recoverAt = oldLogSystem->epochEndVersion.get();
|
req.recoverAt = oldLogSystem->recoverAt.get();
|
||||||
req.knownCommittedVersion = oldLogSystem->knownCommittedVersion;
|
req.knownCommittedVersion = oldLogSystem->knownCommittedVersion;
|
||||||
req.epoch = recoveryCount;
|
req.epoch = recoveryCount;
|
||||||
req.locality = primaryLocality;
|
req.locality = primaryLocality;
|
||||||
|
@ -1753,7 +1756,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
||||||
req.recruitmentID = logSystem->recruitmentID;
|
req.recruitmentID = logSystem->recruitmentID;
|
||||||
req.storeType = configuration.tLogDataStoreType;
|
req.storeType = configuration.tLogDataStoreType;
|
||||||
req.recoverFrom = oldLogSystem->getLogSystemConfig();
|
req.recoverFrom = oldLogSystem->getLogSystemConfig();
|
||||||
req.recoverAt = oldLogSystem->epochEndVersion.get();
|
req.recoverAt = oldLogSystem->recoverAt.get();
|
||||||
req.knownCommittedVersion = oldLogSystem->knownCommittedVersion;
|
req.knownCommittedVersion = oldLogSystem->knownCommittedVersion;
|
||||||
req.epoch = recoveryCount;
|
req.epoch = recoveryCount;
|
||||||
req.locality = tagLocalitySatellite;
|
req.locality = tagLocalitySatellite;
|
||||||
|
@ -2045,11 +2048,11 @@ Future<Void> ILogSystem::recoverAndEndEpoch(Reference<AsyncVar<Reference<ILogSys
|
||||||
return TagPartitionedLogSystem::recoverAndEndEpoch( outLogSystem, dbgid, oldState, rejoins, locality );
|
return TagPartitionedLogSystem::recoverAndEndEpoch( outLogSystem, dbgid, oldState, rejoins, locality );
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference<ILogSystem> ILogSystem::fromLogSystemConfig( UID const& dbgid, struct LocalityData const& locality, struct LogSystemConfig const& conf, bool excludeRemote, bool usePreviousEpochEnd, Optional<PromiseStream<Future<Void>>> addActor ) {
|
Reference<ILogSystem> ILogSystem::fromLogSystemConfig( UID const& dbgid, struct LocalityData const& locality, struct LogSystemConfig const& conf, bool excludeRemote, bool useRecoveredAt, Optional<PromiseStream<Future<Void>>> addActor ) {
|
||||||
if (conf.logSystemType == 0)
|
if (conf.logSystemType == 0)
|
||||||
return Reference<ILogSystem>();
|
return Reference<ILogSystem>();
|
||||||
else if (conf.logSystemType == 2)
|
else if (conf.logSystemType == 2)
|
||||||
return TagPartitionedLogSystem::fromLogSystemConfig( dbgid, locality, conf, excludeRemote, usePreviousEpochEnd, addActor );
|
return TagPartitionedLogSystem::fromLogSystemConfig( dbgid, locality, conf, excludeRemote, useRecoveredAt, addActor );
|
||||||
else
|
else
|
||||||
throw internal_error();
|
throw internal_error();
|
||||||
}
|
}
|
||||||
|
@ -2063,6 +2066,6 @@ Reference<ILogSystem> ILogSystem::fromOldLogSystemConfig( UID const& dbgid, stru
|
||||||
throw internal_error();
|
throw internal_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference<ILogSystem> ILogSystem::fromServerDBInfo( UID const& dbgid, ServerDBInfo const& dbInfo, bool usePreviousEpochEnd, Optional<PromiseStream<Future<Void>>> addActor ) {
|
Reference<ILogSystem> ILogSystem::fromServerDBInfo( UID const& dbgid, ServerDBInfo const& dbInfo, bool useRecoveredAt, Optional<PromiseStream<Future<Void>>> addActor ) {
|
||||||
return fromLogSystemConfig( dbgid, dbInfo.myLocality, dbInfo.logSystemConfig, false, usePreviousEpochEnd, addActor );
|
return fromLogSystemConfig( dbgid, dbInfo.myLocality, dbInfo.logSystemConfig, false, useRecoveredAt, addActor );
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ struct SetMetricsLogRateRequest {
|
||||||
struct EventLogRequest {
|
struct EventLogRequest {
|
||||||
bool getLastError;
|
bool getLastError;
|
||||||
Standalone<StringRef> eventName;
|
Standalone<StringRef> eventName;
|
||||||
ReplyPromise< Standalone<StringRef> > reply;
|
ReplyPromise< TraceEventFields > reply;
|
||||||
|
|
||||||
EventLogRequest() : getLastError(true) {}
|
EventLogRequest() : getLastError(true) {}
|
||||||
explicit EventLogRequest( Standalone<StringRef> eventName ) : eventName( eventName ), getLastError( false ) {}
|
explicit EventLogRequest( Standalone<StringRef> eventName ) : eventName( eventName ), getLastError( false ) {}
|
||||||
|
|
|
@ -311,7 +311,7 @@ ACTOR Future<Void> newTLogServers( Reference<MasterData> self, RecruitFromConfig
|
||||||
self->dcId_locality[remoteDcId] = loc;
|
self->dcId_locality[remoteDcId] = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<RecruitRemoteFromConfigurationReply> fRemoteWorkers = brokenPromiseToNever( self->clusterController.recruitRemoteFromConfiguration.getReply( RecruitRemoteFromConfigurationRequest( self->configuration, remoteDcId, recr.tLogs.size() ) ) );
|
Future<RecruitRemoteFromConfigurationReply> fRemoteWorkers = brokenPromiseToNever( self->clusterController.recruitRemoteFromConfiguration.getReply( RecruitRemoteFromConfigurationRequest( self->configuration, remoteDcId, recr.tLogs.size() * std::max<int>(1, self->configuration.desiredLogRouterCount / std::max<int>(1, recr.tLogs.size())) ) ) );
|
||||||
|
|
||||||
Reference<ILogSystem> newLogSystem = wait( oldLogSystem->newEpoch( recr, fRemoteWorkers, self->configuration, self->cstate.myDBState.recoveryCount + 1, self->dcId_locality[recr.dcId], self->dcId_locality[remoteDcId], self->allTags, self->recruitmentStalled ) );
|
Reference<ILogSystem> newLogSystem = wait( oldLogSystem->newEpoch( recr, fRemoteWorkers, self->configuration, self->cstate.myDBState.recoveryCount + 1, self->dcId_locality[recr.dcId], self->dcId_locality[remoteDcId], self->allTags, self->recruitmentStalled ) );
|
||||||
self->logSystem = newLogSystem;
|
self->logSystem = newLogSystem;
|
||||||
|
|
|
@ -349,6 +349,8 @@ public:
|
||||||
NotifiedVersion oldestVersion; // See also storageVersion()
|
NotifiedVersion oldestVersion; // See also storageVersion()
|
||||||
NotifiedVersion durableVersion; // At least this version will be readable from storage after a power failure
|
NotifiedVersion durableVersion; // At least this version will be readable from storage after a power failure
|
||||||
|
|
||||||
|
int64_t versionLag; // An estimate for how many versions it takes for the data to move from the logs to this storage server
|
||||||
|
|
||||||
uint64_t logProtocol;
|
uint64_t logProtocol;
|
||||||
|
|
||||||
Reference<ILogSystem> logSystem;
|
Reference<ILogSystem> logSystem;
|
||||||
|
@ -366,6 +368,7 @@ public:
|
||||||
|
|
||||||
AsyncMap<Key,bool> watches;
|
AsyncMap<Key,bool> watches;
|
||||||
int64_t watchBytes;
|
int64_t watchBytes;
|
||||||
|
int64_t numWatches;
|
||||||
AsyncVar<bool> noRecentUpdates;
|
AsyncVar<bool> noRecentUpdates;
|
||||||
double lastUpdate;
|
double lastUpdate;
|
||||||
|
|
||||||
|
@ -400,9 +403,10 @@ public:
|
||||||
|
|
||||||
struct Counters {
|
struct Counters {
|
||||||
CounterCollection cc;
|
CounterCollection cc;
|
||||||
Counter allQueries, getKeyQueries, getValueQueries, getRangeQueries, finishedQueries, rowsQueried, bytesQueried;
|
Counter allQueries, getKeyQueries, getValueQueries, getRangeQueries, finishedQueries, rowsQueried, bytesQueried, watchQueries;
|
||||||
Counter bytesInput, bytesDurable, bytesFetched,
|
Counter bytesInput, bytesDurable, bytesFetched,
|
||||||
mutationBytes; // Like bytesInput but without MVCC accounting
|
mutationBytes; // Like bytesInput but without MVCC accounting
|
||||||
|
Counter mutations, setMutations, clearRangeMutations, atomicMutations;
|
||||||
Counter updateBatches, updateVersions;
|
Counter updateBatches, updateVersions;
|
||||||
Counter loops;
|
Counter loops;
|
||||||
|
|
||||||
|
@ -415,10 +419,15 @@ public:
|
||||||
finishedQueries("FinishedQueries", cc),
|
finishedQueries("FinishedQueries", cc),
|
||||||
rowsQueried("RowsQueried", cc),
|
rowsQueried("RowsQueried", cc),
|
||||||
bytesQueried("BytesQueried", cc),
|
bytesQueried("BytesQueried", cc),
|
||||||
|
watchQueries("WatchQueries", cc),
|
||||||
bytesInput("BytesInput", cc),
|
bytesInput("BytesInput", cc),
|
||||||
bytesDurable("BytesDurable", cc),
|
bytesDurable("BytesDurable", cc),
|
||||||
bytesFetched("BytesFetched", cc),
|
bytesFetched("BytesFetched", cc),
|
||||||
mutationBytes("MutationBytes", cc),
|
mutationBytes("MutationBytes", cc),
|
||||||
|
mutations("Mutations", cc),
|
||||||
|
setMutations("SetMutations", cc),
|
||||||
|
clearRangeMutations("ClearRangeMutations", cc),
|
||||||
|
atomicMutations("AtomicMutations", cc),
|
||||||
updateBatches("UpdateBatches", cc),
|
updateBatches("UpdateBatches", cc),
|
||||||
updateVersions("UpdateVersions", cc),
|
updateVersions("UpdateVersions", cc),
|
||||||
loops("Loops", cc)
|
loops("Loops", cc)
|
||||||
|
@ -428,6 +437,7 @@ public:
|
||||||
specialCounter(cc, "StorageVersion", [self](){ return self->storageVersion(); });
|
specialCounter(cc, "StorageVersion", [self](){ return self->storageVersion(); });
|
||||||
specialCounter(cc, "DurableVersion", [self](){ return self->durableVersion.get(); });
|
specialCounter(cc, "DurableVersion", [self](){ return self->durableVersion.get(); });
|
||||||
specialCounter(cc, "DesiredOldestVersion", [self](){ return self->desiredOldestVersion.get(); });
|
specialCounter(cc, "DesiredOldestVersion", [self](){ return self->desiredOldestVersion.get(); });
|
||||||
|
specialCounter(cc, "VersionLag", [self](){ return self->versionLag; });
|
||||||
|
|
||||||
specialCounter(cc, "FetchKeysFetchActive", [self](){ return self->fetchKeysParallelismLock.activePermits(); });
|
specialCounter(cc, "FetchKeysFetchActive", [self](){ return self->fetchKeysParallelismLock.activePermits(); });
|
||||||
specialCounter(cc, "FetchKeysWaiting", [self](){ return self->fetchKeysParallelismLock.waiters(); });
|
specialCounter(cc, "FetchKeysWaiting", [self](){ return self->fetchKeysParallelismLock.waiters(); });
|
||||||
|
@ -435,6 +445,8 @@ public:
|
||||||
specialCounter(cc, "QueryQueueMax", [self](){ return self->getAndResetMaxQueryQueueSize(); });
|
specialCounter(cc, "QueryQueueMax", [self](){ return self->getAndResetMaxQueryQueueSize(); });
|
||||||
|
|
||||||
specialCounter(cc, "BytesStored", [self](){ return self->metrics.byteSample.getEstimate(allKeys); });
|
specialCounter(cc, "BytesStored", [self](){ return self->metrics.byteSample.getEstimate(allKeys); });
|
||||||
|
specialCounter(cc, "ActiveWatches", [self](){ return self->numWatches; });
|
||||||
|
specialCounter(cc, "WatchBytes", [self](){ return self->watchBytes; });
|
||||||
|
|
||||||
specialCounter(cc, "KvstoreBytesUsed", [self](){ return self->storage.getStorageBytes().used; });
|
specialCounter(cc, "KvstoreBytesUsed", [self](){ return self->storage.getStorageBytes().used; });
|
||||||
specialCounter(cc, "KvstoreBytesFree", [self](){ return self->storage.getStorageBytes().free; });
|
specialCounter(cc, "KvstoreBytesFree", [self](){ return self->storage.getStorageBytes().free; });
|
||||||
|
@ -447,10 +459,11 @@ public:
|
||||||
: instanceID(g_random->randomUniqueID().first()),
|
: instanceID(g_random->randomUniqueID().first()),
|
||||||
storage(this, storage), db(db),
|
storage(this, storage), db(db),
|
||||||
lastTLogVersion(0), lastVersionWithData(0), restoredVersion(0),
|
lastTLogVersion(0), lastVersionWithData(0), restoredVersion(0),
|
||||||
|
versionLag(0),
|
||||||
updateEagerReads(0),
|
updateEagerReads(0),
|
||||||
shardChangeCounter(0),
|
shardChangeCounter(0),
|
||||||
fetchKeysParallelismLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM_BYTES),
|
fetchKeysParallelismLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM_BYTES),
|
||||||
shuttingDown(false), debug_inApplyUpdate(false), debug_lastValidateTime(0), watchBytes(0),
|
shuttingDown(false), debug_inApplyUpdate(false), debug_lastValidateTime(0), watchBytes(0), numWatches(0),
|
||||||
logProtocol(0), counters(this), tag(invalidTag), maxQueryQueue(0), thisServerID(ssi.id()),
|
logProtocol(0), counters(this), tag(invalidTag), maxQueryQueue(0), thisServerID(ssi.id()),
|
||||||
readQueueSizeMetric(LiteralStringRef("StorageServer.ReadQueueSize")),
|
readQueueSizeMetric(LiteralStringRef("StorageServer.ReadQueueSize")),
|
||||||
behind(false), byteSampleClears(false, LiteralStringRef("\xff\xff\xff")), noRecentUpdates(false),
|
behind(false), byteSampleClears(false, LiteralStringRef("\xff\xff\xff")), noRecentUpdates(false),
|
||||||
|
@ -760,6 +773,8 @@ ACTOR Future<Void> getValueQ( StorageServer* data, GetValueRequest req ) {
|
||||||
|
|
||||||
ACTOR Future<Void> watchValue_impl( StorageServer* data, WatchValueRequest req ) {
|
ACTOR Future<Void> watchValue_impl( StorageServer* data, WatchValueRequest req ) {
|
||||||
try {
|
try {
|
||||||
|
++data->counters.watchQueries;
|
||||||
|
|
||||||
if( req.debugID.present() )
|
if( req.debugID.present() )
|
||||||
g_traceBatch.addEvent("WatchValueDebug", req.debugID.get().first(), "watchValueQ.Before"); //.detail("TaskID", g_network->getCurrentTask());
|
g_traceBatch.addEvent("WatchValueDebug", req.debugID.get().first(), "watchValueQ.Before"); //.detail("TaskID", g_network->getCurrentTask());
|
||||||
|
|
||||||
|
@ -792,11 +807,14 @@ ACTOR Future<Void> watchValue_impl( StorageServer* data, WatchValueRequest req )
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++data->numWatches;
|
||||||
data->watchBytes += ( req.key.expectedSize() + req.value.expectedSize() + 1000 );
|
data->watchBytes += ( req.key.expectedSize() + req.value.expectedSize() + 1000 );
|
||||||
try {
|
try {
|
||||||
Void _ = wait( watchFuture );
|
Void _ = wait( watchFuture );
|
||||||
|
--data->numWatches;
|
||||||
data->watchBytes -= ( req.key.expectedSize() + req.value.expectedSize() + 1000 );
|
data->watchBytes -= ( req.key.expectedSize() + req.value.expectedSize() + 1000 );
|
||||||
} catch( Error &e ) {
|
} catch( Error &e ) {
|
||||||
|
--data->numWatches;
|
||||||
data->watchBytes -= ( req.key.expectedSize() + req.value.expectedSize() + 1000 );
|
data->watchBytes -= ( req.key.expectedSize() + req.value.expectedSize() + 1000 );
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -1809,6 +1827,9 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
||||||
state int debug_nextRetryToLog = 1;
|
state int debug_nextRetryToLog = 1;
|
||||||
state bool isTooOld = false;
|
state bool isTooOld = false;
|
||||||
|
|
||||||
|
//FIXME: The client cache does not notice when servers are added to a team. To read from a local storage server we must refresh the cache manually.
|
||||||
|
data->cx->invalidateCache(keys);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
try {
|
try {
|
||||||
TEST(true); // Fetching keys for transferred shard
|
TEST(true); // Fetching keys for transferred shard
|
||||||
|
@ -1888,6 +1909,11 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
||||||
// Throw away deferred updates from before fetchVersion, since we don't need them to use blocks fetched at that version
|
// Throw away deferred updates from before fetchVersion, since we don't need them to use blocks fetched at that version
|
||||||
while (!shard->updates.empty() && shard->updates[0].version <= fetchVersion) shard->updates.pop_front();
|
while (!shard->updates.empty() && shard->updates[0].version <= fetchVersion) shard->updates.pop_front();
|
||||||
|
|
||||||
|
//FIXME: remove when we no longer support upgrades from 5.X
|
||||||
|
if(debug_getRangeRetries >= 100) {
|
||||||
|
data->cx->enableLocalityLoadBalance = false;
|
||||||
|
}
|
||||||
|
|
||||||
debug_getRangeRetries++;
|
debug_getRangeRetries++;
|
||||||
if (debug_nextRetryToLog==debug_getRangeRetries){
|
if (debug_nextRetryToLog==debug_getRangeRetries){
|
||||||
debug_nextRetryToLog += std::min(debug_nextRetryToLog, 1024);
|
debug_nextRetryToLog += std::min(debug_nextRetryToLog, 1024);
|
||||||
|
@ -1902,6 +1928,9 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FIXME: remove when we no longer support upgrades from 5.X
|
||||||
|
data->cx->enableLocalityLoadBalance = true;
|
||||||
|
|
||||||
// We have completed the fetch and write of the data, now we wait for MVCC window to pass.
|
// We have completed the fetch and write of the data, now we wait for MVCC window to pass.
|
||||||
// As we have finished this work, we will allow more work to start...
|
// As we have finished this work, we will allow more work to start...
|
||||||
shard->fetchComplete.send(Void());
|
shard->fetchComplete.send(Void());
|
||||||
|
@ -2354,6 +2383,7 @@ ACTOR Future<Void> update( StorageServer* data, bool* pReceivedUpdate )
|
||||||
|
|
||||||
++data->counters.updateBatches;
|
++data->counters.updateBatches;
|
||||||
data->lastTLogVersion = cursor->getMaxKnownVersion();
|
data->lastTLogVersion = cursor->getMaxKnownVersion();
|
||||||
|
data->versionLag = std::max<int64_t>(0, data->lastTLogVersion - data->version.get());
|
||||||
|
|
||||||
ASSERT(*pReceivedUpdate == false);
|
ASSERT(*pReceivedUpdate == false);
|
||||||
*pReceivedUpdate = true;
|
*pReceivedUpdate = true;
|
||||||
|
@ -2477,6 +2507,28 @@ ACTOR Future<Void> update( StorageServer* data, bool* pReceivedUpdate )
|
||||||
updater.applyMutation(data, msg, ver);
|
updater.applyMutation(data, msg, ver);
|
||||||
|
|
||||||
data->counters.mutationBytes += msg.totalSize();
|
data->counters.mutationBytes += msg.totalSize();
|
||||||
|
++data->counters.mutations;
|
||||||
|
switch(msg.type) {
|
||||||
|
case MutationRef::SetValue:
|
||||||
|
++data->counters.setMutations;
|
||||||
|
break;
|
||||||
|
case MutationRef::ClearRange:
|
||||||
|
++data->counters.clearRangeMutations;
|
||||||
|
break;
|
||||||
|
case MutationRef::AddValue:
|
||||||
|
case MutationRef::And:
|
||||||
|
case MutationRef::AndV2:
|
||||||
|
case MutationRef::AppendIfFits:
|
||||||
|
case MutationRef::ByteMax:
|
||||||
|
case MutationRef::ByteMin:
|
||||||
|
case MutationRef::Max:
|
||||||
|
case MutationRef::Min:
|
||||||
|
case MutationRef::MinV2:
|
||||||
|
case MutationRef::Or:
|
||||||
|
case MutationRef::Xor:
|
||||||
|
++data->counters.atomicMutations;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
TraceEvent(SevError, "DiscardingPeekedData", data->thisServerID).detail("Mutation", msg.toString()).detail("Version", cloneCursor2->version().toString());
|
TraceEvent(SevError, "DiscardingPeekedData", data->thisServerID).detail("Mutation", msg.toString()).detail("Version", cloneCursor2->version().toString());
|
||||||
|
@ -3140,8 +3192,8 @@ ACTOR Future<Void> storageServerCore( StorageServer* self, StorageServerInterfac
|
||||||
if( self->db->get().recoveryState >= RecoveryState::FULLY_RECOVERED ) {
|
if( self->db->get().recoveryState >= RecoveryState::FULLY_RECOVERED ) {
|
||||||
self->logSystem = ILogSystem::fromServerDBInfo( self->thisServerID, self->db->get() );
|
self->logSystem = ILogSystem::fromServerDBInfo( self->thisServerID, self->db->get() );
|
||||||
if (self->logSystem) {
|
if (self->logSystem) {
|
||||||
if(self->logSystem->getLogSystemConfig().oldTLogs.size()) {
|
if(self->db->get().logSystemConfig.recoveredAt.present()) {
|
||||||
self->poppedAllAfter = self->logSystem->getLogSystemConfig().oldTLogs[0].epochEnd;
|
self->poppedAllAfter = self->db->get().logSystemConfig.recoveredAt.get();
|
||||||
}
|
}
|
||||||
self->logCursor = self->logSystem->peekSingle( self->thisServerID, self->version.get() + 1, self->tag, self->history );
|
self->logCursor = self->logSystem->peekSingle( self->thisServerID, self->version.get() + 1, self->tag, self->history );
|
||||||
self->popVersion( self->durableVersion.get() + 1, true );
|
self->popVersion( self->durableVersion.get() + 1, true );
|
||||||
|
|
|
@ -820,11 +820,11 @@ ACTOR Future<Void> workerServer( Reference<ClusterConnectionFile> connFile, Refe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when( EventLogRequest req = waitNext(interf.eventLogRequest.getFuture()) ) {
|
when( EventLogRequest req = waitNext(interf.eventLogRequest.getFuture()) ) {
|
||||||
Standalone<StringRef> e;
|
TraceEventFields e;
|
||||||
if( req.getLastError )
|
if( req.getLastError )
|
||||||
e = StringRef( latestEventCache.getLatestError() );
|
e = latestEventCache.getLatestError();
|
||||||
else
|
else
|
||||||
e = StringRef( latestEventCache.get( req.eventName.toString() ) );
|
e = latestEventCache.get( req.eventName.toString() );
|
||||||
req.reply.send(e);
|
req.reply.send(e);
|
||||||
}
|
}
|
||||||
when( TraceBatchDumpRequest req = waitNext(interf.traceBatchDumpRequest.getFuture()) ) {
|
when( TraceBatchDumpRequest req = waitNext(interf.traceBatchDumpRequest.getFuture()) ) {
|
||||||
|
|
|
@ -369,29 +369,22 @@ struct ConsistencyCheckWorkload : TestWorkload
|
||||||
ACTOR Future<bool> getKeyLocations(Database cx, vector<pair<KeyRange, vector<StorageServerInterface>>> shards, ConsistencyCheckWorkload *self, Promise<Standalone<VectorRef<KeyValueRef>>> keyLocationPromise)
|
ACTOR Future<bool> getKeyLocations(Database cx, vector<pair<KeyRange, vector<StorageServerInterface>>> shards, ConsistencyCheckWorkload *self, Promise<Standalone<VectorRef<KeyValueRef>>> keyLocationPromise)
|
||||||
{
|
{
|
||||||
state Standalone<VectorRef<KeyValueRef>> keyLocations;
|
state Standalone<VectorRef<KeyValueRef>> keyLocations;
|
||||||
state Key beginKey = allKeys.begin;
|
state Key beginKey = allKeys.begin.withPrefix(keyServersPrefix);
|
||||||
|
state Key endKey = allKeys.end.withPrefix(keyServersPrefix);
|
||||||
state int i = 0;
|
state int i = 0;
|
||||||
|
|
||||||
//If the responses are too big, we may use multiple requests to get the key locations. Each request begins where the last left off
|
//If the responses are too big, we may use multiple requests to get the key locations. Each request begins where the last left off
|
||||||
for ( ; i < shards.size(); i++)
|
for ( ; i < shards.size(); i++)
|
||||||
{
|
{
|
||||||
// skip serverList shards
|
while(beginKey < shards[i].first.end)
|
||||||
if (!shards[i].first.begin.startsWith(keyServersPrefix)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
state Key endKey = shards[i].first.end.startsWith(keyServersPrefix) ? shards[i].first.end.removePrefix(keyServersPrefix) : allKeys.end;
|
|
||||||
|
|
||||||
while(beginKey < endKey)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Version version = wait(self->getVersion(cx, self));
|
Version version = wait(self->getVersion(cx, self));
|
||||||
|
|
||||||
GetKeyValuesRequest req;
|
GetKeyValuesRequest req;
|
||||||
Key prefixBegin = beginKey.withPrefix(keyServersPrefix);
|
req.begin = firstGreaterOrEqual(beginKey);
|
||||||
req.begin = firstGreaterOrEqual(prefixBegin);
|
req.end = firstGreaterOrEqual(std::min<KeyRef>(shards[i].first.end, endKey));
|
||||||
req.end = firstGreaterOrEqual(keyServersEnd);
|
|
||||||
req.limit = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT;
|
req.limit = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT;
|
||||||
req.limitBytes = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES;
|
req.limitBytes = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES;
|
||||||
req.version = version;
|
req.version = version;
|
||||||
|
@ -443,17 +436,26 @@ struct ConsistencyCheckWorkload : TestWorkload
|
||||||
}
|
}
|
||||||
|
|
||||||
auto keyValueResponse = keyValueFutures[firstValidStorageServer].get().get();
|
auto keyValueResponse = keyValueFutures[firstValidStorageServer].get().get();
|
||||||
Standalone<RangeResultRef> currentLocations = krmDecodeRanges( keyServersPrefix, KeyRangeRef(beginKey, endKey), RangeResultRef( keyValueResponse.data, keyValueResponse.more) );
|
Standalone<RangeResultRef> currentLocations = krmDecodeRanges( keyServersPrefix, KeyRangeRef(beginKey.removePrefix(keyServersPrefix), std::min<KeyRef>(shards[i].first.end, endKey).removePrefix(keyServersPrefix)), RangeResultRef( keyValueResponse.data, keyValueResponse.more) );
|
||||||
|
|
||||||
//Push all but the last item, which will be pushed as the first item next iteration
|
if(keyValueResponse.data.size() && beginKey == keyValueResponse.data[0].key) {
|
||||||
keyLocations.append_deep(keyLocations.arena(), currentLocations.begin(), currentLocations.size() - 1);
|
keyLocations.push_back_deep(keyLocations.arena(), currentLocations[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentLocations.size() > 2) {
|
||||||
|
keyLocations.append_deep(keyLocations.arena(), ¤tLocations[1], currentLocations.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
//Next iteration should pick up where we left off
|
//Next iteration should pick up where we left off
|
||||||
ASSERT(currentLocations.size() > 1);
|
ASSERT(currentLocations.size() > 1);
|
||||||
beginKey = currentLocations.end()[-1].key;
|
if(!keyValueResponse.more) {
|
||||||
|
beginKey = shards[i].first.end;
|
||||||
|
} else {
|
||||||
|
beginKey = keyValueResponse.data.end()[-1].key;
|
||||||
|
}
|
||||||
|
|
||||||
//If this is the last iteration, then push the allKeys.end KV pair
|
//If this is the last iteration, then push the allKeys.end KV pair
|
||||||
if(beginKey == allKeys.end)
|
if(beginKey >= endKey)
|
||||||
keyLocations.push_back_deep(keyLocations.arena(), currentLocations.end()[-1]);
|
keyLocations.push_back_deep(keyLocations.arena(), currentLocations.end()[-1]);
|
||||||
}
|
}
|
||||||
catch(Error &e)
|
catch(Error &e)
|
||||||
|
@ -626,7 +628,7 @@ struct ConsistencyCheckWorkload : TestWorkload
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
//In a quiescent database, check that the team size is the same as the desired team size
|
//In a quiescent database, check that the team size is the same as the desired team size
|
||||||
if(self->firstClient && self->performQuiescentChecks && sourceStorageServers.size() != configuration.storageTeamSize)
|
if(self->firstClient && self->performQuiescentChecks && sourceStorageServers.size() != configuration.usableRegions*configuration.storageTeamSize)
|
||||||
{
|
{
|
||||||
TraceEvent("ConsistencyCheck_InvalidTeamSize").detail("ShardBegin", printable(range.begin)).detail("ShardEnd", printable(range.end)).detail("TeamSize", sourceStorageServers.size()).detail("DesiredTeamSize", configuration.storageTeamSize);
|
TraceEvent("ConsistencyCheck_InvalidTeamSize").detail("ShardBegin", printable(range.begin)).detail("ShardEnd", printable(range.end)).detail("TeamSize", sourceStorageServers.size()).detail("DesiredTeamSize", configuration.storageTeamSize);
|
||||||
self->testFailure("Invalid team size");
|
self->testFailure("Invalid team size");
|
||||||
|
@ -971,7 +973,7 @@ struct ConsistencyCheckWorkload : TestWorkload
|
||||||
//Min and max shard sizes have a 3 * shardBounds.permittedError.bytes cushion for error since shard sizes are not precise
|
//Min and max shard sizes have a 3 * shardBounds.permittedError.bytes cushion for error since shard sizes are not precise
|
||||||
//Shard splits ignore the first key in a shard, so its size shouldn't be considered when checking the upper bound
|
//Shard splits ignore the first key in a shard, so its size shouldn't be considered when checking the upper bound
|
||||||
//0xff shards are not checked
|
//0xff shards are not checked
|
||||||
if( canSplit && self->performQuiescentChecks && !range.begin.startsWith(keyServersPrefix) &&
|
if( canSplit && sampledKeys > 5 && self->performQuiescentChecks && !range.begin.startsWith(keyServersPrefix) &&
|
||||||
(sampledBytes < shardBounds.min.bytes - 3 * shardBounds.permittedError.bytes || sampledBytes - firstKeySampledBytes > shardBounds.max.bytes + 3 * shardBounds.permittedError.bytes))
|
(sampledBytes < shardBounds.min.bytes - 3 * shardBounds.permittedError.bytes || sampledBytes - firstKeySampledBytes > shardBounds.max.bytes + 3 * shardBounds.permittedError.bytes))
|
||||||
{
|
{
|
||||||
TraceEvent("ConsistencyCheck_InvalidShardSize").detail("Min", shardBounds.min.bytes).detail("Max", shardBounds.max.bytes).detail("Size", shardBytes)
|
TraceEvent("ConsistencyCheck_InvalidShardSize").detail("Min", shardBounds.min.bytes).detail("Max", shardBounds.max.bytes).detail("Size", shardBytes)
|
||||||
|
|
|
@ -41,10 +41,10 @@ struct DDMetricsWorkload : TestWorkload {
|
||||||
WorkerInterface masterWorker = wait(getMasterWorker(cx, self->dbInfo));
|
WorkerInterface masterWorker = wait(getMasterWorker(cx, self->dbInfo));
|
||||||
|
|
||||||
TraceEvent("GetHighPriorityReliocationsInFlight").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
TraceEvent("GetHighPriorityReliocationsInFlight").detail("Database", printable(cx->dbName)).detail("Stage", "ContactingMaster");
|
||||||
Standalone<StringRef> md = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
TraceEventFields md = wait( timeoutError(masterWorker.eventLogRequest.getReply(
|
||||||
EventLogRequest( StringRef( cx->dbName.toString() + "/MovingData" ) ) ), 1.0 ) );
|
EventLogRequest( StringRef( cx->dbName.toString() + "/MovingData" ) ) ), 1.0 ) );
|
||||||
int relocations;
|
int relocations;
|
||||||
sscanf(extractAttribute(md.toString(), "HighPriorityRelocations").c_str(), "%d", &relocations);
|
sscanf(md.getValue("HighPriorityRelocations").c_str(), "%d", &relocations);
|
||||||
return relocations;
|
return relocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,17 +42,17 @@ struct WorkerErrorsWorkload : TestWorkload {
|
||||||
virtual void getMetrics( vector<PerfMetric>& m ) {}
|
virtual void getMetrics( vector<PerfMetric>& m ) {}
|
||||||
|
|
||||||
|
|
||||||
ACTOR Future< std::vector< std::string > > latestEventOnWorkers( std::vector<std::pair<WorkerInterface, ProcessClass>> workers ) {
|
ACTOR Future< std::vector< TraceEventFields > > latestEventOnWorkers( std::vector<std::pair<WorkerInterface, ProcessClass>> workers ) {
|
||||||
state vector<Future<Standalone<StringRef>>> eventTraces;
|
state vector<Future<TraceEventFields>> eventTraces;
|
||||||
for(int c = 0; c < workers.size(); c++) {
|
for(int c = 0; c < workers.size(); c++) {
|
||||||
eventTraces.push_back( workers[c].first.eventLogRequest.getReply( EventLogRequest() ) );
|
eventTraces.push_back( workers[c].first.eventLogRequest.getReply( EventLogRequest() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Void _ = wait( timeoutError( waitForAll( eventTraces ), 2.0 ) );
|
Void _ = wait( timeoutError( waitForAll( eventTraces ), 2.0 ) );
|
||||||
|
|
||||||
vector<std::string> results;
|
vector<TraceEventFields> results;
|
||||||
for(int i = 0; i < eventTraces.size(); i++) {
|
for(int i = 0; i < eventTraces.size(); i++) {
|
||||||
results.push_back( eventTraces[i].get().toString() );
|
results.push_back( eventTraces[i].get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -60,9 +60,9 @@ struct WorkerErrorsWorkload : TestWorkload {
|
||||||
|
|
||||||
ACTOR Future<Void> _start(Database cx, WorkerErrorsWorkload *self) {
|
ACTOR Future<Void> _start(Database cx, WorkerErrorsWorkload *self) {
|
||||||
state vector<std::pair<WorkerInterface, ProcessClass>> workers = wait( getWorkers( self->dbInfo ) );
|
state vector<std::pair<WorkerInterface, ProcessClass>> workers = wait( getWorkers( self->dbInfo ) );
|
||||||
std::vector<std::string> errors = wait( self->latestEventOnWorkers( workers ) );
|
std::vector<TraceEventFields> errors = wait( self->latestEventOnWorkers( workers ) );
|
||||||
for(auto e : errors) {
|
for(auto e : errors) {
|
||||||
printf("%s\n", e.c_str());
|
printf("%s\n", e.toString().c_str());
|
||||||
}
|
}
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* FileTraceLogWriter.cpp
|
||||||
|
*
|
||||||
|
* This source file is part of the FoundationDB open source project
|
||||||
|
*
|
||||||
|
* Copyright 2018 Apple Inc. and the FoundationDB project authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "FileTraceLogWriter.h"
|
||||||
|
#include "flow.h"
|
||||||
|
#include "ThreadHelper.actor.h"
|
||||||
|
|
||||||
|
#if defined(__unixish__)
|
||||||
|
#define __open ::open
|
||||||
|
#define __write ::write
|
||||||
|
#define __close ::close
|
||||||
|
#define __fsync ::fsync
|
||||||
|
#define TRACEFILE_FLAGS O_WRONLY | O_CREAT | O_EXCL
|
||||||
|
#define TRACEFILE_MODE 0664
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#undef max
|
||||||
|
#undef min
|
||||||
|
#include <io.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#define __open _open
|
||||||
|
#define __write _write
|
||||||
|
#define __close _close
|
||||||
|
#define __fsync _commit
|
||||||
|
#define TRACEFILE_FLAGS _O_WRONLY | _O_CREAT | _O_EXCL
|
||||||
|
#define TRACEFILE_MODE _S_IWRITE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
FileTraceLogWriter::FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, uint64_t maxLogsSize, std::function<void()> onError)
|
||||||
|
: directory(directory), processName(processName), basename(basename), extension(extension), maxLogsSize(maxLogsSize), traceFileFD(-1), index(0), onError(onError) {}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::addref() {
|
||||||
|
ReferenceCounted<FileTraceLogWriter>::addref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::delref() {
|
||||||
|
ReferenceCounted<FileTraceLogWriter>::delref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::lastError(int err) {
|
||||||
|
// Whenever we get a serious error writing a trace log, all flush barriers posted between the operation encountering
|
||||||
|
// the error and the occurrence of the error are unblocked, even though we haven't actually succeeded in flushing.
|
||||||
|
// Otherwise a permanent write error would make the program block forever.
|
||||||
|
if (err != 0 && err != EINTR) {
|
||||||
|
onError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::write(const std::string& str) {
|
||||||
|
auto ptr = str.c_str();
|
||||||
|
int remaining = str.size();
|
||||||
|
|
||||||
|
while ( remaining ) {
|
||||||
|
int ret = __write( traceFileFD, ptr, remaining );
|
||||||
|
if ( ret > 0 ) {
|
||||||
|
lastError(0);
|
||||||
|
remaining -= ret;
|
||||||
|
ptr += ret;
|
||||||
|
} else {
|
||||||
|
lastError(errno);
|
||||||
|
threadSleep(0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::open() {
|
||||||
|
cleanupTraceFiles();
|
||||||
|
|
||||||
|
auto finalname = format("%s.%d.%s", basename.c_str(), ++index, extension.c_str());
|
||||||
|
while ( (traceFileFD = __open( finalname.c_str(), TRACEFILE_FLAGS, TRACEFILE_MODE )) == -1 ) {
|
||||||
|
lastError(errno);
|
||||||
|
if (errno == EEXIST)
|
||||||
|
finalname = format("%s.%d.%s", basename.c_str(), ++index, extension.c_str());
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno));
|
||||||
|
|
||||||
|
int errorNum = errno;
|
||||||
|
onMainThreadVoid([finalname, errorNum]{
|
||||||
|
TraceEvent(SevWarnAlways, "TraceFileOpenError")
|
||||||
|
.detail("Filename", finalname)
|
||||||
|
.detail("ErrorCode", errorNum)
|
||||||
|
.detail("Error", strerror(errorNum))
|
||||||
|
.trackLatest("TraceFileOpenError"); }, NULL);
|
||||||
|
threadSleep(FLOW_KNOBS->TRACE_RETRY_OPEN_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMainThreadVoid([]{ latestEventCache.clear("TraceFileOpenError"); }, NULL);
|
||||||
|
lastError(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::close() {
|
||||||
|
if (traceFileFD >= 0) {
|
||||||
|
while ( __close(traceFileFD) ) threadSleep(0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::roll() {
|
||||||
|
close();
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::sync() {
|
||||||
|
__fsync(traceFileFD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::extractTraceFileNameInfo(std::string const& filename, std::string &root, int &index) {
|
||||||
|
int split = filename.find_last_of('.', filename.size() - 5);
|
||||||
|
root = filename.substr(0, split);
|
||||||
|
if(sscanf(filename.substr(split + 1, filename.size() - split - 4).c_str(), "%d", &index) == EOF) {
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileTraceLogWriter::compareTraceFileName (std::string const& f1, std::string const& f2) {
|
||||||
|
std::string root1;
|
||||||
|
std::string root2;
|
||||||
|
|
||||||
|
int index1;
|
||||||
|
int index2;
|
||||||
|
|
||||||
|
extractTraceFileNameInfo(f1, root1, index1);
|
||||||
|
extractTraceFileNameInfo(f2, root2, index2);
|
||||||
|
|
||||||
|
if(root1 != root2)
|
||||||
|
return root1 < root2;
|
||||||
|
if(index1 != index2)
|
||||||
|
return index1 < index2;
|
||||||
|
|
||||||
|
return f1 < f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileTraceLogWriter::reverseCompareTraceFileName(std::string f1, std::string f2) {
|
||||||
|
return compareTraceFileName(f2, f1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTraceLogWriter::cleanupTraceFiles() {
|
||||||
|
// Setting maxLogsSize=0 disables trace file cleanup based on dir size
|
||||||
|
if(!g_network->isSimulated() && maxLogsSize > 0) {
|
||||||
|
try {
|
||||||
|
std::vector<std::string> existingFiles = platform::listFiles(directory, extension);
|
||||||
|
std::vector<std::string> existingTraceFiles;
|
||||||
|
|
||||||
|
for(auto f = existingFiles.begin(); f != existingFiles.end(); ++f) {
|
||||||
|
if(f->substr(0, processName.length()) == processName) {
|
||||||
|
existingTraceFiles.push_back(*f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse sort, so we preserve the most recent files and delete the oldest
|
||||||
|
std::sort(existingTraceFiles.begin(), existingTraceFiles.end(), FileTraceLogWriter::reverseCompareTraceFileName);
|
||||||
|
|
||||||
|
int64_t runningTotal = 0;
|
||||||
|
std::vector<std::string>::iterator fileListIterator = existingTraceFiles.begin();
|
||||||
|
|
||||||
|
while(runningTotal < maxLogsSize && fileListIterator != existingTraceFiles.end()) {
|
||||||
|
runningTotal += (fileSize(joinPath(directory, *fileListIterator)) + FLOW_KNOBS->ZERO_LENGTH_FILE_PAD);
|
||||||
|
++fileListIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(fileListIterator != existingTraceFiles.end()) {
|
||||||
|
deleteFile(joinPath(directory, *fileListIterator));
|
||||||
|
++fileListIterator;
|
||||||
|
}
|
||||||
|
} catch( Error & ) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* FileTraceLogWriter.h
|
||||||
|
*
|
||||||
|
* This source file is part of the FoundationDB open source project
|
||||||
|
*
|
||||||
|
* Copyright 2018 Apple Inc. and the FoundationDB project authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FLOW_FILE_TRACE_LOG_WRITER_H
|
||||||
|
#define FLOW_FILE_TRACE_LOG_WRITER_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "FastRef.h"
|
||||||
|
#include "Trace.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class FileTraceLogWriter : public ITraceLogWriter, ReferenceCounted<FileTraceLogWriter> {
|
||||||
|
private:
|
||||||
|
std::string directory;
|
||||||
|
std::string processName;
|
||||||
|
std::string basename;
|
||||||
|
std::string extension;
|
||||||
|
|
||||||
|
uint64_t maxLogsSize;
|
||||||
|
int traceFileFD;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
std::function<void()> onError;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, uint64_t maxLogsSize, std::function<void()> onError);
|
||||||
|
|
||||||
|
void addref();
|
||||||
|
void delref();
|
||||||
|
|
||||||
|
void lastError(int err);
|
||||||
|
|
||||||
|
void write(const std::string& str);
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
void roll();
|
||||||
|
void sync();
|
||||||
|
|
||||||
|
static void extractTraceFileNameInfo(std::string const& filename, std::string &root, int &index);
|
||||||
|
static bool compareTraceFileName (std::string const& f1, std::string const& f2);
|
||||||
|
static bool reverseCompareTraceFileName(std::string f1, std::string f2);
|
||||||
|
|
||||||
|
void cleanupTraceFiles();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -115,10 +115,12 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) {
|
||||||
init( TRACE_RETRY_OPEN_INTERVAL, 1.00 );
|
init( TRACE_RETRY_OPEN_INTERVAL, 1.00 );
|
||||||
init( MIN_TRACE_SEVERITY, isSimulated ? 0 : 10 ); // Related to the trace severity in Trace.h
|
init( MIN_TRACE_SEVERITY, isSimulated ? 0 : 10 ); // Related to the trace severity in Trace.h
|
||||||
init( MAX_TRACE_SUPPRESSIONS, 1e4 );
|
init( MAX_TRACE_SUPPRESSIONS, 1e4 );
|
||||||
init( TRACE_FSYNC_ENABLED, 0 );
|
init( TRACE_SYNC_ENABLED, 0 );
|
||||||
init( TRACE_EVENT_METRIC_UNITS_PER_SAMPLE, 500 );
|
init( TRACE_EVENT_METRIC_UNITS_PER_SAMPLE, 500 );
|
||||||
init( TRACE_EVENT_THROTLLER_SAMPLE_EXPIRY, 1800.0 ); // 30 mins
|
init( TRACE_EVENT_THROTTLER_SAMPLE_EXPIRY, 1800.0 ); // 30 mins
|
||||||
init( TRACE_EVENT_THROTTLER_MSG_LIMIT, 20000 );
|
init( TRACE_EVENT_THROTTLER_MSG_LIMIT, 20000 );
|
||||||
|
init( TRACE_EVENT_MAX_SIZE, 4000 );
|
||||||
|
init( TRACE_LOG_MAX_PREOPEN_BUFFER, 1000000 );
|
||||||
|
|
||||||
//TDMetrics
|
//TDMetrics
|
||||||
init( MAX_METRICS, 600 );
|
init( MAX_METRICS, 600 );
|
||||||
|
|
|
@ -137,10 +137,12 @@ public:
|
||||||
double TRACE_RETRY_OPEN_INTERVAL;
|
double TRACE_RETRY_OPEN_INTERVAL;
|
||||||
int MIN_TRACE_SEVERITY;
|
int MIN_TRACE_SEVERITY;
|
||||||
int MAX_TRACE_SUPPRESSIONS;
|
int MAX_TRACE_SUPPRESSIONS;
|
||||||
int TRACE_FSYNC_ENABLED;
|
int TRACE_SYNC_ENABLED;
|
||||||
int TRACE_EVENT_METRIC_UNITS_PER_SAMPLE;
|
int TRACE_EVENT_METRIC_UNITS_PER_SAMPLE;
|
||||||
int TRACE_EVENT_THROTLLER_SAMPLE_EXPIRY;
|
int TRACE_EVENT_THROTTLER_SAMPLE_EXPIRY;
|
||||||
int TRACE_EVENT_THROTTLER_MSG_LIMIT;
|
int TRACE_EVENT_THROTTLER_MSG_LIMIT;
|
||||||
|
int TRACE_EVENT_MAX_SIZE;
|
||||||
|
int TRACE_LOG_MAX_PREOPEN_BUFFER;
|
||||||
|
|
||||||
//TDMetrics
|
//TDMetrics
|
||||||
int64_t MAX_METRIC_SIZE;
|
int64_t MAX_METRIC_SIZE;
|
||||||
|
|
|
@ -2317,6 +2317,7 @@ extern void flushTraceFileVoid();
|
||||||
extern "C" void flushAndExit(int exitCode) {
|
extern "C" void flushAndExit(int exitCode) {
|
||||||
flushTraceFileVoid();
|
flushTraceFileVoid();
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
closeTraceFile();
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// This function is documented as being asynchronous, but we suspect it might actually be synchronous in the
|
// This function is documented as being asynchronous, but we suspect it might actually be synchronous in the
|
||||||
// case that it is passed a handle to the current process. If not, then there may be cases where we escalate
|
// case that it is passed a handle to the current process. If not, then there may be cases where we escalate
|
||||||
|
|
|
@ -501,6 +501,16 @@ inline static void* aligned_alloc(size_t alignment, size_t size) { return memali
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
inline static void* aligned_alloc(size_t alignment, size_t size) {
|
inline static void* aligned_alloc(size_t alignment, size_t size) {
|
||||||
|
// Linux's aligned_alloc() requires alignment to be a power of 2. While posix_memalign()
|
||||||
|
// also requires this, in addition it requires alignment to be a multiple of sizeof(void *).
|
||||||
|
// Rather than add this requirement to the platform::aligned_alloc() interface we will simply
|
||||||
|
// upgrade powers of 2 which are less than sizeof(void *) to be exactly sizeof(void *). Non
|
||||||
|
// powers of 2 of any size will fail as they would on other platforms. This change does not
|
||||||
|
// break the platform::aligned_alloc() contract as all addresses which are aligned to
|
||||||
|
// sizeof(void *) are also aligned to any power of 2 less than sizeof(void *).
|
||||||
|
if(alignment != 0 && alignment < sizeof(void *) && (alignment & (alignment - 1)) == 0) {
|
||||||
|
alignment = sizeof(void *);
|
||||||
|
}
|
||||||
void* ptr = nullptr;
|
void* ptr = nullptr;
|
||||||
posix_memalign(&ptr, alignment, size);
|
posix_memalign(&ptr, alignment, size);
|
||||||
return ptr;
|
return ptr;
|
||||||
|
|
816
flow/Trace.cpp
816
flow/Trace.cpp
File diff suppressed because it is too large
Load Diff
182
flow/Trace.h
182
flow/Trace.h
|
@ -52,6 +52,55 @@ enum Severity {
|
||||||
SevMax=1000000
|
SevMax=1000000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TraceEventFields {
|
||||||
|
public:
|
||||||
|
typedef std::pair<std::string, std::string> Field;
|
||||||
|
typedef std::vector<Field> FieldContainer;
|
||||||
|
typedef FieldContainer::const_iterator FieldIterator;
|
||||||
|
|
||||||
|
TraceEventFields();
|
||||||
|
|
||||||
|
size_t size() const;
|
||||||
|
size_t sizeBytes() const;
|
||||||
|
FieldIterator begin() const;
|
||||||
|
FieldIterator end() const;
|
||||||
|
|
||||||
|
void addField(const std::string& key, const std::string& value);
|
||||||
|
void addField(std::string&& key, std::string&& value);
|
||||||
|
|
||||||
|
const Field &operator[] (int index) const;
|
||||||
|
bool tryGetValue(std::string key, std::string &outValue) const;
|
||||||
|
std::string getValue(std::string key) const;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
void validateFormat() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FieldContainer fields;
|
||||||
|
size_t bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
inline void load( Archive& ar, TraceEventFields& value ) {
|
||||||
|
uint32_t count;
|
||||||
|
ar >> count;
|
||||||
|
|
||||||
|
std::string k;
|
||||||
|
std::string v;
|
||||||
|
for(uint32_t i = 0; i < count; ++i) {
|
||||||
|
ar >> k >> v;
|
||||||
|
value.addField(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class Archive>
|
||||||
|
inline void save( Archive& ar, const TraceEventFields& value ) {
|
||||||
|
ar << (uint32_t)value.size();
|
||||||
|
|
||||||
|
for(auto itr : value) {
|
||||||
|
ar << itr.first << itr.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TraceBatch {
|
class TraceBatch {
|
||||||
public:
|
public:
|
||||||
void addEvent( const char *name, uint64_t id, const char *location );
|
void addEvent( const char *name, uint64_t id, const char *location );
|
||||||
|
@ -61,30 +110,18 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct EventInfo {
|
struct EventInfo {
|
||||||
double time;
|
TraceEventFields fields;
|
||||||
const char *name;
|
EventInfo(double time, const char *name, uint64_t id, const char *location);
|
||||||
uint64_t id;
|
|
||||||
const char *location;
|
|
||||||
|
|
||||||
EventInfo(double time, const char *name, uint64_t id, const char *location) : time(time), name(name), id(id), location(location) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AttachInfo {
|
struct AttachInfo {
|
||||||
double time;
|
TraceEventFields fields;
|
||||||
const char *name;
|
AttachInfo(double time, const char *name, uint64_t id, uint64_t to);
|
||||||
uint64_t id;
|
|
||||||
uint64_t to;
|
|
||||||
|
|
||||||
AttachInfo(double time, const char *name, uint64_t id, uint64_t to) : time(time), name(name), id(id), to(to) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuggifyInfo {
|
struct BuggifyInfo {
|
||||||
double time;
|
TraceEventFields fields;
|
||||||
int activated;
|
BuggifyInfo(double time, int activated, int line, std::string file);
|
||||||
int line;
|
|
||||||
std::string file;
|
|
||||||
|
|
||||||
BuggifyInfo(double time, int activated, int line, std::string file) : time(time), activated(activated), line(line), file(file) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<EventInfo> eventBatch;
|
std::vector<EventInfo> eventBatch;
|
||||||
|
@ -97,7 +134,6 @@ class StringRef;
|
||||||
template <class T> class Standalone;
|
template <class T> class Standalone;
|
||||||
template <class T> class Optional;
|
template <class T> class Optional;
|
||||||
|
|
||||||
#if 1
|
|
||||||
struct TraceEvent {
|
struct TraceEvent {
|
||||||
TraceEvent( const char* type, UID id = UID() ); // Assumes SevInfo severity
|
TraceEvent( const char* type, UID id = UID() ); // Assumes SevInfo severity
|
||||||
TraceEvent( Severity, const char* type, UID id = UID() );
|
TraceEvent( Severity, const char* type, UID id = UID() );
|
||||||
|
@ -112,29 +148,28 @@ struct TraceEvent {
|
||||||
static void setNetworkThread();
|
static void setNetworkThread();
|
||||||
static bool isNetworkThread();
|
static bool isNetworkThread();
|
||||||
|
|
||||||
TraceEvent& error(class Error const& e, bool includeCancelled=false);
|
TraceEvent& error(const class Error& e, bool includeCancelled=false);
|
||||||
|
|
||||||
TraceEvent& detail( const char* key, const char* value );
|
TraceEvent& detail( std::string key, std::string value );
|
||||||
TraceEvent& detail( const char* key, const std::string& value );
|
TraceEvent& detail( std::string key, double value );
|
||||||
TraceEvent& detail( const char* key, double value );
|
TraceEvent& detail( std::string key, long int value );
|
||||||
TraceEvent& detail( const char* key, long int value );
|
TraceEvent& detail( std::string key, long unsigned int value );
|
||||||
TraceEvent& detail( const char* key, long unsigned int value );
|
TraceEvent& detail( std::string key, long long int value );
|
||||||
TraceEvent& detail( const char* key, long long int value );
|
TraceEvent& detail( std::string key, long long unsigned int value );
|
||||||
TraceEvent& detail( const char* key, long long unsigned int value );
|
TraceEvent& detail( std::string key, int value );
|
||||||
TraceEvent& detail( const char* key, int value );
|
TraceEvent& detail( std::string key, unsigned value );
|
||||||
TraceEvent& detail( const char* key, unsigned value );
|
TraceEvent& detail( std::string key, const struct NetworkAddress& value );
|
||||||
TraceEvent& detail( const char* key, struct NetworkAddress const& value );
|
TraceEvent& detailf( std::string key, const char* valueFormat, ... );
|
||||||
TraceEvent& detailf( const char* key, const char* valueFormat, ... );
|
TraceEvent& detailext( std::string key, const StringRef& value );
|
||||||
TraceEvent& detailext(const char* key, StringRef const& value);
|
TraceEvent& detailext( std::string key, const Optional<Standalone<StringRef>>& value );
|
||||||
TraceEvent& detailext(const char* key, Optional<Standalone<StringRef>> const& value);
|
|
||||||
private:
|
private:
|
||||||
// Private version of _detailf that does NOT write to the eventMetric. This is to be used by other detail methods
|
// Private version of detailf that does NOT write to the eventMetric. This is to be used by other detail methods
|
||||||
// which can write field metrics of a more appropriate type than string but use detailf() to add to the TraceEvent.
|
// which can write field metrics of a more appropriate type than string but use detailf() to add to the TraceEvent.
|
||||||
TraceEvent& _detailf( const char* key, const char* valueFormat, ... );
|
TraceEvent& detailfNoMetric( std::string&& key, const char* valueFormat, ... );
|
||||||
|
TraceEvent& detailImpl( std::string&& key, std::string&& value, bool writeEventMetricField=true );
|
||||||
public:
|
public:
|
||||||
TraceEvent& detailfv( const char* key, const char* valueFormat, va_list args, bool writeEventMetricField);
|
TraceEvent& detail( std::string key, const UID& value );
|
||||||
TraceEvent& detail( const char* key, UID const& value );
|
TraceEvent& backtrace(const std::string& prefix = "");
|
||||||
TraceEvent& backtrace(std::string prefix = "");
|
|
||||||
TraceEvent& trackLatest( const char* trackingKey );
|
TraceEvent& trackLatest( const char* trackingKey );
|
||||||
TraceEvent& sample( double sampleRate, bool logSampleRate=true );
|
TraceEvent& sample( double sampleRate, bool logSampleRate=true );
|
||||||
TraceEvent& suppressFor( double duration, bool logSuppressedEventCount=true );
|
TraceEvent& suppressFor( double duration, bool logSuppressedEventCount=true );
|
||||||
|
@ -151,7 +186,7 @@ public:
|
||||||
private:
|
private:
|
||||||
bool enabled;
|
bool enabled;
|
||||||
std::string trackingKey;
|
std::string trackingKey;
|
||||||
char buffer[4000];
|
TraceEventFields fields;
|
||||||
int length;
|
int length;
|
||||||
Severity severity;
|
Severity severity;
|
||||||
const char *type;
|
const char *type;
|
||||||
|
@ -162,45 +197,28 @@ private:
|
||||||
|
|
||||||
bool init( Severity, const char* type );
|
bool init( Severity, const char* type );
|
||||||
bool init( Severity, struct TraceInterval& );
|
bool init( Severity, struct TraceInterval& );
|
||||||
|
|
||||||
void write( int length, const void* data );
|
|
||||||
void writef( const char* format, ... );
|
|
||||||
void writeEscaped( const char* data );
|
|
||||||
void writeEscapedfv( const char* format, va_list args );
|
|
||||||
};
|
};
|
||||||
#else
|
|
||||||
struct TraceEvent {
|
|
||||||
TraceEvent(const char* type, UID id = UID()) {}
|
|
||||||
TraceEvent(Severity, const char* type, UID id = UID()) {}
|
|
||||||
TraceEvent(struct TraceInterval&, UID id = UID()) {}
|
|
||||||
TraceEvent(const char* type, StringRef& const id); {} // Assumes SevInfo severity
|
|
||||||
TraceEvent(Severity, const char* type, StringRef& const id); {}
|
|
||||||
|
|
||||||
static bool isEnabled(const char* type) { return false; }
|
struct ITraceLogWriter {
|
||||||
|
virtual void open() = 0;
|
||||||
|
virtual void roll() = 0;
|
||||||
|
virtual void close() = 0;
|
||||||
|
virtual void write(const std::string&) = 0;
|
||||||
|
virtual void sync() = 0;
|
||||||
|
|
||||||
TraceEvent& error(class Error const& e, bool includeCancelled = false) { return *this; }
|
virtual void addref() = 0;
|
||||||
|
virtual void delref() = 0;
|
||||||
TraceEvent& detail(const char* key, const char* value) { return *this; }
|
};
|
||||||
TraceEvent& detail(const char* key, const std::string& value) { return *this; }
|
|
||||||
TraceEvent& detail(const char* key, double value) { return *this; }
|
struct ITraceLogFormatter {
|
||||||
TraceEvent& detail(const char* key, long int value) { return *this; }
|
virtual const char* getExtension() = 0;
|
||||||
TraceEvent& detail(const char* key, long unsigned int value) { return *this; }
|
virtual const char* getHeader() = 0; // Called when starting a new file
|
||||||
TraceEvent& detail(const char* key, long long int value) { return *this; }
|
virtual const char* getFooter() = 0; // Called when ending a file
|
||||||
TraceEvent& detail(const char* key, long long unsigned int value) { return *this; }
|
virtual std::string formatEvent(const TraceEventFields&) = 0; // Called for each event
|
||||||
TraceEvent& detail(const char* key, int value) { return *this; }
|
|
||||||
TraceEvent& detail(const char* key, unsigned value) { return *this; }
|
virtual void addref() = 0;
|
||||||
TraceEvent& detail(const char* key, struct NetworkAddress const& value) { return *this; }
|
virtual void delref() = 0;
|
||||||
TraceEvent& detailf(const char* key, const char* valueFormat, ...) { return *this; }
|
|
||||||
TraceEvent& detailfv(const char* key, const char* valueFormat, va_list args) { return *this; }
|
|
||||||
TraceEvent& detail(const char* key, UID const& value) { return *this; }
|
|
||||||
TraceEvent& detailext(const char* key, StringRef const& value) { return *this; }
|
|
||||||
TraceEvent& detailext(const char* key, Optional<Standalone<StringRef>> const& value); { return *this; }
|
|
||||||
TraceEvent& backtrace(std::string prefix = "") { return *this; }
|
|
||||||
TraceEvent& trackLatest(const char* trackingKey) { return *this; }
|
|
||||||
|
|
||||||
TraceEvent& GetLastError() { return *this; }
|
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
struct TraceInterval {
|
struct TraceInterval {
|
||||||
TraceInterval( const char* type ) : count(-1), type(type), severity(SevInfo) {}
|
TraceInterval( const char* type ) : count(-1), type(type), severity(SevInfo) {}
|
||||||
|
@ -216,20 +234,20 @@ struct TraceInterval {
|
||||||
|
|
||||||
struct LatestEventCache {
|
struct LatestEventCache {
|
||||||
public:
|
public:
|
||||||
void set( std::string tag, std::string contents );
|
void set( std::string tag, const TraceEventFields& fields );
|
||||||
std::string get( std::string tag );
|
TraceEventFields get( std::string tag );
|
||||||
std::vector<std::string> getAll();
|
std::vector<TraceEventFields> getAll();
|
||||||
std::vector<std::string> getAllUnsafe();
|
std::vector<TraceEventFields> getAllUnsafe();
|
||||||
|
|
||||||
void clear( std::string prefix );
|
void clear( std::string prefix );
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
// Latest error tracking only tracks errors when called from the main thread. Other errors are silently ignored.
|
// Latest error tracking only tracks errors when called from the main thread. Other errors are silently ignored.
|
||||||
void setLatestError( std::string contents );
|
void setLatestError( const TraceEventFields& contents );
|
||||||
std::string getLatestError();
|
TraceEventFields getLatestError();
|
||||||
private:
|
private:
|
||||||
std::map<NetworkAddress, std::map<std::string, std::string>> latest;
|
std::map<NetworkAddress, std::map<std::string, TraceEventFields>> latest;
|
||||||
std::map<NetworkAddress, std::string> latestErrors;
|
std::map<NetworkAddress, TraceEventFields> latestErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern LatestEventCache latestEventCache;
|
extern LatestEventCache latestEventCache;
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* XmlTraceLogFormatter.cpp
|
||||||
|
*
|
||||||
|
* This source file is part of the FoundationDB open source project
|
||||||
|
*
|
||||||
|
* Copyright 2018 Apple Inc. and the FoundationDB project authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "actorcompiler.h"
|
||||||
|
#include "XmlTraceLogFormatter.h"
|
||||||
|
|
||||||
|
void XmlTraceLogFormatter::addref() {
|
||||||
|
ReferenceCounted<XmlTraceLogFormatter>::addref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlTraceLogFormatter::delref() {
|
||||||
|
ReferenceCounted<XmlTraceLogFormatter>::delref();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* XmlTraceLogFormatter::getExtension() {
|
||||||
|
return "xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* XmlTraceLogFormatter::getHeader() {
|
||||||
|
return "<?xml version=\"1.0\"?>\r\n<Trace>\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* XmlTraceLogFormatter::getFooter() {
|
||||||
|
return "</Trace>\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlTraceLogFormatter::escape(std::stringstream &ss, std::string source) {
|
||||||
|
loop {
|
||||||
|
int index = source.find_first_of(std::string({'&', '"', '<', '>', '\r', '\n', '\0'}));
|
||||||
|
if(index == source.npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << source.substr(0, index);
|
||||||
|
if(source[index] == '&') {
|
||||||
|
ss << "&";
|
||||||
|
}
|
||||||
|
else if(source[index] == '"') {
|
||||||
|
ss << """;
|
||||||
|
}
|
||||||
|
else if(source[index] == '<') {
|
||||||
|
ss << "<";
|
||||||
|
}
|
||||||
|
else if(source[index] == '>') {
|
||||||
|
ss << ">";
|
||||||
|
}
|
||||||
|
else if(source[index] == '\n' || source[index] == '\r') {
|
||||||
|
ss << " ";
|
||||||
|
}
|
||||||
|
else if(source[index] == '\0') {
|
||||||
|
ss << " ";
|
||||||
|
TraceEvent(SevWarnAlways, "StrippedIllegalCharacterFromTraceEvent").detail("Source", StringRef(source).printable()).detail("Character", StringRef(source.substr(index, 1)).printable());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
source = source.substr(index+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << source;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XmlTraceLogFormatter::formatEvent(const TraceEventFields &fields) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "<Event ";
|
||||||
|
|
||||||
|
for(auto itr : fields) {
|
||||||
|
escape(ss, itr.first);
|
||||||
|
ss << "=\"";
|
||||||
|
escape(ss, itr.second);
|
||||||
|
ss << "\" ";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "/>\r\n";
|
||||||
|
return ss.str();
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* XmlTraceLogFormatter.h
|
||||||
|
*
|
||||||
|
* This source file is part of the FoundationDB open source project
|
||||||
|
*
|
||||||
|
* Copyright 2018 Apple Inc. and the FoundationDB project authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FLOW_XML_TRACE_LOG_FORMATTER_H
|
||||||
|
#define FLOW_XML_TRACE_LOG_FORMATTER_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "FastRef.h"
|
||||||
|
#include "Trace.h"
|
||||||
|
|
||||||
|
struct XmlTraceLogFormatter : public ITraceLogFormatter, ReferenceCounted<XmlTraceLogFormatter> {
|
||||||
|
void addref();
|
||||||
|
void delref();
|
||||||
|
|
||||||
|
const char* getExtension();
|
||||||
|
const char* getHeader();
|
||||||
|
const char* getFooter();
|
||||||
|
|
||||||
|
void escape(std::stringstream &ss, std::string source);
|
||||||
|
std::string formatEvent(const TraceEventFields &fields);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -874,13 +874,13 @@ namespace actorcompiler
|
||||||
case "\r\n": LineCount++; break;
|
case "\r\n": LineCount++; break;
|
||||||
case "\n": LineCount++; break;
|
case "\n": LineCount++; break;
|
||||||
}
|
}
|
||||||
if (tokens[i].Value.StartsWith("/*")) LineCount += tokens[i].Value.Count(c=>c=='\n');
|
|
||||||
if (BraceDepth < 0) throw new Error(LineCount, "Mismatched braces");
|
if (BraceDepth < 0) throw new Error(LineCount, "Mismatched braces");
|
||||||
if (ParenDepth < 0) throw new Error(LineCount, "Mismatched parenthesis");
|
if (ParenDepth < 0) throw new Error(LineCount, "Mismatched parenthesis");
|
||||||
tokens[i].Position = i;
|
tokens[i].Position = i;
|
||||||
tokens[i].SourceLine = LineCount;
|
tokens[i].SourceLine = LineCount;
|
||||||
tokens[i].BraceDepth = BraceDepth;
|
tokens[i].BraceDepth = BraceDepth;
|
||||||
tokens[i].ParenDepth = ParenDepth;
|
tokens[i].ParenDepth = ParenDepth;
|
||||||
|
if (tokens[i].Value.StartsWith("/*")) LineCount += tokens[i].Value.Count(c=>c=='\n');
|
||||||
switch (tokens[i].Value)
|
switch (tokens[i].Value)
|
||||||
{
|
{
|
||||||
case "{": BraceDepth++; if (BraceDepth==1) lastBrace = tokens[i]; break;
|
case "{": BraceDepth++; if (BraceDepth==1) lastBrace = tokens[i]; break;
|
||||||
|
|
|
@ -118,7 +118,7 @@ ERROR( read_version_already_set, 2010, "Transaction already has a read version s
|
||||||
ERROR( version_invalid, 2011, "Version not valid" )
|
ERROR( version_invalid, 2011, "Version not valid" )
|
||||||
ERROR( range_limits_invalid, 2012, "Range limits not valid" )
|
ERROR( range_limits_invalid, 2012, "Range limits not valid" )
|
||||||
ERROR( invalid_database_name, 2013, "Database name must be 'DB'" )
|
ERROR( invalid_database_name, 2013, "Database name must be 'DB'" )
|
||||||
ERROR( attribute_not_found, 2014, "Attribute not found in string" )
|
ERROR( attribute_not_found, 2014, "Attribute not found" )
|
||||||
ERROR( future_not_set, 2015, "Future not ready" )
|
ERROR( future_not_set, 2015, "Future not ready" )
|
||||||
ERROR( future_not_error, 2016, "Future not an error" )
|
ERROR( future_not_error, 2016, "Future not an error" )
|
||||||
ERROR( used_during_commit, 2017, "Operation issued while a commit was outstanding" )
|
ERROR( used_during_commit, 2017, "Operation issued while a commit was outstanding" )
|
||||||
|
|
|
@ -93,38 +93,52 @@ Optional<uint64_t> parse_with_suffix(std::string toparse, std::string default_un
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string format( const char* form, ... ) {
|
int vsformat( std::string &outputString, const char* form, va_list args) {
|
||||||
char buf[200];
|
char buf[200];
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, form);
|
va_list args2;
|
||||||
int size = vsnprintf(buf, sizeof(buf), form, args);
|
va_copy(args2, args);
|
||||||
va_end(args);
|
int size = vsnprintf(buf, sizeof(buf), form, args2);
|
||||||
|
va_end(args2);
|
||||||
|
|
||||||
if(size >= 0 && size < sizeof(buf)) {
|
if(size >= 0 && size < sizeof(buf)) {
|
||||||
return std::string(buf, size);
|
outputString = std::string(buf, size);
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Microsoft's non-standard vsnprintf doesn't return a correct size, but just an error, so determine the necessary size
|
// Microsoft's non-standard vsnprintf doesn't return a correct size, but just an error, so determine the necessary size
|
||||||
va_start(args, form);
|
va_copy(args2, args);
|
||||||
size = _vscprintf(form, args);
|
size = _vscprintf(form, args2);
|
||||||
va_end(args);
|
va_end(args2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (size < 0) throw internal_error();
|
if (size < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(true); //large format result
|
TEST(true); //large format result
|
||||||
|
|
||||||
std::string s;
|
outputString.resize(size + 1);
|
||||||
s.resize(size + 1);
|
size = vsnprintf(&outputString[0], outputString.size(), form, args);
|
||||||
va_start(args, form);
|
if (size < 0 || size >= outputString.size()) {
|
||||||
size = vsnprintf(&s[0], s.size(), form, args);
|
return -1;
|
||||||
va_end(args);
|
}
|
||||||
if (size < 0 || size >= s.size()) throw internal_error();
|
|
||||||
|
|
||||||
s.resize(size);
|
outputString.resize(size);
|
||||||
return s;
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string format( const char* form, ... ) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, form);
|
||||||
|
|
||||||
|
std::string str;
|
||||||
|
int result = vsformat(str, form, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
ASSERT(result >= 0);
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
Standalone<StringRef> strinc(StringRef const& str) {
|
Standalone<StringRef> strinc(StringRef const& str) {
|
||||||
|
|
|
@ -68,6 +68,10 @@ bool validationIsEnabled();
|
||||||
|
|
||||||
extern Optional<uint64_t> parse_with_suffix(std::string toparse, std::string default_unit = "");
|
extern Optional<uint64_t> parse_with_suffix(std::string toparse, std::string default_unit = "");
|
||||||
extern std::string format(const char* form, ...);
|
extern std::string format(const char* form, ...);
|
||||||
|
|
||||||
|
// On success, returns the number of characters written. On failure, returns a negative number.
|
||||||
|
extern int vsformat(std::string &outputString, const char* form, va_list args);
|
||||||
|
|
||||||
extern Standalone<StringRef> strinc(StringRef const& str);
|
extern Standalone<StringRef> strinc(StringRef const& str);
|
||||||
extern StringRef strinc(StringRef const& str, Arena& arena);
|
extern StringRef strinc(StringRef const& str, Arena& arena);
|
||||||
extern Standalone<StringRef> addVersionStampAtEnd(StringRef const& str);
|
extern Standalone<StringRef> addVersionStampAtEnd(StringRef const& str);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
<ProjectConfiguration Include="Debug|X64">
|
<ProjectConfiguration Include="Debug|X64">
|
||||||
|
@ -18,6 +18,10 @@
|
||||||
<ClCompile Include="Error.cpp" />
|
<ClCompile Include="Error.cpp" />
|
||||||
<ClCompile Include="FastAlloc.cpp" />
|
<ClCompile Include="FastAlloc.cpp" />
|
||||||
<ClCompile Include="FaultInjection.cpp" />
|
<ClCompile Include="FaultInjection.cpp" />
|
||||||
|
<ClCompile Include="FileTraceLogWriter.cpp" />
|
||||||
|
<ClCompile Include="XmlTraceLogFormatter.cpp" />
|
||||||
|
<ClInclude Include="FileTraceLogWriter.h" />
|
||||||
|
<ClInclude Include="XmlTraceLogFormatter.h" />
|
||||||
<ClInclude Include="MetricSample.h" />
|
<ClInclude Include="MetricSample.h" />
|
||||||
<ClInclude Include="Profiler.h" />
|
<ClInclude Include="Profiler.h" />
|
||||||
<ActorCompiler Include="Profiler.actor.cpp" />
|
<ActorCompiler Include="Profiler.actor.cpp" />
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
<ClCompile Include="version.cpp" />
|
<ClCompile Include="version.cpp" />
|
||||||
<ClCompile Include="stacktrace.amalgamation.cpp" />
|
<ClCompile Include="stacktrace.amalgamation.cpp" />
|
||||||
<ClCompile Include="SignalSafeUnwind.cpp" />
|
<ClCompile Include="SignalSafeUnwind.cpp" />
|
||||||
|
<ClCompile Include="XmlTraceLogFormatter.cpp" />
|
||||||
|
<ClCompile Include="FileTraceLogWriter.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="ActorCollection.h" />
|
<ClInclude Include="ActorCollection.h" />
|
||||||
|
@ -72,6 +74,8 @@
|
||||||
<ClInclude Include="SignalSafeUnwind.h" />
|
<ClInclude Include="SignalSafeUnwind.h" />
|
||||||
<ClInclude Include="MetricSample.h" />
|
<ClInclude Include="MetricSample.h" />
|
||||||
<ClInclude Include="stacktrace.h" />
|
<ClInclude Include="stacktrace.h" />
|
||||||
|
<ClInclude Include="XmlTraceLogFormatter.h" />
|
||||||
|
<ClInclude Include="FileTraceLogWriter.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="no_intellisense.opt" />
|
<None Include="no_intellisense.opt" />
|
||||||
|
|
|
@ -132,6 +132,18 @@ Future<T> transformErrors( Future<T> f, Error err ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACTOR template <class T>
|
||||||
|
Future<T> transformError( Future<T> f, Error inErr, Error outErr ) {
|
||||||
|
try {
|
||||||
|
T t = wait( f );
|
||||||
|
return t;
|
||||||
|
} catch( Error &e ) {
|
||||||
|
if( e.code() == inErr.code() )
|
||||||
|
throw outErr;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Note that the RequestStream<T> version of forwardPromise doesn't exist, because what to do with errors?
|
// Note that the RequestStream<T> version of forwardPromise doesn't exist, because what to do with errors?
|
||||||
|
|
||||||
ACTOR template <class T>
|
ACTOR template <class T>
|
||||||
|
|
|
@ -44,21 +44,20 @@ public class MicroQueue {
|
||||||
|
|
||||||
// Remove the top element from the queue.
|
// Remove the top element from the queue.
|
||||||
public static Object dequeue(TransactionContext tcx){
|
public static Object dequeue(TransactionContext tcx){
|
||||||
final KeyValue item = firstItem(tcx);
|
|
||||||
if(item == null){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from the top of the queue.
|
// Remove from the top of the queue.
|
||||||
tcx.run(new Function<Transaction,Void>(){
|
return tcx.run(new Function<Transaction,Void>(){
|
||||||
public Void apply(Transaction tr){
|
public Void apply(Transaction tr){
|
||||||
|
final KeyValue item = firstItem(tr);
|
||||||
|
if(item == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
tr.clear(item.getKey());
|
tr.clear(item.getKey());
|
||||||
return null;
|
// Return the old value.
|
||||||
|
return Tuple.fromBytes(item.getValue()).get(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return the old value.
|
|
||||||
return Tuple.fromBytes(item.getValue()).get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an element to the queue.
|
// Add an element to the queue.
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue