conflict solving
This commit is contained in:
commit
d93b57dd88
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
# This source file is part of the FoundationDB open source project
|
||||
#
|
||||
# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
# Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -202,6 +202,7 @@ class TestRunner(object):
|
|||
self.args.types = list(reduce(lambda t1, t2: filter(t1.__contains__, t2), map(lambda tester: tester.types, self.testers)))
|
||||
|
||||
self.args.no_directory_snapshot_ops = self.args.no_directory_snapshot_ops or any([not tester.directory_snapshot_ops_enabled for tester in self.testers])
|
||||
self.args.no_tenants = self.args.no_tenants or any([not tester.tenants_enabled for tester in self.testers])
|
||||
|
||||
def print_test(self):
|
||||
test_instructions = self._generate_test()
|
||||
|
@ -282,6 +283,17 @@ class TestRunner(object):
|
|||
def _insert_instructions(self, test_instructions):
|
||||
util.get_logger().info('\nInserting test into database...')
|
||||
del self.db[:]
|
||||
|
||||
while True:
|
||||
tr = self.db.create_transaction()
|
||||
try:
|
||||
tr.options.set_special_key_space_enable_writes()
|
||||
del tr[b'\xff\xff/management/tenant_map/' : b'\xff\xff/management/tenant_map0']
|
||||
tr.commit().wait()
|
||||
break
|
||||
except fdb.FDBError as e:
|
||||
tr.on_error(e).wait()
|
||||
|
||||
for subspace, thread in test_instructions.items():
|
||||
thread.insert_operations(self.db, subspace)
|
||||
|
||||
|
@ -445,6 +457,8 @@ def parse_args(argv):
|
|||
|
||||
parser.add_argument('--no-directory-snapshot-ops', action='store_true', help='Disables snapshot operations for directory instructions.')
|
||||
|
||||
parser.add_argument('--no-tenants', action='store_true', help='Disables tenant operations.')
|
||||
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ ALL_TYPES = COMMON_TYPES + ['versionstamp']
|
|||
|
||||
|
||||
class Tester:
|
||||
def __init__(self, name, cmd, max_int_bits=64, min_api_version=0, max_api_version=MAX_API_VERSION, threads_enabled=True, types=COMMON_TYPES, directory_snapshot_ops_enabled=True):
|
||||
def __init__(self, name, cmd, max_int_bits=64, min_api_version=0, max_api_version=MAX_API_VERSION, threads_enabled=True, types=COMMON_TYPES, directory_snapshot_ops_enabled=True, tenants_enabled=False):
|
||||
self.name = name
|
||||
self.cmd = cmd
|
||||
self.max_int_bits = max_int_bits
|
||||
|
@ -35,6 +35,7 @@ class Tester:
|
|||
self.threads_enabled = threads_enabled
|
||||
self.types = types
|
||||
self.directory_snapshot_ops_enabled = directory_snapshot_ops_enabled
|
||||
self.tenants_enabled = tenants_enabled
|
||||
|
||||
def supports_api_version(self, api_version):
|
||||
return api_version >= self.min_api_version and api_version <= self.max_api_version
|
||||
|
@ -57,8 +58,8 @@ _java_cmd = 'java -ea -cp %s:%s com.apple.foundationdb.test.' % (
|
|||
|
||||
# We could set min_api_version lower on some of these if the testers were updated to support them
|
||||
testers = {
|
||||
'python': Tester('python', 'python ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES),
|
||||
'python3': Tester('python3', 'python3 ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES),
|
||||
'python': Tester('python', 'python ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES, tenants_enabled=True),
|
||||
'python3': Tester('python3', 'python3 ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES, tenants_enabled=True),
|
||||
'ruby': Tester('ruby', _absolute_path('ruby/tests/tester.rb'), 2040, 23, MAX_API_VERSION),
|
||||
'java': Tester('java', _java_cmd + 'StackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES),
|
||||
'java_async': Tester('java', _java_cmd + 'AsyncStackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES),
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
Overview
|
||||
--------
|
||||
|
||||
Tenant testing is an optional extension to the core binding tester that enables
|
||||
testing of the tenant API. This testing is enabled by adding some additional
|
||||
instructions and modifying the behavior of some existing instructions.
|
||||
|
||||
Additional State and Initialization
|
||||
-----------------------------------
|
||||
|
||||
Your tester should store an additional piece of state tracking the active tenant
|
||||
that is to be used to create transactions. This tenant must support an unset
|
||||
state, in which case transactions will be created directly on the database.
|
||||
|
||||
New Instructions
|
||||
----------------
|
||||
|
||||
The tenant API introduces some new operations:
|
||||
|
||||
#### TENANT_CREATE
|
||||
|
||||
Pops the top item off of the stack as TENANT_NAME. Creates a new tenant
|
||||
in the database with the name TENANT_NAME. May optionally push a future
|
||||
onto the stack.
|
||||
|
||||
#### TENANT_DELETE
|
||||
|
||||
Pops the top item off of the stack as TENANT_NAME. Deletes the tenant with
|
||||
the name TENANT_NAME from the database. May optionally push a future onto
|
||||
the stack.
|
||||
|
||||
#### TENANT_SET_ACTIVE
|
||||
|
||||
Pops the top item off of the stack as TENANT_NAME. Opens the tenant with
|
||||
name TENANT_NAME and stores it as the active tenant.
|
||||
|
||||
#### TENANT_CLEAR_ACTIVE
|
||||
|
||||
Unsets the active tenant.
|
||||
|
||||
Updates to Existing Instructions
|
||||
--------------------------------
|
||||
|
||||
Some existing operations in the binding tester will have slightly modified
|
||||
behavior when tenants are enabled.
|
||||
|
||||
#### NEW_TRANSACTION
|
||||
|
||||
When creating a new transaction, the active tenant should be used. If no active
|
||||
tenant is set, then the transaction should be created as normal using the
|
||||
database.
|
||||
|
||||
#### _TENANT suffix
|
||||
|
||||
Similar to the _DATABASE suffix, an operation with the _TENANT suffix indicates
|
||||
that the operation should be performed on the current active tenant object. If
|
||||
there is no active tenant, then the operation should be performed on the database
|
||||
as if _DATABASE was specified. In any case where the operation suffixed with
|
||||
_DATABASE is allowed to push a future onto the stack, the same operation suffixed
|
||||
with _TENANT is also allowed to push a future onto the stack.
|
||||
|
||||
If your binding does not support operations directly on a tenant object, you should
|
||||
simulate it using an anonymous transaction. Remember that set and clear operations
|
||||
must immediately commit (with appropriate retry behavior!).
|
||||
|
||||
Operations that can include the _TENANT prefix are:
|
||||
|
||||
GET_TENANT
|
||||
GET_KEY_TENANT
|
||||
GET_RANGE_TENANT
|
||||
GET_RANGE_STARTS_WITH_TENANT
|
||||
GET_RANGE_SELECTOR_TENANT
|
||||
SET_TENANT
|
||||
CLEAR_TENANT
|
||||
CLEAR_RANGE_TENANT
|
||||
CLEAR_RANGE_STARTS_WITH_TENANT
|
||||
ATOMIC_OP_TENANT
|
|
@ -58,6 +58,7 @@ class ApiTest(Test):
|
|||
self.outstanding_ops = []
|
||||
self.random = test_util.RandomGenerator(args.max_int_bits, args.api_version, args.types)
|
||||
self.api_version = args.api_version
|
||||
self.allocated_tenants = set()
|
||||
|
||||
def add_stack_items(self, num):
|
||||
self.stack_size += num
|
||||
|
@ -137,6 +138,12 @@ class ApiTest(Test):
|
|||
test_util.to_front(instructions, self.stack_size - read[0])
|
||||
instructions.append('WAIT_FUTURE')
|
||||
|
||||
def choose_tenant(self, new_tenant_probability):
|
||||
if len(self.allocated_tenants) == 0 or random.random() < new_tenant_probability:
|
||||
return self.random.random_string(random.randint(0, 30))
|
||||
else:
|
||||
return random.choice(list(self.allocated_tenants))
|
||||
|
||||
def generate(self, args, thread_number):
|
||||
instructions = InstructionSet()
|
||||
|
||||
|
@ -158,6 +165,7 @@ class ApiTest(Test):
|
|||
write_conflicts = ['WRITE_CONFLICT_RANGE', 'WRITE_CONFLICT_KEY', 'DISABLE_WRITE_CONFLICT']
|
||||
txn_sizes = ['GET_APPROXIMATE_SIZE']
|
||||
storage_metrics = ['GET_ESTIMATED_RANGE_SIZE', 'GET_RANGE_SPLIT_POINTS']
|
||||
tenants = ['TENANT_CREATE', 'TENANT_DELETE', 'TENANT_SET_ACTIVE', 'TENANT_CLEAR_ACTIVE']
|
||||
|
||||
op_choices += reads
|
||||
op_choices += mutations
|
||||
|
@ -173,6 +181,9 @@ class ApiTest(Test):
|
|||
op_choices += txn_sizes
|
||||
op_choices += storage_metrics
|
||||
|
||||
if not args.no_tenants:
|
||||
op_choices += tenants
|
||||
|
||||
idempotent_atomic_ops = ['BIT_AND', 'BIT_OR', 'MAX', 'MIN', 'BYTE_MIN', 'BYTE_MAX']
|
||||
atomic_ops = idempotent_atomic_ops + ['ADD', 'BIT_XOR', 'APPEND_IF_FITS']
|
||||
|
||||
|
@ -195,7 +206,7 @@ class ApiTest(Test):
|
|||
|
||||
# print 'Adding instruction %s at %d' % (op, index)
|
||||
|
||||
if args.concurrency == 1 and (op in database_mutations):
|
||||
if args.concurrency == 1 and (op in database_mutations or op in ['TENANT_CREATE', 'TENANT_DELETE']):
|
||||
self.wait_for_reads(instructions)
|
||||
test_util.blocking_commit(instructions)
|
||||
self.can_get_commit_version = False
|
||||
|
@ -570,18 +581,39 @@ class ApiTest(Test):
|
|||
instructions.push_args(key1, key2, chunkSize)
|
||||
instructions.append(op)
|
||||
self.add_strings(1)
|
||||
|
||||
elif op == 'TENANT_CREATE':
|
||||
tenant_name = self.choose_tenant(0.8)
|
||||
self.allocated_tenants.add(tenant_name)
|
||||
instructions.push_args(tenant_name)
|
||||
instructions.append(op)
|
||||
self.add_strings(1)
|
||||
elif op == 'TENANT_DELETE':
|
||||
tenant_name = self.choose_tenant(0.2)
|
||||
if tenant_name in self.allocated_tenants:
|
||||
self.allocated_tenants.remove(tenant_name)
|
||||
instructions.push_args(tenant_name)
|
||||
instructions.append(op)
|
||||
self.add_strings(1)
|
||||
elif op == 'TENANT_SET_ACTIVE':
|
||||
tenant_name = self.choose_tenant(0.8)
|
||||
instructions.push_args(tenant_name)
|
||||
instructions.append(op)
|
||||
elif op == 'TENANT_CLEAR_ACTIVE':
|
||||
instructions.append(op)
|
||||
else:
|
||||
assert False, 'Unknown operation: ' + op
|
||||
|
||||
if read_performed and op not in database_reads:
|
||||
self.outstanding_ops.append((self.stack_size, len(instructions) - 1))
|
||||
|
||||
if args.concurrency == 1 and (op in database_reads or op in database_mutations):
|
||||
if args.concurrency == 1 and (op in database_reads or op in database_mutations or op in ['TENANT_CREATE', 'TENANT_DELETE']):
|
||||
instructions.append('WAIT_FUTURE')
|
||||
|
||||
instructions.begin_finalization()
|
||||
|
||||
if not args.no_tenants:
|
||||
instructions.append('TENANT_CLEAR_ACTIVE')
|
||||
|
||||
if args.concurrency == 1:
|
||||
self.wait_for_reads(instructions)
|
||||
test_util.blocking_commit(instructions)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -88,6 +88,7 @@ def api_version(ver):
|
|||
'predicates',
|
||||
'Future',
|
||||
'Database',
|
||||
'Tenant',
|
||||
'Transaction',
|
||||
'KeyValue',
|
||||
'KeySelector',
|
||||
|
|
|
@ -34,6 +34,7 @@ import traceback
|
|||
|
||||
import fdb
|
||||
from fdb import six
|
||||
from fdb.tuple import pack, unpack
|
||||
|
||||
_network_thread = None
|
||||
_network_thread_reentrant_lock = threading.RLock()
|
||||
|
@ -198,9 +199,10 @@ def transactional(*tr_args, **tr_kwargs):
|
|||
one of two actions, depending on the type of the parameter passed
|
||||
to the function at call time.
|
||||
|
||||
If given a Database, a Transaction will be created and passed into
|
||||
the wrapped code in place of the Database. After the function is
|
||||
complete, the newly created transaction will be committed.
|
||||
If given a Database or Tenant, a Transaction will be created and
|
||||
passed into the wrapped code in place of the Database or Tenant.
|
||||
After the function is complete, the newly created transaction
|
||||
will be committed.
|
||||
|
||||
It is important to note that the wrapped method may be called
|
||||
multiple times in the event of a commit failure, until the commit
|
||||
|
@ -943,128 +945,114 @@ class FormerFuture(_FDBBase):
|
|||
except:
|
||||
pass
|
||||
|
||||
|
||||
class Database(_FDBBase):
|
||||
def __init__(self, dpointer):
|
||||
self.dpointer = dpointer
|
||||
self.options = _DatabaseOptions(self)
|
||||
|
||||
def __del__(self):
|
||||
# print('Destroying database 0x%x' % self.dpointer)
|
||||
self.capi.fdb_database_destroy(self.dpointer)
|
||||
|
||||
class _TransactionCreator(_FDBBase):
|
||||
def get(self, key):
|
||||
return Database.__database_getitem(self, key)
|
||||
return _TransactionCreator.__creator_getitem(self, key)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, slice):
|
||||
return self.get_range(key.start, key.stop, reverse=(key.step == -1))
|
||||
return Database.__database_getitem(self, key)
|
||||
return _TransactionCreator.__creator_getitem(self, key)
|
||||
|
||||
def get_key(self, key_selector):
|
||||
return Database.__database_get_key(self, key_selector)
|
||||
return _TransactionCreator.__creator_get_key(self, key_selector)
|
||||
|
||||
def get_range(self, begin, end, limit=0, reverse=False, streaming_mode=StreamingMode.want_all):
|
||||
return Database.__database_get_range(self, begin, end, limit, reverse, streaming_mode)
|
||||
return _TransactionCreator.__creator_get_range(self, begin, end, limit, reverse, streaming_mode)
|
||||
|
||||
def get_range_startswith(self, prefix, *args, **kwargs):
|
||||
return Database.__database_get_range_startswith(self, prefix, *args, **kwargs)
|
||||
return _TransactionCreator.__creator_get_range_startswith(self, prefix, *args, **kwargs)
|
||||
|
||||
def set(self, key, value):
|
||||
Database.__database_setitem(self, key, value)
|
||||
_TransactionCreator.__creator_setitem(self, key, value)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
Database.__database_setitem(self, key, value)
|
||||
_TransactionCreator.__creator_setitem(self, key, value)
|
||||
|
||||
def clear(self, key):
|
||||
Database.__database_delitem(self, key)
|
||||
_TransactionCreator.__creator_delitem(self, key)
|
||||
|
||||
def clear_range(self, begin, end):
|
||||
Database.__database_delitem(self, slice(begin, end))
|
||||
_TransactionCreator.__creator_delitem(self, slice(begin, end))
|
||||
|
||||
def __delitem__(self, key_or_slice):
|
||||
Database.__database_delitem(self, key_or_slice)
|
||||
_TransactionCreator.__creator_delitem(self, key_or_slice)
|
||||
|
||||
def clear_range_startswith(self, prefix):
|
||||
Database.__database_clear_range_startswith(self, prefix)
|
||||
_TransactionCreator.__creator_clear_range_startswith(self, prefix)
|
||||
|
||||
def get_and_watch(self, key):
|
||||
return Database.__database_get_and_watch(self, key)
|
||||
return _TransactionCreator.__creator_get_and_watch(self, key)
|
||||
|
||||
def set_and_watch(self, key, value):
|
||||
return Database.__database_set_and_watch(self, key, value)
|
||||
return _TransactionCreator.__creator_set_and_watch(self, key, value)
|
||||
|
||||
def clear_and_watch(self, key):
|
||||
return Database.__database_clear_and_watch(self, key)
|
||||
return _TransactionCreator.__creator_clear_and_watch(self, key)
|
||||
|
||||
def create_transaction(self):
|
||||
pointer = ctypes.c_void_p()
|
||||
self.capi.fdb_database_create_transaction(self.dpointer, ctypes.byref(pointer))
|
||||
return Transaction(pointer.value, self)
|
||||
|
||||
def _set_option(self, option, param, length):
|
||||
self.capi.fdb_database_set_option(self.dpointer, option, param, length)
|
||||
pass
|
||||
|
||||
def _atomic_operation(self, opcode, key, param):
|
||||
Database.__database_atomic_operation(self, opcode, key, param)
|
||||
_TransactionCreator.__creator_atomic_operation(self, opcode, key, param)
|
||||
|
||||
#### Transaction implementations ####
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_getitem(tr, key):
|
||||
def __creator_getitem(tr, key):
|
||||
return tr[key].value
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_get_key(tr, key_selector):
|
||||
def __creator_get_key(tr, key_selector):
|
||||
return tr.get_key(key_selector).value
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_get_range(tr, begin, end, limit, reverse, streaming_mode):
|
||||
def __creator_get_range(tr, begin, end, limit, reverse, streaming_mode):
|
||||
return tr.get_range(begin, end, limit, reverse, streaming_mode).to_list()
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_get_range_startswith(tr, prefix, *args, **kwargs):
|
||||
def __creator_get_range_startswith(tr, prefix, *args, **kwargs):
|
||||
return tr.get_range_startswith(prefix, *args, **kwargs).to_list()
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_setitem(tr, key, value):
|
||||
def __creator_setitem(tr, key, value):
|
||||
tr[key] = value
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_clear_range_startswith(tr, prefix):
|
||||
def __creator_clear_range_startswith(tr, prefix):
|
||||
tr.clear_range_startswith(prefix)
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_get_and_watch(tr, key):
|
||||
def __creator_get_and_watch(tr, key):
|
||||
v = tr.get(key)
|
||||
return v, tr.watch(key)
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_set_and_watch(tr, key, value):
|
||||
def __creator_set_and_watch(tr, key, value):
|
||||
tr.set(key, value)
|
||||
return tr.watch(key)
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_clear_and_watch(tr, key):
|
||||
def __creator_clear_and_watch(tr, key):
|
||||
del tr[key]
|
||||
return tr.watch(key)
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_delitem(tr, key_or_slice):
|
||||
def __creator_delitem(tr, key_or_slice):
|
||||
del tr[key_or_slice]
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_atomic_operation(tr, opcode, key, param):
|
||||
def __creator_atomic_operation(tr, opcode, key, param):
|
||||
tr._atomic_operation(opcode, key, param)
|
||||
|
||||
# Asynchronous transactions
|
||||
|
@ -1074,11 +1062,11 @@ class Database(_FDBBase):
|
|||
From = asyncio.From
|
||||
coroutine = asyncio.coroutine
|
||||
|
||||
class Database:
|
||||
class TransactionCreator:
|
||||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_getitem(tr, key):
|
||||
def __creator_getitem(tr, key):
|
||||
# raise Return(( yield From( tr[key] ) ))
|
||||
raise Return(tr[key])
|
||||
yield None
|
||||
|
@ -1086,26 +1074,26 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_get_key(tr, key_selector):
|
||||
def __creator_get_key(tr, key_selector):
|
||||
raise Return(tr.get_key(key_selector))
|
||||
yield None
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_get_range(tr, begin, end, limit, reverse, streaming_mode):
|
||||
def __creator_get_range(tr, begin, end, limit, reverse, streaming_mode):
|
||||
raise Return((yield From(tr.get_range(begin, end, limit, reverse, streaming_mode).to_list())))
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_get_range_startswith(tr, prefix, *args, **kwargs):
|
||||
def __creator_get_range_startswith(tr, prefix, *args, **kwargs):
|
||||
raise Return((yield From(tr.get_range_startswith(prefix, *args, **kwargs).to_list())))
|
||||
|
||||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_setitem(tr, key, value):
|
||||
def __creator_setitem(tr, key, value):
|
||||
tr[key] = value
|
||||
raise Return()
|
||||
yield None
|
||||
|
@ -1113,7 +1101,7 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_clear_range_startswith(tr, prefix):
|
||||
def __creator_clear_range_startswith(tr, prefix):
|
||||
tr.clear_range_startswith(prefix)
|
||||
raise Return()
|
||||
yield None
|
||||
|
@ -1121,7 +1109,7 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_get_and_watch(tr, key):
|
||||
def __creator_get_and_watch(tr, key):
|
||||
v = tr.get(key)
|
||||
raise Return(v, tr.watch(key))
|
||||
yield None
|
||||
|
@ -1129,7 +1117,7 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_set_and_watch(tr, key, value):
|
||||
def __creator_set_and_watch(tr, key, value):
|
||||
tr.set(key, value)
|
||||
raise Return(tr.watch(key))
|
||||
yield None
|
||||
|
@ -1137,7 +1125,7 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_clear_and_watch(tr, key):
|
||||
def __creator_clear_and_watch(tr, key):
|
||||
del tr[key]
|
||||
raise Return(tr.watch(key))
|
||||
yield None
|
||||
|
@ -1145,7 +1133,7 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_delitem(tr, key_or_slice):
|
||||
def __creator_delitem(tr, key_or_slice):
|
||||
del tr[key_or_slice]
|
||||
raise Return()
|
||||
yield None
|
||||
|
@ -1153,11 +1141,101 @@ class Database(_FDBBase):
|
|||
@staticmethod
|
||||
@transactional
|
||||
@coroutine
|
||||
def __database_atomic_operation(tr, opcode, key, param):
|
||||
def __creator_atomic_operation(tr, opcode, key, param):
|
||||
tr._atomic_operation(opcode, key, param)
|
||||
raise Return()
|
||||
yield None
|
||||
return Database
|
||||
return TransactionCreator
|
||||
|
||||
def process_tenant_name(name):
|
||||
if isinstance(name, tuple):
|
||||
return pack(name)
|
||||
elif isinstance(name, bytes):
|
||||
return name
|
||||
else:
|
||||
raise TypeError('Tenant name must be of type ' + bytes.__name__ + ' or of type ' + tuple.__name__)
|
||||
|
||||
class Database(_TransactionCreator):
|
||||
def __init__(self, dpointer):
|
||||
self.dpointer = dpointer
|
||||
self.options = _DatabaseOptions(self)
|
||||
|
||||
def __del__(self):
|
||||
# print('Destroying database 0x%x' % self.dpointer)
|
||||
self.capi.fdb_database_destroy(self.dpointer)
|
||||
|
||||
def _set_option(self, option, param, length):
|
||||
self.capi.fdb_database_set_option(self.dpointer, option, param, length)
|
||||
|
||||
def open_tenant(self, name):
|
||||
tname = process_tenant_name(name)
|
||||
pointer = ctypes.c_void_p()
|
||||
self.capi.fdb_database_open_tenant(self.dpointer, tname, len(tname), ctypes.byref(pointer))
|
||||
return Tenant(pointer.value)
|
||||
|
||||
def create_transaction(self):
|
||||
pointer = ctypes.c_void_p()
|
||||
self.capi.fdb_database_create_transaction(self.dpointer, ctypes.byref(pointer))
|
||||
return Transaction(pointer.value, self)
|
||||
|
||||
def allocate_tenant(self, name):
|
||||
Database.__database_allocate_tenant(self, process_tenant_name(name), [])
|
||||
|
||||
def delete_tenant(self, name):
|
||||
Database.__database_delete_tenant(self, process_tenant_name(name), [])
|
||||
|
||||
# Attempt to allocate a tenant in the cluster. If the tenant already exists,
|
||||
# this function will return a tenant_already_exists error. If the tenant is created
|
||||
# concurrently, then this function may return success even if another caller creates
|
||||
# it.
|
||||
#
|
||||
# The existence_check_marker is expected to be an empty list. This function will
|
||||
# modify the list after completing the existence check to avoid checking for existence
|
||||
# on retries. This allows the operation to be idempotent.
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_allocate_tenant(tr, name, existence_check_marker):
|
||||
tr.options.set_special_key_space_enable_writes()
|
||||
key = b'\xff\xff/management/tenant_map/%s' % name
|
||||
if not existence_check_marker:
|
||||
existing_tenant = tr[key].wait()
|
||||
existence_check_marker.append(None)
|
||||
if existing_tenant != None:
|
||||
raise fdb.FDBError(2132) # tenant_already_exists
|
||||
tr[key] = b''
|
||||
|
||||
# Attempt to remove a tenant in the cluster. If the tenant doesn't exist, this
|
||||
# function will return a tenant_not_found error. If the tenant is deleted
|
||||
# concurrently, then this function may return success even if another caller deletes
|
||||
# it.
|
||||
#
|
||||
# The existence_check_marker is expected to be an empty list. This function will
|
||||
# modify the list after completing the existence check to avoid checking for existence
|
||||
# on retries. This allows the operation to be idempotent.
|
||||
@staticmethod
|
||||
@transactional
|
||||
def __database_delete_tenant(tr, name, existence_check_marker):
|
||||
tr.options.set_special_key_space_enable_writes()
|
||||
key = b'\xff\xff/management/tenant_map/%s' % name
|
||||
if not existence_check_marker:
|
||||
existing_tenant = tr[key].wait()
|
||||
existence_check_marker.append(None)
|
||||
if existing_tenant == None:
|
||||
raise fdb.FDBError(2131) # tenant_not_found
|
||||
del tr[key]
|
||||
|
||||
|
||||
class Tenant(_TransactionCreator):
|
||||
def __init__(self, tpointer):
|
||||
self.tpointer = tpointer
|
||||
|
||||
def __del__(self):
|
||||
self.capi.fdb_tenant_destroy(self.tpointer)
|
||||
|
||||
def create_transaction(self):
|
||||
pointer = ctypes.c_void_p()
|
||||
self.capi.fdb_tenant_create_transaction(self.tpointer, ctypes.byref(pointer))
|
||||
return Transaction(pointer.value, self)
|
||||
|
||||
|
||||
fill_operations()
|
||||
|
@ -1458,6 +1536,10 @@ def init_c_api():
|
|||
_capi.fdb_database_destroy.argtypes = [ctypes.c_void_p]
|
||||
_capi.fdb_database_destroy.restype = None
|
||||
|
||||
_capi.fdb_database_open_tenant.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p)]
|
||||
_capi.fdb_database_open_tenant.restype = ctypes.c_int
|
||||
_capi.fdb_database_open_tenant.errcheck = check_error_code
|
||||
|
||||
_capi.fdb_database_create_transaction.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
|
||||
_capi.fdb_database_create_transaction.restype = ctypes.c_int
|
||||
_capi.fdb_database_create_transaction.errcheck = check_error_code
|
||||
|
@ -1466,6 +1548,13 @@ def init_c_api():
|
|||
_capi.fdb_database_set_option.restype = ctypes.c_int
|
||||
_capi.fdb_database_set_option.errcheck = check_error_code
|
||||
|
||||
_capi.fdb_tenant_destroy.argtypes = [ctypes.c_void_p]
|
||||
_capi.fdb_tenant_destroy.restype = None
|
||||
|
||||
_capi.fdb_tenant_create_transaction.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
|
||||
_capi.fdb_tenant_create_transaction.restype = ctypes.c_int
|
||||
_capi.fdb_tenant_create_transaction.errcheck = check_error_code
|
||||
|
||||
_capi.fdb_transaction_destroy.argtypes = [ctypes.c_void_p]
|
||||
_capi.fdb_transaction_destroy.restype = None
|
||||
|
||||
|
@ -1686,10 +1775,10 @@ def init(event_model=None):
|
|||
raise asyncio.Return(self)
|
||||
return it()
|
||||
FDBRange.iterate = iterate
|
||||
AT = Database.declare_asynchronous_transactions()
|
||||
AT = _TransactionCreator.declare_asynchronous_transactions()
|
||||
for name in dir(AT):
|
||||
if name.startswith("_Database__database_"):
|
||||
setattr(Database, name, getattr(AT, name))
|
||||
if name.startswith("__TransactionCreator__creator_"):
|
||||
setattr(_TransactionCreator, name, getattr(AT, name))
|
||||
|
||||
def to_list(self):
|
||||
if self._mode == StreamingMode.iterator:
|
||||
|
|
|
@ -542,6 +542,103 @@ def triggerddteaminfolog(logger):
|
|||
output = run_fdbcli_command('triggerddteaminfolog')
|
||||
assert output == 'Triggered team info logging in data distribution.'
|
||||
|
||||
@enable_logging()
|
||||
def tenants(logger):
|
||||
output = run_fdbcli_command('listtenants')
|
||||
assert output == 'The cluster has no tenants'
|
||||
|
||||
output = run_fdbcli_command('createtenant tenant')
|
||||
assert output == 'The tenant `tenant\' has been created'
|
||||
|
||||
output = run_fdbcli_command('createtenant tenant2')
|
||||
assert output == 'The tenant `tenant2\' has been created'
|
||||
|
||||
output = run_fdbcli_command('listtenants')
|
||||
assert output == '1. tenant\n 2. tenant2'
|
||||
|
||||
output = run_fdbcli_command('listtenants a z 1')
|
||||
assert output == '1. tenant'
|
||||
|
||||
output = run_fdbcli_command('listtenants a tenant2')
|
||||
assert output == '1. tenant'
|
||||
|
||||
output = run_fdbcli_command('listtenants tenant2 z')
|
||||
assert output == '1. tenant2'
|
||||
|
||||
output = run_fdbcli_command('gettenant tenant')
|
||||
lines = output.split('\n')
|
||||
assert len(lines) == 2
|
||||
assert lines[0].strip().startswith('id: ')
|
||||
assert lines[1].strip().startswith('prefix: ')
|
||||
|
||||
output = run_fdbcli_command('usetenant')
|
||||
assert output == 'Using the default tenant'
|
||||
|
||||
output = run_fdbcli_command_and_get_error('usetenant tenant3')
|
||||
assert output == 'ERROR: Tenant `tenant3\' does not exist'
|
||||
|
||||
# Test writing keys to different tenants and make sure they all work correctly
|
||||
run_fdbcli_command('writemode on; set tenant_test default_tenant')
|
||||
output = run_fdbcli_command('get tenant_test')
|
||||
assert output == '`tenant_test\' is `default_tenant\''
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'usetenant tenant', 'get tenant_test', 'set tenant_test tenant']
|
||||
output, _ = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-3:]
|
||||
assert lines[0] == 'Using tenant `tenant\''
|
||||
assert lines[1] == '`tenant_test\': not found'
|
||||
assert lines[2].startswith('Committed')
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'usetenant tenant2', 'get tenant_test', 'set tenant_test tenant2', 'get tenant_test']
|
||||
output, _ = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-4:]
|
||||
assert lines[0] == 'Using tenant `tenant2\''
|
||||
assert lines[1] == '`tenant_test\': not found'
|
||||
assert lines[2].startswith('Committed')
|
||||
assert lines[3] == '`tenant_test\' is `tenant2\''
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['usetenant tenant', 'get tenant_test', 'defaulttenant', 'get tenant_test']
|
||||
output, _ = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-4:]
|
||||
assert lines[0] == 'Using tenant `tenant\''
|
||||
assert lines[1] == '`tenant_test\' is `tenant\''
|
||||
assert lines[2] == 'Using the default tenant'
|
||||
assert lines[3] == '`tenant_test\' is `default_tenant\''
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'usetenant tenant', 'clear tenant_test', 'deletetenant tenant', 'get tenant_test', 'defaulttenant', 'usetenant tenant']
|
||||
output, error_output = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-7:]
|
||||
error_lines = error_output.decode().strip().split('\n')[-2:]
|
||||
assert lines[0] == 'Using tenant `tenant\''
|
||||
assert lines[1].startswith('Committed')
|
||||
assert lines[2] == 'The tenant `tenant\' has been deleted'
|
||||
assert lines[3] == 'WARNING: the active tenant was deleted. Use the `usetenant\' or `defaulttenant\''
|
||||
assert lines[4] == 'command to choose a new tenant.'
|
||||
assert error_lines[0] == 'ERROR: Tenant does not exist (2131)'
|
||||
assert lines[6] == 'Using the default tenant'
|
||||
assert error_lines[1] == 'ERROR: Tenant `tenant\' does not exist'
|
||||
|
||||
process = subprocess.Popen(command_template[:-1], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=fdbcli_env)
|
||||
cmd_sequence = ['writemode on', 'deletetenant tenant2', 'usetenant tenant2', 'clear tenant_test', 'defaulttenant', 'deletetenant tenant2']
|
||||
output, error_output = process.communicate(input='\n'.join(cmd_sequence).encode())
|
||||
|
||||
lines = output.decode().strip().split('\n')[-4:]
|
||||
error_lines = error_output.decode().strip().split('\n')[-1:]
|
||||
assert error_lines[0] == 'ERROR: Cannot delete a non-empty tenant (2133)'
|
||||
assert lines[0] == 'Using tenant `tenant2\''
|
||||
assert lines[1].startswith('Committed')
|
||||
assert lines[2] == 'Using the default tenant'
|
||||
assert lines[3] == 'The tenant `tenant2\' has been deleted'
|
||||
|
||||
run_fdbcli_command('writemode on; clear tenant_test')
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
|
||||
|
@ -586,6 +683,7 @@ if __name__ == '__main__':
|
|||
transaction()
|
||||
throttle()
|
||||
triggerddteaminfolog()
|
||||
tenants()
|
||||
else:
|
||||
assert args.process_number > 1, "Process number should be positive"
|
||||
coordinators()
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# tenant_tests.py
|
||||
#
|
||||
# This source file is part of the FoundationDB open source project
|
||||
#
|
||||
# Copyright 2013-2022 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.
|
||||
#
|
||||
import fdb
|
||||
import sys
|
||||
import json
|
||||
from fdb.tuple import pack
|
||||
|
||||
if __name__ == '__main__':
|
||||
fdb.api_version(710)
|
||||
|
||||
def test_tenant_tuple_name(db):
|
||||
tuplename=(b'test', b'level', b'hierarchy', 3, 1.24, 'str')
|
||||
db.allocate_tenant(tuplename)
|
||||
|
||||
tenant=db.open_tenant(tuplename)
|
||||
tenant[b'foo'] = b'bar'
|
||||
|
||||
assert tenant[b'foo'] == b'bar'
|
||||
|
||||
del tenant[b'foo']
|
||||
db.delete_tenant(tuplename)
|
||||
|
||||
def cleanup_tenant(db, tenant_name):
|
||||
try:
|
||||
tenant = db.open_tenant(tenant_name)
|
||||
del tenant[:]
|
||||
db.delete_tenant(tenant_name)
|
||||
except fdb.FDBError as e:
|
||||
if e.code == 2131: # tenant not found
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def test_tenant_operations(db):
|
||||
cleanup_tenant(db, b'tenant1')
|
||||
cleanup_tenant(db, b'tenant2')
|
||||
|
||||
db.allocate_tenant(b'tenant1')
|
||||
db.allocate_tenant(b'tenant2')
|
||||
|
||||
tenant1 = db.open_tenant(b'tenant1')
|
||||
tenant2 = db.open_tenant(b'tenant2')
|
||||
|
||||
db[b'tenant_test_key'] = b'no_tenant'
|
||||
tenant1[b'tenant_test_key'] = b'tenant1'
|
||||
tenant2[b'tenant_test_key'] = b'tenant2'
|
||||
|
||||
tenant1_entry = db[b'\xff\xff/management/tenant_map/tenant1']
|
||||
tenant1_json = json.loads(tenant1_entry)
|
||||
prefix1 = tenant1_json['prefix'].encode('utf8')
|
||||
|
||||
tenant2_entry = db[b'\xff\xff/management/tenant_map/tenant2']
|
||||
tenant2_json = json.loads(tenant2_entry)
|
||||
prefix2 = tenant2_json['prefix'].encode('utf8')
|
||||
|
||||
assert tenant1[b'tenant_test_key'] == b'tenant1'
|
||||
assert db[prefix1 + b'tenant_test_key'] == b'tenant1'
|
||||
assert tenant2[b'tenant_test_key'] == b'tenant2'
|
||||
assert db[prefix2 + b'tenant_test_key'] == b'tenant2'
|
||||
assert db[b'tenant_test_key'] == b'no_tenant'
|
||||
|
||||
tr1 = tenant1.create_transaction()
|
||||
try:
|
||||
del tr1[:]
|
||||
tr1.commit().wait()
|
||||
except fdb.FDBError as e:
|
||||
tr.on_error(e).wait()
|
||||
|
||||
assert tenant1[b'tenant_test_key'] == None
|
||||
assert db[prefix1 + b'tenant_test_key'] == None
|
||||
assert tenant2[b'tenant_test_key'] == b'tenant2'
|
||||
assert db[prefix2 + b'tenant_test_key'] == b'tenant2'
|
||||
assert db[b'tenant_test_key'] == b'no_tenant'
|
||||
|
||||
db.delete_tenant(b'tenant1')
|
||||
try:
|
||||
tenant1[b'tenant_test_key']
|
||||
assert False
|
||||
except fdb.FDBError as e:
|
||||
assert e.code == 2131 # tenant not found
|
||||
|
||||
del tenant2[:]
|
||||
db.delete_tenant(b'tenant2')
|
||||
|
||||
assert db[prefix1 + b'tenant_test_key'] == None
|
||||
assert db[prefix2 + b'tenant_test_key'] == None
|
||||
assert db[b'tenant_test_key'] == b'no_tenant'
|
||||
|
||||
del db[b'tenant_test_key']
|
||||
|
||||
assert db[b'tenant_test_key'] == None
|
||||
|
||||
def test_tenants(db):
|
||||
test_tenant_tuple_name(db)
|
||||
test_tenant_operations(db)
|
||||
|
||||
# Expect a cluster file as input. This test will write to the FDB cluster, so
|
||||
# be aware of potential side effects.
|
||||
if __name__ == '__main__':
|
||||
clusterFile = sys.argv[1]
|
||||
db = fdb.open(clusterFile)
|
||||
db.options.set_transaction_timeout(2000) # 2 seconds
|
||||
db.options.set_transaction_retry_limit(3)
|
||||
|
||||
test_tenants(db)
|
|
@ -49,6 +49,7 @@ from cancellation_timeout_tests import test_db_retry_limits
|
|||
from cancellation_timeout_tests import test_combinations
|
||||
|
||||
from size_limit_tests import test_size_limit_option, test_get_approximate_size
|
||||
from tenant_tests import test_tenants
|
||||
|
||||
random.seed(0)
|
||||
|
||||
|
@ -112,12 +113,13 @@ class Stack:
|
|||
|
||||
|
||||
class Instruction:
|
||||
def __init__(self, tr, stack, op, index, isDatabase=False, isSnapshot=False):
|
||||
def __init__(self, tr, stack, op, index, isDatabase=False, isTenant=False, isSnapshot=False):
|
||||
self.tr = tr
|
||||
self.stack = stack
|
||||
self.op = op
|
||||
self.index = index
|
||||
self.isDatabase = isDatabase
|
||||
self.isTenant = isTenant
|
||||
self.isSnapshot = isSnapshot
|
||||
|
||||
def pop(self, count=None, with_idx=False):
|
||||
|
@ -277,6 +279,7 @@ class Tester:
|
|||
|
||||
def __init__(self, db, prefix):
|
||||
self.db = db
|
||||
self.tenant = None
|
||||
|
||||
self.instructions = self.db[fdb.tuple.range((prefix,))]
|
||||
|
||||
|
@ -317,7 +320,8 @@ class Tester:
|
|||
|
||||
def new_transaction(self):
|
||||
with Tester.tr_map_lock:
|
||||
Tester.tr_map[self.tr_name] = self.db.create_transaction()
|
||||
tr_source = self.tenant if self.tenant is not None else self.db
|
||||
Tester.tr_map[self.tr_name] = tr_source.create_transaction()
|
||||
|
||||
def switch_transaction(self, name):
|
||||
self.tr_name = name
|
||||
|
@ -335,18 +339,22 @@ class Tester:
|
|||
# print("%d. Instruction is %s" % (idx, op))
|
||||
|
||||
isDatabase = op.endswith(six.u('_DATABASE'))
|
||||
isTenant = op.endswith(six.u('_TENANT'))
|
||||
isSnapshot = op.endswith(six.u('_SNAPSHOT'))
|
||||
|
||||
if isDatabase:
|
||||
op = op[:-9]
|
||||
obj = self.db
|
||||
elif isTenant:
|
||||
op = op[:-7]
|
||||
obj = self.tenant if self.tenant else self.db
|
||||
elif isSnapshot:
|
||||
op = op[:-9]
|
||||
obj = self.current_transaction().snapshot
|
||||
else:
|
||||
obj = self.current_transaction()
|
||||
|
||||
inst = Instruction(obj, self.stack, op, idx, isDatabase, isSnapshot)
|
||||
inst = Instruction(obj, self.stack, op, idx, isDatabase, isTenant, isSnapshot)
|
||||
|
||||
try:
|
||||
if inst.op == six.u("PUSH"):
|
||||
|
@ -583,6 +591,19 @@ class Tester:
|
|||
prefix = inst.pop()
|
||||
Tester.wait_empty(self.db, prefix)
|
||||
inst.push(b"WAITED_FOR_EMPTY")
|
||||
elif inst.op == six.u("TENANT_CREATE"):
|
||||
name = inst.pop()
|
||||
self.db.allocate_tenant(name)
|
||||
inst.push(b"RESULT_NOT_PRESENT")
|
||||
elif inst.op == six.u("TENANT_DELETE"):
|
||||
name = inst.pop()
|
||||
self.db.delete_tenant(name)
|
||||
inst.push(b"RESULT_NOT_PRESENT")
|
||||
elif inst.op == six.u("TENANT_SET_ACTIVE"):
|
||||
name = inst.pop()
|
||||
self.tenant = self.db.open_tenant(name)
|
||||
elif inst.op == six.u("TENANT_CLEAR_ACTIVE"):
|
||||
self.tenant = None
|
||||
elif inst.op == six.u("UNIT_TESTS"):
|
||||
try:
|
||||
test_db_options(db)
|
||||
|
@ -600,6 +621,8 @@ class Tester:
|
|||
test_size_limit_option(db)
|
||||
test_get_approximate_size(db)
|
||||
|
||||
test_tenants(db)
|
||||
|
||||
except fdb.FDBError as e:
|
||||
print("Unit tests failed: %s" % e.description)
|
||||
traceback.print_exc()
|
||||
|
|
|
@ -346,7 +346,7 @@ function createDatabase
|
|||
|
||||
# Configure the database.
|
||||
else
|
||||
"${BINDIR}/fdbcli" -C "${FDBCONF}" --exec 'configure new single memory; status' --timeout "${CONFIGUREWAIT}" --log --log-dir "${LOGDIR}" &>> "${LOGDIR}/fdbclient.log"
|
||||
"${BINDIR}/fdbcli" -C "${FDBCONF}" --exec 'configure new single memory tenant_mode=optional_experimental; status' --timeout "${CONFIGUREWAIT}" --log --log-dir "${LOGDIR}" &>> "${LOGDIR}/fdbclient.log"
|
||||
|
||||
if ! displayMessage "Checking if config succeeded"
|
||||
then
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# alloc_instrumentation_traces.py
|
||||
#
|
||||
# This source file is part of the FoundationDB open source project
|
||||
#
|
||||
# Copyright 2013-2022 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.
|
||||
#
|
||||
|
||||
|
||||
"""
|
||||
Example trace:
|
||||
{ "Severity": "10", "Time": "194.878474", "DateTime": "2022-02-01T16:28:27Z", "Type": "MemSample", "Machine": "2.1.1.0:2", "ID": "0000000000000000", "Count": "943", "TotalSize": "540000000", "SampleCount": "54", "Hash": "980074757", "Bt": "addr2line -e fdbserver.debug -p -C -f -i 0x1919b72 0x3751d43 0x37518cc 0x19930f8 0x199dac3 0x1999e7c 0x21a1061 0x31e8fc5 0x31e784a 0x10ab3a8 0x36bf4c6 0x36bf304 0x36beea4 0x36bf352 0x36bfa1c 0x10ab3a8 0x37b22fe 0x37a16ee 0x368c754 0x19202d5 0x7fb3fe2d6555 0x1077029", "ThreadID": "10074331651862410074", "LogGroup": "default" }
|
||||
"""
|
||||
|
||||
|
||||
# This program analyzes MemSample trace events produced by setting ALLOC_INSTRUMENTATION in FastAlloc.h
|
||||
# It outputs the top memory users by total size as well as number of allocations.
|
||||
|
||||
# Example usage: cat trace.* | ./alloc_instrumentation_traces.py
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
||||
byCnt = []
|
||||
bySize = []
|
||||
totalSize = 0
|
||||
|
||||
lastTimestamp = ""
|
||||
|
||||
for line in sys.stdin:
|
||||
ev = json.loads(line.rstrip())
|
||||
type = ev["Type"]
|
||||
|
||||
if (type != 'MemSample'):
|
||||
continue
|
||||
bt = ev["Bt"]
|
||||
|
||||
if (bt == "na"):
|
||||
continue
|
||||
|
||||
timestamp = ev["Time"]
|
||||
cnt = int(ev["Count"])
|
||||
scnt = int(ev["SampleCount"])
|
||||
size = int(ev["TotalSize"])
|
||||
h = ev["Hash"]
|
||||
|
||||
if (timestamp != lastTimestamp):
|
||||
byCnt = []
|
||||
bySize = []
|
||||
totalSize = 0
|
||||
lastTimestamp = timestamp
|
||||
|
||||
|
||||
# print(str(cnt) + " " + str(scnt) + " " + str(size) + " " + h)
|
||||
|
||||
byCnt.append( (cnt, scnt, size, h, bt) )
|
||||
bySize.append( (size, cnt, size, h, bt) )
|
||||
totalSize += size
|
||||
|
||||
byCnt.sort(reverse=True)
|
||||
bySize.sort(reverse=True)
|
||||
|
||||
btByHash = {}
|
||||
|
||||
byte_suffix = ["Bytes", "KB", "MB", "GB", "TB"]
|
||||
def byte_str(bytes):
|
||||
suffix_idx = 0
|
||||
while (bytes >= 1024 * 10):
|
||||
suffix_idx += 1
|
||||
bytes //= 1024
|
||||
return str(bytes) + ' ' + byte_suffix[suffix_idx]
|
||||
|
||||
print("By Size")
|
||||
print("-------\r\n")
|
||||
for x in bySize[:10]:
|
||||
# print(str(x[0]) + ": " + x[3])
|
||||
print(str(x[1]) + " / " + byte_str(x[0]) + " (" + byte_str(x[0] // x[1]) + " per alloc):\r\n" + x[4] + "\r\n")
|
||||
btByHash[x[3]] = x[4]
|
||||
|
||||
print()
|
||||
print("By Count")
|
||||
print("--------\r\n")
|
||||
for x in byCnt[:5]:
|
||||
# print(str(x[0]) + ": " + x[3])
|
||||
print(str(x[0]) + " / " + byte_str(x[2]) + " (" + byte_str(x[2] // x[0]) + " per alloc):\r\n" + x[4] + "\r\n")
|
||||
btByHash[x[3]] = x[4]
|
||||
|
|
@ -3,3 +3,4 @@ setuptools>=20.10.0,<=57.4.0
|
|||
sphinx==1.5.6
|
||||
sphinx-bootstrap-theme==0.4.8
|
||||
docutils==0.16
|
||||
Jinja2==3.0.3
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
.. |database-type| replace:: ``Database``
|
||||
.. |database-class| replace:: :class:`Database`
|
||||
.. |database-auto| replace:: the :func:`@fdb.transactional <transactional>` decorator
|
||||
.. |tenant-type| replace:: FIXME
|
||||
.. |tenant-type| replace:: :class:`Tenant`
|
||||
.. |transaction-class| replace:: :class:`Transaction`
|
||||
.. |get-key-func| replace:: :func:`Transaction.get_key`
|
||||
.. |get-range-func| replace:: :func:`Transaction.get_range`
|
||||
|
@ -316,9 +316,29 @@ A |database-blurb1| |database-blurb2|
|
|||
|
||||
Returns a new :class:`Transaction` object. Consider using the :func:`@fdb.transactional <transactional>` decorator to create transactions instead, since it will automatically provide you with appropriate retry behavior.
|
||||
|
||||
.. method:: Database.open_tenant(tenant_name)
|
||||
|
||||
Opens an existing tenant to be used for running transactions and returns it as a :class`Tenant` object.
|
||||
|
||||
The tenant name can be either a byte string or a tuple. If a tuple is provided, the tuple will be packed using the tuple layer to generate the byte string tenant name.
|
||||
|
||||
.. |sync-read| replace:: This read is fully synchronous.
|
||||
.. |sync-write| replace:: This change will be committed immediately, and is fully synchronous.
|
||||
|
||||
.. method:: Database.allocate_tenant(tenant_name):
|
||||
|
||||
Creates a new tenant in the cluster. |sync-write|
|
||||
|
||||
The tenant name can be either a byte string or a tuple and cannot start with the ``\xff`` byte. If a tuple is provided, the tuple will be packed using the tuple layer to generate the byte string tenant name.
|
||||
|
||||
.. method:: Database.delete_tenant(tenant_name):
|
||||
|
||||
Delete a tenant from the cluster. |sync-write|
|
||||
|
||||
The tenant name can be either a byte string or a tuple. If a tuple is provided, the tuple will be packed using the tuple layer to generate the byte string tenant name.
|
||||
|
||||
It is an error to delete a tenant that still has data. To delete a non-empty tenant, first clear all of the keys in the tenant.
|
||||
|
||||
.. method:: Database.get(key)
|
||||
|
||||
Returns the value associated with the specified key in the database (or ``None`` if the key does not exist). |sync-read|
|
||||
|
@ -460,6 +480,17 @@ Database options
|
|||
.. method:: Database.options.set_snapshot_ryw_disable()
|
||||
|
||||
|option-db-snapshot-ryw-disable-blurb|
|
||||
|
||||
Tenant objects
|
||||
==============
|
||||
|
||||
.. class:: Tenant
|
||||
|
||||
|tenant-blurb1|
|
||||
|
||||
.. method:: Tenant.create_transaction()
|
||||
|
||||
Returns a new :class:`Transaction` object. Consider using the :func:`@fdb.transactional <transactional>` decorator to create transactions instead, since it will automatically provide you with appropriate retry behavior.
|
||||
|
||||
.. _api-python-transactional-decorator:
|
||||
|
||||
|
@ -479,9 +510,9 @@ Transactional decoration
|
|||
|
||||
The ``@fdb.transactional`` decorator makes ``simple_function`` a transactional function. All functions using this decorator must have an argument **named** ``tr``. This specially named argument is passed a transaction that the function can use to do reads and writes.
|
||||
|
||||
A caller of a transactionally decorated function can pass a :class:`Database` instead of a transaction for the ``tr`` parameter. Then a transaction will be created automatically, and automatically committed before returning to the caller. The decorator will retry calling the decorated function until the transaction successfully commits.
|
||||
A caller of a transactionally decorated function can pass a :class:`Database` or :class:`Tenant` instead of a transaction for the ``tr`` parameter. Then a transaction will be created automatically, and automatically committed before returning to the caller. The decorator will retry calling the decorated function until the transaction successfully commits.
|
||||
|
||||
If ``db`` is a :class:`Database`, a call like ::
|
||||
If ``db`` is a :class:`Database` or :class:`Tenant`, a call like ::
|
||||
|
||||
simple_function(db, 'a', 'b')
|
||||
|
||||
|
@ -744,7 +775,7 @@ Committing
|
|||
|
||||
.. decorator:: transactional()
|
||||
|
||||
The ``transactional`` decorator makes it easy to write transactional functions which accept either a :class:`Database` or a :class:`Transaction` as a parameter and automatically commit. See :func:`@fdb.transactional <transactional>` for explanation and examples.
|
||||
The ``transactional`` decorator makes it easy to write transactional functions which accept a :class:`Database`, :class`Tenant`, or :class:`Transaction` as a parameter and automatically commit. See :func:`@fdb.transactional <transactional>` for explanation and examples.
|
||||
|
||||
.. method :: Transaction.commit()
|
||||
|
||||
|
@ -754,7 +785,7 @@ Committing
|
|||
|
||||
|commit-outstanding-reads-blurb|
|
||||
|
||||
.. note :: Consider using the :func:`@fdb.transactional <transactional>` decorator, which not only calls :meth:`Database.create_transaction` and :meth:`Transaction.commit()` for you but also implements the required error handling and retry logic for transactions.
|
||||
.. note :: Consider using the :func:`@fdb.transactional <transactional>` decorator, which not only calls :meth:`Database.create_transaction` or :meth`Tenant.create_transaction` and :meth:`Transaction.commit()` for you but also implements the required error handling and retry logic for transactions.
|
||||
|
||||
.. warning :: |used-during-commit-blurb|
|
||||
|
||||
|
|
|
@ -155,6 +155,12 @@ Here is a complete list of valid parameters:
|
|||
|
||||
**Example**: The URL parameter *header=x-amz-storage-class:REDUCED_REDUNDANCY* would send the HTTP header required to use the reduced redundancy storage option in the S3 API.
|
||||
|
||||
Signing Protocol
|
||||
=================
|
||||
|
||||
AWS signature version 4 is the default signing protocol choice. This boolean knob ``--knob_http_request_aws_v4_header`` can be used to select between v4 style and v2 style signatures.
|
||||
If the knob is set to ``true`` then v4 signature will be used and if set to ``false`` then v2 signature will be used.
|
||||
|
||||
.. _blob-credential-files:
|
||||
|
||||
Blob Credential Files
|
||||
|
|
|
@ -153,6 +153,27 @@ If ``description=<DESC>`` is specified, the description field in the cluster fil
|
|||
|
||||
For more information on setting the cluster description, see :ref:`configuration-setting-cluster-description`.
|
||||
|
||||
createtenant
|
||||
------------
|
||||
|
||||
The ``createtenant`` command is used to create new tenants in the cluster. Its syntax is ``createtenant <TENANT_NAME>``.
|
||||
|
||||
The tenant name can be any byte string that does not begin with the ``\xff`` byte. If the tenant already exists, ``fdbcli`` will report an error.
|
||||
|
||||
defaulttenant
|
||||
-------------
|
||||
|
||||
The ``defaulttenant`` command configures ``fdbcli`` to run its commands without a tenant. This is the default behavior.
|
||||
|
||||
The active tenant cannot be changed while a transaction (using ``begin``) is open.
|
||||
|
||||
deletetenant
|
||||
------------
|
||||
|
||||
The ``deletetenant`` command is used to delete tenants from the cluster. Its syntax is ``deletetenant <TENANT_NAME>``.
|
||||
|
||||
In order to delete a tenant, it must be empty. To delete a tenant with data, first clear that data using the ``clear`` command. If the tenant does not exist, ``fdbcli`` will report an error.
|
||||
|
||||
exclude
|
||||
-------
|
||||
|
||||
|
@ -210,6 +231,13 @@ The ``getrangekeys`` command fetches keys in a range. Its syntax is ``getrangeke
|
|||
|
||||
Note that :ref:`characters can be escaped <cli-escaping>` when specifying keys (or values) in ``fdbcli``.
|
||||
|
||||
gettenant
|
||||
---------
|
||||
|
||||
The ``gettenant`` command fetches metadata for a given tenant and displays it. Its syntax is ``gettenant <TENANT_NAME>``.
|
||||
|
||||
Included in the output of this command are the ``id`` and ``prefix`` assigned to the tenant. If the tenant does not exist, ``fdbcli`` will report an error.
|
||||
|
||||
getversion
|
||||
----------
|
||||
|
||||
|
@ -300,6 +328,13 @@ Attempts to kill all specified processes. Each address should include the IP and
|
|||
|
||||
Attempts to kill all known processes in the cluster.
|
||||
|
||||
listtenants
|
||||
-----------
|
||||
|
||||
The ``listtenants`` command prints the names of tenants in the cluster. Its syntax is ``listtenants [BEGIN] [END] [LIMIT]``.
|
||||
|
||||
By default, the ``listtenants`` command will print up to 100 entries from the entire range of tenants. A narrower sub-range can be printed using the optional ``[BEGIN]`` and ``[END]`` parameters, and the limit can be changed by specifying an integer ``[LIMIT]`` parameter.
|
||||
|
||||
lock
|
||||
----
|
||||
|
||||
|
@ -512,6 +547,17 @@ unlock
|
|||
|
||||
The ``unlock`` command unlocks the database with the specified lock UID. Because this is a potentially dangerous operation, users must copy a passphrase before the unlock command is executed.
|
||||
|
||||
usetenant
|
||||
---------
|
||||
|
||||
The ``usetenant`` command configures ``fdbcli`` to run transactions within the specified tenant. Its syntax is ``usetenant <TENANT_NAME>``.
|
||||
|
||||
When configured, transactions will read and write keys from the key-space associated with the specified tenant. By default, ``fdbcli`` runs without a tenant. Management operations that modify keys (e.g. ``exclude``) will not operate within the tenant.
|
||||
|
||||
If the tenant chosen does not exist, ``fdbcli`` will report an error.
|
||||
|
||||
The active tenant cannot be changed while a transaction (using ``begin``) is open.
|
||||
|
||||
writemode
|
||||
---------
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -46,6 +46,7 @@ enum {
|
|||
OPT_HEX_KEY_PREFIX,
|
||||
OPT_BEGIN_VERSION_FILTER,
|
||||
OPT_END_VERSION_FILTER,
|
||||
OPT_KNOB,
|
||||
OPT_HELP
|
||||
};
|
||||
|
||||
|
@ -72,6 +73,7 @@ CSimpleOpt::SOption gConverterOptions[] = { { OPT_CONTAINER, "-r", SO_REQ_SEP },
|
|||
{ OPT_HEX_KEY_PREFIX, "--hex-prefix", SO_REQ_SEP },
|
||||
{ OPT_BEGIN_VERSION_FILTER, "--begin-version-filter", SO_REQ_SEP },
|
||||
{ OPT_END_VERSION_FILTER, "--end-version-filter", SO_REQ_SEP },
|
||||
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
|
||||
{ OPT_HELP, "-?", SO_NONE },
|
||||
{ OPT_HELP, "-h", SO_NONE },
|
||||
{ OPT_HELP, "--help", SO_NONE },
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2019 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -26,17 +26,21 @@
|
|||
#include <vector>
|
||||
|
||||
#include "fdbbackup/BackupTLSConfig.h"
|
||||
#include "fdbclient/BuildFlags.h"
|
||||
#include "fdbbackup/FileConverter.h"
|
||||
#include "fdbclient/BackupAgent.actor.h"
|
||||
#include "fdbclient/BackupContainer.h"
|
||||
#include "fdbbackup/FileConverter.h"
|
||||
#include "fdbclient/CommitTransaction.h"
|
||||
#include "fdbclient/FDBTypes.h"
|
||||
#include "fdbclient/IKnobCollection.h"
|
||||
#include "fdbclient/Knobs.h"
|
||||
#include "fdbclient/MutationList.h"
|
||||
#include "flow/ArgParseUtil.h"
|
||||
#include "flow/IRandom.h"
|
||||
#include "flow/Trace.h"
|
||||
#include "flow/flow.h"
|
||||
#include "flow/serialize.h"
|
||||
#include "fdbclient/BuildFlags.h"
|
||||
|
||||
#include "flow/actorcompiler.h" // has to be last include
|
||||
|
||||
#define SevDecodeInfo SevVerbose
|
||||
|
@ -73,11 +77,13 @@ void printDecodeUsage() {
|
|||
" --list-only Print file list and exit.\n"
|
||||
" -k KEY_PREFIX Use the prefix for filtering mutations\n"
|
||||
" --hex-prefix HEX_PREFIX\n"
|
||||
" The prefix specified in HEX format, e.g., \\x05\\x01.\n"
|
||||
" The prefix specified in HEX format, e.g., \"\\\\x05\\\\x01\".\n"
|
||||
" --begin-version-filter BEGIN_VERSION\n"
|
||||
" The version range's begin version (inclusive) for filtering.\n"
|
||||
" --end-version-filter END_VERSION\n"
|
||||
" The version range's end version (exclusive) for filtering.\n"
|
||||
" --knob-KNOBNAME KNOBVALUE\n"
|
||||
" Changes a knob value. KNOBNAME should be lowercase."
|
||||
"\n";
|
||||
return;
|
||||
}
|
||||
|
@ -97,6 +103,8 @@ struct DecodeParams {
|
|||
Version beginVersionFilter = 0;
|
||||
Version endVersionFilter = std::numeric_limits<Version>::max();
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> knobs;
|
||||
|
||||
// Returns if [begin, end) overlap with the filter range
|
||||
bool overlap(Version begin, Version end) const {
|
||||
// Filter [100, 200), [50,75) [200, 300)
|
||||
|
@ -130,8 +138,39 @@ struct DecodeParams {
|
|||
if (!prefix.empty()) {
|
||||
s.append(", KeyPrefix: ").append(printable(KeyRef(prefix)));
|
||||
}
|
||||
for (const auto& [knob, value] : knobs) {
|
||||
s.append(", KNOB-").append(knob).append(" = ").append(value);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void updateKnobs() {
|
||||
auto& g_knobs = IKnobCollection::getMutableGlobalKnobCollection();
|
||||
for (const auto& [knobName, knobValueString] : knobs) {
|
||||
try {
|
||||
auto knobValue = g_knobs.parseKnobValue(knobName, knobValueString);
|
||||
g_knobs.setKnob(knobName, knobValue);
|
||||
} catch (Error& e) {
|
||||
if (e.code() == error_code_invalid_option_value) {
|
||||
std::cerr << "WARNING: Invalid value '" << knobValueString << "' for knob option '" << knobName
|
||||
<< "'\n";
|
||||
TraceEvent(SevWarnAlways, "InvalidKnobValue")
|
||||
.detail("Knob", printable(knobName))
|
||||
.detail("Value", printable(knobValueString));
|
||||
} else {
|
||||
std::cerr << "ERROR: Failed to set knob option '" << knobName << "': " << e.what() << "\n";
|
||||
TraceEvent(SevError, "FailedToSetKnob")
|
||||
.errorUnsuppressed(e)
|
||||
.detail("Knob", printable(knobName))
|
||||
.detail("Value", printable(knobValueString));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reinitialize knobs in order to update knobs that are dependent on explicitly set knobs
|
||||
g_knobs.initialize(Randomize::True, IsSimulated::False);
|
||||
}
|
||||
};
|
||||
|
||||
// Decode an ASCII string, e.g., "\x15\x1b\x19\x04\xaf\x0c\x28\x0a",
|
||||
|
@ -256,6 +295,16 @@ int parseDecodeCommandLine(DecodeParams* param, CSimpleOpt* args) {
|
|||
param->tlsConfig.blobCredentials.push_back(args->OptionArg());
|
||||
break;
|
||||
|
||||
case OPT_KNOB: {
|
||||
Optional<std::string> knobName = extractPrefixedArgument("--knob", args->OptionSyntax());
|
||||
if (!knobName.present()) {
|
||||
std::cerr << "ERROR: unable to parse knob option '" << args->OptionSyntax() << "'\n";
|
||||
return FDB_EXIT_ERROR;
|
||||
}
|
||||
param->knobs.emplace_back(knobName.get(), args->OptionArg());
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef TLS_DISABLED
|
||||
case TLSConfig::OPT_TLS_PLUGIN:
|
||||
args->OptionArg();
|
||||
|
@ -457,7 +506,8 @@ ACTOR Future<Void> process_file(Reference<IBackupContainer> container, LogFile f
|
|||
print = m.param1.startsWith(StringRef(params.prefix));
|
||||
} else if (m.type == MutationRef::ClearRange) {
|
||||
KeyRange range(KeyRangeRef(m.param1, m.param2));
|
||||
print = range.contains(StringRef(params.prefix));
|
||||
KeyRange range2 = prefixRange(StringRef(params.prefix));
|
||||
print = range.intersects(range2);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
@ -551,6 +601,9 @@ int main(int argc, char** argv) {
|
|||
StringRef url(param.container_url);
|
||||
setupNetwork(0, UseMetrics::True);
|
||||
|
||||
// Must be called after setupNetwork() to be effective
|
||||
param.updateKnobs();
|
||||
|
||||
TraceEvent::setNetworkThread();
|
||||
openTraceFile(NetworkAddress(), 10 << 20, 500 << 20, param.log_dir, "decode", param.trace_log_group);
|
||||
param.tlsConfig.setupBlobCredentials();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -62,12 +62,27 @@ ACTOR Future<Void> setBlobRange(Database db, Key startKey, Key endKey, Value val
|
|||
|
||||
namespace fdb_cli {
|
||||
|
||||
ACTOR Future<bool> blobRangeCommandActor(Database localDb, std::vector<StringRef> tokens) {
|
||||
ACTOR Future<bool> blobRangeCommandActor(Database localDb,
|
||||
Optional<TenantMapEntry> tenantEntry,
|
||||
std::vector<StringRef> tokens) {
|
||||
// enables blob writing for the given range
|
||||
if (tokens.size() != 4) {
|
||||
printUsage(tokens[0]);
|
||||
return false;
|
||||
} else if (tokens[3] > LiteralStringRef("\xff")) {
|
||||
}
|
||||
|
||||
Key begin;
|
||||
Key end;
|
||||
|
||||
if (tenantEntry.present()) {
|
||||
begin = tokens[2].withPrefix(tenantEntry.get().prefix);
|
||||
end = tokens[3].withPrefix(tenantEntry.get().prefix);
|
||||
} else {
|
||||
begin = tokens[2];
|
||||
end = tokens[3];
|
||||
}
|
||||
|
||||
if (end > LiteralStringRef("\xff")) {
|
||||
// TODO is this something we want?
|
||||
printf("Cannot blobbify system keyspace! Problematic End Key: %s\n", tokens[3].printable().c_str());
|
||||
return false;
|
||||
|
@ -78,12 +93,12 @@ ACTOR Future<bool> blobRangeCommandActor(Database localDb, std::vector<StringRef
|
|||
printf("Starting blobbify range for [%s - %s)\n",
|
||||
tokens[2].printable().c_str(),
|
||||
tokens[3].printable().c_str());
|
||||
wait(setBlobRange(localDb, tokens[2], tokens[3], LiteralStringRef("1")));
|
||||
wait(setBlobRange(localDb, begin, end, LiteralStringRef("1")));
|
||||
} else if (tokencmp(tokens[1], "stop")) {
|
||||
printf("Stopping blobbify range for [%s - %s)\n",
|
||||
tokens[2].printable().c_str(),
|
||||
tokens[3].printable().c_str());
|
||||
wait(setBlobRange(localDb, tokens[2], tokens[3], StringRef()));
|
||||
wait(setBlobRange(localDb, begin, end, StringRef()));
|
||||
} else {
|
||||
printUsage(tokens[0]);
|
||||
printf("Usage: blobrange <start|stop> <startkey> <endkey>");
|
||||
|
|
|
@ -24,6 +24,7 @@ set(FDBCLI_SRCS
|
|||
SnapshotCommand.actor.cpp
|
||||
StatusCommand.actor.cpp
|
||||
SuspendCommand.actor.cpp
|
||||
TenantCommands.actor.cpp
|
||||
ThrottleCommand.actor.cpp
|
||||
TriggerDDTeamInfoLogCommand.actor.cpp
|
||||
TssqCommand.actor.cpp
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -75,7 +75,10 @@ ACTOR Future<Void> requestVersionUpdate(Database localDb, Reference<ChangeFeedDa
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR Future<bool> changeFeedCommandActor(Database localDb, std::vector<StringRef> tokens, Future<Void> warn) {
|
||||
ACTOR Future<bool> changeFeedCommandActor(Database localDb,
|
||||
Optional<TenantMapEntry> tenantEntry,
|
||||
std::vector<StringRef> tokens,
|
||||
Future<Void> warn) {
|
||||
if (tokens.size() == 1) {
|
||||
printUsage(tokens[0]);
|
||||
return false;
|
||||
|
@ -92,8 +95,15 @@ ACTOR Future<bool> changeFeedCommandActor(Database localDb, std::vector<StringRe
|
|||
printUsage(tokens[0]);
|
||||
return false;
|
||||
}
|
||||
wait(updateChangeFeed(
|
||||
localDb, tokens[2], ChangeFeedStatus::CHANGE_FEED_CREATE, KeyRangeRef(tokens[3], tokens[4])));
|
||||
|
||||
KeyRange range;
|
||||
if (tenantEntry.present()) {
|
||||
range = KeyRangeRef(tokens[3], tokens[4]).withPrefix(tenantEntry.get().prefix);
|
||||
} else {
|
||||
range = KeyRangeRef(tokens[3], tokens[4]);
|
||||
}
|
||||
|
||||
wait(updateChangeFeed(localDb, tokens[2], ChangeFeedStatus::CHANGE_FEED_CREATE, range));
|
||||
} else if (tokencmp(tokens[1], "stop")) {
|
||||
if (tokens.size() != 3) {
|
||||
printUsage(tokens[0]);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -264,7 +264,8 @@ CommandFactory configureFactory(
|
|||
"<single|double|triple|three_data_hall|three_datacenter|ssd|memory|memory-radixtree-beta|proxies=<PROXIES>|"
|
||||
"commit_proxies=<COMMIT_PROXIES>|grv_proxies=<GRV_PROXIES>|logs=<LOGS>|resolvers=<RESOLVERS>>*|"
|
||||
"count=<TSS_COUNT>|perpetual_storage_wiggle=<WIGGLE_SPEED>|perpetual_storage_wiggle_locality="
|
||||
"<<LOCALITY_KEY>:<LOCALITY_VALUE>|0>|storage_migration_type={disabled|gradual|aggressive}",
|
||||
"<<LOCALITY_KEY>:<LOCALITY_VALUE>|0>|storage_migration_type={disabled|gradual|aggressive}"
|
||||
"|tenant_mode={disabled|optional_experimental|required_experimental}|blob_granules_enabled={0|1}",
|
||||
"change the database configuration",
|
||||
"The `new' option, if present, initializes a new database with the given configuration rather than changing "
|
||||
"the configuration of an existing one. When used, both a redundancy mode and a storage engine must be "
|
||||
|
@ -295,6 +296,10 @@ CommandFactory configureFactory(
|
|||
"perpetual_storage_wiggle_locality=<<LOCALITY_KEY>:<LOCALITY_VALUE>|0>: Set the process filter for wiggling. "
|
||||
"The processes that match the given locality key and locality value are only wiggled. The value 0 will disable "
|
||||
"the locality filter and matches all the processes for wiggling.\n\n"
|
||||
"tenant_mode=<disabled|optional_experimental|required_experimental>: Sets the tenant mode for the cluster. If "
|
||||
"optional, then transactions can be run with or without specifying tenants. If required, all data must be "
|
||||
"accessed using tenants.\n\n"
|
||||
|
||||
"See the FoundationDB Administration Guide for more information."));
|
||||
|
||||
} // namespace fdb_cli
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* TenantCommands.actor.cpp
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2022 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 "fdbcli/fdbcli.actor.h"
|
||||
|
||||
#include "fdbclient/FDBOptions.g.h"
|
||||
#include "fdbclient/IClientApi.h"
|
||||
#include "fdbclient/Knobs.h"
|
||||
#include "fdbclient/ManagementAPI.actor.h"
|
||||
#include "fdbclient/Schemas.h"
|
||||
|
||||
#include "flow/Arena.h"
|
||||
#include "flow/FastRef.h"
|
||||
#include "flow/ThreadHelper.actor.h"
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
namespace fdb_cli {
|
||||
|
||||
const KeyRangeRef tenantSpecialKeyRange(LiteralStringRef("\xff\xff/management/tenant_map/"),
|
||||
LiteralStringRef("\xff\xff/management/tenant_map0"));
|
||||
|
||||
// createtenant command
|
||||
ACTOR Future<bool> createTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) {
|
||||
if (tokens.size() != 2) {
|
||||
printUsage(tokens[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
state Key tenantNameKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(tokens[1]);
|
||||
state Reference<ITransaction> tr = db->createTransaction();
|
||||
state bool doneExistenceCheck = false;
|
||||
|
||||
loop {
|
||||
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
|
||||
try {
|
||||
if (!doneExistenceCheck) {
|
||||
Optional<Value> existingTenant = wait(safeThreadFutureToFuture(tr->get(tenantNameKey)));
|
||||
if (existingTenant.present()) {
|
||||
throw tenant_already_exists();
|
||||
}
|
||||
doneExistenceCheck = true;
|
||||
}
|
||||
|
||||
tr->set(tenantNameKey, ValueRef());
|
||||
wait(safeThreadFutureToFuture(tr->commit()));
|
||||
break;
|
||||
} catch (Error& e) {
|
||||
state Error err(e);
|
||||
if (e.code() == error_code_special_keys_api_failure) {
|
||||
std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr));
|
||||
fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str());
|
||||
return false;
|
||||
}
|
||||
wait(safeThreadFutureToFuture(tr->onError(err)));
|
||||
}
|
||||
}
|
||||
|
||||
printf("The tenant `%s' has been created\n", printable(tokens[1]).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
CommandFactory createTenantFactory("createtenant",
|
||||
CommandHelp("createtenant <TENANT_NAME>",
|
||||
"creates a new tenant in the cluster",
|
||||
"Creates a new tenant in the cluster with the specified name."));
|
||||
|
||||
// deletetenant command
|
||||
ACTOR Future<bool> deleteTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) {
|
||||
if (tokens.size() != 2) {
|
||||
printUsage(tokens[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
state Key tenantNameKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(tokens[1]);
|
||||
state Reference<ITransaction> tr = db->createTransaction();
|
||||
state bool doneExistenceCheck = false;
|
||||
|
||||
loop {
|
||||
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
|
||||
try {
|
||||
if (!doneExistenceCheck) {
|
||||
Optional<Value> existingTenant = wait(safeThreadFutureToFuture(tr->get(tenantNameKey)));
|
||||
if (!existingTenant.present()) {
|
||||
throw tenant_not_found();
|
||||
}
|
||||
doneExistenceCheck = true;
|
||||
}
|
||||
|
||||
tr->clear(tenantNameKey);
|
||||
wait(safeThreadFutureToFuture(tr->commit()));
|
||||
break;
|
||||
} catch (Error& e) {
|
||||
state Error err(e);
|
||||
if (e.code() == error_code_special_keys_api_failure) {
|
||||
std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr));
|
||||
fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str());
|
||||
return false;
|
||||
}
|
||||
wait(safeThreadFutureToFuture(tr->onError(err)));
|
||||
}
|
||||
}
|
||||
|
||||
printf("The tenant `%s' has been deleted\n", printable(tokens[1]).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
CommandFactory deleteTenantFactory(
|
||||
"deletetenant",
|
||||
CommandHelp(
|
||||
"deletetenant <TENANT_NAME>",
|
||||
"deletes a tenant from the cluster",
|
||||
"Deletes a tenant from the cluster. Deletion will be allowed only if the specified tenant contains no data."));
|
||||
|
||||
// listtenants command
|
||||
ACTOR Future<bool> listTenantsCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) {
|
||||
if (tokens.size() > 4) {
|
||||
printUsage(tokens[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
StringRef beginTenant = ""_sr;
|
||||
StringRef endTenant = "\xff\xff"_sr;
|
||||
state int limit = 100;
|
||||
|
||||
if (tokens.size() >= 2) {
|
||||
beginTenant = tokens[1];
|
||||
}
|
||||
if (tokens.size() >= 3) {
|
||||
endTenant = tokens[2];
|
||||
if (endTenant <= beginTenant) {
|
||||
fprintf(stderr, "ERROR: end must be larger than begin");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (tokens.size() == 4) {
|
||||
int n = 0;
|
||||
if (sscanf(tokens[3].toString().c_str(), "%d%n", &limit, &n) != 1 || n != tokens[3].size()) {
|
||||
fprintf(stderr, "ERROR: invalid limit %s\n", tokens[3].toString().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
state Key beginTenantKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(beginTenant);
|
||||
state Key endTenantKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(endTenant);
|
||||
state Reference<ITransaction> tr = db->createTransaction();
|
||||
|
||||
loop {
|
||||
try {
|
||||
RangeResult tenants = wait(safeThreadFutureToFuture(
|
||||
tr->getRange(firstGreaterOrEqual(beginTenantKey), firstGreaterOrEqual(endTenantKey), limit)));
|
||||
|
||||
if (tenants.empty()) {
|
||||
if (tokens.size() == 1) {
|
||||
printf("The cluster has no tenants\n");
|
||||
} else {
|
||||
printf("The cluster has no tenants in the specified range\n");
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (auto tenant : tenants) {
|
||||
printf(" %d. %s\n",
|
||||
++index,
|
||||
printable(tenant.key.removePrefix(fdb_cli::tenantSpecialKeyRange.begin)).c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Error& e) {
|
||||
state Error err(e);
|
||||
if (e.code() == error_code_special_keys_api_failure) {
|
||||
std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr));
|
||||
fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str());
|
||||
return false;
|
||||
}
|
||||
wait(safeThreadFutureToFuture(tr->onError(err)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CommandFactory listTenantsFactory(
|
||||
"listtenants",
|
||||
CommandHelp("listtenants [BEGIN] [END] [LIMIT]",
|
||||
"print a list of tenants in the cluster",
|
||||
"Print a list of tenants in the cluster. Only tenants in the range [BEGIN] - [END] will be printed. "
|
||||
"The number of tenants to print can be specified using the [LIMIT] parameter, which defaults to 100."));
|
||||
|
||||
// gettenant command
|
||||
ACTOR Future<bool> getTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens) {
|
||||
if (tokens.size() != 2) {
|
||||
printUsage(tokens[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
state Key tenantNameKey = fdb_cli::tenantSpecialKeyRange.begin.withSuffix(tokens[1]);
|
||||
state Reference<ITransaction> tr = db->createTransaction();
|
||||
|
||||
loop {
|
||||
try {
|
||||
Optional<Value> tenant = wait(safeThreadFutureToFuture(tr->get(tenantNameKey)));
|
||||
if (!tenant.present()) {
|
||||
throw tenant_not_found();
|
||||
}
|
||||
|
||||
json_spirit::mValue jsonObject;
|
||||
json_spirit::read_string(tenant.get().toString(), jsonObject);
|
||||
JSONDoc doc(jsonObject);
|
||||
|
||||
int64_t id;
|
||||
std::string prefix;
|
||||
doc.get("id", id);
|
||||
doc.get("prefix", prefix);
|
||||
|
||||
printf(" id: %" PRId64 "\n", id);
|
||||
printf(" prefix: %s\n", printable(prefix).c_str());
|
||||
return true;
|
||||
} catch (Error& e) {
|
||||
state Error err(e);
|
||||
if (e.code() == error_code_special_keys_api_failure) {
|
||||
std::string errorMsgStr = wait(fdb_cli::getSpecialKeysFailureErrorMessage(tr));
|
||||
fprintf(stderr, "ERROR: %s\n", errorMsgStr.c_str());
|
||||
return false;
|
||||
}
|
||||
wait(safeThreadFutureToFuture(tr->onError(err)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CommandFactory getTenantFactory("gettenant",
|
||||
CommandHelp("gettenant <TENANT_NAME>",
|
||||
"prints the metadata for a tenant",
|
||||
"Prints the metadata for a tenant."));
|
||||
} // namespace fdb_cli
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -55,6 +55,7 @@
|
|||
#include "fdbcli/fdbcli.actor.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <type_traits>
|
||||
#include <signal.h>
|
||||
|
||||
|
@ -530,6 +531,19 @@ void initHelp() {
|
|||
helpMap["writemode"] = CommandHelp("writemode <on|off>",
|
||||
"enables or disables sets and clears",
|
||||
"Setting or clearing keys from the CLI is not recommended.");
|
||||
helpMap["usetenant"] =
|
||||
CommandHelp("usetenant [NAME]",
|
||||
"prints or configures the tenant used for transactions",
|
||||
"If no name is given, prints the name of the current active tenant. Otherwise, all commands that "
|
||||
"are used to read or write keys are done inside the tenant with the specified NAME. By default, "
|
||||
"no tenant is configured and operations are performed on the raw key-space. The tenant cannot be "
|
||||
"configured while a transaction started with `begin' is open.");
|
||||
helpMap["defaulttenant"] =
|
||||
CommandHelp("defaulttenant",
|
||||
"configures transactions to not use a named tenant",
|
||||
"All commands that are used to read or write keys will be done without a tenant and will operate "
|
||||
"on the raw key-space. This is the default behavior. The tenant cannot be configured while a "
|
||||
"transaction started with `begin' is open.");
|
||||
}
|
||||
|
||||
void printVersion() {
|
||||
|
@ -670,13 +684,18 @@ ACTOR Future<bool> createSnapshot(Database db, std::vector<StringRef> tokens) {
|
|||
|
||||
// TODO: Update the function to get rid of the Database after refactoring
|
||||
Reference<ITransaction> getTransaction(Reference<IDatabase> db,
|
||||
Reference<ITenant> tenant,
|
||||
Reference<ITransaction>& tr,
|
||||
FdbOptions* options,
|
||||
bool intrans) {
|
||||
// Update "tr" to point to a brand new transaction object when it's not initialized or "intrans" flag is "false",
|
||||
// which indicates we need a new transaction object
|
||||
if (!tr || !intrans) {
|
||||
tr = db->createTransaction();
|
||||
if (tenant) {
|
||||
tr = tenant->createTransaction();
|
||||
} else {
|
||||
tr = db->createTransaction();
|
||||
}
|
||||
options->apply(tr);
|
||||
}
|
||||
|
||||
|
@ -769,7 +788,9 @@ void configureGenerator(const char* text, const char* line, std::vector<std::str
|
|||
"resolvers=",
|
||||
"perpetual_storage_wiggle=",
|
||||
"perpetual_storage_wiggle_locality=",
|
||||
"storage_migration_type=",
|
||||
"storage_migration_type="
|
||||
"tenant_mode=",
|
||||
"blob_granules_enabled=",
|
||||
nullptr };
|
||||
arrayGenerator(text, line, opts, lc);
|
||||
}
|
||||
|
@ -1152,6 +1173,13 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
|
||||
state Database localDb;
|
||||
state Reference<IDatabase> db;
|
||||
state Reference<ITenant> tenant;
|
||||
state Optional<Standalone<StringRef>> tenantName;
|
||||
state Optional<TenantMapEntry> tenantEntry;
|
||||
|
||||
// This tenant is kept empty for operations that perform management tasks (e.g. killing a process)
|
||||
state const Reference<ITenant> managementTenant;
|
||||
|
||||
state Reference<ITransaction> tr;
|
||||
state Transaction trx;
|
||||
|
||||
|
@ -1212,7 +1240,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
// The 3.0 timeout is a guard to avoid waiting forever when the cli cannot talk to any coordinators
|
||||
loop {
|
||||
try {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, managementTenant, tr, options, intrans);
|
||||
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
|
||||
wait(delay(3.0) || success(safeThreadFutureToFuture(tr->getReadVersion())));
|
||||
break;
|
||||
|
@ -1373,8 +1401,8 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "waitopen")) {
|
||||
wait(makeInterruptable(
|
||||
success(safeThreadFutureToFuture(getTransaction(db, tr, options, intrans)->getReadVersion()))));
|
||||
wait(makeInterruptable(success(safeThreadFutureToFuture(
|
||||
getTransaction(db, managementTenant, tr, options, intrans)->getReadVersion()))));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1478,14 +1506,14 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "changefeed")) {
|
||||
bool _result = wait(makeInterruptable(changeFeedCommandActor(localDb, tokens, warn)));
|
||||
bool _result = wait(makeInterruptable(changeFeedCommandActor(localDb, tenantEntry, tokens, warn)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "blobrange")) {
|
||||
bool _result = wait(makeInterruptable(blobRangeCommandActor(localDb, tokens)));
|
||||
bool _result = wait(makeInterruptable(blobRangeCommandActor(localDb, tenantEntry, tokens)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
continue;
|
||||
|
@ -1536,7 +1564,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
} else {
|
||||
activeOptions = FdbOptions(globalOptions);
|
||||
options = &activeOptions;
|
||||
getTransaction(db, tr, options, false);
|
||||
getTransaction(db, tenant, tr, options, false);
|
||||
intrans = true;
|
||||
printf("Transaction started\n");
|
||||
}
|
||||
|
@ -1597,7 +1625,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
is_error = true;
|
||||
} else {
|
||||
state ThreadFuture<Optional<Value>> valueF =
|
||||
getTransaction(db, tr, options, intrans)->get(tokens[1]);
|
||||
getTransaction(db, tenant, tr, options, intrans)->get(tokens[1]);
|
||||
Optional<Standalone<StringRef>> v = wait(makeInterruptable(safeThreadFutureToFuture(valueF)));
|
||||
|
||||
if (v.present())
|
||||
|
@ -1613,8 +1641,8 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
printUsage(tokens[0]);
|
||||
is_error = true;
|
||||
} else {
|
||||
Version v = wait(makeInterruptable(
|
||||
safeThreadFutureToFuture(getTransaction(db, tr, options, intrans)->getReadVersion())));
|
||||
Version v = wait(makeInterruptable(safeThreadFutureToFuture(
|
||||
getTransaction(db, tenant, tr, options, intrans)->getReadVersion())));
|
||||
fmt::print("{}\n", v);
|
||||
}
|
||||
continue;
|
||||
|
@ -1628,7 +1656,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "kill")) {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, managementTenant, tr, options, intrans);
|
||||
bool _result = wait(makeInterruptable(killCommandActor(db, tr, tokens, &address_interface)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
|
@ -1636,7 +1664,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "suspend")) {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, managementTenant, tr, options, intrans);
|
||||
bool _result = wait(makeInterruptable(suspendCommandActor(db, tr, tokens, &address_interface)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
|
@ -1658,7 +1686,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "consistencycheck")) {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, managementTenant, tr, options, intrans);
|
||||
bool _result = wait(makeInterruptable(consistencyCheckCommandActor(tr, tokens, intrans)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
|
@ -1666,7 +1694,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "profile")) {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, managementTenant, tr, options, intrans);
|
||||
bool _result = wait(makeInterruptable(profileCommandActor(tr, tokens, intrans)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
|
@ -1674,7 +1702,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
}
|
||||
|
||||
if (tokencmp(tokens[0], "expensive_data_check")) {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, managementTenant, tr, options, intrans);
|
||||
bool _result =
|
||||
wait(makeInterruptable(expensiveDataCheckCommandActor(db, tr, tokens, &address_interface)));
|
||||
if (!_result)
|
||||
|
@ -1734,8 +1762,8 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
endKey = strinc(tokens[1]);
|
||||
}
|
||||
|
||||
state ThreadFuture<RangeResult> kvsF =
|
||||
getTransaction(db, tr, options, intrans)->getRange(KeyRangeRef(tokens[1], endKey), limit);
|
||||
getTransaction(db, tenant, tr, options, intrans);
|
||||
state ThreadFuture<RangeResult> kvsF = tr->getRange(KeyRangeRef(tokens[1], endKey), limit);
|
||||
RangeResult kvs = wait(makeInterruptable(safeThreadFutureToFuture(kvsF)));
|
||||
|
||||
printf("\nRange limited to %d keys\n", limit);
|
||||
|
@ -1779,7 +1807,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
printUsage(tokens[0]);
|
||||
is_error = true;
|
||||
} else {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, tenant, tr, options, intrans);
|
||||
tr->set(tokens[1], tokens[2]);
|
||||
|
||||
if (!intrans) {
|
||||
|
@ -1800,7 +1828,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
printUsage(tokens[0]);
|
||||
is_error = true;
|
||||
} else {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, tenant, tr, options, intrans);
|
||||
tr->clear(tokens[1]);
|
||||
|
||||
if (!intrans) {
|
||||
|
@ -1821,7 +1849,7 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
printUsage(tokens[0]);
|
||||
is_error = true;
|
||||
} else {
|
||||
getTransaction(db, tr, options, intrans);
|
||||
getTransaction(db, tenant, tr, options, intrans);
|
||||
tr->clear(KeyRangeRef(tokens[1], tokens[2]));
|
||||
|
||||
if (!intrans) {
|
||||
|
@ -1910,6 +1938,86 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "usetenant")) {
|
||||
if (tokens.size() > 2) {
|
||||
printUsage(tokens[0]);
|
||||
is_error = true;
|
||||
} else if (intrans && tokens.size() == 2) {
|
||||
fprintf(stderr, "ERROR: Tenant cannot be changed while a transaction is open\n");
|
||||
is_error = true;
|
||||
} else if (tokens.size() == 1) {
|
||||
if (!tenantName.present()) {
|
||||
printf("Using the default tenant\n");
|
||||
} else {
|
||||
printf("Using tenant `%s'\n", printable(tenantName.get()).c_str());
|
||||
}
|
||||
} else {
|
||||
Optional<TenantMapEntry> entry =
|
||||
wait(makeInterruptable(ManagementAPI::tryGetTenant(db, tokens[1])));
|
||||
if (!entry.present()) {
|
||||
fprintf(stderr, "ERROR: Tenant `%s' does not exist\n", printable(tokens[1]).c_str());
|
||||
is_error = true;
|
||||
} else {
|
||||
tenant = db->openTenant(tokens[1]);
|
||||
tenantName = tokens[1];
|
||||
tenantEntry = entry;
|
||||
printf("Using tenant `%s'\n", printable(tokens[1]).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "defaulttenant")) {
|
||||
if (tokens.size() != 1) {
|
||||
printUsage(tokens[0]);
|
||||
is_error = true;
|
||||
} else if (intrans) {
|
||||
fprintf(stderr, "ERROR: Tenant cannot be changed while a transaction is open\n");
|
||||
is_error = true;
|
||||
} else {
|
||||
tenant = Reference<ITenant>();
|
||||
tenantName = Optional<Standalone<StringRef>>();
|
||||
tenantEntry = Optional<TenantMapEntry>();
|
||||
printf("Using the default tenant\n");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "createtenant")) {
|
||||
bool _result = wait(makeInterruptable(createTenantCommandActor(db, tokens)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "deletetenant")) {
|
||||
bool _result = wait(makeInterruptable(deleteTenantCommandActor(db, tokens)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
else if (tenantName.present() && tokens[1] == tenantName.get()) {
|
||||
printAtCol("WARNING: the active tenant was deleted. Use the `usetenant' or `defaulttenant' "
|
||||
"command to choose a new tenant.\n",
|
||||
80);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "listtenants")) {
|
||||
bool _result = wait(makeInterruptable(listTenantsCommandActor(db, tokens)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "gettenant")) {
|
||||
bool _result = wait(makeInterruptable(getTenantCommandActor(db, tokens)));
|
||||
if (!_result)
|
||||
is_error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "ERROR: Unknown command `%s'. Try `help'?\n", formatStringRef(tokens[0]).c_str());
|
||||
is_error = true;
|
||||
}
|
||||
|
@ -1917,8 +2025,14 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
TraceEvent(SevInfo, "CLICommandLog", randomID).detail("Command", line).detail("IsError", is_error);
|
||||
|
||||
} catch (Error& e) {
|
||||
if (e.code() != error_code_actor_cancelled)
|
||||
if (e.code() == error_code_tenant_name_required) {
|
||||
printAtCol("ERROR: tenant name required. Use the `usetenant' command to select a tenant or enable the "
|
||||
"`RAW_ACCESS' option to read raw keys.",
|
||||
80,
|
||||
stderr);
|
||||
} else if (e.code() != error_code_actor_cancelled) {
|
||||
fprintf(stderr, "ERROR: %s (%d)\n", e.what(), e.code());
|
||||
}
|
||||
is_error = true;
|
||||
if (intrans) {
|
||||
printf("Rolling back current transaction\n");
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
@ -138,8 +138,12 @@ ACTOR Future<bool> consistencyCheckCommandActor(Reference<ITransaction> tr,
|
|||
bool intrans);
|
||||
// coordinators command
|
||||
ACTOR Future<bool> coordinatorsCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// createtenant command
|
||||
ACTOR Future<bool> createTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// datadistribution command
|
||||
ACTOR Future<bool> dataDistributionCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// deletetenant command
|
||||
ACTOR Future<bool> deleteTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// exclude command
|
||||
ACTOR Future<bool> excludeCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens, Future<Void> warn);
|
||||
// expensive_data_check command
|
||||
|
@ -155,6 +159,8 @@ ACTOR Future<bool> fileConfigureCommandActor(Reference<IDatabase> db,
|
|||
bool force);
|
||||
// force_recovery_with_data_loss command
|
||||
ACTOR Future<bool> forceRecoveryWithDataLossCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// gettenant command
|
||||
ACTOR Future<bool> getTenantCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// include command
|
||||
ACTOR Future<bool> includeCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// kill command
|
||||
|
@ -162,13 +168,20 @@ ACTOR Future<bool> killCommandActor(Reference<IDatabase> db,
|
|||
Reference<ITransaction> tr,
|
||||
std::vector<StringRef> tokens,
|
||||
std::map<Key, std::pair<Value, ClientLeaderRegInterface>>* address_interface);
|
||||
// listtenants command
|
||||
ACTOR Future<bool> listTenantsCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
// lock/unlock command
|
||||
ACTOR Future<bool> lockCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens);
|
||||
ACTOR Future<bool> unlockDatabaseActor(Reference<IDatabase> db, UID uid);
|
||||
// changefeed command
|
||||
ACTOR Future<bool> changeFeedCommandActor(Database localDb, std::vector<StringRef> tokens, Future<Void> warn);
|
||||
ACTOR Future<bool> changeFeedCommandActor(Database localDb,
|
||||
Optional<TenantMapEntry> tenantEntry,
|
||||
std::vector<StringRef> tokens,
|
||||
Future<Void> warn);
|
||||
// blobrange command
|
||||
ACTOR Future<bool> blobRangeCommandActor(Database localDb, std::vector<StringRef> tokens);
|
||||
ACTOR Future<bool> blobRangeCommandActor(Database localDb,
|
||||
Optional<TenantMapEntry> tenantEntry,
|
||||
std::vector<StringRef> tokens);
|
||||
// maintenance command
|
||||
ACTOR Future<bool> setHealthyZone(Reference<IDatabase> db, StringRef zoneId, double seconds, bool printWarning = false);
|
||||
ACTOR Future<bool> clearHealthyZone(Reference<IDatabase> db,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2021 Apple Inc. and the FoundationDB project authors
|
||||
* Copyright 2013-2022 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.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue