Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c37f5938fd | ||
|
|
fa90112787 | ||
|
|
f5895e5c04 | ||
|
|
ce944911f2 | ||
|
|
b220130e4a | ||
|
|
de4f5b3b1f | ||
|
|
84a42570ec | ||
|
|
001f57ec0c | ||
|
|
3279718d52 | ||
|
|
c128994f90 | ||
|
|
9af2e5ead1 | ||
|
|
be695a7353 | ||
|
|
cc9285697d | ||
|
|
21d140febc | ||
|
|
77e05ca482 | ||
|
|
5375b8f1a1 | ||
|
|
7a82432e38 | ||
|
|
146f7b5421 | ||
|
|
e1c7a906f0 | ||
|
|
c5d6e75db8 | ||
|
|
da630e80ea | ||
|
|
8ea1cbe78d | ||
|
|
03b04d40f2 | ||
|
|
175d004513 | ||
|
|
091b794742 | ||
|
|
8be87bb0b1 | ||
|
|
a84142705a | ||
|
|
fc32534aee | ||
|
|
4e526ad88a | ||
|
|
176f253aa3 | ||
|
|
c49cd5d9b6 | ||
|
|
5d4abb521b | ||
|
|
cfc2562dec | ||
|
|
4a1171e2fa | ||
|
|
542a508c50 | ||
|
|
dd018d4de4 | ||
|
|
70098a1991 | ||
|
|
008f2ff370 | ||
|
|
f71cdede5e | ||
|
|
0fd17af2ee | ||
|
|
77cb6596c4 | ||
|
|
c81c7d2d89 | ||
|
|
b3a762f179 | ||
|
|
2bba07bdf4 | ||
|
|
87bfb57ccf | ||
|
|
6ca8590540 | ||
|
|
da57d6c7cd | ||
|
|
61469d62b8 | ||
|
|
c63092038e | ||
|
|
cb7fbb859b | ||
|
|
01920c1293 | ||
|
|
fd64cae856 | ||
|
|
b1032a2699 | ||
|
|
90941622df | ||
|
|
4250ab27d8 | ||
|
|
475e0d0893 | ||
|
|
27333587a8 | ||
|
|
0cfe0e8c8e | ||
|
|
cb3225f2de | ||
|
|
69daa9fd00 | ||
|
|
f91cea66a6 | ||
|
|
9dce1e4b2b | ||
|
|
99b8c04a40 | ||
|
|
74cd6928c0 | ||
|
|
a178098277 | ||
|
|
da1a9c7bc7 | ||
|
|
3666bb84a7 | ||
|
|
df6d471e08 | ||
|
|
92b85da380 | ||
|
|
d214d91a09 | ||
|
|
a7a1e59594 | ||
|
|
856aeb5ddb | ||
|
|
61659fdbdb | ||
|
|
b06e9447b1 | ||
|
|
6a407984d8 | ||
|
|
74df68c67f | ||
|
|
a6b3a2b945 | ||
|
|
2cf998e418 | ||
|
|
6a23208ce4 | ||
|
|
94d73d2d26 | ||
|
|
033d56234b | ||
|
|
35d89298da | ||
|
|
8011d181b5 | ||
|
|
bfafb84567 | ||
|
|
0d9c09ed04 | ||
|
|
36a4eba22e | ||
|
|
e9b1f10654 | ||
|
|
6057be3f42 | ||
|
|
673f823d8b | ||
|
|
0082968bd8 | ||
|
|
542cd7aff1 | ||
|
|
2b29568bf4 | ||
|
|
93457807b8 | ||
|
|
cee62ab41b | ||
|
|
728a5e433f | ||
|
|
9a2d4a7cc7 | ||
|
|
cc050fd499 | ||
|
|
61145af5d9 | ||
|
|
11bd83e319 | ||
|
|
b58305d919 | ||
|
|
065111b42b | ||
|
|
ebd2c9bab0 | ||
|
|
bf9e1f6d2e | ||
|
|
a710866235 | ||
|
|
3c3fc18777 | ||
|
|
960f93383b | ||
|
|
1c0557c638 | ||
|
|
f23052ae64 | ||
|
|
15a157611a | ||
|
|
d0b82e1e68 | ||
|
|
840ca41393 | ||
|
|
07051f25f2 | ||
|
|
8437f71b1b | ||
|
|
9f32f5a60c | ||
|
|
910bc2417a | ||
|
|
7790beb655 | ||
|
|
1379f141c2 | ||
|
|
d14ec86e7d | ||
|
|
bbada5b9e4 | ||
|
|
d73ec88916 | ||
|
|
2efb458c7a | ||
|
|
c05d8fcef1 | ||
|
|
d29960da47 | ||
|
|
e0d67ad6e4 | ||
|
|
00da6b5e9e | ||
|
|
4200e52444 | ||
|
|
f5e402ea7a | ||
|
|
05c6a40f05 | ||
|
|
2b3bc9f174 | ||
|
|
4bfa605c38 | ||
|
|
dbb550e1a7 | ||
|
|
dffbcabbb1 | ||
|
|
a715c27a7f | ||
|
|
6804332291 | ||
|
|
a20991ad62 | ||
|
|
ff585e0834 | ||
|
|
9a58deaaa2 | ||
|
|
ee48ed2864 | ||
|
|
03f2eb529f | ||
|
|
c270014121 | ||
|
|
35804855f9 | ||
|
|
a810e57684 | ||
|
|
7b19cc17d6 | ||
|
|
101e0e611b | ||
|
|
ba23eb733d | ||
|
|
ec20ff0988 | ||
|
|
3613b082bc | ||
|
|
b94208009f | ||
|
|
05c266c02a | ||
|
|
4b7319a870 | ||
|
|
5ce71f3a29 | ||
|
|
f06f4f6ee1 | ||
|
|
c2d9247574 | ||
|
|
b4e393d215 | ||
|
|
1a2b7037cd | ||
|
|
048f5efe1c | ||
|
|
056293b95f | ||
|
|
f96ea8e011 | ||
|
|
49cd0b87f0 | ||
|
|
0977a73ab2 | ||
|
|
9fc582ee83 | ||
|
|
4be14c2249 | ||
|
|
3160dd4b59 | ||
|
|
50a8eab1a2 | ||
|
|
04615436a0 | ||
|
|
d1ab37654e | ||
|
|
b47bdb053d | ||
|
|
e11ae8c58f | ||
|
|
e4132edef3 | ||
|
|
492f0802fb | ||
|
|
34f22e1df1 | ||
|
|
acb921845f | ||
|
|
5b6c284281 | ||
|
|
7d15319a8a | ||
|
|
a06412fd24 | ||
|
|
2500dd1dc4 | ||
|
|
fd868722dd | ||
|
|
f470c5d4de | ||
|
|
3677a72a21 | ||
|
|
46e6273821 | ||
|
|
ce7e31013c |
28
.github/CODEOWNERS
vendored
28
.github/CODEOWNERS
vendored
@@ -4,7 +4,7 @@ auth/* @elcallio @vladzcloudius
|
||||
# CACHE
|
||||
row_cache* @tgrabiec @haaawk
|
||||
*mutation* @tgrabiec @haaawk
|
||||
test/boost/mvcc* @tgrabiec @haaawk
|
||||
tests/mvcc* @tgrabiec @haaawk
|
||||
|
||||
# CDC
|
||||
cdc/* @haaawk @kbr- @elcallio @piodul @jul-stas
|
||||
@@ -19,13 +19,13 @@ db/batch* @elcallio
|
||||
service/storage_proxy* @gleb-cloudius
|
||||
|
||||
# COMPACTION
|
||||
compaction/* @raphaelsc @nyh
|
||||
sstables/compaction* @raphaelsc @nyh
|
||||
|
||||
# CQL TRANSPORT LAYER
|
||||
transport/*
|
||||
transport/* @penberg
|
||||
|
||||
# CQL QUERY LANGUAGE
|
||||
cql3/* @tgrabiec @psarna @cvybhu
|
||||
cql3/* @tgrabiec @penberg @psarna
|
||||
|
||||
# COUNTERS
|
||||
counters* @haaawk @jul-stas
|
||||
@@ -35,7 +35,7 @@ tests/counter_test* @haaawk @jul-stas
|
||||
gms/* @tgrabiec @asias
|
||||
|
||||
# DOCKER
|
||||
dist/docker/*
|
||||
dist/docker/* @penberg
|
||||
|
||||
# LSA
|
||||
utils/logalloc* @tgrabiec
|
||||
@@ -58,9 +58,9 @@ service/migration* @tgrabiec @nyh
|
||||
schema* @tgrabiec @nyh
|
||||
|
||||
# SECONDARY INDEXES
|
||||
db/index/* @nyh @psarna
|
||||
cql3/statements/*index* @nyh @psarna
|
||||
test/boost/*index* @nyh @psarna
|
||||
db/index/* @nyh @penberg @psarna
|
||||
cql3/statements/*index* @nyh @penberg @psarna
|
||||
test/boost/*index* @nyh @penberg @psarna
|
||||
|
||||
# SSTABLES
|
||||
sstables/* @tgrabiec @raphaelsc @nyh
|
||||
@@ -78,20 +78,10 @@ db/hints/* @haaawk @piodul @vladzcloudius
|
||||
|
||||
# REDIS
|
||||
redis/* @nyh @syuu1228
|
||||
test/redis/* @nyh @syuu1228
|
||||
redis-test/* @nyh @syuu1228
|
||||
|
||||
# READERS
|
||||
reader_* @denesb
|
||||
querier* @denesb
|
||||
test/boost/mutation_reader_test.cc @denesb
|
||||
test/boost/querier_cache_test.cc @denesb
|
||||
|
||||
# PYTEST-BASED CQL TESTS
|
||||
test/cql-pytest/* @nyh
|
||||
|
||||
# RAFT
|
||||
raft/* @kbr- @gleb-cloudius @kostja
|
||||
test/raft/* @kbr- @gleb-cloudius @kostja
|
||||
|
||||
# HEAT-WEIGHTED LOAD BALANCING
|
||||
db/heat_load_balance.* @nyh @gleb-cloudius
|
||||
|
||||
25
.github/workflows/docs-pr@v1.yaml
vendored
25
.github/workflows/docs-pr@v1.yaml
vendored
@@ -1,25 +0,0 @@
|
||||
name: "Docs / Build PR"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Build docs
|
||||
run: make -C docs test
|
||||
@@ -1,16 +1,17 @@
|
||||
name: "Docs / Publish"
|
||||
name: "CI Docs"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "docs/**"
|
||||
workflow_dispatch:
|
||||
|
||||
- 'docs/**'
|
||||
jobs:
|
||||
release:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
LATEST_VERSION: master
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -22,8 +23,11 @@ jobs:
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Build docs
|
||||
run: make -C docs multiversion
|
||||
run: |
|
||||
export PATH=$PATH:~/.local/bin
|
||||
cd docs
|
||||
make multiversion
|
||||
- name: Deploy
|
||||
run: ./docs/_utils/deploy.sh
|
||||
run : ./docs/_utils/deploy.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -27,5 +27,3 @@ test/*/*.reject
|
||||
.vscode
|
||||
docs/_build
|
||||
docs/poetry.lock
|
||||
compile_commands.json
|
||||
.ccls-cache/
|
||||
|
||||
127
CMakeLists.txt
127
CMakeLists.txt
@@ -32,13 +32,8 @@ if(target_arch)
|
||||
set(target_arch_flag "-march=${target_arch}")
|
||||
endif()
|
||||
|
||||
set(cxx_coro_flag)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(cxx_coro_flag -fcoroutines)
|
||||
endif()
|
||||
|
||||
# Configure Seastar compile options to align with Scylla
|
||||
set(Seastar_CXX_FLAGS ${cxx_coro_flag} ${target_arch_flag} CACHE INTERNAL "" FORCE)
|
||||
set(Seastar_CXX_FLAGS -fcoroutines ${target_arch_flag} CACHE INTERNAL "" FORCE)
|
||||
set(Seastar_CXX_DIALECT gnu++20 CACHE INTERNAL "" FORCE)
|
||||
|
||||
add_subdirectory(seastar)
|
||||
@@ -101,7 +96,7 @@ endfunction()
|
||||
scylla_generate_thrift(
|
||||
TARGET scylla_thrift_gen_cassandra
|
||||
VAR scylla_thrift_gen_cassandra_files
|
||||
IN_FILE "${CMAKE_SOURCE_DIR}/interface/cassandra.thrift"
|
||||
IN_FILE interface/cassandra.thrift
|
||||
OUT_DIR ${scylla_gen_build_dir}
|
||||
SERVICE Cassandra)
|
||||
|
||||
@@ -158,7 +153,7 @@ foreach(f ${antlr3_grammar_files})
|
||||
scylla_generate_antlr3(
|
||||
TARGET scylla_antlr3_gen_${grammar_file_name}
|
||||
VAR scylla_antlr3_gen_${grammar_file_name}_files
|
||||
IN_FILE "${CMAKE_SOURCE_DIR}/${f}"
|
||||
IN_FILE ${f}
|
||||
OUT_DIR ${scylla_gen_build_dir}/${f_dir})
|
||||
list(APPEND antlr3_gen_files "${scylla_antlr3_gen_${grammar_file_name}_files}")
|
||||
endforeach()
|
||||
@@ -167,7 +162,7 @@ endforeach()
|
||||
seastar_generate_ragel(
|
||||
TARGET scylla_ragel_gen_protocol_parser
|
||||
VAR scylla_ragel_gen_protocol_parser_file
|
||||
IN_FILE "${CMAKE_SOURCE_DIR}/redis/protocol_parser.rl"
|
||||
IN_FILE redis/protocol_parser.rl
|
||||
OUT_FILE ${scylla_gen_build_dir}/redis/protocol_parser.hh)
|
||||
|
||||
# Generate C++ sources from Swagger definitions
|
||||
@@ -199,7 +194,7 @@ foreach(f ${swagger_files})
|
||||
seastar_generate_swagger(
|
||||
TARGET scylla_swagger_gen_${fname}
|
||||
VAR scylla_swagger_gen_${fname}_files
|
||||
IN_FILE "${CMAKE_SOURCE_DIR}/${f}"
|
||||
IN_FILE "${f}"
|
||||
OUT_DIR "${scylla_gen_build_dir}/${dir}")
|
||||
list(APPEND swagger_gen_files "${scylla_swagger_gen_${fname}_files}")
|
||||
endforeach()
|
||||
@@ -234,7 +229,6 @@ set(idl_serializers
|
||||
idl/frozen_mutation.idl.hh
|
||||
idl/frozen_schema.idl.hh
|
||||
idl/gossip_digest.idl.hh
|
||||
idl/hinted_handoff.idl.hh
|
||||
idl/idl_test.idl.hh
|
||||
idl/keys.idl.hh
|
||||
idl/messaging_service.idl.hh
|
||||
@@ -243,7 +237,6 @@ set(idl_serializers
|
||||
idl/partition_checksum.idl.hh
|
||||
idl/paxos.idl.hh
|
||||
idl/query.idl.hh
|
||||
idl/raft.idl.hh
|
||||
idl/range.idl.hh
|
||||
idl/read_command.idl.hh
|
||||
idl/reconcilable_result.idl.hh
|
||||
@@ -267,7 +260,7 @@ foreach(f ${idl_serializers})
|
||||
scylla_generate_idl_serializer(
|
||||
TARGET scylla_idl_gen_${idl_target}
|
||||
VAR scylla_idl_gen_${idl_target}_files
|
||||
IN_FILE "${CMAKE_SOURCE_DIR}/${f}"
|
||||
IN_FILE ${f}
|
||||
OUT_FILE ${scylla_gen_build_dir}/${idl_dir}/${idl_out_hdr_name})
|
||||
list(APPEND idl_gen_files "${scylla_idl_gen_${idl_target}_files}")
|
||||
endforeach()
|
||||
@@ -275,8 +268,8 @@ endforeach()
|
||||
set(scylla_sources
|
||||
absl-flat_hash_map.cc
|
||||
alternator/auth.cc
|
||||
alternator/base64.cc
|
||||
alternator/conditions.cc
|
||||
alternator/controller.cc
|
||||
alternator/executor.cc
|
||||
alternator/expressions.cc
|
||||
alternator/serialization.cc
|
||||
@@ -321,7 +314,6 @@ set(scylla_sources
|
||||
auth/standard_role_manager.cc
|
||||
auth/transitional.cc
|
||||
bytes.cc
|
||||
caching_options.cc
|
||||
canonical_mutation.cc
|
||||
cdc/cdc_partitioner.cc
|
||||
cdc/generation.cc
|
||||
@@ -330,12 +322,6 @@ set(scylla_sources
|
||||
cdc/split.cc
|
||||
clocks-impl.cc
|
||||
collection_mutation.cc
|
||||
compaction/compaction.cc
|
||||
compaction/compaction_manager.cc
|
||||
compaction/compaction_strategy.cc
|
||||
compaction/leveled_compaction_strategy.cc
|
||||
compaction/size_tiered_compaction_strategy.cc
|
||||
compaction/time_window_compaction_strategy.cc
|
||||
compress.cc
|
||||
connection_notifier.cc
|
||||
converting_mutation_partition_applier.cc
|
||||
@@ -349,7 +335,6 @@ set(scylla_sources
|
||||
cql3/constants.cc
|
||||
cql3/cql3_type.cc
|
||||
cql3/expr/expression.cc
|
||||
cql3/expr/term_expr.cc
|
||||
cql3/functions/aggregate_fcts.cc
|
||||
cql3/functions/castas_fcts.cc
|
||||
cql3/functions/error_injection_fcts.cc
|
||||
@@ -360,7 +345,6 @@ set(scylla_sources
|
||||
cql3/lists.cc
|
||||
cql3/maps.cc
|
||||
cql3/operation.cc
|
||||
cql3/prepare_context.cc
|
||||
cql3/query_options.cc
|
||||
cql3/query_processor.cc
|
||||
cql3/relation.cc
|
||||
@@ -376,32 +360,25 @@ set(scylla_sources
|
||||
cql3/sets.cc
|
||||
cql3/single_column_relation.cc
|
||||
cql3/statements/alter_keyspace_statement.cc
|
||||
cql3/statements/alter_service_level_statement.cc
|
||||
cql3/statements/alter_table_statement.cc
|
||||
cql3/statements/alter_type_statement.cc
|
||||
cql3/statements/alter_view_statement.cc
|
||||
cql3/statements/attach_service_level_statement.cc
|
||||
cql3/statements/authentication_statement.cc
|
||||
cql3/statements/authorization_statement.cc
|
||||
cql3/statements/batch_statement.cc
|
||||
cql3/statements/cas_request.cc
|
||||
cql3/statements/cf_prop_defs.cc
|
||||
cql3/statements/cf_statement.cc
|
||||
cql3/statements/create_aggregate_statement.cc
|
||||
cql3/statements/create_function_statement.cc
|
||||
cql3/statements/create_index_statement.cc
|
||||
cql3/statements/create_keyspace_statement.cc
|
||||
cql3/statements/create_service_level_statement.cc
|
||||
cql3/statements/create_table_statement.cc
|
||||
cql3/statements/create_type_statement.cc
|
||||
cql3/statements/create_view_statement.cc
|
||||
cql3/statements/delete_statement.cc
|
||||
cql3/statements/detach_service_level_statement.cc
|
||||
cql3/statements/drop_aggregate_statement.cc
|
||||
cql3/statements/drop_function_statement.cc
|
||||
cql3/statements/drop_index_statement.cc
|
||||
cql3/statements/drop_keyspace_statement.cc
|
||||
cql3/statements/drop_service_level_statement.cc
|
||||
cql3/statements/drop_table_statement.cc
|
||||
cql3/statements/drop_type_statement.cc
|
||||
cql3/statements/drop_view_statement.cc
|
||||
@@ -411,8 +388,6 @@ set(scylla_sources
|
||||
cql3/statements/index_target.cc
|
||||
cql3/statements/ks_prop_defs.cc
|
||||
cql3/statements/list_permissions_statement.cc
|
||||
cql3/statements/list_service_level_attachments_statement.cc
|
||||
cql3/statements/list_service_level_statement.cc
|
||||
cql3/statements/list_users_statement.cc
|
||||
cql3/statements/modification_statement.cc
|
||||
cql3/statements/permission_altering_statement.cc
|
||||
@@ -422,8 +397,6 @@ set(scylla_sources
|
||||
cql3/statements/role-management-statements.cc
|
||||
cql3/statements/schema_altering_statement.cc
|
||||
cql3/statements/select_statement.cc
|
||||
cql3/statements/service_level_statement.cc
|
||||
cql3/statements/sl_prop_defs.cc
|
||||
cql3/statements/truncate_statement.cc
|
||||
cql3/statements/update_statement.cc
|
||||
cql3/statements/use_statement.cc
|
||||
@@ -433,9 +406,11 @@ set(scylla_sources
|
||||
cql3/untyped_result_set.cc
|
||||
cql3/update_parameters.cc
|
||||
cql3/user_types.cc
|
||||
cql3/util.cc
|
||||
cql3/ut_name.cc
|
||||
cql3/util.cc
|
||||
cql3/values.cc
|
||||
cql3/variable_specifications.cc
|
||||
data/cell.cc
|
||||
database.cc
|
||||
db/batchlog_manager.cc
|
||||
db/commitlog/commitlog.cc
|
||||
@@ -447,10 +422,8 @@ set(scylla_sources
|
||||
db/data_listeners.cc
|
||||
db/extensions.cc
|
||||
db/heat_load_balance.cc
|
||||
db/hints/host_filter.cc
|
||||
db/hints/manager.cc
|
||||
db/hints/resource_manager.cc
|
||||
db/hints/sync_point.cc
|
||||
db/large_data_handler.cc
|
||||
db/legacy_schema_migrator.cc
|
||||
db/marshal/type_parser.cc
|
||||
@@ -463,7 +436,6 @@ set(scylla_sources
|
||||
db/view/row_locking.cc
|
||||
db/view/view.cc
|
||||
db/view/view_update_generator.cc
|
||||
db/virtual_table.cc
|
||||
dht/boot_strapper.cc
|
||||
dht/i_partitioner.cc
|
||||
dht/murmur3_partitioner.cc
|
||||
@@ -475,18 +447,17 @@ set(scylla_sources
|
||||
flat_mutation_reader.cc
|
||||
frozen_mutation.cc
|
||||
frozen_schema.cc
|
||||
generic_server.cc
|
||||
gms/application_state.cc
|
||||
gms/endpoint_state.cc
|
||||
gms/failure_detector.cc
|
||||
gms/feature_service.cc
|
||||
gms/gossip_digest_ack2.cc
|
||||
gms/gossip_digest_ack.cc
|
||||
gms/gossip_digest_ack2.cc
|
||||
gms/gossip_digest_syn.cc
|
||||
gms/gossiper.cc
|
||||
gms/inet_address.cc
|
||||
gms/versioned_value.cc
|
||||
gms/version_generator.cc
|
||||
gms/versioned_value.cc
|
||||
hashers.cc
|
||||
index/secondary_index.cc
|
||||
index/secondary_index_manager.cc
|
||||
@@ -494,7 +465,6 @@ set(scylla_sources
|
||||
keys.cc
|
||||
lister.cc
|
||||
locator/abstract_replication_strategy.cc
|
||||
locator/azure_snitch.cc
|
||||
locator/ec2_multi_region_snitch.cc
|
||||
locator/ec2_snitch.cc
|
||||
locator/everywhere_replication_strategy.cc
|
||||
@@ -508,33 +478,32 @@ set(scylla_sources
|
||||
locator/simple_strategy.cc
|
||||
locator/snitch_base.cc
|
||||
locator/token_metadata.cc
|
||||
lang/lua.cc
|
||||
lua.cc
|
||||
main.cc
|
||||
memtable.cc
|
||||
message/messaging_service.cc
|
||||
multishard_mutation_query.cc
|
||||
mutation.cc
|
||||
raft/fsm.cc
|
||||
raft/log.cc
|
||||
raft/progress.cc
|
||||
raft/raft.cc
|
||||
raft/server.cc
|
||||
mutation_fragment.cc
|
||||
mutation_partition.cc
|
||||
mutation_partition_serializer.cc
|
||||
mutation_partition_view.cc
|
||||
mutation_query.cc
|
||||
mutation_reader.cc
|
||||
mutation_writer/feed_writers.cc
|
||||
mutation_writer/multishard_writer.cc
|
||||
mutation_writer/partition_based_splitting_writer.cc
|
||||
mutation_writer/shard_based_splitting_writer.cc
|
||||
mutation_writer/timestamp_based_splitting_writer.cc
|
||||
mutation_writer/feed_writers.cc
|
||||
partition_slice_builder.cc
|
||||
partition_version.cc
|
||||
querier.cc
|
||||
query.cc
|
||||
query-result-set.cc
|
||||
raft/fsm.cc
|
||||
raft/log.cc
|
||||
raft/raft.cc
|
||||
raft/server.cc
|
||||
raft/tracker.cc
|
||||
query.cc
|
||||
range_tombstone.cc
|
||||
range_tombstone_list.cc
|
||||
reader_concurrency_semaphore.cc
|
||||
@@ -550,16 +519,15 @@ set(scylla_sources
|
||||
redis/server.cc
|
||||
redis/service.cc
|
||||
redis/stats.cc
|
||||
release.cc
|
||||
repair/repair.cc
|
||||
repair/row_level.cc
|
||||
row_cache.cc
|
||||
schema.cc
|
||||
schema_mutations.cc
|
||||
schema_registry.cc
|
||||
serializer.cc
|
||||
service/client_state.cc
|
||||
service/migration_manager.cc
|
||||
service/migration_task.cc
|
||||
service/misc_services.cc
|
||||
service/pager/paging_state.cc
|
||||
service/pager/query_pagers.cc
|
||||
@@ -568,33 +536,29 @@ set(scylla_sources
|
||||
service/paxos/prepare_summary.cc
|
||||
service/paxos/proposal.cc
|
||||
service/priority_manager.cc
|
||||
service/qos/qos_common.cc
|
||||
service/qos/service_level_controller.cc
|
||||
service/qos/standard_service_level_distributed_data_accessor.cc
|
||||
service/raft/raft_gossip_failure_detector.cc
|
||||
service/raft/raft_group_registry.cc
|
||||
service/raft/raft_rpc.cc
|
||||
service/raft/raft_sys_table_storage.cc
|
||||
service/raft/schema_raft_state_machine.cc
|
||||
service/storage_proxy.cc
|
||||
service/storage_service.cc
|
||||
sstables/compaction.cc
|
||||
sstables/compaction_manager.cc
|
||||
sstables/compaction_strategy.cc
|
||||
sstables/compress.cc
|
||||
sstables/integrity_checked_file_impl.cc
|
||||
sstables/kl/reader.cc
|
||||
sstables/metadata_collector.cc
|
||||
sstables/kl/writer.cc
|
||||
sstables/leveled_compaction_strategy.cc
|
||||
sstables/m_format_read_helpers.cc
|
||||
sstables/mx/reader.cc
|
||||
sstables/metadata_collector.cc
|
||||
sstables/mp_row_consumer.cc
|
||||
sstables/mx/writer.cc
|
||||
sstables/partition.cc
|
||||
sstables/prepended_input_stream.cc
|
||||
sstables/random_access_reader.cc
|
||||
sstables/size_tiered_compaction_strategy.cc
|
||||
sstables/sstable_directory.cc
|
||||
sstables/sstable_mutation_reader.cc
|
||||
sstables/sstables.cc
|
||||
sstables/sstable_set.cc
|
||||
sstables/sstables_manager.cc
|
||||
sstables/sstable_version.cc
|
||||
sstables/sstables.cc
|
||||
sstables/sstables_manager.cc
|
||||
sstables/time_window_compaction_strategy.cc
|
||||
sstables/writer.cc
|
||||
streaming/consumer.cc
|
||||
streaming/progress_info.cc
|
||||
streaming/session_info.cc
|
||||
streaming/stream_coordinator.cc
|
||||
@@ -616,13 +580,11 @@ set(scylla_sources
|
||||
thrift/server.cc
|
||||
thrift/thrift_validation.cc
|
||||
timeout_config.cc
|
||||
tools/scylla-sstable-index.cc
|
||||
tools/scylla-types.cc
|
||||
tracing/traced_file.cc
|
||||
tracing/trace_keyspace_helper.cc
|
||||
tracing/trace_state.cc
|
||||
tracing/tracing_backend_registry.cc
|
||||
tracing/traced_file.cc
|
||||
tracing/tracing.cc
|
||||
tracing/tracing_backend_registry.cc
|
||||
transport/controller.cc
|
||||
transport/cql_protocol_extension.cc
|
||||
transport/event.cc
|
||||
@@ -631,10 +593,10 @@ set(scylla_sources
|
||||
transport/server.cc
|
||||
types.cc
|
||||
unimplemented.cc
|
||||
utils/UUID_gen.cc
|
||||
utils/arch/powerpc/crc32-vpmsum/crc32_wrapper.cc
|
||||
utils/array-search.cc
|
||||
utils/ascii.cc
|
||||
utils/base64.cc
|
||||
utils/big_decimal.cc
|
||||
utils/bloom_calculations.cc
|
||||
utils/bloom_filter.cc
|
||||
@@ -649,7 +611,6 @@ set(scylla_sources
|
||||
utils/file_lock.cc
|
||||
utils/generation-number.cc
|
||||
utils/gz/crc_combine.cc
|
||||
utils/gz/gen_crc_combine_table.cc
|
||||
utils/human_readable.cc
|
||||
utils/i_filter.cc
|
||||
utils/large_bitset.cc
|
||||
@@ -665,10 +626,10 @@ set(scylla_sources
|
||||
utils/updateable_value.cc
|
||||
utils/utf8.cc
|
||||
utils/uuid.cc
|
||||
utils/UUID_gen.cc
|
||||
validation.cc
|
||||
vint-serialization.cc
|
||||
zstd.cc)
|
||||
zstd.cc
|
||||
release.cc)
|
||||
|
||||
set(scylla_gen_sources
|
||||
"${scylla_thrift_gen_cassandra_files}"
|
||||
@@ -729,7 +690,7 @@ target_link_libraries(scylla PRIVATE
|
||||
|
||||
target_compile_options(scylla PRIVATE
|
||||
-std=gnu++20
|
||||
${cxx_coro_flag}
|
||||
-fcoroutines # TODO: Clang does not have this flag, adjust to both variants
|
||||
${target_arch_flag})
|
||||
# Hacks needed to expose internal APIs for xxhash dependencies
|
||||
target_compile_definitions(scylla PRIVATE XXH_PRIVATE_API HAVE_LZ4_COMPRESS_DEFAULT)
|
||||
@@ -749,7 +710,7 @@ target_link_libraries(crc_combine_table PRIVATE seastar)
|
||||
target_include_directories(crc_combine_table PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_compile_options(crc_combine_table PRIVATE
|
||||
-std=gnu++20
|
||||
${cxx_coro_flag}
|
||||
-fcoroutines
|
||||
${target_arch_flag})
|
||||
add_dependencies(scylla crc_combine_table)
|
||||
|
||||
@@ -762,15 +723,15 @@ target_sources(scylla PRIVATE "${scylla_gen_build_dir}/utils/gz/crc_combine_tabl
|
||||
###
|
||||
### Generate version file and supply appropriate compile definitions for release.cc
|
||||
###
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/SCYLLA-VERSION-GEN --output-dir "${CMAKE_BINARY_DIR}/gen" RESULT_VARIABLE scylla_version_gen_res)
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/SCYLLA-VERSION-GEN RESULT_VARIABLE scylla_version_gen_res)
|
||||
if(scylla_version_gen_res)
|
||||
message(SEND_ERROR "Version file generation failed. Return code: ${scylla_version_gen_res}")
|
||||
endif()
|
||||
|
||||
file(READ "${CMAKE_BINARY_DIR}/gen/SCYLLA-VERSION-FILE" scylla_version)
|
||||
file(READ build/SCYLLA-VERSION-FILE scylla_version)
|
||||
string(STRIP "${scylla_version}" scylla_version)
|
||||
|
||||
file(READ "${CMAKE_BINARY_DIR}/gen/SCYLLA-RELEASE-FILE" scylla_release)
|
||||
file(READ build/SCYLLA-RELEASE-FILE scylla_release)
|
||||
string(STRIP "${scylla_release}" scylla_release)
|
||||
|
||||
get_property(release_cdefs SOURCE "${CMAKE_SOURCE_DIR}/release.cc" PROPERTY COMPILE_DEFINITIONS)
|
||||
@@ -782,7 +743,7 @@ set_source_files_properties("${CMAKE_SOURCE_DIR}/release.cc" PROPERTIES COMPILE_
|
||||
###
|
||||
set(libdeflate_lib "${scylla_build_dir}/libdeflate/libdeflate.a")
|
||||
add_custom_command(OUTPUT "${libdeflate_lib}"
|
||||
COMMAND make -C "${CMAKE_SOURCE_DIR}/libdeflate"
|
||||
COMMAND make -C libdeflate
|
||||
BUILD_DIR=../build/${BUILD_TYPE}/libdeflate/
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
"CFLAGS=${target_arch_flag}"
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
# Contributing to Scylla
|
||||
# Contributing
|
||||
|
||||
## Asking questions or requesting help
|
||||
|
||||
Use the [Scylla Users mailing list](https://groups.google.com/g/scylladb-users) or the [Slack workspace](http://slack.scylladb.com) for general questions and help.
|
||||
|
||||
Join the [Scylla Developers mailing list](https://groups.google.com/g/scylladb-dev) for deeper technical discussions and to discuss your ideas for contributions.
|
||||
Use the [ScyllaDB user mailing list](https://groups.google.com/forum/#!forum/scylladb-users) or the [Slack workspace](http://slack.scylladb.com) for general questions and help.
|
||||
|
||||
## Reporting an issue
|
||||
|
||||
Please use the [issue tracker](https://github.com/scylladb/scylla/issues/) to report issues or to suggest features. Fill in as much information as you can in the issue template, especially for performance problems.
|
||||
Please use the [Issue Tracker](https://github.com/scylladb/scylla/issues/) to report issues. Fill in as much information as you can in the issue template, especially for performance problems.
|
||||
|
||||
## Contributing code to Scylla
|
||||
## Contributing Code to Scylla
|
||||
|
||||
Before you can contribute code to Scylla for the first time, you should sign the [Contributor License Agreement](https://www.scylladb.com/open-source/contributor-agreement/) and send the signed form cla@scylladb.com. You can then submit your changes as patches to the to the [scylladb-dev mailing list](https://groups.google.com/forum/#!forum/scylladb-dev) or as a pull request to the [Scylla project on github](https://github.com/scylladb/scylla).
|
||||
If you need help formatting or sending patches, [check out these instructions](https://github.com/scylladb/scylla/wiki/Formatting-and-sending-patches).
|
||||
|
||||
The Scylla C++ source code uses the [Seastar coding style](https://github.com/scylladb/seastar/blob/master/coding-style.md) so please adhere to that in your patches. Note that Scylla code is written with `using namespace seastar`, so should not explicitly add the `seastar::` prefix to Seastar symbols. You will usually not need to add `using namespace seastar` to new source files, because most Scylla header files have `#include "seastarx.hh"`, which does this.
|
||||
|
||||
Header files in Scylla must be self-contained, i.e., each can be included without having to include specific other headers first. To verify that your change did not break this property, run `ninja dev-headers`. If you added or removed header files, you must `touch configure.py` first - this will cause `configure.py` to be automatically re-run to generate a fresh list of header files.
|
||||
To contribute code to Scylla, you need to sign the [Contributor License Agreement](https://www.scylladb.com/open-source/contributor-agreement/) and send your changes as [patches](https://github.com/scylladb/scylla/wiki/Formatting-and-sending-patches) to the [mailing list](https://groups.google.com/forum/#!forum/scylladb-dev). We don't accept pull requests on GitHub.
|
||||
|
||||
28
HACKING.md
28
HACKING.md
@@ -172,8 +172,12 @@ and you will get output like this:
|
||||
```
|
||||
CQL QUERY LANGUAGE
|
||||
Tomasz Grabiec <tgrabiec@scylladb.com> [maintainer]
|
||||
Pekka Enberg <penberg@scylladb.com> [maintainer]
|
||||
MATERIALIZED VIEWS
|
||||
Pekka Enberg <penberg@scylladb.com> [maintainer]
|
||||
Duarte Nunes <duarte@scylladb.com> [maintainer]
|
||||
Nadav Har'El <nyh@scylladb.com> [reviewer]
|
||||
Duarte Nunes <duarte@scylladb.com> [reviewer]
|
||||
```
|
||||
|
||||
### Running Scylla
|
||||
@@ -362,27 +366,7 @@ $ git remote update
|
||||
$ git checkout -t local/my_local_seastar_branch
|
||||
```
|
||||
|
||||
### Generating code coverage report
|
||||
|
||||
Install dependencies:
|
||||
|
||||
$ dnf install llvm # for llvm-profdata and llvm-cov
|
||||
$ dnf install lcov # for genhtml
|
||||
|
||||
Instruct `configure.py` to generate build files for `coverage` mode:
|
||||
|
||||
$ ./configure.py --mode=coverage
|
||||
|
||||
Build the tests you want to run, then run them via `test.py` (important!):
|
||||
|
||||
$ ./test.py --mode=coverage [...]
|
||||
|
||||
Alternatively, you can run individual tests via `./scripts/coverage.py --run`.
|
||||
|
||||
Open the link printed at the end. Be horrified. Go and write more tests.
|
||||
|
||||
For more details see `./scripts/coverage.py --help`.
|
||||
|
||||
### Core dump debugging
|
||||
|
||||
See [debugging.md](debugging.md).
|
||||
Slides:
|
||||
2018.11.20: https://www.slideshare.net/tomekgrabiec/scylla-core-dump-debugging-tools
|
||||
|
||||
@@ -5,7 +5,3 @@ It includes files from https://github.com/antonblanchard/crc32-vpmsum (author An
|
||||
These files are located in utils/arch/powerpc/crc32-vpmsum. Their license may be found in licenses/LICENSE-crc32-vpmsum.TXT.
|
||||
|
||||
It includes modified code from https://gitbox.apache.org/repos/asf?p=cassandra-dtest.git (owned by The Apache Software Foundation)
|
||||
|
||||
It includes modified tests from https://github.com/etcd-io/etcd.git (owned by The etcd Authors)
|
||||
|
||||
It includes files from https://github.com/bytecodealliance/wasmtime-cpp (owned by Bytecode Alliance), licensed with Apache License 2.0.
|
||||
|
||||
@@ -42,8 +42,8 @@ For further information, please see:
|
||||
* [Docker image build documentation] for information on how to build Docker images.
|
||||
|
||||
[developer documentation]: HACKING.md
|
||||
[build documentation]: docs/guides/building.md
|
||||
[docker image build documentation]: dist/docker/debian/README.md
|
||||
[build documentation]: docs/building.md
|
||||
[docker image build documentation]: dist/docker/redhat/README.md
|
||||
|
||||
## Running Scylla
|
||||
|
||||
@@ -65,7 +65,7 @@ $ ./tools/toolchain/dbuild ./build/release/scylla --help
|
||||
|
||||
## Testing
|
||||
|
||||
See [test.py manual](docs/guides/testing.md).
|
||||
See [test.py manual](docs/testing.md).
|
||||
|
||||
## Scylla APIs and compatibility
|
||||
By default, Scylla is compatible with Apache Cassandra and its APIs - CQL and
|
||||
|
||||
@@ -1,66 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
USAGE=$(cat <<-END
|
||||
Usage: $(basename "$0") [-h|--help] [-o|--output-dir PATH] -- generate Scylla version and build information files.
|
||||
|
||||
Options:
|
||||
-h|--help show this help message.
|
||||
-o|--output-dir PATH specify destination path at which the version files are to be created.
|
||||
|
||||
By default, the script will attempt to parse 'version' file
|
||||
in the current directory, which should contain a string of
|
||||
'\$version-\$release' form.
|
||||
|
||||
Otherwise, it will call 'git log' on the source tree (the
|
||||
directory, which contains the script) to obtain current
|
||||
commit hash and use it for building the version and release
|
||||
strings.
|
||||
|
||||
The script assumes that it's called from the Scylla source
|
||||
tree.
|
||||
|
||||
The files created are:
|
||||
SCYLLA-VERSION-FILE
|
||||
SCYLLA-RELEASE-FILE
|
||||
SCYLLA-PRODUCT-FILE
|
||||
|
||||
By default, these files are created in the 'build'
|
||||
subdirectory under the directory containing the script.
|
||||
The destination directory can be overriden by
|
||||
using '-o PATH' option.
|
||||
END
|
||||
)
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
opt="$1"
|
||||
case $opt in
|
||||
-h|--help)
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
;;
|
||||
-o|--output-dir)
|
||||
OUTPUT_DIR="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected argument found: $1"
|
||||
echo
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(dirname "$0")"
|
||||
|
||||
if [ -z "$OUTPUT_DIR" ]; then
|
||||
OUTPUT_DIR="$SCRIPT_DIR/build"
|
||||
fi
|
||||
|
||||
# Default scylla product/version tags
|
||||
PRODUCT=scylla
|
||||
VERSION=4.6.11
|
||||
VERSION=4.4.9
|
||||
|
||||
if test -f version
|
||||
then
|
||||
@@ -68,7 +9,7 @@ then
|
||||
SCYLLA_RELEASE=$(cat version | awk -F'-' '{print $2}')
|
||||
else
|
||||
DATE=$(date +%Y%m%d)
|
||||
GIT_COMMIT=$(git -C "$SCRIPT_DIR" log --pretty=format:'%h' -n 1)
|
||||
GIT_COMMIT=$(git log --pretty=format:'%h' -n 1)
|
||||
SCYLLA_VERSION=$VERSION
|
||||
# For custom package builds, replace "0" with "counter.your_name",
|
||||
# where counter starts at 1 and increments for successive versions.
|
||||
@@ -78,15 +19,16 @@ else
|
||||
SCYLLA_RELEASE=$SCYLLA_BUILD.$DATE.$GIT_COMMIT
|
||||
fi
|
||||
|
||||
if [ -f "$OUTPUT_DIR/SCYLLA-RELEASE-FILE" ]; then
|
||||
GIT_COMMIT_FILE=$(cat "$OUTPUT_DIR/SCYLLA-RELEASE-FILE" |cut -d . -f 3)
|
||||
if [ -f build/SCYLLA-RELEASE-FILE ]; then
|
||||
RELEASE_FILE=$(cat build/SCYLLA-RELEASE-FILE)
|
||||
GIT_COMMIT_FILE=$(cat build/SCYLLA-RELEASE-FILE |cut -d . -f 3)
|
||||
if [ "$GIT_COMMIT" = "$GIT_COMMIT_FILE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$SCYLLA_VERSION-$SCYLLA_RELEASE"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
echo "$SCYLLA_VERSION" > "$OUTPUT_DIR/SCYLLA-VERSION-FILE"
|
||||
echo "$SCYLLA_RELEASE" > "$OUTPUT_DIR/SCYLLA-RELEASE-FILE"
|
||||
echo "$PRODUCT" > "$OUTPUT_DIR/SCYLLA-PRODUCT-FILE"
|
||||
mkdir -p build
|
||||
echo "$SCYLLA_VERSION" > build/SCYLLA-VERSION-FILE
|
||||
echo "$SCYLLA_RELEASE" > build/SCYLLA-RELEASE-FILE
|
||||
echo "$PRODUCT" > build/SCYLLA-PRODUCT-FILE
|
||||
|
||||
2
abseil
2
abseil
Submodule abseil updated: f70eadadd7...1e3d25b265
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020-present ScyllaDB
|
||||
* Copyright (C) 2020 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020-present ScyllaDB
|
||||
* Copyright (C) 2020 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <seastar/util/defer.hh>
|
||||
#include "hashers.hh"
|
||||
#include "bytes.hh"
|
||||
#include "alternator/auth.hh"
|
||||
@@ -31,13 +32,8 @@
|
||||
#include "auth/common.hh"
|
||||
#include "auth/password_authenticator.hh"
|
||||
#include "auth/roles-metadata.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "alternator/executor.hh"
|
||||
#include "cql3/selection/selection.hh"
|
||||
#include "database.hh"
|
||||
#include "query-result-set.hh"
|
||||
#include "cql3/result_set.hh"
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "cql3/untyped_result_set.hh"
|
||||
|
||||
namespace alternator {
|
||||
|
||||
@@ -66,14 +62,6 @@ static std::string apply_sha256(std::string_view msg) {
|
||||
return to_hex(hasher.finalize());
|
||||
}
|
||||
|
||||
static std::string apply_sha256(const std::vector<temporary_buffer<char>>& msg) {
|
||||
sha256_hasher hasher;
|
||||
for (const temporary_buffer<char>& buf : msg) {
|
||||
hasher.update(buf.get(), buf.size());
|
||||
}
|
||||
return to_hex(hasher.finalize());
|
||||
}
|
||||
|
||||
static std::string format_time_point(db_clock::time_point tp) {
|
||||
time_t time_point_repr = db_clock::to_time_t(tp);
|
||||
std::string time_point_str;
|
||||
@@ -103,7 +91,7 @@ void check_expiry(std::string_view signature_date) {
|
||||
|
||||
std::string get_signature(std::string_view access_key_id, std::string_view secret_access_key, std::string_view host, std::string_view method,
|
||||
std::string_view orig_datestamp, std::string_view signed_headers_str, const std::map<std::string_view, std::string_view>& signed_headers_map,
|
||||
const std::vector<temporary_buffer<char>>& body_content, std::string_view region, std::string_view service, std::string_view query_string) {
|
||||
std::string_view body_content, std::string_view region, std::string_view service, std::string_view query_string) {
|
||||
auto amz_date_it = signed_headers_map.find("x-amz-date");
|
||||
if (amz_date_it == signed_headers_map.end()) {
|
||||
throw api_error::invalid_signature("X-Amz-Date header is mandatory for signature verification");
|
||||
@@ -136,36 +124,24 @@ std::string get_signature(std::string_view access_key_id, std::string_view secre
|
||||
return to_hex(bytes_view(reinterpret_cast<const int8_t*>(signature.data()), signature.size()));
|
||||
}
|
||||
|
||||
future<std::string> get_key_from_roles(service::storage_proxy& proxy, std::string username) {
|
||||
schema_ptr schema = proxy.get_db().local().find_schema("system_auth", "roles");
|
||||
partition_key pk = partition_key::from_single_value(*schema, utf8_type->decompose(username));
|
||||
dht::partition_range_vector partition_ranges{dht::partition_range(dht::decorate_key(*schema, pk))};
|
||||
std::vector<query::clustering_range> bounds{query::clustering_range::make_open_ended_both_sides()};
|
||||
const column_definition* salted_hash_col = schema->get_column_definition(bytes("salted_hash"));
|
||||
if (!salted_hash_col) {
|
||||
co_return coroutine::make_exception(api_error::unrecognized_client(format("Credentials cannot be fetched for: {}", username)));
|
||||
}
|
||||
auto selection = cql3::selection::selection::for_columns(schema, {salted_hash_col});
|
||||
auto partition_slice = query::partition_slice(std::move(bounds), {}, query::column_id_vector{salted_hash_col->id}, selection->get_query_options());
|
||||
auto command = ::make_lw_shared<query::read_command>(schema->id(), schema->version(), partition_slice, proxy.get_max_result_size(partition_slice));
|
||||
future<std::string> get_key_from_roles(cql3::query_processor& qp, std::string username) {
|
||||
static const sstring query = format("SELECT salted_hash FROM {} WHERE {} = ?",
|
||||
auth::meta::roles_table::qualified_name, auth::meta::roles_table::role_col_name);
|
||||
|
||||
auto cl = auth::password_authenticator::consistency_for_user(username);
|
||||
|
||||
service::client_state client_state{service::client_state::internal_tag()};
|
||||
service::storage_proxy::coordinator_query_result qr = co_await proxy.query(schema, std::move(command), std::move(partition_ranges), cl,
|
||||
service::storage_proxy::coordinator_query_options(executor::default_timeout(), empty_service_permit(), client_state));
|
||||
|
||||
cql3::selection::result_set_builder builder(*selection, gc_clock::now(), cql_serialization_format::latest());
|
||||
query::result_view::consume(*qr.query_result, partition_slice, cql3::selection::result_set_builder::visitor(builder, *schema, *selection));
|
||||
|
||||
auto result_set = builder.build();
|
||||
if (result_set->empty()) {
|
||||
co_return coroutine::make_exception(api_error::unrecognized_client(format("User not found: {}", username)));
|
||||
}
|
||||
const bytes_opt& salted_hash = result_set->rows().front().front(); // We only asked for 1 row and 1 column
|
||||
if (!salted_hash) {
|
||||
co_return coroutine::make_exception(api_error::unrecognized_client(format("No password found for user: {}", username)));
|
||||
}
|
||||
co_return value_cast<sstring>(utf8_type->deserialize(*salted_hash));
|
||||
auto& timeout = auth::internal_distributed_timeout_config();
|
||||
return qp.execute_internal(query, cl, timeout, {sstring(username)}, true).then_wrapped([username = std::move(username)] (future<::shared_ptr<cql3::untyped_result_set>> f) {
|
||||
auto res = f.get0();
|
||||
auto salted_hash = std::optional<sstring>();
|
||||
if (res->empty()) {
|
||||
throw api_error::unrecognized_client(fmt::format("User not found: {}", username));
|
||||
}
|
||||
salted_hash = res->one().get_opt<sstring>("salted_hash");
|
||||
if (!salted_hash) {
|
||||
throw api_error::unrecognized_client(fmt::format("No password found for user: {}", username));
|
||||
}
|
||||
return make_ready_future<std::string>(*salted_hash);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -27,8 +27,8 @@
|
||||
#include "gc_clock.hh"
|
||||
#include "utils/loading_cache.hh"
|
||||
|
||||
namespace service {
|
||||
class storage_proxy;
|
||||
namespace cql3 {
|
||||
class query_processor;
|
||||
}
|
||||
|
||||
namespace alternator {
|
||||
@@ -39,8 +39,8 @@ using key_cache = utils::loading_cache<std::string, std::string>;
|
||||
|
||||
std::string get_signature(std::string_view access_key_id, std::string_view secret_access_key, std::string_view host, std::string_view method,
|
||||
std::string_view orig_datestamp, std::string_view signed_headers_str, const std::map<std::string_view, std::string_view>& signed_headers_map,
|
||||
const std::vector<temporary_buffer<char>>& body_content, std::string_view region, std::string_view service, std::string_view query_string);
|
||||
std::string_view body_content, std::string_view region, std::string_view service, std::string_view query_string);
|
||||
|
||||
future<std::string> get_key_from_roles(service::storage_proxy& proxy, std::string username);
|
||||
future<std::string> get_key_from_roles(cql3::query_processor& qp, std::string username);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -19,6 +19,10 @@
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// The DynamoAPI dictates that "binary" (a.k.a. "bytes" or "blob") values
|
||||
// be encoded in the JSON API as base64-encoded strings. This is code to
|
||||
// convert byte arrays to base64-encoded strings, and back.
|
||||
|
||||
#include "base64.hh"
|
||||
|
||||
#include <ctype.h>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,11 +23,16 @@
|
||||
|
||||
#include <string_view>
|
||||
#include "bytes.hh"
|
||||
#include "utils/rjson.hh"
|
||||
|
||||
std::string base64_encode(bytes_view);
|
||||
|
||||
bytes base64_decode(std::string_view);
|
||||
|
||||
inline bytes base64_decode(const rjson::value& v) {
|
||||
return base64_decode(std::string_view(v.GetString(), v.GetStringLength()));
|
||||
}
|
||||
|
||||
size_t base64_decoded_len(std::string_view str);
|
||||
|
||||
bool base64_begins_with(std::string_view base, std::string_view operand);
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -28,8 +28,7 @@
|
||||
#include <unordered_map>
|
||||
#include "utils/rjson.hh"
|
||||
#include "serialization.hh"
|
||||
#include "utils/base64.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include "base64.hh"
|
||||
#include <stdexcept>
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
@@ -263,7 +262,7 @@ bool check_CONTAINS(const rjson::value* v1, const rjson::value& v2) {
|
||||
if (kv1.name == "S" && kv2.name == "S") {
|
||||
return rjson::to_string_view(kv1.value).find(rjson::to_string_view(kv2.value)) != std::string_view::npos;
|
||||
} else if (kv1.name == "B" && kv2.name == "B") {
|
||||
return rjson::base64_decode(kv1.value).find(rjson::base64_decode(kv2.value)) != bytes::npos;
|
||||
return base64_decode(kv1.value).find(base64_decode(kv2.value)) != bytes::npos;
|
||||
} else if (is_set_of(kv1.name, kv2.name)) {
|
||||
for (auto i = kv1.value.Begin(); i != kv1.value.End(); ++i) {
|
||||
if (*i == kv2.value) {
|
||||
@@ -387,7 +386,7 @@ bool check_compare(const rjson::value* v1, const rjson::value& v2, const Compara
|
||||
std::string_view(kv2.value.GetString(), kv2.value.GetStringLength()));
|
||||
}
|
||||
if (kv1.name == "B") {
|
||||
return cmp(rjson::base64_decode(kv1.value), rjson::base64_decode(kv2.value));
|
||||
return cmp(base64_decode(kv1.value), base64_decode(kv2.value));
|
||||
}
|
||||
// cannot reach here, as check_comparable_type() verifies the type is one
|
||||
// of the above options.
|
||||
@@ -477,7 +476,7 @@ static bool check_BETWEEN(const rjson::value* v, const rjson::value& lb, const r
|
||||
bounds_from_query);
|
||||
}
|
||||
if (kv_v.name == "B") {
|
||||
return check_BETWEEN(rjson::base64_decode(kv_v.value), rjson::base64_decode(kv_lb.value), rjson::base64_decode(kv_ub.value), bounds_from_query);
|
||||
return check_BETWEEN(base64_decode(kv_v.value), base64_decode(kv_lb.value), base64_decode(kv_ub.value), bounds_from_query);
|
||||
}
|
||||
if (v_from_query) {
|
||||
throw api_error::validation(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021-present ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <seastar/net/dns.hh>
|
||||
#include "controller.hh"
|
||||
#include "server.hh"
|
||||
#include "executor.hh"
|
||||
#include "rmw_operation.hh"
|
||||
#include "db/config.hh"
|
||||
#include "cdc/generation_service.hh"
|
||||
#include "service/memory_limiter.hh"
|
||||
|
||||
using namespace seastar;
|
||||
|
||||
namespace alternator {
|
||||
|
||||
static logging::logger logger("alternator_controller");
|
||||
|
||||
controller::controller(
|
||||
sharded<gms::gossiper>& gossiper,
|
||||
sharded<service::storage_proxy>& proxy,
|
||||
sharded<service::migration_manager>& mm,
|
||||
sharded<db::system_distributed_keyspace>& sys_dist_ks,
|
||||
sharded<cdc::generation_service>& cdc_gen_svc,
|
||||
sharded<service::memory_limiter>& memory_limiter,
|
||||
const db::config& config)
|
||||
: _gossiper(gossiper)
|
||||
, _proxy(proxy)
|
||||
, _mm(mm)
|
||||
, _sys_dist_ks(sys_dist_ks)
|
||||
, _cdc_gen_svc(cdc_gen_svc)
|
||||
, _memory_limiter(memory_limiter)
|
||||
, _config(config)
|
||||
{
|
||||
}
|
||||
|
||||
future<> controller::start() {
|
||||
return seastar::async([this] {
|
||||
auto preferred = _config.listen_interface_prefer_ipv6() ? std::make_optional(net::inet_address::family::INET6) : std::nullopt;
|
||||
auto family = _config.enable_ipv6_dns_lookup() || preferred ? std::nullopt : std::make_optional(net::inet_address::family::INET);
|
||||
|
||||
// Create an smp_service_group to be used for limiting the
|
||||
// concurrency when forwarding Alternator request between
|
||||
// shards - if necessary for LWT.
|
||||
smp_service_group_config c;
|
||||
c.max_nonlocal_requests = 5000;
|
||||
_ssg = create_smp_service_group(c).get0();
|
||||
|
||||
rmw_operation::set_default_write_isolation(_config.alternator_write_isolation());
|
||||
executor::set_default_timeout(std::chrono::milliseconds(_config.alternator_timeout_in_ms()));
|
||||
|
||||
net::inet_address addr;
|
||||
try {
|
||||
addr = net::dns::get_host_by_name(_config.alternator_address(), family).get0().addr_list.front();
|
||||
} catch (...) {
|
||||
std::throw_with_nested(std::runtime_error(fmt::format("Unable to resolve alternator_address {}", _config.alternator_address())));
|
||||
}
|
||||
|
||||
auto get_cdc_metadata = [] (cdc::generation_service& svc) { return std::ref(svc.get_cdc_metadata()); };
|
||||
|
||||
_executor.start(std::ref(_gossiper), std::ref(_proxy), std::ref(_mm), std::ref(_sys_dist_ks), sharded_parameter(get_cdc_metadata, std::ref(_cdc_gen_svc)), _ssg.value()).get();
|
||||
_server.start(std::ref(_executor), std::ref(_proxy), std::ref(_gossiper)).get();
|
||||
std::optional<uint16_t> alternator_port;
|
||||
if (_config.alternator_port()) {
|
||||
alternator_port = _config.alternator_port();
|
||||
}
|
||||
std::optional<uint16_t> alternator_https_port;
|
||||
std::optional<tls::credentials_builder> creds;
|
||||
if (_config.alternator_https_port()) {
|
||||
alternator_https_port = _config.alternator_https_port();
|
||||
creds.emplace();
|
||||
auto opts = _config.alternator_encryption_options();
|
||||
if (opts.empty()) {
|
||||
// Earlier versions mistakenly configured Alternator's
|
||||
// HTTPS parameters via the "server_encryption_option"
|
||||
// configuration parameter. We *temporarily* continue
|
||||
// to allow this, for backward compatibility.
|
||||
opts = _config.server_encryption_options();
|
||||
if (!opts.empty()) {
|
||||
logger.warn("Setting server_encryption_options to configure "
|
||||
"Alternator's HTTPS encryption is deprecated. Please "
|
||||
"switch to setting alternator_encryption_options instead.");
|
||||
}
|
||||
}
|
||||
opts.erase("require_client_auth");
|
||||
opts.erase("truststore");
|
||||
utils::configure_tls_creds_builder(creds.value(), std::move(opts)).get();
|
||||
}
|
||||
bool alternator_enforce_authorization = _config.alternator_enforce_authorization();
|
||||
_server.invoke_on_all(
|
||||
[this, addr, alternator_port, alternator_https_port, creds = std::move(creds), alternator_enforce_authorization] (server& server) mutable {
|
||||
return server.init(addr, alternator_port, alternator_https_port, creds, alternator_enforce_authorization,
|
||||
&_memory_limiter.local().get_semaphore(),
|
||||
_config.max_concurrent_requests_per_shard);
|
||||
}).then([addr, alternator_port, alternator_https_port] {
|
||||
logger.info("Alternator server listening on {}, HTTP port {}, HTTPS port {}",
|
||||
addr, alternator_port ? std::to_string(*alternator_port) : "OFF", alternator_https_port ? std::to_string(*alternator_https_port) : "OFF");
|
||||
}).get();
|
||||
});
|
||||
}
|
||||
|
||||
future<> controller::stop() {
|
||||
return seastar::async([this] {
|
||||
_server.stop().get();
|
||||
_executor.stop().get();
|
||||
destroy_smp_service_group(_ssg.value()).get();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021-present ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
#include <seastar/core/smp.hh>
|
||||
|
||||
namespace service {
|
||||
class storage_proxy;
|
||||
class migration_manager;
|
||||
class memory_limiter;
|
||||
}
|
||||
|
||||
namespace db {
|
||||
class system_distributed_keyspace;
|
||||
class config;
|
||||
}
|
||||
|
||||
namespace cdc {
|
||||
class generation_service;
|
||||
}
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
}
|
||||
|
||||
namespace alternator {
|
||||
|
||||
using namespace seastar;
|
||||
|
||||
class executor;
|
||||
class server;
|
||||
|
||||
class controller {
|
||||
sharded<gms::gossiper>& _gossiper;
|
||||
sharded<service::storage_proxy>& _proxy;
|
||||
sharded<service::migration_manager>& _mm;
|
||||
sharded<db::system_distributed_keyspace>& _sys_dist_ks;
|
||||
sharded<cdc::generation_service>& _cdc_gen_svc;
|
||||
sharded<service::memory_limiter>& _memory_limiter;
|
||||
const db::config& _config;
|
||||
|
||||
sharded<executor> _executor;
|
||||
sharded<server> _server;
|
||||
std::optional<smp_service_group> _ssg;
|
||||
|
||||
public:
|
||||
controller(
|
||||
sharded<gms::gossiper>& gossiper,
|
||||
sharded<service::storage_proxy>& proxy,
|
||||
sharded<service::migration_manager>& mm,
|
||||
sharded<db::system_distributed_keyspace>& sys_dist_ks,
|
||||
sharded<cdc::generation_service>& cdc_gen_svc,
|
||||
sharded<service::memory_limiter>& memory_limiter,
|
||||
const db::config& config);
|
||||
|
||||
future<> start();
|
||||
future<> stop();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -34,7 +34,7 @@ namespace alternator {
|
||||
// "ResourceNotFoundException", and a human readable message.
|
||||
// Eventually alternator::api_handler will convert a returned or thrown
|
||||
// api_error into a JSON object, and that is returned to the user.
|
||||
class api_error final : public std::exception {
|
||||
class api_error final {
|
||||
public:
|
||||
using status_type = httpd::reply::status_type;
|
||||
status_type _http_code;
|
||||
@@ -80,20 +80,9 @@ public:
|
||||
static api_error trimmed_data_access_exception(std::string msg) {
|
||||
return api_error("TrimmedDataAccessException", std::move(msg));
|
||||
}
|
||||
static api_error request_limit_exceeded(std::string msg) {
|
||||
return api_error("RequestLimitExceeded", std::move(msg));
|
||||
}
|
||||
static api_error internal(std::string msg) {
|
||||
return api_error("InternalServerError", std::move(msg), reply::status_type::internal_server_error);
|
||||
}
|
||||
|
||||
// Provide the "std::exception" interface, to make it easier to print this
|
||||
// exception in log messages. Note that this function is *not* used to
|
||||
// format the error to send it back to the client - server.cc has
|
||||
// generate_error_reply() to format an api_error as the DynamoDB protocol
|
||||
// requires.
|
||||
virtual const char* what() const noexcept override;
|
||||
mutable std::string _what_string;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -21,9 +21,8 @@
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "utils/base64.hh"
|
||||
#include "base64.hh"
|
||||
|
||||
#include <seastar/core/sleep.hh>
|
||||
#include "alternator/executor.hh"
|
||||
#include "log.hh"
|
||||
#include "schema_builder.hh"
|
||||
@@ -58,9 +57,6 @@
|
||||
#include "alternator/rmw_operation.hh"
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <boost/range/adaptors.hpp>
|
||||
#include <boost/range/algorithm/find_end.hpp>
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "gms/gossiper.hh"
|
||||
|
||||
logging::logger elogger("alternator-executor");
|
||||
|
||||
@@ -92,10 +88,10 @@ std::string json_string::to_json() const {
|
||||
}
|
||||
|
||||
void executor::supplement_table_info(rjson::value& descr, const schema& schema) const {
|
||||
rjson::add(descr, "CreationDateTime", rjson::value(std::chrono::duration_cast<std::chrono::seconds>(gc_clock::now().time_since_epoch()).count()));
|
||||
rjson::add(descr, "TableStatus", "ACTIVE");
|
||||
rjson::set(descr, "CreationDateTime", rjson::value(std::chrono::duration_cast<std::chrono::seconds>(gc_clock::now().time_since_epoch()).count()));
|
||||
rjson::set(descr, "TableStatus", "ACTIVE");
|
||||
auto schema_id_str = schema.id().to_sstring();
|
||||
rjson::add(descr, "TableId", rjson::from_string(schema_id_str));
|
||||
rjson::set(descr, "TableId", rjson::from_string(schema_id_str));
|
||||
|
||||
executor::supplement_table_stream_info(descr, schema);
|
||||
}
|
||||
@@ -201,7 +197,7 @@ schema_ptr executor::find_table(service::storage_proxy& proxy, const rjson::valu
|
||||
}
|
||||
}
|
||||
|
||||
schema_ptr get_table(service::storage_proxy& proxy, const rjson::value& request) {
|
||||
static schema_ptr get_table(service::storage_proxy& proxy, const rjson::value& request) {
|
||||
auto schema = executor::find_table(proxy, request);
|
||||
if (!schema) {
|
||||
// if we get here then the name was missing, since syntax or missing actual CF
|
||||
@@ -341,8 +337,8 @@ void executor::describe_key_schema(rjson::value& parent, const schema& schema, s
|
||||
rjson::value key_schema = rjson::empty_array();
|
||||
for (const column_definition& cdef : schema.partition_key_columns()) {
|
||||
rjson::value key = rjson::empty_object();
|
||||
rjson::add(key, "AttributeName", rjson::from_string(cdef.name_as_text()));
|
||||
rjson::add(key, "KeyType", "HASH");
|
||||
rjson::set(key, "AttributeName", rjson::from_string(cdef.name_as_text()));
|
||||
rjson::set(key, "KeyType", "HASH");
|
||||
rjson::push_back(key_schema, std::move(key));
|
||||
if (attribute_types) {
|
||||
(*attribute_types)[cdef.name_as_text()] = type_to_string(cdef.type);
|
||||
@@ -350,8 +346,8 @@ void executor::describe_key_schema(rjson::value& parent, const schema& schema, s
|
||||
}
|
||||
for (const column_definition& cdef : schema.clustering_key_columns()) {
|
||||
rjson::value key = rjson::empty_object();
|
||||
rjson::add(key, "AttributeName", rjson::from_string(cdef.name_as_text()));
|
||||
rjson::add(key, "KeyType", "RANGE");
|
||||
rjson::set(key, "AttributeName", rjson::from_string(cdef.name_as_text()));
|
||||
rjson::set(key, "KeyType", "RANGE");
|
||||
rjson::push_back(key_schema, std::move(key));
|
||||
if (attribute_types) {
|
||||
(*attribute_types)[cdef.name_as_text()] = type_to_string(cdef.type);
|
||||
@@ -362,7 +358,7 @@ void executor::describe_key_schema(rjson::value& parent, const schema& schema, s
|
||||
// reproducer in test_gsi_2_describe_table_schema.
|
||||
break;
|
||||
}
|
||||
rjson::add(parent, "KeySchema", std::move(key_schema));
|
||||
rjson::set(parent, "KeySchema", std::move(key_schema));
|
||||
|
||||
}
|
||||
|
||||
@@ -395,10 +391,10 @@ future<executor::request_return_type> executor::describe_table(client_state& cli
|
||||
tracing::add_table_name(trace_state, schema->ks_name(), schema->cf_name());
|
||||
|
||||
rjson::value table_description = rjson::empty_object();
|
||||
rjson::add(table_description, "TableName", rjson::from_string(schema->cf_name()));
|
||||
rjson::set(table_description, "TableName", rjson::from_string(schema->cf_name()));
|
||||
// FIXME: take the tables creation time, not the current time!
|
||||
size_t creation_date_seconds = std::chrono::duration_cast<std::chrono::seconds>(gc_clock::now().time_since_epoch()).count();
|
||||
rjson::add(table_description, "CreationDateTime", rjson::value(creation_date_seconds));
|
||||
rjson::set(table_description, "CreationDateTime", rjson::value(creation_date_seconds));
|
||||
// FIXME: In DynamoDB the CreateTable implementation is asynchronous, and
|
||||
// the table may be in "Creating" state until creating is finished.
|
||||
// We don't currently do this in Alternator - instead CreateTable waits
|
||||
@@ -406,20 +402,15 @@ future<executor::request_return_type> executor::describe_table(client_state& cli
|
||||
// ACTIVE or doesn't exist at all (and DescribeTable returns an error).
|
||||
// The other states (CREATING, UPDATING, DELETING) are not currently
|
||||
// returned.
|
||||
rjson::add(table_description, "TableStatus", "ACTIVE");
|
||||
rjson::add(table_description, "TableArn", generate_arn_for_table(*schema));
|
||||
rjson::add(table_description, "TableId", rjson::from_string(schema->id().to_sstring()));
|
||||
rjson::set(table_description, "TableStatus", "ACTIVE");
|
||||
rjson::set(table_description, "TableArn", generate_arn_for_table(*schema));
|
||||
rjson::set(table_description, "TableId", rjson::from_string(schema->id().to_sstring()));
|
||||
// FIXME: Instead of hardcoding, we should take into account which mode was chosen
|
||||
// when the table was created. But, Spark jobs expect something to be returned
|
||||
// and PAY_PER_REQUEST seems closer to reality than PROVISIONED.
|
||||
rjson::add(table_description, "BillingModeSummary", rjson::empty_object());
|
||||
rjson::add(table_description["BillingModeSummary"], "BillingMode", "PAY_PER_REQUEST");
|
||||
rjson::add(table_description["BillingModeSummary"], "LastUpdateToPayPerRequestDateTime", rjson::value(creation_date_seconds));
|
||||
// In PAY_PER_REQUEST billing mode, provisioned capacity should return 0
|
||||
rjson::add(table_description, "ProvisionedThroughput", rjson::empty_object());
|
||||
rjson::add(table_description["ProvisionedThroughput"], "ReadCapacityUnits", 0);
|
||||
rjson::add(table_description["ProvisionedThroughput"], "WriteCapacityUnits", 0);
|
||||
rjson::add(table_description["ProvisionedThroughput"], "NumberOfDecreasesToday", 0);
|
||||
rjson::set(table_description, "BillingModeSummary", rjson::empty_object());
|
||||
rjson::set(table_description["BillingModeSummary"], "BillingMode", "PAY_PER_REQUEST");
|
||||
rjson::set(table_description["BillingModeSummary"], "LastUpdateToPayPerRequestDateTime", rjson::value(creation_date_seconds));
|
||||
|
||||
std::unordered_map<std::string,std::string> key_attribute_types;
|
||||
// Add base table's KeySchema and collect types for AttributeDefinitions:
|
||||
@@ -438,7 +429,7 @@ future<executor::request_return_type> executor::describe_table(client_state& cli
|
||||
continue;
|
||||
}
|
||||
sstring index_name = cf_name.substr(delim_it + 1);
|
||||
rjson::add(view_entry, "IndexName", rjson::from_string(index_name));
|
||||
rjson::set(view_entry, "IndexName", rjson::from_string(index_name));
|
||||
// Add indexes's KeySchema and collect types for AttributeDefinitions:
|
||||
describe_key_schema(view_entry, *vptr, key_attribute_types);
|
||||
// Local secondary indexes are marked by an extra '!' sign occurring before the ':' delimiter
|
||||
@@ -446,10 +437,10 @@ future<executor::request_return_type> executor::describe_table(client_state& cli
|
||||
rjson::push_back(index_array, std::move(view_entry));
|
||||
}
|
||||
if (!lsi_array.Empty()) {
|
||||
rjson::add(table_description, "LocalSecondaryIndexes", std::move(lsi_array));
|
||||
rjson::set(table_description, "LocalSecondaryIndexes", std::move(lsi_array));
|
||||
}
|
||||
if (!gsi_array.Empty()) {
|
||||
rjson::add(table_description, "GlobalSecondaryIndexes", std::move(gsi_array));
|
||||
rjson::set(table_description, "GlobalSecondaryIndexes", std::move(gsi_array));
|
||||
}
|
||||
}
|
||||
// Use map built by describe_key_schema() for base and indexes to produce
|
||||
@@ -457,18 +448,18 @@ future<executor::request_return_type> executor::describe_table(client_state& cli
|
||||
rjson::value attribute_definitions = rjson::empty_array();
|
||||
for (auto& type : key_attribute_types) {
|
||||
rjson::value key = rjson::empty_object();
|
||||
rjson::add(key, "AttributeName", rjson::from_string(type.first));
|
||||
rjson::add(key, "AttributeType", rjson::from_string(type.second));
|
||||
rjson::set(key, "AttributeName", rjson::from_string(type.first));
|
||||
rjson::set(key, "AttributeType", rjson::from_string(type.second));
|
||||
rjson::push_back(attribute_definitions, std::move(key));
|
||||
}
|
||||
rjson::add(table_description, "AttributeDefinitions", std::move(attribute_definitions));
|
||||
rjson::set(table_description, "AttributeDefinitions", std::move(attribute_definitions));
|
||||
|
||||
supplement_table_stream_info(table_description, *schema);
|
||||
|
||||
// FIXME: still missing some response fields (issue #5026)
|
||||
|
||||
rjson::value response = rjson::empty_object();
|
||||
rjson::add(response, "Table", std::move(table_description));
|
||||
rjson::set(response, "Table", std::move(table_description));
|
||||
elogger.trace("returning {}", response);
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(response)));
|
||||
}
|
||||
@@ -490,10 +481,10 @@ future<executor::request_return_type> executor::delete_table(client_state& clien
|
||||
}).then([table_name = std::move(table_name)] {
|
||||
// FIXME: need more attributes?
|
||||
rjson::value table_description = rjson::empty_object();
|
||||
rjson::add(table_description, "TableName", rjson::from_string(table_name));
|
||||
rjson::add(table_description, "TableStatus", "DELETING");
|
||||
rjson::set(table_description, "TableName", rjson::from_string(table_name));
|
||||
rjson::set(table_description, "TableStatus", "DELETING");
|
||||
rjson::value response = rjson::empty_object();
|
||||
rjson::add(response, "TableDescription", std::move(table_description));
|
||||
rjson::set(response, "TableDescription", std::move(table_description));
|
||||
elogger.trace("returning {}", response);
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(response)));
|
||||
});
|
||||
@@ -710,10 +701,10 @@ static void update_tags_map(const rjson::value& tags, std::map<sstring, sstring>
|
||||
// FIXME: Updating tags currently relies on updating schema, which may be subject
|
||||
// to races during concurrent updates of the same table. Once Scylla schema updates
|
||||
// are fixed, this issue will automatically get fixed as well.
|
||||
future<> update_tags(service::migration_manager& mm, schema_ptr schema, std::map<sstring, sstring>&& tags_map) {
|
||||
static future<> update_tags(service::migration_manager& mm, schema_ptr schema, std::map<sstring, sstring>&& tags_map) {
|
||||
schema_builder builder(schema);
|
||||
builder.add_extension(tags_extension::NAME, ::make_shared<tags_extension>(std::move(tags_map)));
|
||||
return mm.announce_column_family_update(builder.build(), false, std::vector<view_ptr>(), std::nullopt);
|
||||
return mm.announce_column_family_update(builder.build(), false, std::vector<view_ptr>());
|
||||
}
|
||||
|
||||
future<executor::request_return_type> executor::tag_resource(client_state& client_state, service_permit permit, rjson::value request) {
|
||||
@@ -767,13 +758,13 @@ future<executor::request_return_type> executor::list_tags_of_resource(client_sta
|
||||
|
||||
auto tags_map = get_tags_of_table(schema);
|
||||
rjson::value ret = rjson::empty_object();
|
||||
rjson::add(ret, "Tags", rjson::empty_array());
|
||||
rjson::set(ret, "Tags", rjson::empty_array());
|
||||
|
||||
rjson::value& tags = ret["Tags"];
|
||||
for (auto& tag_entry : tags_map) {
|
||||
rjson::value new_entry = rjson::empty_object();
|
||||
rjson::add(new_entry, "Key", rjson::from_string(tag_entry.first));
|
||||
rjson::add(new_entry, "Value", rjson::from_string(tag_entry.second));
|
||||
rjson::set(new_entry, "Key", rjson::from_string(tag_entry.first));
|
||||
rjson::set(new_entry, "Value", rjson::from_string(tag_entry.second));
|
||||
rjson::push_back(tags, std::move(new_entry));
|
||||
}
|
||||
|
||||
@@ -903,10 +894,6 @@ future<executor::request_return_type> executor::create_table(client_state& clien
|
||||
}
|
||||
std::string vname(lsi_name(table_name, index_name->GetString()));
|
||||
elogger.trace("Adding LSI {}", index_name->GetString());
|
||||
if (range_key.empty()) {
|
||||
return make_ready_future<request_return_type>(api_error::validation(
|
||||
"LocalSecondaryIndex requires that the base table have a range key"));
|
||||
}
|
||||
// FIXME: read and handle "Projection" parameter. This will
|
||||
// require the MV code to copy just parts of the attrs map.
|
||||
schema_builder view_builder(keyspace_name, vname);
|
||||
@@ -1007,7 +994,7 @@ future<executor::request_return_type> executor::create_table(client_state& clien
|
||||
}).then([this, table_info = std::move(table_info), schema] () mutable {
|
||||
rjson::value status = rjson::empty_object();
|
||||
supplement_table_info(table_info, *schema);
|
||||
rjson::add(status, "TableDescription", std::move(table_info));
|
||||
rjson::set(status, "TableDescription", std::move(table_info));
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(status)));
|
||||
});
|
||||
});
|
||||
@@ -1022,16 +1009,18 @@ future<executor::request_return_type> executor::update_table(client_state& clien
|
||||
_stats.api_operations.update_table++;
|
||||
elogger.trace("Updating table {}", request);
|
||||
|
||||
schema_ptr tab = get_table(_proxy, request);
|
||||
// the ugly but harmless conversion to string_view here is because
|
||||
// Seastar's sstring is missing a find(std::string_view) :-()
|
||||
if (std::string_view(tab->cf_name()).find(INTERNAL_TABLE_PREFIX) == 0) {
|
||||
std::string table_name = get_table_name(request);
|
||||
if (table_name.find(INTERNAL_TABLE_PREFIX) == 0) {
|
||||
return make_ready_future<request_return_type>(api_error::validation(
|
||||
format("Prefix {} is reserved for accessing internal tables", INTERNAL_TABLE_PREFIX)));
|
||||
}
|
||||
tracing::add_table_name(trace_state, tab->ks_name(), tab->cf_name());
|
||||
std::string keyspace_name = executor::KEYSPACE_NAME_PREFIX + table_name;
|
||||
tracing::add_table_name(trace_state, keyspace_name, table_name);
|
||||
|
||||
schema_builder builder(tab);
|
||||
auto& db = _proxy.get_db().local();
|
||||
auto& cf = db.find_column_family(keyspace_name, table_name);
|
||||
|
||||
schema_builder builder(cf.schema());
|
||||
|
||||
rjson::value* stream_specification = rjson::find(request, "StreamSpecification");
|
||||
if (stream_specification && stream_specification->IsObject()) {
|
||||
@@ -1058,12 +1047,12 @@ future<executor::request_return_type> executor::update_table(client_state& clien
|
||||
|
||||
auto schema = builder.build();
|
||||
|
||||
return _mm.announce_column_family_update(schema, false, {}, std::nullopt).then([this] {
|
||||
return _mm.announce_column_family_update(schema, false, {}).then([this] {
|
||||
return wait_for_schema_agreement(_mm, db::timeout_clock::now() + 10s);
|
||||
}).then([this, table_info = std::move(request), schema] () mutable {
|
||||
rjson::value status = rjson::empty_object();
|
||||
supplement_table_info(table_info, *schema);
|
||||
rjson::add(status, "TableDescription", std::move(table_info));
|
||||
rjson::set(status, "TableDescription", std::move(table_info));
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(status)));
|
||||
});
|
||||
}
|
||||
@@ -1383,7 +1372,7 @@ std::optional<shard_id> rmw_operation::shard_for_execute(bool needs_read_before_
|
||||
static future<executor::request_return_type> rmw_operation_return(rjson::value&& attributes) {
|
||||
rjson::value ret = rjson::empty_object();
|
||||
if (!attributes.IsNull()) {
|
||||
rjson::add(ret, "Attributes", std::move(attributes));
|
||||
rjson::set(ret, "Attributes", std::move(attributes));
|
||||
}
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
}
|
||||
@@ -1880,7 +1869,7 @@ future<executor::request_return_type> executor::batch_write_item(client_state& c
|
||||
// need to return a list of these failed updates in UnprocessedItems
|
||||
// rather than fail the whole write (issue #5650).
|
||||
rjson::value ret = rjson::empty_object();
|
||||
rjson::add(ret, "UnprocessedItems", rjson::empty_object());
|
||||
rjson::set(ret, "UnprocessedItems", rjson::empty_object());
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
});
|
||||
}
|
||||
@@ -1926,13 +1915,11 @@ static bool hierarchy_filter(rjson::value& val, const attribute_path_map_node<T>
|
||||
if (x->second) {
|
||||
// Only a part of this attribute is to be filtered, do it.
|
||||
if (hierarchy_filter(it->value, *x->second)) {
|
||||
// because newv started empty and attr are unique
|
||||
// (keys of v), we can use add() here
|
||||
rjson::add_with_string_name(newv, attr, std::move(it->value));
|
||||
rjson::set_with_string_name(newv, attr, std::move(it->value));
|
||||
}
|
||||
} else {
|
||||
// The entire attribute is to be kept
|
||||
rjson::add_with_string_name(newv, attr, std::move(it->value));
|
||||
rjson::set_with_string_name(newv, attr, std::move(it->value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2011,7 +1998,7 @@ void attribute_path_map_add(const char* source, attribute_path_map<T>& map, cons
|
||||
typename node::members_t& members = h->get_members();
|
||||
auto it = members.find(member);
|
||||
if (it == members.end()) {
|
||||
it = members.insert({member, std::make_unique<node>()}).first;
|
||||
it = members.insert({member, make_shared<node>()}).first;
|
||||
}
|
||||
h = it->second.get();
|
||||
},
|
||||
@@ -2026,7 +2013,7 @@ void attribute_path_map_add(const char* source, attribute_path_map<T>& map, cons
|
||||
typename node::indexes_t& indexes = h->get_indexes();
|
||||
auto it = indexes.find(index);
|
||||
if (it == indexes.end()) {
|
||||
it = indexes.insert({index, std::make_unique<node>()}).first;
|
||||
it = indexes.insert({index, make_shared<node>()}).first;
|
||||
}
|
||||
h = it->second.get();
|
||||
}
|
||||
@@ -2083,9 +2070,6 @@ static attrs_to_get calculate_attrs_to_get(const rjson::value& req, std::unorder
|
||||
for (auto it = attributes_to_get.Begin(); it != attributes_to_get.End(); ++it) {
|
||||
attribute_path_map_add("AttributesToGet", ret, it->GetString());
|
||||
}
|
||||
if (ret.empty()) {
|
||||
throw api_error::validation("Empty AttributesToGet is not allowed. Consider using Select=COUNT instead.");
|
||||
}
|
||||
return ret;
|
||||
} else if (has_projection_expression) {
|
||||
const rjson::value& projection_expression = req["ProjectionExpression"];
|
||||
@@ -2134,11 +2118,9 @@ void executor::describe_single_item(const cql3::selection::selection& selection,
|
||||
std::string column_name = (*column_it)->name_as_text();
|
||||
if (cell && column_name != executor::ATTRS_COLUMN_NAME) {
|
||||
if (attrs_to_get.empty() || attrs_to_get.contains(column_name)) {
|
||||
// item is expected to start empty, and column_name are unique
|
||||
// so add() makes sense
|
||||
rjson::add_with_string_name(item, column_name, rjson::empty_object());
|
||||
rjson::set_with_string_name(item, column_name, rjson::empty_object());
|
||||
rjson::value& field = item[column_name.c_str()];
|
||||
rjson::add_with_string_name(field, type_to_string((*column_it)->type), json_key_column_value(*cell, **column_it));
|
||||
rjson::set_with_string_name(field, type_to_string((*column_it)->type), json_key_column_value(*cell, **column_it));
|
||||
}
|
||||
} else if (cell) {
|
||||
auto deserialized = attrs_type()->deserialize(*cell, cql_serialization_format::latest());
|
||||
@@ -2152,12 +2134,10 @@ void executor::describe_single_item(const cql3::selection::selection& selection,
|
||||
if (it != attrs_to_get.end()) {
|
||||
// attrs_to_get may have asked for only part of this attribute:
|
||||
if (hierarchy_filter(v, it->second)) {
|
||||
// item is expected to start empty, and attribute
|
||||
// names are unique so add() makes sense
|
||||
rjson::add_with_string_name(item, attr_name, std::move(v));
|
||||
rjson::set_with_string_name(item, attr_name, std::move(v));
|
||||
}
|
||||
} else {
|
||||
rjson::add_with_string_name(item, attr_name, std::move(v));
|
||||
rjson::set_with_string_name(item, attr_name, std::move(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2322,33 +2302,10 @@ update_item_operation::update_item_operation(service::storage_proxy& proxy, rjso
|
||||
}
|
||||
}
|
||||
|
||||
// These are the cases where update_item_operation::apply() needs to use
|
||||
// "previous_item" for certain AttributeUpdates operations (ADD or DELETE)
|
||||
static bool check_needs_read_before_write_attribute_updates(rjson::value *attribute_updates) {
|
||||
if (!attribute_updates) {
|
||||
return false;
|
||||
}
|
||||
// We already confirmed in update_item_operation::update_item_operation()
|
||||
// that _attribute_updates, when it exists, is a map
|
||||
for (auto it = attribute_updates->MemberBegin(); it != attribute_updates->MemberEnd(); ++it) {
|
||||
rjson::value* action = rjson::find(it->value, "Action");
|
||||
if (action) {
|
||||
std::string_view action_s = rjson::to_string_view(*action);
|
||||
if (action_s == "ADD") {
|
||||
return true;
|
||||
}
|
||||
// FIXME: we also need to read before write in certain cases
|
||||
// of the DELETE action.
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
update_item_operation::needs_read_before_write() const {
|
||||
return check_needs_read_before_write(_update_expression) ||
|
||||
check_needs_read_before_write(_condition_expression) ||
|
||||
check_needs_read_before_write_attribute_updates(_attribute_updates) ||
|
||||
_request.HasMember("Expected") ||
|
||||
(_returnvalues != returnvalues::NONE && _returnvalues != returnvalues::UPDATED_NEW);
|
||||
}
|
||||
@@ -2483,12 +2440,10 @@ static bool hierarchy_actions(
|
||||
if (subh.has_value()) {
|
||||
std::optional<rjson::value> newv = action_result(subh.get_value(), previous_item);
|
||||
if (newv) {
|
||||
// This is the !subobj case, so v doesn't have an
|
||||
// attr member so we can use add()
|
||||
rjson::add_with_string_name(v, attr, std::move(*newv));
|
||||
rjson::set_with_string_name(v, attr, std::move(*newv));
|
||||
} else {
|
||||
// Removing a.b when a is a map but a.b doesn't exist
|
||||
// is silently ignored. It's not considered an error.
|
||||
throw api_error::validation(format("Can't remove document path {} - not present in item",
|
||||
subh.get_value()._path));
|
||||
}
|
||||
} else {
|
||||
throw api_error::validation(format("UpdateExpression: document paths not valid for this item:{}", h));
|
||||
@@ -2562,14 +2517,11 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
// If the operation was only on specific attribute paths,
|
||||
// leave only them in _return_attributes.
|
||||
if (hierarchy_filter(v, *h)) {
|
||||
// In the UPDATED_NEW case, _return_attributes starts
|
||||
// empty and the attribute names are unique, so we can
|
||||
// use add().
|
||||
rjson::add_with_string_name(_return_attributes,
|
||||
rjson::set_with_string_name(_return_attributes,
|
||||
to_sstring_view(column_name), std::move(v));
|
||||
}
|
||||
} else {
|
||||
rjson::add_with_string_name(_return_attributes,
|
||||
rjson::set_with_string_name(_return_attributes,
|
||||
to_sstring_view(column_name), std::move(v));
|
||||
}
|
||||
} else if (_returnvalues == returnvalues::UPDATED_OLD && previous_item) {
|
||||
@@ -2579,13 +2531,10 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
rjson::value&& v = rjson::copy(*col);
|
||||
if (h) {
|
||||
if (hierarchy_filter(v, *h)) {
|
||||
// In the UPDATED_OLD case, _return_attributes starts
|
||||
// empty and the attribute names are unique, so we can
|
||||
// use add().
|
||||
rjson::add_with_string_name(_return_attributes, cn, std::move(v));
|
||||
rjson::set_with_string_name(_return_attributes, cn, std::move(v));
|
||||
}
|
||||
} else {
|
||||
rjson::add_with_string_name(_return_attributes, cn, std::move(v));
|
||||
rjson::set_with_string_name(_return_attributes, cn, std::move(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2606,9 +2555,7 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
std::string_view cn = to_sstring_view(column_name);
|
||||
const rjson::value* col = rjson::find(*previous_item, cn);
|
||||
if (col) {
|
||||
// In the UPDATED_OLD case the item starts empty and column
|
||||
// names are unique, so we can use add()
|
||||
rjson::add_with_string_name(_return_attributes, cn, rjson::copy(*col));
|
||||
rjson::set_with_string_name(_return_attributes, cn, rjson::copy(*col));
|
||||
}
|
||||
}
|
||||
const column_definition* cdef = _schema->get_column_definition(column_name);
|
||||
@@ -2701,7 +2648,6 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
// "Value" field is missing. If it were not missing, we would
|
||||
// we need to verify the old type and/or value is same as
|
||||
// specified before deleting... We don't do this yet.
|
||||
// This is issue #5864.
|
||||
if (it->value.HasMember("Value")) {
|
||||
throw api_error::validation(
|
||||
format("UpdateItem DELETE with checking old value not yet supported"));
|
||||
@@ -2711,44 +2657,8 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
const rjson::value& value = (it->value)["Value"];
|
||||
validate_value(value, "AttributeUpdates");
|
||||
do_update(std::move(column_name), value);
|
||||
} else if (action == "ADD") {
|
||||
// Note that check_needs_read_before_write_attribute_updates()
|
||||
// made sure we retrieved previous_item (if exists) when there
|
||||
// is an ADD action.
|
||||
const rjson::value* v1 = previous_item ? rjson::find(*previous_item, to_sstring_view(column_name)) : nullptr;
|
||||
const rjson::value& v2 = (it->value)["Value"];
|
||||
validate_value(v2, "AttributeUpdates");
|
||||
// An ADD can be used to create a new attribute (when
|
||||
// !v1) or to add to a pre-existing attribute:
|
||||
if (!v1) {
|
||||
std::string v2_type = get_item_type_string(v2);
|
||||
if (v2_type == "N" || v2_type == "SS" || v2_type == "NS" || v2_type == "BS" || v2_type == "L") {
|
||||
do_update(std::move(column_name), v2);
|
||||
} else {
|
||||
throw api_error::validation(format("An operand in the AttributeUpdates ADD has an incorrect data type: {}", v2));
|
||||
}
|
||||
} else {
|
||||
std::string v1_type = get_item_type_string(*v1);
|
||||
std::string v2_type = get_item_type_string(v2);
|
||||
if (v2_type != v1_type) {
|
||||
throw api_error::validation(format("Operand type mismatch in AttributeUpdates ADD. Expected {}, got {}", v1_type, v2_type));
|
||||
}
|
||||
if (v1_type == "N") {
|
||||
do_update(std::move(column_name), number_add(*v1, v2));
|
||||
} else if (v1_type == "SS" || v1_type == "NS" || v1_type == "BS") {
|
||||
do_update(std::move(column_name), set_sum(*v1, v2));
|
||||
} else if (v1_type == "L") {
|
||||
// The DynamoDB documentation doesn't say it supports
|
||||
// lists in ADD operations, but it turns out that it
|
||||
// does. Interestingly, this is only true for
|
||||
// AttributeUpdates (this code) - the similar ADD
|
||||
// in UpdateExpression doesn't support lists.
|
||||
do_update(std::move(column_name), list_concatenate(*v1, v2));
|
||||
} else {
|
||||
throw api_error::validation(format("An operand in the AttributeUpdates ADD has an incorrect data type: {}", *v1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// FIXME: need to support "ADD" as well.
|
||||
throw api_error::validation(
|
||||
format("Unknown Action value '{}' in AttributeUpdates", action));
|
||||
}
|
||||
@@ -2843,7 +2753,7 @@ static rjson::value describe_item(schema_ptr schema,
|
||||
return rjson::empty_object();
|
||||
}
|
||||
rjson::value item_descr = rjson::empty_object();
|
||||
rjson::add(item_descr, "Item", std::move(*opt_item));
|
||||
rjson::set(item_descr, "Item", std::move(*opt_item));
|
||||
return item_descr;
|
||||
}
|
||||
|
||||
@@ -2907,7 +2817,7 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
struct table_requests {
|
||||
schema_ptr schema;
|
||||
db::consistency_level cl;
|
||||
::shared_ptr<const alternator::attrs_to_get> attrs_to_get;
|
||||
attrs_to_get attrs_to_get;
|
||||
struct single_request {
|
||||
partition_key pk;
|
||||
clustering_key ck;
|
||||
@@ -2922,7 +2832,7 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
tracing::add_table_name(trace_state, sstring(executor::KEYSPACE_NAME_PREFIX) + rs.schema->cf_name(), rs.schema->cf_name());
|
||||
rs.cl = get_read_consistency(it->value);
|
||||
std::unordered_set<std::string> used_attribute_names;
|
||||
rs.attrs_to_get = ::make_shared<const attrs_to_get>(calculate_attrs_to_get(it->value, used_attribute_names));
|
||||
rs.attrs_to_get = calculate_attrs_to_get(it->value, used_attribute_names);
|
||||
verify_all_are_used(request, "ExpressionAttributeNames", used_attribute_names, "GetItem");
|
||||
auto& keys = (it->value)["Keys"];
|
||||
for (const rjson::value& key : keys.GetArray()) {
|
||||
@@ -2952,7 +2862,7 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
future<std::tuple<std::string, std::optional<rjson::value>>> f = _proxy.query(rs.schema, std::move(command), std::move(partition_ranges), rs.cl,
|
||||
service::storage_proxy::coordinator_query_options(executor::default_timeout(), permit, client_state, trace_state)).then(
|
||||
[schema = rs.schema, partition_slice = std::move(partition_slice), selection = std::move(selection), attrs_to_get = rs.attrs_to_get] (service::storage_proxy::coordinator_query_result qr) mutable {
|
||||
std::optional<rjson::value> json = describe_single_item(schema, partition_slice, *selection, *qr.query_result, *attrs_to_get);
|
||||
std::optional<rjson::value> json = describe_single_item(schema, partition_slice, *selection, *qr.query_result, std::move(attrs_to_get));
|
||||
return make_ready_future<std::tuple<std::string, std::optional<rjson::value>>>(
|
||||
std::make_tuple(schema->cf_name(), std::move(json)));
|
||||
});
|
||||
@@ -2970,11 +2880,11 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
return when_all_succeed(response_futures.begin(), response_futures.end()).then(
|
||||
[] (std::vector<std::tuple<std::string, std::optional<rjson::value>>> responses) {
|
||||
rjson::value response = rjson::empty_object();
|
||||
rjson::add(response, "Responses", rjson::empty_object());
|
||||
rjson::add(response, "UnprocessedKeys", rjson::empty_object());
|
||||
rjson::set(response, "Responses", rjson::empty_object());
|
||||
rjson::set(response, "UnprocessedKeys", rjson::empty_object());
|
||||
for (auto& t : responses) {
|
||||
if (!response["Responses"].HasMember(std::get<0>(t).c_str())) {
|
||||
rjson::add_with_string_name(response["Responses"], std::get<0>(t), rjson::empty_array());
|
||||
rjson::set_with_string_name(response["Responses"], std::get<0>(t), rjson::empty_array());
|
||||
}
|
||||
if (std::get<1>(t)) {
|
||||
rjson::push_back(response["Responses"][std::get<0>(t)], std::move(*std::get<1>(t)));
|
||||
@@ -3167,10 +3077,10 @@ public:
|
||||
if (column_name != executor::ATTRS_COLUMN_NAME) {
|
||||
if (_attrs_to_get.empty() || _attrs_to_get.contains(column_name) || _extra_filter_attrs.contains(column_name)) {
|
||||
if (!_item.HasMember(column_name.c_str())) {
|
||||
rjson::add_with_string_name(_item, column_name, rjson::empty_object());
|
||||
rjson::set_with_string_name(_item, column_name, rjson::empty_object());
|
||||
}
|
||||
rjson::value& field = _item[column_name.c_str()];
|
||||
rjson::add_with_string_name(field, type_to_string((*_column_it)->type), json_key_column_value(bv, **_column_it));
|
||||
rjson::set_with_string_name(field, type_to_string((*_column_it)->type), json_key_column_value(bv, **_column_it));
|
||||
}
|
||||
} else {
|
||||
auto deserialized = attrs_type()->deserialize(bv, cql_serialization_format::latest());
|
||||
@@ -3185,7 +3095,7 @@ public:
|
||||
// need the other parts (it was easier for us to keep
|
||||
// extra_filter_attrs at top-level granularity). We'll
|
||||
// filter the unneeded parts after item filtering.
|
||||
rjson::add_with_string_name(_item, attr_name, deserialize_item(value));
|
||||
rjson::set_with_string_name(_item, attr_name, deserialize_item(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3234,9 +3144,9 @@ static rjson::value describe_items(schema_ptr schema, const query::partition_sli
|
||||
auto scanned_count = visitor.get_scanned_count();
|
||||
rjson::value items = std::move(visitor).get_items();
|
||||
rjson::value items_descr = rjson::empty_object();
|
||||
rjson::add(items_descr, "Count", rjson::value(items.Size()));
|
||||
rjson::add(items_descr, "ScannedCount", rjson::value(scanned_count));
|
||||
rjson::add(items_descr, "Items", std::move(items));
|
||||
rjson::set(items_descr, "Count", rjson::value(items.Size()));
|
||||
rjson::set(items_descr, "ScannedCount", rjson::value(scanned_count));
|
||||
rjson::set(items_descr, "Items", std::move(items));
|
||||
return items_descr;
|
||||
}
|
||||
|
||||
@@ -3245,9 +3155,9 @@ static rjson::value encode_paging_state(const schema& schema, const service::pag
|
||||
std::vector<bytes> exploded_pk = paging_state.get_partition_key().explode();
|
||||
auto exploded_pk_it = exploded_pk.begin();
|
||||
for (const column_definition& cdef : schema.partition_key_columns()) {
|
||||
rjson::add_with_string_name(last_evaluated_key, std::string_view(cdef.name_as_text()), rjson::empty_object());
|
||||
rjson::set_with_string_name(last_evaluated_key, std::string_view(cdef.name_as_text()), rjson::empty_object());
|
||||
rjson::value& key_entry = last_evaluated_key[cdef.name_as_text()];
|
||||
rjson::add_with_string_name(key_entry, type_to_string(cdef.type), json_key_column_value(*exploded_pk_it, cdef));
|
||||
rjson::set_with_string_name(key_entry, type_to_string(cdef.type), json_key_column_value(*exploded_pk_it, cdef));
|
||||
++exploded_pk_it;
|
||||
}
|
||||
auto ck = paging_state.get_clustering_key();
|
||||
@@ -3255,9 +3165,9 @@ static rjson::value encode_paging_state(const schema& schema, const service::pag
|
||||
auto exploded_ck = ck->explode();
|
||||
auto exploded_ck_it = exploded_ck.begin();
|
||||
for (const column_definition& cdef : schema.clustering_key_columns()) {
|
||||
rjson::add_with_string_name(last_evaluated_key, std::string_view(cdef.name_as_text()), rjson::empty_object());
|
||||
rjson::set_with_string_name(last_evaluated_key, std::string_view(cdef.name_as_text()), rjson::empty_object());
|
||||
rjson::value& key_entry = last_evaluated_key[cdef.name_as_text()];
|
||||
rjson::add_with_string_name(key_entry, type_to_string(cdef.type), json_key_column_value(*exploded_ck_it, cdef));
|
||||
rjson::set_with_string_name(key_entry, type_to_string(cdef.type), json_key_column_value(*exploded_ck_it, cdef));
|
||||
++exploded_ck_it;
|
||||
}
|
||||
}
|
||||
@@ -3304,7 +3214,7 @@ static future<executor::request_return_type> do_query(service::storage_proxy& pr
|
||||
auto query_state_ptr = std::make_unique<service::query_state>(client_state, trace_state, std::move(permit));
|
||||
|
||||
command->slice.options.set<query::partition_slice::option::allow_short_read>();
|
||||
auto query_options = std::make_unique<cql3::query_options>(cl, std::vector<cql3::raw_value>{});
|
||||
auto query_options = std::make_unique<cql3::query_options>(cl, infinite_timeout_config, std::vector<cql3::raw_value>{});
|
||||
query_options = std::make_unique<cql3::query_options>(std::move(query_options), std::move(paging_state));
|
||||
auto p = service::pager::query_pagers::pager(schema, selection, *query_state_ptr, *query_options, command, std::move(partition_ranges), nullptr);
|
||||
|
||||
@@ -3321,7 +3231,7 @@ static future<executor::request_return_type> do_query(service::storage_proxy& pr
|
||||
bool has_filter = filter;
|
||||
auto items = describe_items(schema, partition_slice, *selection, std::move(rs), std::move(attrs_to_get), std::move(filter));
|
||||
if (paging_state) {
|
||||
rjson::add(items, "LastEvaluatedKey", encode_paging_state(*schema, *paging_state));
|
||||
rjson::set(items, "LastEvaluatedKey", encode_paging_state(*schema, *paging_state));
|
||||
}
|
||||
if (has_filter){
|
||||
cql_stats.filtered_rows_read_total += p->stats().rows_read_total;
|
||||
@@ -3404,16 +3314,7 @@ future<executor::request_return_type> executor::scan(client_state& client_state,
|
||||
|
||||
dht::partition_range_vector partition_ranges;
|
||||
if (segment) {
|
||||
auto range = get_range_for_segment(*segment, *total_segments);
|
||||
if (exclusive_start_key) {
|
||||
auto ring_pos = dht::ring_position{dht::decorate_key(*schema, pk_from_json(*exclusive_start_key, schema))};
|
||||
if (!range.contains(ring_pos, dht::ring_position_comparator(*schema))) {
|
||||
return make_ready_future<request_return_type>(api_error::validation(
|
||||
format("The provided starting key is invalid: Invalid ExclusiveStartKey. Please use ExclusiveStartKey "
|
||||
"with correct Segment. TotalSegments: {} Segment: {}", *total_segments, *segment)));
|
||||
}
|
||||
}
|
||||
partition_ranges.push_back(range);
|
||||
partition_ranges.push_back(get_range_for_segment(*segment, *total_segments));
|
||||
} else {
|
||||
partition_ranges.push_back(dht::partition_range::make_open_ended_both_sides());
|
||||
}
|
||||
@@ -3442,7 +3343,7 @@ static dht::partition_range calculate_pk_bound(schema_ptr schema, const column_d
|
||||
throw api_error::validation(format("A single attribute is required for a hash key EQ restriction: {}", attrs));
|
||||
}
|
||||
bytes raw_value = get_key_from_typed_value(attrs[0], pk_cdef);
|
||||
partition_key pk = partition_key::from_singular_bytes(*schema, std::move(raw_value));
|
||||
partition_key pk = partition_key::from_singular(*schema, pk_cdef.type->deserialize(raw_value));
|
||||
auto decorated_key = dht::decorate_key(*schema, pk);
|
||||
return dht::partition_range(decorated_key);
|
||||
}
|
||||
@@ -3746,7 +3647,7 @@ calculate_bounds_condition_expression(schema_ptr schema,
|
||||
"KeyConditionExpression allows only one condition for each key");
|
||||
}
|
||||
bytes raw_value = get_constant_value(cond._values[!toplevel_ind], pk_cdef);
|
||||
partition_key pk = partition_key::from_singular_bytes(*schema, std::move(raw_value));
|
||||
partition_key pk = partition_key::from_singular(*schema, pk_cdef.type->deserialize(raw_value));
|
||||
auto decorated_key = dht::decorate_key(*schema, pk);
|
||||
partition_ranges.push_back(dht::partition_range(decorated_key));
|
||||
} else if (ck_cdef && sstring(key) == ck_cdef->name_as_text()) {
|
||||
@@ -3929,7 +3830,7 @@ future<executor::request_return_type> executor::list_tables(client_state& client
|
||||
});
|
||||
|
||||
rjson::value response = rjson::empty_object();
|
||||
rjson::add(response, "TableNames", rjson::empty_array());
|
||||
rjson::set(response, "TableNames", rjson::empty_array());
|
||||
rjson::value& all_tables = response["TableNames"];
|
||||
|
||||
//TODO(sarna): Dynamo doesn't declare any ordering when listing tables,
|
||||
@@ -3953,7 +3854,7 @@ future<executor::request_return_type> executor::list_tables(client_state& client
|
||||
|
||||
if (table_names_it != table_names.end()) {
|
||||
auto& last_table_name = *std::prev(all_tables.End());
|
||||
rjson::add(response, "LastEvaluatedTableName", rjson::copy(last_table_name));
|
||||
rjson::set(response, "LastEvaluatedTableName", rjson::copy(last_table_name));
|
||||
}
|
||||
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(response)));
|
||||
@@ -3971,17 +3872,17 @@ future<executor::request_return_type> executor::describe_endpoints(client_state&
|
||||
if (host_header.empty()) {
|
||||
return make_ready_future<request_return_type>(api_error::validation("DescribeEndpoints needs a 'Host:' header in request"));
|
||||
}
|
||||
rjson::add(response, "Endpoints", rjson::empty_array());
|
||||
rjson::set(response, "Endpoints", rjson::empty_array());
|
||||
rjson::push_back(response["Endpoints"], rjson::empty_object());
|
||||
rjson::add(response["Endpoints"][0], "Address", rjson::from_string(host_header));
|
||||
rjson::add(response["Endpoints"][0], "CachePeriodInMinutes", rjson::value(1440));
|
||||
rjson::set(response["Endpoints"][0], "Address", rjson::from_string(host_header));
|
||||
rjson::set(response["Endpoints"][0], "CachePeriodInMinutes", rjson::value(1440));
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(response)));
|
||||
}
|
||||
|
||||
static std::map<sstring, sstring> get_network_topology_options(gms::gossiper& gossiper, int rf) {
|
||||
static std::map<sstring, sstring> get_network_topology_options(int rf) {
|
||||
std::map<sstring, sstring> options;
|
||||
sstring rf_str = std::to_string(rf);
|
||||
for (const gms::inet_address& addr : gossiper.get_live_members()) {
|
||||
for (const gms::inet_address& addr : gms::get_local_gossiper().get_live_members()) {
|
||||
options.emplace(locator::i_endpoint_snitch::get_local_snitch_ptr()->get_datacenter(addr), rf_str);
|
||||
};
|
||||
return options;
|
||||
@@ -3995,19 +3896,35 @@ static std::map<sstring, sstring> get_network_topology_options(gms::gossiper& go
|
||||
// manually create the keyspace to override this predefined behavior.
|
||||
future<> executor::create_keyspace(std::string_view keyspace_name) {
|
||||
sstring keyspace_name_str(keyspace_name);
|
||||
return gms::get_all_endpoint_count(_gossiper).then([this, keyspace_name_str = std::move(keyspace_name_str)] (int endpoint_count) {
|
||||
return gms::get_all_endpoint_count().then([this, keyspace_name_str = std::move(keyspace_name_str)] (int endpoint_count) {
|
||||
int rf = 3;
|
||||
if (endpoint_count < rf) {
|
||||
rf = 1;
|
||||
elogger.warn("Creating keyspace '{}' for Alternator with unsafe RF={} because cluster only has {} nodes.",
|
||||
keyspace_name_str, rf, endpoint_count);
|
||||
}
|
||||
auto opts = get_network_topology_options(_gossiper, rf);
|
||||
auto opts = get_network_topology_options(rf);
|
||||
auto ksm = keyspace_metadata::new_keyspace(keyspace_name_str, "org.apache.cassandra.locator.NetworkTopologyStrategy", std::move(opts), true);
|
||||
return _mm.announce_new_keyspace(ksm, api::new_timestamp());
|
||||
});
|
||||
}
|
||||
|
||||
static tracing::trace_state_ptr create_tracing_session() {
|
||||
tracing::trace_state_props_set props;
|
||||
props.set<tracing::trace_state_props::full_tracing>();
|
||||
return tracing::tracing::get_local_tracing_instance().create_session(tracing::trace_type::QUERY, props);
|
||||
}
|
||||
|
||||
tracing::trace_state_ptr executor::maybe_trace_query(client_state& client_state, sstring_view op, sstring_view query) {
|
||||
tracing::trace_state_ptr trace_state;
|
||||
if (tracing::tracing::get_local_tracing_instance().trace_next_query()) {
|
||||
trace_state = create_tracing_session();
|
||||
tracing::add_query(trace_state, query);
|
||||
tracing::begin(trace_state, format("Alternator {}", op), client_state.get_client_address());
|
||||
}
|
||||
return trace_state;
|
||||
}
|
||||
|
||||
future<> executor::start() {
|
||||
// Currently, nothing to do on initialization. We delay the keyspace
|
||||
// creation (create_keyspace()) until a table is actually created.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -27,9 +27,9 @@
|
||||
#include <seastar/json/json_elements.hh>
|
||||
#include <seastar/core/sharded.hh>
|
||||
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "service/migration_manager.hh"
|
||||
#include "service/client_state.hh"
|
||||
#include "service_permit.hh"
|
||||
#include "db/timeout_clock.hh"
|
||||
|
||||
#include "alternator/error.hh"
|
||||
@@ -50,17 +50,7 @@ namespace cql3::selection {
|
||||
}
|
||||
|
||||
namespace service {
|
||||
class storage_proxy;
|
||||
}
|
||||
|
||||
namespace cdc {
|
||||
class metadata;
|
||||
}
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
class storage_service;
|
||||
}
|
||||
|
||||
namespace alternator {
|
||||
@@ -84,10 +74,6 @@ namespace parsed {
|
||||
class path;
|
||||
};
|
||||
|
||||
const std::map<sstring, sstring>& get_tags_of_table(schema_ptr schema);
|
||||
future<> update_tags(service::migration_manager& mm, schema_ptr schema, std::map<sstring, sstring>&& tags_map);
|
||||
schema_ptr get_table(service::storage_proxy& proxy, const rjson::value& request);
|
||||
|
||||
// An attribute_path_map object is used to hold data for various attributes
|
||||
// paths (parsed::path) in a hierarchy of attribute paths. Each attribute path
|
||||
// has a root attribute, and then modified by member and index operators -
|
||||
@@ -119,12 +105,17 @@ template<typename T>
|
||||
class attribute_path_map_node {
|
||||
public:
|
||||
using data_t = T;
|
||||
// We need the extra unique_ptr<> here because libstdc++ unordered_map
|
||||
// doesn't work with incomplete types :-(
|
||||
using members_t = std::unordered_map<std::string, std::unique_ptr<attribute_path_map_node<T>>>;
|
||||
// We need the extra shared_ptr<> here because libstdc++ unordered_map
|
||||
// doesn't work with incomplete types :-( We couldn't use lw_shared_ptr<>
|
||||
// because it doesn't work for incomplete types either. We couldn't use
|
||||
// std::unique_ptr<> because it makes the entire object uncopyable. We
|
||||
// don't often need to copy such a map, but we do have some code that
|
||||
// copies an attrs_to_get object, and is hard to find and remove.
|
||||
// The shared_ptr should never be null.
|
||||
using members_t = std::unordered_map<std::string, seastar::shared_ptr<attribute_path_map_node<T>>>;
|
||||
// The indexes list is sorted because DynamoDB requires handling writes
|
||||
// beyond the end of a list in index order.
|
||||
using indexes_t = std::map<unsigned, std::unique_ptr<attribute_path_map_node<T>>>;
|
||||
using indexes_t = std::map<unsigned, seastar::shared_ptr<attribute_path_map_node<T>>>;
|
||||
// The prohibition on "overlap" and "conflict" explained above means
|
||||
// That only one of data, members or indexes is non-empty.
|
||||
std::optional<std::variant<data_t, members_t, indexes_t>> _content;
|
||||
@@ -150,11 +141,10 @@ using attrs_to_get = attribute_path_map<std::monostate>;
|
||||
|
||||
|
||||
class executor : public peering_sharded_service<executor> {
|
||||
gms::gossiper& _gossiper;
|
||||
service::storage_proxy& _proxy;
|
||||
service::migration_manager& _mm;
|
||||
db::system_distributed_keyspace& _sdks;
|
||||
cdc::metadata& _cdc_metadata;
|
||||
service::storage_service& _ss;
|
||||
// An smp_service_group to be used for limiting the concurrency when
|
||||
// forwarding Alternator request between shards - if necessary for LWT.
|
||||
smp_service_group _ssg;
|
||||
@@ -167,8 +157,8 @@ public:
|
||||
static constexpr auto KEYSPACE_NAME_PREFIX = "alternator_";
|
||||
static constexpr std::string_view INTERNAL_TABLE_PREFIX = ".scylla.alternator.";
|
||||
|
||||
executor(gms::gossiper& gossiper, service::storage_proxy& proxy, service::migration_manager& mm, db::system_distributed_keyspace& sdks, cdc::metadata& cdc_metadata, smp_service_group ssg)
|
||||
: _gossiper(gossiper), _proxy(proxy), _mm(mm), _sdks(sdks), _cdc_metadata(cdc_metadata), _ssg(ssg) {}
|
||||
executor(service::storage_proxy& proxy, service::migration_manager& mm, db::system_distributed_keyspace& sdks, service::storage_service& ss, smp_service_group ssg)
|
||||
: _proxy(proxy), _mm(mm), _sdks(sdks), _ss(ss), _ssg(ssg) {}
|
||||
|
||||
future<request_return_type> create_table(client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> describe_table(client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value request);
|
||||
@@ -187,8 +177,6 @@ public:
|
||||
future<request_return_type> tag_resource(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> untag_resource(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> list_tags_of_resource(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> update_time_to_live(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> describe_time_to_live(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> list_streams(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> describe_stream(client_state& client_state, service_permit permit, rjson::value request);
|
||||
future<request_return_type> get_shard_iterator(client_state& client_state, service_permit permit, rjson::value request);
|
||||
@@ -199,6 +187,8 @@ public:
|
||||
|
||||
future<> create_keyspace(std::string_view keyspace_name);
|
||||
|
||||
static tracing::trace_state_ptr maybe_trace_query(client_state& client_state, sstring_view op, sstring_view query);
|
||||
|
||||
static sstring table_name(const schema&);
|
||||
static db::timeout_clock::time_point default_timeout();
|
||||
static void set_default_timeout(db::timeout_clock::duration timeout);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "expressions.hh"
|
||||
#include "serialization.hh"
|
||||
#include "utils/base64.hh"
|
||||
#include "base64.hh"
|
||||
#include "conditions.hh"
|
||||
#include "alternator/expressionsLexer.hpp"
|
||||
#include "alternator/expressionsParser.hpp"
|
||||
@@ -428,6 +428,24 @@ void for_condition_expression_on(const parsed::condition_expression& ce, const n
|
||||
// expression. The parsed expression is assumed to have been "resolved", with
|
||||
// the matching resolve_* function.
|
||||
|
||||
// Take two JSON-encoded list values (remember that a list value is
|
||||
// {"L": [...the actual list]}) and return the concatenation, again as
|
||||
// a list value.
|
||||
static rjson::value list_concatenate(const rjson::value& v1, const rjson::value& v2) {
|
||||
const rjson::value* list1 = unwrap_list(v1);
|
||||
const rjson::value* list2 = unwrap_list(v2);
|
||||
if (!list1 || !list2) {
|
||||
throw api_error::validation("UpdateExpression: list_append() given a non-list");
|
||||
}
|
||||
rjson::value cat = rjson::copy(*list1);
|
||||
for (const auto& a : list2->GetArray()) {
|
||||
rjson::push_back(cat, rjson::copy(a));
|
||||
}
|
||||
rjson::value ret = rjson::empty_object();
|
||||
rjson::set(ret, "L", std::move(cat));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// calculate_size() is ConditionExpression's size() function, i.e., it takes
|
||||
// a JSON-encoded value and returns its "size" as defined differently for the
|
||||
// different types - also as a JSON-encoded number.
|
||||
@@ -464,11 +482,11 @@ static rjson::value calculate_size(const rjson::value& v) {
|
||||
ret = base64_decoded_len(rjson::to_string_view(it->value));
|
||||
} else {
|
||||
rjson::value json_ret = rjson::empty_object();
|
||||
rjson::add(json_ret, "null", rjson::value(true));
|
||||
rjson::set(json_ret, "null", rjson::value(true));
|
||||
return json_ret;
|
||||
}
|
||||
rjson::value json_ret = rjson::empty_object();
|
||||
rjson::add(json_ret, "N", rjson::from_string(std::to_string(ret)));
|
||||
rjson::set(json_ret, "N", rjson::from_string(std::to_string(ret)));
|
||||
return json_ret;
|
||||
}
|
||||
|
||||
@@ -487,7 +505,7 @@ static const rjson::value& calculate_value(const parsed::constant& c) {
|
||||
|
||||
static rjson::value to_bool_json(bool b) {
|
||||
rjson::value json_ret = rjson::empty_object();
|
||||
rjson::add(json_ret, "BOOL", rjson::value(b));
|
||||
rjson::set(json_ret, "BOOL", rjson::value(b));
|
||||
return json_ret;
|
||||
}
|
||||
|
||||
@@ -512,11 +530,7 @@ std::unordered_map<std::string_view, function_handler_type*> function_handlers {
|
||||
}
|
||||
rjson::value v1 = calculate_value(f._parameters[0], caller, previous_item);
|
||||
rjson::value v2 = calculate_value(f._parameters[1], caller, previous_item);
|
||||
rjson::value ret = list_concatenate(v1, v2);
|
||||
if (ret.IsNull()) {
|
||||
throw api_error::validation("UpdateExpression: list_append() given a non-list");
|
||||
}
|
||||
return ret;
|
||||
return list_concatenate(v1, v2);
|
||||
}
|
||||
},
|
||||
{"if_not_exists", [] (calculate_value_caller caller, const rjson::value* previous_item, const parsed::value::function_call& f) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*
|
||||
* This file is part of Scylla. See the LICENSE.PROPRIETARY file in the
|
||||
* top-level directory for licensing information.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-present ScyllaDB
|
||||
* Copyright 2020 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -22,7 +22,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "seastarx.hh"
|
||||
#include "service/paxos/cas_request.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include "executor.hh"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -19,8 +19,7 @@
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "utils/base64.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include "base64.hh"
|
||||
#include "log.hh"
|
||||
#include "serialization.hh"
|
||||
#include "error.hh"
|
||||
@@ -69,7 +68,7 @@ struct from_json_visitor {
|
||||
bo.write(t.from_string(rjson::to_string_view(v)));
|
||||
}
|
||||
void operator()(const bytes_type_impl& t) const {
|
||||
bo.write(rjson::base64_decode(v));
|
||||
bo.write(base64_decode(v));
|
||||
}
|
||||
void operator()(const boolean_type_impl& t) const {
|
||||
bo.write(boolean_type->decompose(v.GetBool()));
|
||||
@@ -115,18 +114,18 @@ struct to_json_visitor {
|
||||
void operator()(const decimal_type_impl& t) const {
|
||||
auto s = to_json_string(*decimal_type, bytes(bv));
|
||||
//FIXME(sarna): unnecessary copy
|
||||
rjson::add_with_string_name(deserialized, type_ident, rjson::from_string(s));
|
||||
rjson::set_with_string_name(deserialized, type_ident, rjson::from_string(s));
|
||||
}
|
||||
void operator()(const string_type_impl& t) {
|
||||
rjson::add_with_string_name(deserialized, type_ident, rjson::from_string(reinterpret_cast<const char *>(bv.data()), bv.size()));
|
||||
rjson::set_with_string_name(deserialized, type_ident, rjson::from_string(reinterpret_cast<const char *>(bv.data()), bv.size()));
|
||||
}
|
||||
void operator()(const bytes_type_impl& t) const {
|
||||
std::string b64 = base64_encode(bv);
|
||||
rjson::add_with_string_name(deserialized, type_ident, rjson::from_string(b64));
|
||||
rjson::set_with_string_name(deserialized, type_ident, rjson::from_string(b64));
|
||||
}
|
||||
// default
|
||||
void operator()(const abstract_type& t) const {
|
||||
rjson::add_with_string_name(deserialized, type_ident, rjson::parse(to_json_string(t, bytes(bv))));
|
||||
rjson::set_with_string_name(deserialized, type_ident, rjson::parse(to_json_string(t, bytes(bv))));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -197,7 +196,7 @@ bytes get_key_from_typed_value(const rjson::value& key_typed_value, const column
|
||||
format("The AttributeValue for a key attribute cannot contain an empty string value. Key: {}", column.name_as_text()));
|
||||
}
|
||||
if (column.type == bytes_type) {
|
||||
return rjson::base64_decode(it->value);
|
||||
return base64_decode(it->value);
|
||||
} else {
|
||||
return column.type->from_string(rjson::to_string_view(it->value));
|
||||
}
|
||||
@@ -302,7 +301,7 @@ rjson::value number_add(const rjson::value& v1, const rjson::value& v2) {
|
||||
auto n2 = unwrap_number(v2, "UpdateExpression");
|
||||
rjson::value ret = rjson::empty_object();
|
||||
std::string str_ret = std::string((n1 + n2).to_string());
|
||||
rjson::add(ret, "N", rjson::from_string(str_ret));
|
||||
rjson::set(ret, "N", rjson::from_string(str_ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -311,7 +310,7 @@ rjson::value number_subtract(const rjson::value& v1, const rjson::value& v2) {
|
||||
auto n2 = unwrap_number(v2, "UpdateExpression");
|
||||
rjson::value ret = rjson::empty_object();
|
||||
std::string str_ret = std::string((n1 - n2).to_string());
|
||||
rjson::add(ret, "N", rjson::from_string(str_ret));
|
||||
rjson::set(ret, "N", rjson::from_string(str_ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -337,7 +336,7 @@ rjson::value set_sum(const rjson::value& v1, const rjson::value& v2) {
|
||||
}
|
||||
}
|
||||
rjson::value ret = rjson::empty_object();
|
||||
rjson::add_with_string_name(ret, set1_type, std::move(sum));
|
||||
rjson::set_with_string_name(ret, set1_type, std::move(sum));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -365,7 +364,7 @@ std::optional<rjson::value> set_diff(const rjson::value& v1, const rjson::value&
|
||||
return std::nullopt;
|
||||
}
|
||||
rjson::value ret = rjson::empty_object();
|
||||
rjson::add_with_string_name(ret, set1_type, rjson::empty_array());
|
||||
rjson::set_with_string_name(ret, set1_type, rjson::empty_array());
|
||||
rjson::value& result_set = ret[set1_type];
|
||||
for (const auto& a : set1_raw) {
|
||||
rjson::push_back(result_set, rjson::copy(a));
|
||||
@@ -373,23 +372,4 @@ std::optional<rjson::value> set_diff(const rjson::value& v1, const rjson::value&
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Take two JSON-encoded list values (remember that a list value is
|
||||
// {"L": [...the actual list]}) and return the concatenation, again as
|
||||
// a list value.
|
||||
// Returns a null value if one of the arguments is not actually a list.
|
||||
rjson::value list_concatenate(const rjson::value& v1, const rjson::value& v2) {
|
||||
const rjson::value* list1 = unwrap_list(v1);
|
||||
const rjson::value* list2 = unwrap_list(v2);
|
||||
if (!list1 || !list2) {
|
||||
return rjson::null_value();
|
||||
}
|
||||
rjson::value cat = rjson::copy(*list1);
|
||||
for (const auto& a : list2->GetArray()) {
|
||||
rjson::push_back(cat, rjson::copy(a));
|
||||
}
|
||||
rjson::value ret = rjson::empty_object();
|
||||
rjson::add(ret, "L", std::move(cat));
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -85,11 +85,5 @@ rjson::value set_sum(const rjson::value& v1, const rjson::value& v2);
|
||||
// DynamoDB does not allow empty sets, so if resulting set is empty, return
|
||||
// an unset optional instead.
|
||||
std::optional<rjson::value> set_diff(const rjson::value& v1, const rjson::value& v2);
|
||||
// Take two JSON-encoded list values (remember that a list value is
|
||||
// {"L": [...the actual list]}) and return the concatenation, again as
|
||||
// a list value.
|
||||
// Returns a null value if one of the arguments is not actually a list.
|
||||
rjson::value list_concatenate(const rjson::value& v1, const rjson::value& v2);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -22,20 +22,15 @@
|
||||
#include "alternator/server.hh"
|
||||
#include "log.hh"
|
||||
#include <seastar/http/function_handlers.hh>
|
||||
#include <seastar/http/short_streams.hh>
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <seastar/json/json_elements.hh>
|
||||
#include <seastar/util/defer.hh>
|
||||
#include "seastarx.hh"
|
||||
#include "error.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include "auth.hh"
|
||||
#include <cctype>
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "locator/snitch_base.hh"
|
||||
#include "gms/gossiper.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "service/storage_service.hh"
|
||||
#include "utils/overloaded_functor.hh"
|
||||
#include "utils/fb_utilities.hh"
|
||||
|
||||
static logging::logger slogger("alternator-server");
|
||||
|
||||
@@ -64,40 +59,6 @@ inline std::vector<std::string_view> split(std::string_view text, char separator
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Handle CORS (Cross-origin resource sharing) in the HTTP request:
|
||||
// If the request has the "Origin" header specifying where the script which
|
||||
// makes this request comes from, we need to reply with the header
|
||||
// "Access-Control-Allow-Origin: *" saying that this (and any) origin is fine.
|
||||
// Additionally, if preflight==true (i.e., this is an OPTIONS request),
|
||||
// the script can also "request" in headers that the server allows it to use
|
||||
// some HTTP methods and headers in the followup request, and the server
|
||||
// should respond by "allowing" them in the response headers.
|
||||
// We also add the header "Access-Control-Expose-Headers" to let the script
|
||||
// access additional headers in the response.
|
||||
// This handle_CORS() should be used when handling any HTTP method - both the
|
||||
// usual GET and POST, and also the "preflight" OPTIONS method.
|
||||
static void handle_CORS(const request& req, reply& rep, bool preflight) {
|
||||
if (!req.get_header("origin").empty()) {
|
||||
rep.add_header("Access-Control-Allow-Origin", "*");
|
||||
// This is the list that DynamoDB returns for expose headers. I am
|
||||
// not sure why not just return "*" here, what's the risk?
|
||||
rep.add_header("Access-Control-Expose-Headers", "x-amzn-RequestId,x-amzn-ErrorType,x-amzn-ErrorMessage,Date");
|
||||
if (preflight) {
|
||||
sstring s = req.get_header("Access-Control-Request-Headers");
|
||||
if (!s.empty()) {
|
||||
rep.add_header("Access-Control-Allow-Headers", std::move(s));
|
||||
}
|
||||
s = req.get_header("Access-Control-Request-Method");
|
||||
if (!s.empty()) {
|
||||
rep.add_header("Access-Control-Allow-Methods", std::move(s));
|
||||
}
|
||||
// Our CORS response never change anyway, let the browser cache it
|
||||
// for two hours (Chrome's maximum):
|
||||
rep.add_header("Access-Control-Max-Age", "7200");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DynamoDB HTTP error responses are structured as follows
|
||||
// https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.Errors.html
|
||||
// Our handlers throw an exception to report an error. If the exception
|
||||
@@ -153,7 +114,6 @@ public:
|
||||
api_handler(const api_handler&) = default;
|
||||
future<std::unique_ptr<reply>> handle(const sstring& path,
|
||||
std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
|
||||
handle_CORS(*req, *rep, false);
|
||||
return _f_handle(std::move(req), std::move(rep)).then(
|
||||
[this](std::unique_ptr<reply> rep) {
|
||||
rep->set_mime_type("application/x-amz-json-1.0");
|
||||
@@ -190,7 +150,6 @@ public:
|
||||
health_handler(seastar::gate& pending_requests) : gated_handler(pending_requests) {}
|
||||
protected:
|
||||
virtual future<std::unique_ptr<reply>> do_handle(const sstring& path, std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
|
||||
handle_CORS(*req, *rep, false);
|
||||
rep->set_status(reply::status_type::ok);
|
||||
rep->write_body("txt", format("healthy: {}", req->get_header("Host")));
|
||||
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
|
||||
@@ -198,25 +157,21 @@ protected:
|
||||
};
|
||||
|
||||
class local_nodelist_handler : public gated_handler {
|
||||
service::storage_proxy& _proxy;
|
||||
gms::gossiper& _gossiper;
|
||||
public:
|
||||
local_nodelist_handler(seastar::gate& pending_requests, service::storage_proxy& proxy, gms::gossiper& gossiper)
|
||||
: gated_handler(pending_requests)
|
||||
, _proxy(proxy)
|
||||
, _gossiper(gossiper) {}
|
||||
local_nodelist_handler(seastar::gate& pending_requests) : gated_handler(pending_requests) {}
|
||||
protected:
|
||||
virtual future<std::unique_ptr<reply>> do_handle(const sstring& path, std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
|
||||
rjson::value results = rjson::empty_array();
|
||||
// It's very easy to get a list of all live nodes on the cluster,
|
||||
// using _gossiper().get_live_members(). But getting
|
||||
// using gms::get_local_gossiper().get_live_members(). But getting
|
||||
// just the list of live nodes in this DC needs more elaborate code:
|
||||
sstring local_dc = locator::i_endpoint_snitch::get_local_snitch_ptr()->get_datacenter(
|
||||
utils::fb_utilities::get_broadcast_address());
|
||||
std::unordered_set<gms::inet_address> local_dc_nodes =
|
||||
_proxy.get_token_metadata_ptr()->get_topology().get_datacenter_endpoints().at(local_dc);
|
||||
service::get_local_storage_service().get_token_metadata().
|
||||
get_topology().get_datacenter_endpoints().at(local_dc);
|
||||
for (auto& ip : local_dc_nodes) {
|
||||
if (_gossiper.is_alive(ip)) {
|
||||
if (gms::get_local_gossiper().is_alive(ip)) {
|
||||
rjson::push_back(results, rjson::from_string(ip.to_sstring()));
|
||||
}
|
||||
}
|
||||
@@ -227,26 +182,10 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
// The CORS (Cross-origin resource sharing) protocol can send an OPTIONS
|
||||
// request before ("pre-flight") the main request. The response to this
|
||||
// request can be empty, but needs to have the right headers (which we
|
||||
// fill with handle_CORS())
|
||||
class options_handler : public gated_handler {
|
||||
public:
|
||||
options_handler(seastar::gate& pending_requests) : gated_handler(pending_requests) {}
|
||||
protected:
|
||||
virtual future<std::unique_ptr<reply>> do_handle(const sstring& path, std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
|
||||
handle_CORS(*req, *rep, true);
|
||||
rep->set_status(reply::status_type::ok);
|
||||
rep->write_body("txt", sstring(""));
|
||||
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
|
||||
}
|
||||
};
|
||||
|
||||
future<std::string> server::verify_signature(const request& req, const chunked_content& content) {
|
||||
future<> server::verify_signature(const request& req) {
|
||||
if (!_enforce_authorization) {
|
||||
slogger.debug("Skipping authorization");
|
||||
return make_ready_future<std::string>("<unauthenticated request>");
|
||||
return make_ready_future<>();
|
||||
}
|
||||
auto host_it = req._headers.find("Host");
|
||||
if (host_it == req._headers.end()) {
|
||||
@@ -316,10 +255,10 @@ future<std::string> server::verify_signature(const request& req, const chunked_c
|
||||
}
|
||||
}
|
||||
|
||||
auto cache_getter = [&proxy = _proxy] (std::string username) {
|
||||
return get_key_from_roles(proxy, std::move(username));
|
||||
auto cache_getter = [&qp = _qp] (std::string username) {
|
||||
return get_key_from_roles(qp, std::move(username));
|
||||
};
|
||||
return _key_cache.get_ptr(user, cache_getter).then([this, &req, &content,
|
||||
return _key_cache.get_ptr(user, cache_getter).then([this, &req,
|
||||
user = std::move(user),
|
||||
host = std::move(host),
|
||||
datestamp = std::move(datestamp),
|
||||
@@ -329,102 +268,52 @@ future<std::string> server::verify_signature(const request& req, const chunked_c
|
||||
service = std::move(service),
|
||||
user_signature = std::move(user_signature)] (key_cache::value_ptr key_ptr) {
|
||||
std::string signature = get_signature(user, *key_ptr, std::string_view(host), req._method,
|
||||
datestamp, signed_headers_str, signed_headers_map, content, region, service, "");
|
||||
datestamp, signed_headers_str, signed_headers_map, req.content, region, service, "");
|
||||
|
||||
if (signature != std::string_view(user_signature)) {
|
||||
_key_cache.remove(user);
|
||||
throw api_error::unrecognized_client("The security token included in the request is invalid.");
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
static tracing::trace_state_ptr create_tracing_session(tracing::tracing& tracing_instance) {
|
||||
tracing::trace_state_props_set props;
|
||||
props.set<tracing::trace_state_props::full_tracing>();
|
||||
props.set_if<tracing::trace_state_props::log_slow_query>(tracing_instance.slow_query_tracing_enabled());
|
||||
return tracing_instance.create_session(tracing::trace_type::QUERY, props);
|
||||
}
|
||||
|
||||
// truncated_content_view() prints a potentially long chunked_content for
|
||||
// debugging purposes. In the common case when the content is not excessively
|
||||
// long, it just returns a view into the given content, without any copying.
|
||||
// But when the content is very long, it is truncated after some arbitrary
|
||||
// max_len (or one chunk, whichever comes first), with "<truncated>" added at
|
||||
// the end. To do this modification to the string, we need to create a new
|
||||
// std::string, so the caller must pass us a reference to one, "buf", where
|
||||
// we can store the content. The returned view is only alive for as long this
|
||||
// buf is kept alive.
|
||||
static std::string_view truncated_content_view(const chunked_content& content, std::string& buf) {
|
||||
constexpr size_t max_len = 1024;
|
||||
if (content.empty()) {
|
||||
return std::string_view();
|
||||
} else if (content.size() == 1 && content.begin()->size() <= max_len) {
|
||||
return std::string_view(content.begin()->get(), content.begin()->size());
|
||||
} else {
|
||||
buf = std::string(content.begin()->get(), std::min(content.begin()->size(), max_len)) + "<truncated>";
|
||||
return std::string_view(buf);
|
||||
}
|
||||
}
|
||||
|
||||
static tracing::trace_state_ptr maybe_trace_query(service::client_state& client_state, std::string_view username, sstring_view op, const chunked_content& query) {
|
||||
tracing::trace_state_ptr trace_state;
|
||||
tracing::tracing& tracing_instance = tracing::tracing::get_local_tracing_instance();
|
||||
if (tracing_instance.trace_next_query() || tracing_instance.slow_query_tracing_enabled()) {
|
||||
trace_state = create_tracing_session(tracing_instance);
|
||||
std::string buf;
|
||||
tracing::add_session_param(trace_state, "alternator_op", op);
|
||||
tracing::add_query(trace_state, truncated_content_view(query, buf));
|
||||
tracing::begin(trace_state, format("Alternator {}", op), client_state.get_client_address());
|
||||
tracing::set_username(trace_state, auth::authenticated_user(username));
|
||||
}
|
||||
return trace_state;
|
||||
}
|
||||
|
||||
future<executor::request_return_type> server::handle_api_request(std::unique_ptr<request> req) {
|
||||
future<executor::request_return_type> server::handle_api_request(std::unique_ptr<request>&& req) {
|
||||
_executor._stats.total_operations++;
|
||||
sstring target = req->get_header(TARGET);
|
||||
std::vector<std::string_view> split_target = split(target, '.');
|
||||
//NOTICE(sarna): Target consists of Dynamo API version followed by a dot '.' and operation type (e.g. CreateTable)
|
||||
std::string op = split_target.empty() ? std::string() : std::string(split_target.back());
|
||||
// JSON parsing can allocate up to roughly 2x the size of the raw
|
||||
// document, + a couple of bytes for maintenance.
|
||||
// TODO: consider the case where req->content_length is missing. Maybe
|
||||
// we need to take the content_length_limit and return some of the units
|
||||
// when we finish read_content_and_verify_signature?
|
||||
size_t mem_estimate = req->content_length * 2 + 8000;
|
||||
auto units_fut = get_units(*_memory_limiter, mem_estimate);
|
||||
if (_memory_limiter->waiters()) {
|
||||
++_executor._stats.requests_blocked_memory;
|
||||
}
|
||||
auto units = co_await std::move(units_fut);
|
||||
assert(req->content_stream);
|
||||
chunked_content content = co_await httpd::read_entire_stream(*req->content_stream);
|
||||
auto username = co_await verify_signature(*req, content);
|
||||
|
||||
if (slogger.is_enabled(log_level::trace)) {
|
||||
std::string buf;
|
||||
slogger.trace("Request: {} {} {}", op, truncated_content_view(content, buf), req->_headers);
|
||||
}
|
||||
auto callback_it = _callbacks.find(op);
|
||||
if (callback_it == _callbacks.end()) {
|
||||
_executor._stats.unsupported_operations++;
|
||||
co_return api_error::unknown_operation(format("Unsupported operation {}", op));
|
||||
}
|
||||
if (_pending_requests.get_count() >= _max_concurrent_requests) {
|
||||
_executor._stats.requests_shed++;
|
||||
co_return api_error::request_limit_exceeded(format("too many in-flight requests (configured via max_concurrent_requests_per_shard): {}", _pending_requests.get_count()));
|
||||
}
|
||||
_pending_requests.enter();
|
||||
auto leave = defer([this] () noexcept { _pending_requests.leave(); });
|
||||
//FIXME: Client state can provide more context, e.g. client's endpoint address
|
||||
// We use unique_ptr because client_state cannot be moved or copied
|
||||
executor::client_state client_state{executor::client_state::internal_tag()};
|
||||
tracing::trace_state_ptr trace_state = maybe_trace_query(client_state, username, op, content);
|
||||
tracing::trace(trace_state, op);
|
||||
rjson::value json_request = co_await _json_parser.parse(std::move(content));
|
||||
co_return co_await callback_it->second(_executor, client_state, trace_state,
|
||||
make_service_permit(std::move(units)), std::move(json_request), std::move(req));
|
||||
slogger.trace("Request: {} {} {}", op, req->content, req->_headers);
|
||||
return verify_signature(*req).then([this, op, req = std::move(req)] () mutable {
|
||||
auto callback_it = _callbacks.find(op);
|
||||
if (callback_it == _callbacks.end()) {
|
||||
_executor._stats.unsupported_operations++;
|
||||
throw api_error::unknown_operation(format("Unsupported operation {}", op));
|
||||
}
|
||||
return with_gate(_pending_requests, [this, callback_it = std::move(callback_it), op = std::move(op), req = std::move(req)] () mutable {
|
||||
//FIXME: Client state can provide more context, e.g. client's endpoint address
|
||||
// We use unique_ptr because client_state cannot be moved or copied
|
||||
return do_with(std::make_unique<executor::client_state>(executor::client_state::internal_tag()),
|
||||
[this, callback_it = std::move(callback_it), op = std::move(op), req = std::move(req)] (std::unique_ptr<executor::client_state>& client_state) mutable {
|
||||
tracing::trace_state_ptr trace_state = executor::maybe_trace_query(*client_state, op, req->content);
|
||||
tracing::trace(trace_state, op);
|
||||
// JSON parsing can allocate up to roughly 2x the size of the raw document, + a couple of bytes for maintenance.
|
||||
// FIXME: by this time, the whole HTTP request was already read, so some memory is already occupied.
|
||||
// Once HTTP allows working on streams, we should grab the permit *before* reading the HTTP payload.
|
||||
size_t mem_estimate = req->content.size() * 3 + 8000;
|
||||
auto units_fut = get_units(*_memory_limiter, mem_estimate);
|
||||
if (_memory_limiter->waiters()) {
|
||||
++_executor._stats.requests_blocked_memory;
|
||||
}
|
||||
return units_fut.then([this, callback_it = std::move(callback_it), &client_state, trace_state, req = std::move(req)] (semaphore_units<> units) mutable {
|
||||
return _json_parser.parse(req->content).then([this, callback_it = std::move(callback_it), &client_state, trace_state,
|
||||
units = std::move(units), req = std::move(req)] (rjson::value json_request) mutable {
|
||||
return callback_it->second(_executor, *client_state, trace_state, make_service_permit(std::move(units)), std::move(json_request), std::move(req)).finally([trace_state] {});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void server::set_routes(routes& r) {
|
||||
@@ -445,19 +334,17 @@ void server::set_routes(routes& r) {
|
||||
// consider this to be a security risk, because an attacker can already
|
||||
// scan an entire subnet for nodes responding to the health request,
|
||||
// or even just scan for open ports.
|
||||
r.put(operation_type::GET, "/localnodes", new local_nodelist_handler(_pending_requests, _proxy, _gossiper));
|
||||
r.put(operation_type::OPTIONS, "/", new options_handler(_pending_requests));
|
||||
r.put(operation_type::GET, "/localnodes", new local_nodelist_handler(_pending_requests));
|
||||
}
|
||||
|
||||
//FIXME: A way to immediately invalidate the cache should be considered,
|
||||
// e.g. when the system table which stores the keys is changed.
|
||||
// For now, this propagation may take up to 1 minute.
|
||||
server::server(executor& exec, service::storage_proxy& proxy, gms::gossiper& gossiper)
|
||||
server::server(executor& exec, cql3::query_processor& qp)
|
||||
: _http_server("http-alternator")
|
||||
, _https_server("https-alternator")
|
||||
, _executor(exec)
|
||||
, _proxy(proxy)
|
||||
, _gossiper(gossiper)
|
||||
, _qp(qp)
|
||||
, _key_cache(1024, 1min, slogger)
|
||||
, _enforce_authorization(false)
|
||||
, _enabled_servers{}
|
||||
@@ -514,12 +401,6 @@ server::server(executor& exec, service::storage_proxy& proxy, gms::gossiper& gos
|
||||
{"ListTagsOfResource", [] (executor& e, executor::client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value json_request, std::unique_ptr<request> req) {
|
||||
return e.list_tags_of_resource(client_state, std::move(permit), std::move(json_request));
|
||||
}},
|
||||
{"UpdateTimeToLive", [] (executor& e, executor::client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value json_request, std::unique_ptr<request> req) {
|
||||
return e.update_time_to_live(client_state, std::move(permit), std::move(json_request));
|
||||
}},
|
||||
{"DescribeTimeToLive", [] (executor& e, executor::client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value json_request, std::unique_ptr<request> req) {
|
||||
return e.describe_time_to_live(client_state, std::move(permit), std::move(json_request));
|
||||
}},
|
||||
{"ListStreams", [] (executor& e, executor::client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value json_request, std::unique_ptr<request> req) {
|
||||
return e.list_streams(client_state, std::move(permit), std::move(json_request));
|
||||
}},
|
||||
@@ -536,10 +417,9 @@ server::server(executor& exec, service::storage_proxy& proxy, gms::gossiper& gos
|
||||
}
|
||||
|
||||
future<> server::init(net::inet_address addr, std::optional<uint16_t> port, std::optional<uint16_t> https_port, std::optional<tls::credentials_builder> creds,
|
||||
bool enforce_authorization, semaphore* memory_limiter, utils::updateable_value<uint32_t> max_concurrent_requests) {
|
||||
bool enforce_authorization, semaphore* memory_limiter) {
|
||||
_memory_limiter = memory_limiter;
|
||||
_enforce_authorization = enforce_authorization;
|
||||
_max_concurrent_requests = std::move(max_concurrent_requests);
|
||||
if (!port && !https_port) {
|
||||
return make_exception_future<>(std::runtime_error("Either regular port or TLS port"
|
||||
" must be specified in order to init an alternator HTTP server instance"));
|
||||
@@ -551,14 +431,12 @@ future<> server::init(net::inet_address addr, std::optional<uint16_t> port, std:
|
||||
if (port) {
|
||||
set_routes(_http_server._routes);
|
||||
_http_server.set_content_length_limit(server::content_length_limit);
|
||||
_http_server.set_content_streaming(true);
|
||||
_http_server.listen(socket_address{addr, *port}).get();
|
||||
_enabled_servers.push_back(std::ref(_http_server));
|
||||
}
|
||||
if (https_port) {
|
||||
set_routes(_https_server._routes);
|
||||
_https_server.set_content_length_limit(server::content_length_limit);
|
||||
_https_server.set_content_streaming(true);
|
||||
_https_server.set_tls_credentials(creds->build_reloadable_server_credentials([](const std::unordered_set<sstring>& files, std::exception_ptr ep) {
|
||||
if (ep) {
|
||||
slogger.warn("Exception loading {}: {}", files, ep);
|
||||
@@ -596,7 +474,7 @@ server::json_parser::json_parser() : _run_parse_json_thread(async([this] {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_parsed_document = rjson::parse_yieldable(std::move(_raw_document));
|
||||
_parsed_document = rjson::parse_yieldable(_raw_document);
|
||||
_current_exception = nullptr;
|
||||
} catch (...) {
|
||||
_current_exception = std::current_exception();
|
||||
@@ -606,12 +484,12 @@ server::json_parser::json_parser() : _run_parse_json_thread(async([this] {
|
||||
})) {
|
||||
}
|
||||
|
||||
future<rjson::value> server::json_parser::parse(chunked_content&& content) {
|
||||
future<rjson::value> server::json_parser::parse(std::string_view content) {
|
||||
if (content.size() < yieldable_parsing_threshold) {
|
||||
return make_ready_future<rjson::value>(rjson::parse(std::move(content)));
|
||||
return make_ready_future<rjson::value>(rjson::parse(content));
|
||||
}
|
||||
return with_semaphore(_parsing_sem, 1, [this, content = std::move(content)] () mutable {
|
||||
_raw_document = std::move(content);
|
||||
return with_semaphore(_parsing_sem, 1, [this, content] {
|
||||
_raw_document = content;
|
||||
_document_waiting.signal();
|
||||
return _document_parsed.wait().then([this] {
|
||||
if (_current_exception) {
|
||||
@@ -629,12 +507,5 @@ future<> server::json_parser::stop() {
|
||||
return std::move(_run_parse_json_thread);
|
||||
}
|
||||
|
||||
const char* api_error::what() const noexcept {
|
||||
if (_what_string.empty()) {
|
||||
_what_string = format("{} {}: {}", _http_code, _type, _msg);
|
||||
}
|
||||
return _what_string.c_str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,19 +23,15 @@
|
||||
|
||||
#include "alternator/executor.hh"
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/condition-variable.hh>
|
||||
#include <seastar/http/httpd.hh>
|
||||
#include <seastar/net/tls.hh>
|
||||
#include <optional>
|
||||
#include "alternator/auth.hh"
|
||||
#include "utils/small_vector.hh"
|
||||
#include "utils/updateable_value.hh"
|
||||
#include <seastar/core/units.hh>
|
||||
|
||||
namespace alternator {
|
||||
|
||||
using chunked_content = rjson::chunked_content;
|
||||
|
||||
class server {
|
||||
static constexpr size_t content_length_limit = 16*MB;
|
||||
using alternator_callback = std::function<future<executor::request_return_type>(executor&, executor::client_state&,
|
||||
@@ -45,8 +41,7 @@ class server {
|
||||
http_server _http_server;
|
||||
http_server _https_server;
|
||||
executor& _executor;
|
||||
service::storage_proxy& _proxy;
|
||||
gms::gossiper& _gossiper;
|
||||
cql3::query_processor& _qp;
|
||||
|
||||
key_cache _key_cache;
|
||||
bool _enforce_authorization;
|
||||
@@ -55,11 +50,10 @@ class server {
|
||||
alternator_callbacks_map _callbacks;
|
||||
|
||||
semaphore* _memory_limiter;
|
||||
utils::updateable_value<uint32_t> _max_concurrent_requests;
|
||||
|
||||
class json_parser {
|
||||
static constexpr size_t yieldable_parsing_threshold = 16*KB;
|
||||
chunked_content _raw_document;
|
||||
std::string_view _raw_document;
|
||||
rjson::value _parsed_document;
|
||||
std::exception_ptr _current_exception;
|
||||
semaphore _parsing_sem{1};
|
||||
@@ -69,25 +63,21 @@ class server {
|
||||
future<> _run_parse_json_thread;
|
||||
public:
|
||||
json_parser();
|
||||
// Moving a chunked_content into parse() allows parse() to free each
|
||||
// chunk as soon as it is parsed, so when chunks are relatively small,
|
||||
// we don't need to store the sum of unparsed and parsed sizes.
|
||||
future<rjson::value> parse(chunked_content&& content);
|
||||
future<rjson::value> parse(std::string_view content);
|
||||
future<> stop();
|
||||
};
|
||||
json_parser _json_parser;
|
||||
|
||||
public:
|
||||
server(executor& executor, service::storage_proxy& proxy, gms::gossiper& gossiper);
|
||||
server(executor& executor, cql3::query_processor& qp);
|
||||
|
||||
future<> init(net::inet_address addr, std::optional<uint16_t> port, std::optional<uint16_t> https_port, std::optional<tls::credentials_builder> creds,
|
||||
bool enforce_authorization, semaphore* memory_limiter, utils::updateable_value<uint32_t> max_concurrent_requests);
|
||||
bool enforce_authorization, semaphore* memory_limiter);
|
||||
future<> stop();
|
||||
private:
|
||||
void set_routes(seastar::httpd::routes& r);
|
||||
// If verification succeeds, returns the authenticated user's username
|
||||
future<std::string> verify_signature(const seastar::httpd::request&, const chunked_content&);
|
||||
future<executor::request_return_type> handle_api_request(std::unique_ptr<request> req);
|
||||
future<> verify_signature(const seastar::httpd::request& r);
|
||||
future<executor::request_return_type> handle_api_request(std::unique_ptr<request>&& req);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -97,8 +97,6 @@ stats::stats() : api_operations{} {
|
||||
seastar::metrics::description("number writes that had to be bounced from this shard because of LWT requirements")),
|
||||
seastar::metrics::make_total_operations("requests_blocked_memory", requests_blocked_memory,
|
||||
seastar::metrics::description("Counts a number of requests blocked due to memory pressure.")),
|
||||
seastar::metrics::make_total_operations("requests_shed", requests_shed,
|
||||
seastar::metrics::description("Counts a number of requests shed due to overload.")),
|
||||
seastar::metrics::make_total_operations("filtered_rows_read_total", cql_stats.filtered_rows_read_total,
|
||||
seastar::metrics::description("number of rows read during filtering operations")),
|
||||
seastar::metrics::make_total_operations("filtered_rows_matched_total", cql_stats.filtered_rows_matched_total,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -92,7 +92,6 @@ public:
|
||||
uint64_t write_using_lwt = 0;
|
||||
uint64_t shard_bounce_for_lwt = 0;
|
||||
uint64_t requests_blocked_memory = 0;
|
||||
uint64_t requests_shed = 0;
|
||||
// CQL-derived stats
|
||||
cql3::cql_stats cql_stats;
|
||||
private:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-present ScyllaDB
|
||||
* Copyright 2020 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include <seastar/json/formatter.hh>
|
||||
|
||||
#include "utils/base64.hh"
|
||||
#include "base64.hh"
|
||||
#include "log.hh"
|
||||
#include "database.hh"
|
||||
#include "db/config.hh"
|
||||
@@ -34,15 +34,13 @@
|
||||
#include "cdc/log.hh"
|
||||
#include "cdc/generation.hh"
|
||||
#include "cdc/cdc_options.hh"
|
||||
#include "cdc/metadata.hh"
|
||||
#include "db/system_distributed_keyspace.hh"
|
||||
#include "utils/UUID_gen.hh"
|
||||
#include "cql3/selection/selection.hh"
|
||||
#include "cql3/result_set.hh"
|
||||
#include "cql3/type_json.hh"
|
||||
#include "cql3/column_identifier.hh"
|
||||
#include "schema_builder.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "service/storage_service.hh"
|
||||
#include "gms/feature.hh"
|
||||
#include "gms/feature_service.hh"
|
||||
|
||||
@@ -90,7 +88,7 @@ struct rapidjson::internal::TypeHelper<ValueType, utils::UUID>
|
||||
{};
|
||||
|
||||
static db_clock::time_point as_timepoint(const utils::UUID& uuid) {
|
||||
return db_clock::time_point{utils::UUID_gen::unix_timestamp(uuid)};
|
||||
return db_clock::time_point{std::chrono::milliseconds(utils::UUID_gen::get_adjusted_timestamp(uuid))};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,7 +194,7 @@ future<alternator::executor::request_return_type> alternator::executor::list_str
|
||||
if (table && ks_name != table->ks_name()) {
|
||||
continue;
|
||||
}
|
||||
if (cdc::is_log_for_some_table(db, ks_name, cf_name)) {
|
||||
if (cdc::is_log_for_some_table(ks_name, cf_name)) {
|
||||
if (table && table != cdc::get_base_table(db, *s)) {
|
||||
continue;
|
||||
}
|
||||
@@ -204,19 +202,19 @@ future<alternator::executor::request_return_type> alternator::executor::list_str
|
||||
rjson::value new_entry = rjson::empty_object();
|
||||
|
||||
last = i->first;
|
||||
rjson::add(new_entry, "StreamArn", *last);
|
||||
rjson::add(new_entry, "StreamLabel", rjson::from_string(stream_label(*s)));
|
||||
rjson::add(new_entry, "TableName", rjson::from_string(cdc::base_name(table_name(*s))));
|
||||
rjson::set(new_entry, "StreamArn", *last);
|
||||
rjson::set(new_entry, "StreamLabel", rjson::from_string(stream_label(*s)));
|
||||
rjson::set(new_entry, "TableName", rjson::from_string(cdc::base_name(table_name(*s))));
|
||||
rjson::push_back(streams, std::move(new_entry));
|
||||
|
||||
--limit;
|
||||
}
|
||||
}
|
||||
|
||||
rjson::add(ret, "Streams", std::move(streams));
|
||||
rjson::set(ret, "Streams", std::move(streams));
|
||||
|
||||
if (last) {
|
||||
rjson::add(ret, "LastEvaluatedStreamArn", *last);
|
||||
rjson::set(ret, "LastEvaluatedStreamArn", *last);
|
||||
}
|
||||
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
@@ -296,8 +294,7 @@ sequence_number::sequence_number(std::string_view v)
|
||||
// view directly.
|
||||
uint128_t tmp{std::string(v)};
|
||||
// see above
|
||||
return utils::UUID_gen::get_time_UUID_raw(utils::UUID_gen::decimicroseconds{uint64_t(tmp >> 64)},
|
||||
uint64_t(tmp & std::numeric_limits<uint64_t>::max()));
|
||||
return utils::UUID_gen::get_time_UUID_raw(uint64_t(tmp >> 64), uint64_t(tmp & std::numeric_limits<uint64_t>::max()));
|
||||
}())
|
||||
{}
|
||||
|
||||
@@ -473,7 +470,8 @@ future<executor::request_return_type> executor::describe_stream(client_state& cl
|
||||
auto status = "DISABLED";
|
||||
|
||||
if (opts.enabled()) {
|
||||
if (!_cdc_metadata.streams_available()) {
|
||||
auto& metadata = _ss.get_cdc_metadata();
|
||||
if (!metadata.streams_available()) {
|
||||
status = "ENABLING";
|
||||
} else {
|
||||
status = "ENABLED";
|
||||
@@ -482,18 +480,18 @@ future<executor::request_return_type> executor::describe_stream(client_state& cl
|
||||
|
||||
auto ttl = std::chrono::seconds(opts.ttl());
|
||||
|
||||
rjson::add(stream_desc, "StreamStatus", rjson::from_string(status));
|
||||
rjson::set(stream_desc, "StreamStatus", rjson::from_string(status));
|
||||
|
||||
stream_view_type type = cdc_options_to_steam_view_type(opts);
|
||||
|
||||
rjson::add(stream_desc, "StreamArn", alternator::stream_arn(schema->id()));
|
||||
rjson::add(stream_desc, "StreamViewType", type);
|
||||
rjson::add(stream_desc, "TableName", rjson::from_string(table_name(*bs)));
|
||||
rjson::set(stream_desc, "StreamArn", alternator::stream_arn(schema->id()));
|
||||
rjson::set(stream_desc, "StreamViewType", type);
|
||||
rjson::set(stream_desc, "TableName", rjson::from_string(table_name(*bs)));
|
||||
|
||||
describe_key_schema(stream_desc, *bs);
|
||||
|
||||
if (!opts.enabled()) {
|
||||
rjson::add(ret, "StreamDescription", std::move(stream_desc));
|
||||
rjson::set(ret, "StreamDescription", std::move(stream_desc));
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
}
|
||||
|
||||
@@ -590,19 +588,19 @@ future<executor::request_return_type> executor::describe_stream(client_state& cl
|
||||
pid = std::prev(pid);
|
||||
}
|
||||
if (pid != pids.end()) {
|
||||
rjson::add(shard, "ParentShardId", shard_id(prev->first, *pid));
|
||||
rjson::set(shard, "ParentShardId", shard_id(prev->first, *pid));
|
||||
}
|
||||
}
|
||||
|
||||
last.emplace(ts, id);
|
||||
rjson::add(shard, "ShardId", *last);
|
||||
rjson::set(shard, "ShardId", *last);
|
||||
auto range = rjson::empty_object();
|
||||
rjson::add(range, "StartingSequenceNumber", sequence_number(utils::UUID_gen::min_time_UUID(ts.time_since_epoch())));
|
||||
rjson::set(range, "StartingSequenceNumber", sequence_number(utils::UUID_gen::min_time_UUID(ts.time_since_epoch().count())));
|
||||
if (expired) {
|
||||
rjson::add(range, "EndingSequenceNumber", sequence_number(utils::UUID_gen::min_time_UUID(expired->time_since_epoch())));
|
||||
rjson::set(range, "EndingSequenceNumber", sequence_number(utils::UUID_gen::min_time_UUID(expired->time_since_epoch().count())));
|
||||
}
|
||||
|
||||
rjson::add(shard, "SequenceNumberRange", std::move(range));
|
||||
rjson::set(shard, "SequenceNumberRange", std::move(range));
|
||||
rjson::push_back(shards, std::move(shard));
|
||||
|
||||
if (--limit == 0) {
|
||||
@@ -614,11 +612,11 @@ future<executor::request_return_type> executor::describe_stream(client_state& cl
|
||||
}
|
||||
|
||||
if (last) {
|
||||
rjson::add(stream_desc, "LastEvaluatedShardId", *last);
|
||||
rjson::set(stream_desc, "LastEvaluatedShardId", *last);
|
||||
}
|
||||
|
||||
rjson::add(stream_desc, "Shards", std::move(shards));
|
||||
rjson::add(ret, "StreamDescription", std::move(stream_desc));
|
||||
rjson::set(stream_desc, "Shards", std::move(shards));
|
||||
rjson::set(ret, "StreamDescription", std::move(stream_desc));
|
||||
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
});
|
||||
@@ -763,7 +761,7 @@ future<executor::request_return_type> executor::get_shard_iterator(client_state&
|
||||
inclusive_of_threshold = true;
|
||||
break;
|
||||
case shard_iterator_type::LATEST:
|
||||
threshold = utils::UUID_gen::min_time_UUID((db_clock::now() - confidence_interval(db)).time_since_epoch());
|
||||
threshold = utils::UUID_gen::min_time_UUID((db_clock::now() - confidence_interval(db)).time_since_epoch().count());
|
||||
inclusive_of_threshold = true;
|
||||
break;
|
||||
}
|
||||
@@ -771,7 +769,7 @@ future<executor::request_return_type> executor::get_shard_iterator(client_state&
|
||||
shard_iterator iter(stream_arn, *sid, threshold, inclusive_of_threshold);
|
||||
|
||||
auto ret = rjson::empty_object();
|
||||
rjson::add(ret, "ShardIterator", iter);
|
||||
rjson::set(ret, "ShardIterator", iter);
|
||||
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
}
|
||||
@@ -835,7 +833,7 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
dht::partition_range_vector partition_ranges{ dht::partition_range::make_singular(dht::decorate_key(*schema, pk)) };
|
||||
|
||||
auto high_ts = db_clock::now() - confidence_interval(db);
|
||||
auto high_uuid = utils::UUID_gen::min_time_UUID(high_ts.time_since_epoch());
|
||||
auto high_uuid = utils::UUID_gen::min_time_UUID(high_ts.time_since_epoch().count());
|
||||
auto lo = clustering_key_prefix::from_exploded(*schema, { iter.threshold.serialize() });
|
||||
auto hi = clustering_key_prefix::from_exploded(*schema, { high_uuid.serialize() });
|
||||
|
||||
@@ -927,13 +925,13 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
|
||||
auto maybe_add_record = [&] {
|
||||
if (!dynamodb.ObjectEmpty()) {
|
||||
rjson::add(record, "dynamodb", std::move(dynamodb));
|
||||
rjson::set(record, "dynamodb", std::move(dynamodb));
|
||||
dynamodb = rjson::empty_object();
|
||||
}
|
||||
if (!record.ObjectEmpty()) {
|
||||
// TODO: awsRegion?
|
||||
rjson::add(record, "eventID", event_id(iter.shard.id, *timestamp));
|
||||
rjson::add(record, "eventSource", "scylladb:alternator");
|
||||
rjson::set(record, "eventID", event_id(iter.shard.id, *timestamp));
|
||||
rjson::set(record, "eventSource", "scylladb:alternator");
|
||||
rjson::push_back(records, std::move(record));
|
||||
record = rjson::empty_object();
|
||||
--limit;
|
||||
@@ -948,10 +946,10 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
if (!dynamodb.HasMember("Keys")) {
|
||||
auto keys = rjson::empty_object();
|
||||
describe_single_item(*selection, row, key_names, keys);
|
||||
rjson::add(dynamodb, "Keys", std::move(keys));
|
||||
rjson::add(dynamodb, "ApproximateCreationDateTime", utils::UUID_gen::unix_timestamp_in_sec(ts).count());
|
||||
rjson::add(dynamodb, "SequenceNumber", sequence_number(ts));
|
||||
rjson::add(dynamodb, "StreamViewType", type);
|
||||
rjson::set(dynamodb, "Keys", std::move(keys));
|
||||
rjson::set(dynamodb, "ApproximateCreationDateTime", utils::UUID_gen::unix_timestamp_in_sec(ts).count());
|
||||
rjson::set(dynamodb, "SequenceNumber", sequence_number(ts));
|
||||
rjson::set(dynamodb, "StreamViewType", type);
|
||||
//TODO: SizeInBytes
|
||||
}
|
||||
|
||||
@@ -983,17 +981,17 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
auto item = rjson::empty_object();
|
||||
describe_single_item(*selection, row, attr_names, item, true);
|
||||
describe_single_item(*selection, row, key_names, item);
|
||||
rjson::add(dynamodb, op == cdc::operation::pre_image ? "OldImage" : "NewImage", std::move(item));
|
||||
rjson::set(dynamodb, op == cdc::operation::pre_image ? "OldImage" : "NewImage", std::move(item));
|
||||
break;
|
||||
}
|
||||
case cdc::operation::update:
|
||||
rjson::add(record, "eventName", "MODIFY");
|
||||
rjson::set(record, "eventName", "MODIFY");
|
||||
break;
|
||||
case cdc::operation::insert:
|
||||
rjson::add(record, "eventName", "INSERT");
|
||||
rjson::set(record, "eventName", "INSERT");
|
||||
break;
|
||||
default:
|
||||
rjson::add(record, "eventName", "REMOVE");
|
||||
rjson::set(record, "eventName", "REMOVE");
|
||||
break;
|
||||
}
|
||||
if (eor) {
|
||||
@@ -1007,7 +1005,7 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
|
||||
auto ret = rjson::empty_object();
|
||||
auto nrecords = records.Size();
|
||||
rjson::add(ret, "Records", std::move(records));
|
||||
rjson::set(ret, "Records", std::move(records));
|
||||
|
||||
if (nrecords != 0) {
|
||||
// #9642. Set next iterators threshold to > last
|
||||
@@ -1016,7 +1014,7 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
// without checking if maybe we reached the end-of-shard. If the
|
||||
// shard did end, then the next read will have nrecords == 0 and
|
||||
// will notice end end of shard and not return NextShardIterator.
|
||||
rjson::add(ret, "NextShardIterator", next_iter);
|
||||
rjson::set(ret, "NextShardIterator", next_iter);
|
||||
_stats.api_operations.get_records_latency.add(std::chrono::steady_clock::now() - start_time);
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
}
|
||||
@@ -1037,8 +1035,8 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
// a search from it until high_ts and found nothing, so we
|
||||
// can also start the next search from high_ts.
|
||||
// TODO: but why? It's simpler just to leave the iterator be.
|
||||
shard_iterator next_iter(iter.table, iter.shard, utils::UUID_gen::min_time_UUID(high_ts.time_since_epoch()), true);
|
||||
rjson::add(ret, "NextShardIterator", iter);
|
||||
shard_iterator next_iter(iter.table, iter.shard, utils::UUID_gen::min_time_UUID(high_ts.time_since_epoch().count()), true);
|
||||
rjson::set(ret, "NextShardIterator", iter);
|
||||
}
|
||||
_stats.api_operations.get_records_latency.add(std::chrono::steady_clock::now() - start_time);
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
@@ -1096,11 +1094,11 @@ void executor::supplement_table_stream_info(rjson::value& descr, const schema& s
|
||||
auto& db = _proxy.get_db().local();
|
||||
auto& cf = db.find_column_family(schema.ks_name(), cdc::log_name(schema.cf_name()));
|
||||
stream_arn arn(cf.schema()->id());
|
||||
rjson::add(descr, "LatestStreamArn", arn);
|
||||
rjson::add(descr, "LatestStreamLabel", rjson::from_string(stream_label(*cf.schema())));
|
||||
rjson::set(descr, "LatestStreamArn", arn);
|
||||
rjson::set(descr, "LatestStreamLabel", rjson::from_string(stream_label(*cf.schema())));
|
||||
|
||||
auto stream_desc = rjson::empty_object();
|
||||
rjson::add(stream_desc, "StreamEnabled", true);
|
||||
rjson::set(stream_desc, "StreamEnabled", true);
|
||||
|
||||
auto mode = stream_view_type::KEYS_ONLY;
|
||||
if (opts.preimage() && opts.postimage()) {
|
||||
@@ -1110,8 +1108,8 @@ void executor::supplement_table_stream_info(rjson::value& descr, const schema& s
|
||||
} else if (opts.postimage()) {
|
||||
mode = stream_view_type::NEW_IMAGE;
|
||||
}
|
||||
rjson::add(stream_desc, "StreamViewType", mode);
|
||||
rjson::add(descr, "StreamSpecification", std::move(stream_desc));
|
||||
rjson::set(stream_desc, "StreamViewType", mode);
|
||||
rjson::set(descr, "StreamSpecification", std::move(stream_desc));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-present ScyllaDB
|
||||
* Copyright 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021-present ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include <seastar/core/coroutine.hh>
|
||||
|
||||
#include "executor.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "gms/feature_service.hh"
|
||||
#include "database.hh"
|
||||
#include "utils/rjson.hh"
|
||||
|
||||
namespace alternator {
|
||||
|
||||
// We write the expiration-time attribute enabled on a table using a
|
||||
// tag TTL_TAG_KEY.
|
||||
// Currently, the *value* of this tag is simply the name of the attribute,
|
||||
// and the expiration scanner interprets it as an Alternator attribute name -
|
||||
// It can refer to a real column or if that doesn't exist, to a member of
|
||||
// the ":attrs" map column. Although this is designed for Alternator, it may
|
||||
// be good enough for CQL as well (there, the ":attrs" column won't exist).
|
||||
static const sstring TTL_TAG_KEY("system:ttl_attribute");
|
||||
|
||||
future<executor::request_return_type> executor::update_time_to_live(client_state& client_state, service_permit permit, rjson::value request) {
|
||||
_stats.api_operations.update_time_to_live++;
|
||||
if (!_proxy.get_db().local().features().cluster_supports_alternator_ttl()) {
|
||||
co_return api_error::unknown_operation("UpdateTimeToLive not yet supported. Experimental support is available if the 'alternator_ttl' experimental feature is enabled on all nodes.");
|
||||
}
|
||||
|
||||
schema_ptr schema = get_table(_proxy, request);
|
||||
rjson::value* spec = rjson::find(request, "TimeToLiveSpecification");
|
||||
if (!spec || !spec->IsObject()) {
|
||||
co_return api_error::validation("UpdateTimeToLive missing mandatory TimeToLiveSpecification");
|
||||
}
|
||||
const rjson::value* v = rjson::find(*spec, "Enabled");
|
||||
if (!v || !v->IsBool()) {
|
||||
co_return api_error::validation("UpdateTimeToLive requires boolean Enabled");
|
||||
}
|
||||
bool enabled = v->GetBool();
|
||||
v = rjson::find(*spec, "AttributeName");
|
||||
if (!v || !v->IsString()) {
|
||||
co_return api_error::validation("UpdateTimeToLive requires string AttributeName");
|
||||
}
|
||||
// Although the DynamoDB documentation specifies that attribute names
|
||||
// should be between 1 and 64K bytes, in practice, it only allows
|
||||
// between 1 and 255 bytes. There are no other limitations on which
|
||||
// characters are allowed in the name.
|
||||
if (v->GetStringLength() < 1 || v->GetStringLength() > 255) {
|
||||
co_return api_error::validation("The length of AttributeName must be between 1 and 255");
|
||||
}
|
||||
sstring attribute_name(v->GetString(), v->GetStringLength());
|
||||
|
||||
std::map<sstring, sstring> tags_map = get_tags_of_table(schema);
|
||||
if (enabled) {
|
||||
if (tags_map.contains(TTL_TAG_KEY)) {
|
||||
co_return api_error::validation("TTL is already enabled");
|
||||
}
|
||||
tags_map[TTL_TAG_KEY] = attribute_name;
|
||||
} else {
|
||||
auto i = tags_map.find(TTL_TAG_KEY);
|
||||
if (i == tags_map.end()) {
|
||||
co_return api_error::validation("TTL is already disabled");
|
||||
} else if (i->second != attribute_name) {
|
||||
co_return api_error::validation(format(
|
||||
"Requested to disable TTL on attribute {}, but a different attribute {} is enabled.",
|
||||
attribute_name, i->second));
|
||||
}
|
||||
tags_map.erase(TTL_TAG_KEY);
|
||||
}
|
||||
co_await update_tags(_mm, schema, std::move(tags_map));
|
||||
// Prepare the response, which contains a TimeToLiveSpecification
|
||||
// basically identical to the request's
|
||||
rjson::value response = rjson::empty_object();
|
||||
rjson::add(response, "TimeToLiveSpecification", std::move(*spec));
|
||||
co_return make_jsonable(std::move(response));
|
||||
}
|
||||
|
||||
future<executor::request_return_type> executor::describe_time_to_live(client_state& client_state, service_permit permit, rjson::value request) {
|
||||
_stats.api_operations.describe_time_to_live++;
|
||||
schema_ptr schema = get_table(_proxy, request);
|
||||
std::map<sstring, sstring> tags_map = get_tags_of_table(schema);
|
||||
rjson::value desc = rjson::empty_object();
|
||||
auto i = tags_map.find(TTL_TAG_KEY);
|
||||
if (i == tags_map.end()) {
|
||||
rjson::add(desc, "TimeToLiveStatus", "DISABLED");
|
||||
} else {
|
||||
rjson::add(desc, "TimeToLiveStatus", "ENABLED");
|
||||
rjson::add(desc, "AttributeName", rjson::from_string(i->second));
|
||||
}
|
||||
rjson::value response = rjson::empty_object();
|
||||
rjson::add(response, "TimeToLiveDescription", std::move(desc));
|
||||
co_return make_jsonable(std::move(response));
|
||||
}
|
||||
|
||||
} // namespace alternator
|
||||
@@ -89,7 +89,7 @@
|
||||
"description":"true if the output of the major compaction should be split in several sstables",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"type":"bool",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -7,61 +7,6 @@
|
||||
"application/json"
|
||||
],
|
||||
"apis":[
|
||||
{
|
||||
"path":"/hinted_handoff/sync_point",
|
||||
"operations":[
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"Creates a hints sync point. It can be used to wait until hints between given nodes are replayed. A sync point allows you to wait for hints accumulated at the moment of its creation - it won't wait for hints generated later. A sync point is described entirely by its ID - there is no state kept server-side, so there is no need to delete it.",
|
||||
"type":"string",
|
||||
"nickname":"create_hints_sync_point",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"target_hosts",
|
||||
"description":"A list of nodes towards which hints should be replayed. Multiple hosts can be listed by separating them with commas. If not provided or empty, the point will resolve when current hints towards all nodes in the cluster are sent.",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Get the status of a hints sync point, possibly waiting for it to be reached.",
|
||||
"type":"string",
|
||||
"enum":[
|
||||
"DONE",
|
||||
"IN_PROGRESS"
|
||||
],
|
||||
"nickname":"get_hints_sync_point",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"id",
|
||||
"description":"The ID of the hint sync point which should be checked or waited on",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"timeout",
|
||||
"description":"Timeout in seconds after which the query returns even if hints are still being replayed. No value or 0 will cause the query to return immediately. A negative value will cause the query to wait until the sync point is reached",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"long",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/hinted_handoff/hints",
|
||||
"operations":[
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
"items":{
|
||||
"type":"message_counter"
|
||||
},
|
||||
"nickname":"get_replied_messages",
|
||||
"nickname":"get_completed_messages",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
@@ -252,7 +252,7 @@
|
||||
"UNUSED__STREAM_MUTATION",
|
||||
"STREAM_MUTATION_DONE",
|
||||
"COMPLETE_MESSAGE",
|
||||
"UNUSED__REPAIR_CHECKSUM_RANGE",
|
||||
"REPAIR_CHECKSUM_RANGE",
|
||||
"GET_SCHEMA_VERSION"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -104,68 +104,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/toppartitions/",
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Toppartitions query",
|
||||
"type":"toppartitions_query_results",
|
||||
"nickname":"toppartitions_generic",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"table_filters",
|
||||
"description":"Optional list of table name filters in keyspace:name format",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"string"
|
||||
},
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"keyspace_filters",
|
||||
"description":"Optional list of keyspace filters",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"string"
|
||||
},
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"duration",
|
||||
"description":"Duration (in milliseconds) of monitoring operation",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type": "long",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"list_size",
|
||||
"description":"number of the top partitions to list",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type": "long",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"capacity",
|
||||
"description":"capacity of stream summary: determines amount of resources used in query processing",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type": "long",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/nodes/leaving",
|
||||
"operations":[
|
||||
@@ -637,14 +575,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"sf",
|
||||
"description":"Skip flush. When set to \"true\", do not flush memtables before snapshotting (snapshot will not contain unflushed data)",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -770,7 +700,7 @@
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Scrub (deserialize + reserialize at the latest version, resolving corruptions if any) the given keyspace. If columnFamilies array is empty, all CFs are scrubbed. Scrubbed CFs will be snapshotted first, if disableSnapshot is false. Scrub has the following modes: Abort (default) - abort scrub if corruption is detected; Skip (same as `skip_corrupted=true`) skip over corrupt data, omitting them from the output; Segregate - segregate data into multiple sstables if needed, such that each sstable contains data with valid order; Validate - read (no rewrite) and validate data, logging any problems found.",
|
||||
"summary":"Scrub (deserialize + reserialize at the latest version, skipping bad rows if any) the given keyspace. If columnFamilies array is empty, all CFs are scrubbed. Scrubbed CFs will be snapshotted first, if disableSnapshot is false",
|
||||
"type": "long",
|
||||
"nickname":"scrub",
|
||||
"produces":[
|
||||
@@ -793,20 +723,6 @@
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"scrub_mode",
|
||||
"description":"How to handle corrupt data (overrides 'skip_corrupted'); ",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"enum":[
|
||||
"ABORT",
|
||||
"SKIP",
|
||||
"SEGREGATE",
|
||||
"VALIDATE"
|
||||
],
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"keyspace",
|
||||
"description":"The keyspace to query about",
|
||||
@@ -1054,14 +970,6 @@
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"ignore_nodes",
|
||||
"description":"Which hosts are to ignore in this repair. Multiple hosts can be listed separated by commas.",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"trace",
|
||||
"description":"If the value is the string 'true' with any capitalization, enable tracing of the repair.",
|
||||
@@ -1197,14 +1105,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"ignore_nodes",
|
||||
"description":"List of dead nodes to ingore in removenode operation",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1856,22 +1756,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"load_and_stream",
|
||||
"description":"Load the sstables and stream to all replica nodes that owns the data",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"primary_replica_only",
|
||||
"description":"Load the sstables and stream to primary replica node that owns the data. Repair is needed after the load and stream process",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1982,14 +1866,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"long",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"fast",
|
||||
"description":"Lightweight tracing mode: if true, slow queries tracing records only session headers",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2488,10 +2364,6 @@
|
||||
"threshold":{
|
||||
"type":"long",
|
||||
"description":"The slow query logging threshold in microseconds. Queries that takes longer, will be logged"
|
||||
},
|
||||
"fast":{
|
||||
"type":"boolean",
|
||||
"description":"Is lightweight tracing mode enabled. In that mode tracing ignore events and tracks only sessions."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -52,22 +52,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/system/drop_sstable_caches",
|
||||
"operations":[
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"Drop in-memory caches for data which is in sstables",
|
||||
"type":"void",
|
||||
"nickname":"drop_sstable_caches",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/system/uptime_ms",
|
||||
"operations":[
|
||||
|
||||
81
api/api.cc
81
api/api.cc
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-present ScyllaDB
|
||||
* Copyright 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -75,10 +75,10 @@ future<> set_server_init(http_context& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
future<> set_server_config(http_context& ctx, const db::config& cfg) {
|
||||
future<> set_server_config(http_context& ctx) {
|
||||
auto rb02 = std::make_shared < api_registry_builder20 > (ctx.api_doc, "/v2");
|
||||
return ctx.http_server.set_routes([&ctx, &cfg, rb02](routes& r) {
|
||||
set_config(rb02, ctx, r, cfg);
|
||||
return ctx.http_server.set_routes([&ctx, rb02](routes& r) {
|
||||
set_config(rb02, ctx, r);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,30 +109,12 @@ future<> unset_rpc_controller(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_rpc_controller(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_server_storage_service(http_context& ctx, sharded<service::storage_service>& ss, sharded<gms::gossiper>& g, sharded<cdc::generation_service>& cdc_gs) {
|
||||
return register_api(ctx, "storage_service", "The storage service API", [&ss, &g, &cdc_gs] (http_context& ctx, routes& r) {
|
||||
set_storage_service(ctx, r, ss, g.local(), cdc_gs);
|
||||
});
|
||||
future<> set_server_storage_service(http_context& ctx) {
|
||||
return register_api(ctx, "storage_service", "The storage service API", set_storage_service);
|
||||
}
|
||||
|
||||
future<> set_server_sstables_loader(http_context& ctx, sharded<sstables_loader>& sst_loader) {
|
||||
return ctx.http_server.set_routes([&ctx, &sst_loader] (routes& r) { set_sstables_loader(ctx, r, sst_loader); });
|
||||
}
|
||||
|
||||
future<> unset_server_sstables_loader(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_sstables_loader(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_server_view_builder(http_context& ctx, sharded<db::view::view_builder>& vb) {
|
||||
return ctx.http_server.set_routes([&ctx, &vb] (routes& r) { set_view_builder(ctx, r, vb); });
|
||||
}
|
||||
|
||||
future<> unset_server_view_builder(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_view_builder(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_server_repair(http_context& ctx, sharded<repair_service>& repair) {
|
||||
return ctx.http_server.set_routes([&ctx, &repair] (routes& r) { set_repair(ctx, r, repair); });
|
||||
future<> set_server_repair(http_context& ctx, sharded<netw::messaging_service>& ms) {
|
||||
return ctx.http_server.set_routes([&ctx, &ms] (routes& r) { set_repair(ctx, r, ms); });
|
||||
}
|
||||
|
||||
future<> unset_server_repair(http_context& ctx) {
|
||||
@@ -151,11 +133,9 @@ future<> set_server_snitch(http_context& ctx) {
|
||||
return register_api(ctx, "endpoint_snitch_info", "The endpoint snitch info API", set_endpoint_snitch);
|
||||
}
|
||||
|
||||
future<> set_server_gossip(http_context& ctx, sharded<gms::gossiper>& g) {
|
||||
future<> set_server_gossip(http_context& ctx) {
|
||||
return register_api(ctx, "gossiper",
|
||||
"The gossiper API", [&g] (http_context& ctx, routes& r) {
|
||||
set_gossiper(ctx, r, g.local());
|
||||
});
|
||||
"The gossiper API", set_gossiper);
|
||||
}
|
||||
|
||||
future<> set_server_load_sstable(http_context& ctx) {
|
||||
@@ -173,11 +153,9 @@ future<> unset_server_messaging_service(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_messaging_service(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_server_storage_proxy(http_context& ctx, sharded<service::storage_service>& ss) {
|
||||
future<> set_server_storage_proxy(http_context& ctx) {
|
||||
return register_api(ctx, "storage_proxy",
|
||||
"The storage proxy API", [&ss] (http_context& ctx, routes& r) {
|
||||
set_storage_proxy(ctx, r, ss);
|
||||
});
|
||||
"The storage proxy API", set_storage_proxy);
|
||||
}
|
||||
|
||||
future<> set_server_stream_manager(http_context& ctx) {
|
||||
@@ -190,34 +168,13 @@ future<> set_server_cache(http_context& ctx) {
|
||||
"The cache service API", set_cache_service);
|
||||
}
|
||||
|
||||
future<> set_hinted_handoff(http_context& ctx, sharded<gms::gossiper>& g) {
|
||||
return register_api(ctx, "hinted_handoff",
|
||||
"The hinted handoff API", [&g] (http_context& ctx, routes& r) {
|
||||
set_hinted_handoff(ctx, r, g.local());
|
||||
});
|
||||
}
|
||||
|
||||
future<> unset_hinted_handoff(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_hinted_handoff(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_server_gossip_settle(http_context& ctx, sharded<gms::gossiper>& g) {
|
||||
auto rb = std::make_shared < api_registry_builder > (ctx.api_doc);
|
||||
|
||||
return ctx.http_server.set_routes([rb, &ctx, &g](routes& r) {
|
||||
rb->register_function(r, "failure_detector",
|
||||
"The failure detector API");
|
||||
set_failure_detector(ctx, r, g.local());
|
||||
});
|
||||
}
|
||||
|
||||
future<> set_server_compaction_manager(http_context& ctx) {
|
||||
future<> set_server_gossip_settle(http_context& ctx) {
|
||||
auto rb = std::make_shared < api_registry_builder > (ctx.api_doc);
|
||||
|
||||
return ctx.http_server.set_routes([rb, &ctx](routes& r) {
|
||||
rb->register_function(r, "compaction_manager",
|
||||
"The Compaction manager API");
|
||||
set_compaction_manager(ctx, r);
|
||||
rb->register_function(r, "failure_detector",
|
||||
"The failure detector API");
|
||||
set_failure_detector(ctx,r);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -225,12 +182,18 @@ future<> set_server_done(http_context& ctx) {
|
||||
auto rb = std::make_shared < api_registry_builder > (ctx.api_doc);
|
||||
|
||||
return ctx.http_server.set_routes([rb, &ctx](routes& r) {
|
||||
rb->register_function(r, "compaction_manager",
|
||||
"The Compaction manager API");
|
||||
set_compaction_manager(ctx, r);
|
||||
rb->register_function(r, "lsa", "Log-structured allocator API");
|
||||
set_lsa(ctx, r);
|
||||
|
||||
rb->register_function(r, "commitlog",
|
||||
"The commit log API");
|
||||
set_commitlog(ctx,r);
|
||||
rb->register_function(r, "hinted_handoff",
|
||||
"The hinted handoff API");
|
||||
set_hinted_handoff(ctx, r);
|
||||
rb->register_function(r, "collectd",
|
||||
"The collectd API");
|
||||
set_collectd(ctx, r);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-present ScyllaDB
|
||||
* Copyright 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <boost/units/detail/utility.hpp>
|
||||
#include "api/api-doc/utils.json.hh"
|
||||
#include "utils/histogram.hh"
|
||||
#include "utils/estimated_histogram.hh"
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "api_init.hh"
|
||||
#include "seastarx.hh"
|
||||
@@ -71,7 +70,7 @@ T map_sum(T&& dest, const S& src) {
|
||||
for (auto i : src) {
|
||||
dest[i.first] += i.second;
|
||||
}
|
||||
return std::move(dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename MAP>
|
||||
|
||||
@@ -19,48 +19,16 @@
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <seastar/http/httpd.hh>
|
||||
#include <seastar/core/future.hh>
|
||||
|
||||
#include "database_fwd.hh"
|
||||
#include "seastarx.hh"
|
||||
|
||||
namespace service {
|
||||
|
||||
class load_meter;
|
||||
class storage_proxy;
|
||||
class storage_service;
|
||||
|
||||
} // namespace service
|
||||
|
||||
class sstables_loader;
|
||||
|
||||
namespace locator {
|
||||
|
||||
class token_metadata;
|
||||
class shared_token_metadata;
|
||||
|
||||
} // namespace locator
|
||||
#include "service/storage_proxy.hh"
|
||||
#include <seastar/http/httpd.hh>
|
||||
|
||||
namespace service { class load_meter; }
|
||||
namespace locator { class shared_token_metadata; }
|
||||
namespace cql_transport { class controller; }
|
||||
class thrift_controller;
|
||||
namespace db {
|
||||
class snapshot_ctl;
|
||||
class config;
|
||||
namespace view {
|
||||
class view_builder;
|
||||
}
|
||||
}
|
||||
namespace db { class snapshot_ctl; }
|
||||
namespace netw { class messaging_service; }
|
||||
class repair_service;
|
||||
namespace cdc { class generation_service; }
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
}
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -83,14 +51,10 @@ struct http_context {
|
||||
};
|
||||
|
||||
future<> set_server_init(http_context& ctx);
|
||||
future<> set_server_config(http_context& ctx, const db::config& cfg);
|
||||
future<> set_server_config(http_context& ctx);
|
||||
future<> set_server_snitch(http_context& ctx);
|
||||
future<> set_server_storage_service(http_context& ctx, sharded<service::storage_service>& ss, sharded<gms::gossiper>& g, sharded<cdc::generation_service>& cdc_gs);
|
||||
future<> set_server_sstables_loader(http_context& ctx, sharded<sstables_loader>& sst_loader);
|
||||
future<> unset_server_sstables_loader(http_context& ctx);
|
||||
future<> set_server_view_builder(http_context& ctx, sharded<db::view::view_builder>& vb);
|
||||
future<> unset_server_view_builder(http_context& ctx);
|
||||
future<> set_server_repair(http_context& ctx, sharded<repair_service>& repair);
|
||||
future<> set_server_storage_service(http_context& ctx);
|
||||
future<> set_server_repair(http_context& ctx, sharded<netw::messaging_service>& ms);
|
||||
future<> unset_server_repair(http_context& ctx);
|
||||
future<> set_transport_controller(http_context& ctx, cql_transport::controller& ctl);
|
||||
future<> unset_transport_controller(http_context& ctx);
|
||||
@@ -98,17 +62,14 @@ future<> set_rpc_controller(http_context& ctx, thrift_controller& ctl);
|
||||
future<> unset_rpc_controller(http_context& ctx);
|
||||
future<> set_server_snapshot(http_context& ctx, sharded<db::snapshot_ctl>& snap_ctl);
|
||||
future<> unset_server_snapshot(http_context& ctx);
|
||||
future<> set_server_gossip(http_context& ctx, sharded<gms::gossiper>& g);
|
||||
future<> set_server_gossip(http_context& ctx);
|
||||
future<> set_server_load_sstable(http_context& ctx);
|
||||
future<> set_server_messaging_service(http_context& ctx, sharded<netw::messaging_service>& ms);
|
||||
future<> unset_server_messaging_service(http_context& ctx);
|
||||
future<> set_server_storage_proxy(http_context& ctx, sharded<service::storage_service>& ss);
|
||||
future<> set_server_storage_proxy(http_context& ctx);
|
||||
future<> set_server_stream_manager(http_context& ctx);
|
||||
future<> set_hinted_handoff(http_context& ctx, sharded<gms::gossiper>& g);
|
||||
future<> unset_hinted_handoff(http_context& ctx);
|
||||
future<> set_server_gossip_settle(http_context& ctx, sharded<gms::gossiper>& g);
|
||||
future<> set_server_gossip_settle(http_context& ctx);
|
||||
future<> set_server_cache(http_context& ctx);
|
||||
future<> set_server_compaction_manager(http_context& ctx);
|
||||
future<> set_server_done(http_context& ctx);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -24,13 +24,10 @@
|
||||
#include <vector>
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "sstables/sstables.hh"
|
||||
#include "sstables/metadata_collector.hh"
|
||||
#include "utils/estimated_histogram.hh"
|
||||
#include <algorithm>
|
||||
#include "db/system_keyspace_view_types.hh"
|
||||
#include "db/data_listeners.hh"
|
||||
#include "storage_service.hh"
|
||||
#include "unimplemented.hh"
|
||||
|
||||
extern logging::logger apilog;
|
||||
|
||||
@@ -183,7 +180,7 @@ static future<json::json_return_type> get_cf_unleveled_sstables(http_context& ct
|
||||
|
||||
static int64_t min_partition_size(column_family& cf) {
|
||||
int64_t res = INT64_MAX;
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res = std::min(res, i->get_stats_metadata().estimated_partition_size.min());
|
||||
}
|
||||
return (res == INT64_MAX) ? 0 : res;
|
||||
@@ -191,7 +188,7 @@ static int64_t min_partition_size(column_family& cf) {
|
||||
|
||||
static int64_t max_partition_size(column_family& cf) {
|
||||
int64_t res = 0;
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res = std::max(i->get_stats_metadata().estimated_partition_size.max(), res);
|
||||
}
|
||||
return res;
|
||||
@@ -199,7 +196,7 @@ static int64_t max_partition_size(column_family& cf) {
|
||||
|
||||
static integral_ratio_holder mean_partition_size(column_family& cf) {
|
||||
integral_ratio_holder res;
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
auto c = i->get_stats_metadata().estimated_partition_size.count();
|
||||
res.sub += i->get_stats_metadata().estimated_partition_size.mean() * c;
|
||||
res.total += c;
|
||||
@@ -277,7 +274,7 @@ public:
|
||||
|
||||
static double get_compression_ratio(column_family& cf) {
|
||||
sum_ratio<double> result;
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i : *cf.get_sstables()) {
|
||||
auto compression_ratio = i->get_compression_ratio();
|
||||
if (compression_ratio != sstables::metadata_collector::NO_COMPRESSION_RATIO) {
|
||||
result(compression_ratio);
|
||||
@@ -314,7 +311,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
cf::get_column_family.set(r, [&ctx] (std::unique_ptr<request> req){
|
||||
std::list<cf::column_family_info> res;
|
||||
vector<cf::column_family_info> res;
|
||||
for (auto i: ctx.db.local().get_column_families_mapping()) {
|
||||
cf::column_family_info info;
|
||||
info.ks = i.first.first;
|
||||
@@ -322,7 +319,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
info.type = "ColumnFamilies";
|
||||
res.push_back(info);
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(json::stream_range_as_array(std::move(res), std::identity()));
|
||||
return make_ready_future<json::json_return_type>(json::stream_object(std::move(res)));
|
||||
});
|
||||
|
||||
cf::get_column_family_name_keyspace.set(r, [&ctx] (const_req req){
|
||||
@@ -427,7 +424,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
cf::get_estimated_row_size_histogram.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](column_family& cf) {
|
||||
utils::estimated_histogram res(0);
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res.merge(i->get_stats_metadata().estimated_partition_size);
|
||||
}
|
||||
return res;
|
||||
@@ -439,7 +436,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
cf::get_estimated_row_count.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](column_family& cf) {
|
||||
uint64_t res = 0;
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res += i->get_stats_metadata().estimated_partition_size.count();
|
||||
}
|
||||
return res;
|
||||
@@ -450,7 +447,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
cf::get_estimated_column_count_histogram.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](column_family& cf) {
|
||||
utils::estimated_histogram res(0);
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res.merge(i->get_stats_metadata().estimated_cells_count);
|
||||
}
|
||||
return res;
|
||||
@@ -602,8 +599,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_get_false_positive();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -611,8 +607,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_all_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_get_false_positive();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -620,8 +615,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_recent_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_get_recent_false_positive();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -629,8 +623,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_all_recent_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_get_recent_false_positive();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -662,8 +655,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_bloom_filter_disk_space_used.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_size();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -671,8 +663,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_all_bloom_filter_disk_space_used.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_size();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -680,8 +671,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_bloom_filter_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_memory_size();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -689,8 +679,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_all_bloom_filter_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_memory_size();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -698,8 +687,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_index_summary_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->get_summary().memory_footprint();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -707,8 +695,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
|
||||
cf::get_all_index_summary_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->get_summary().memory_footprint();
|
||||
});
|
||||
}, std::plus<uint64_t>());
|
||||
@@ -862,24 +849,18 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
cf::enable_auto_compaction.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return ctx.db.invoke_on(0, [&ctx, req = std::move(req)] (database& db) {
|
||||
auto g = database::autocompaction_toggle_guard(db);
|
||||
return foreach_column_family(ctx, req->param["name"], [](column_family &cf) {
|
||||
cf.enable_auto_compaction();
|
||||
}).then([g = std::move(g)] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
return foreach_column_family(ctx, req->param["name"], [](column_family &cf) {
|
||||
cf.enable_auto_compaction();
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
cf::disable_auto_compaction.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return ctx.db.invoke_on(0, [&ctx, req = std::move(req)] (database& db) {
|
||||
auto g = database::autocompaction_toggle_guard(db);
|
||||
return foreach_column_family(ctx, req->param["name"], [](column_family &cf) {
|
||||
cf.disable_auto_compaction();
|
||||
}).then([g = std::move(g)] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
return foreach_column_family(ctx, req->param["name"], [](column_family &cf) {
|
||||
cf.disable_auto_compaction();
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -887,7 +868,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
auto ks_cf = parse_fully_qualified_cf_name(req->param["name"]);
|
||||
auto&& ks = std::get<0>(ks_cf);
|
||||
auto&& cf_name = std::get<1>(ks_cf);
|
||||
return db::system_keyspace::load_view_build_progress().then([ks, cf_name, &ctx](const std::vector<db::system_keyspace_view_build_progress>& vb) mutable {
|
||||
return db::system_keyspace::load_view_build_progress().then([ks, cf_name, &ctx](const std::vector<db::system_keyspace::view_build_progress>& vb) mutable {
|
||||
std::set<sstring> vp;
|
||||
for (auto b : vb) {
|
||||
if (b.view.first == ks) {
|
||||
@@ -992,20 +973,45 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
cf::toppartitions.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
auto name = req->param["name"];
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(name);
|
||||
auto name_param = req->param["name"];
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(name_param);
|
||||
|
||||
api::req_param<std::chrono::milliseconds, unsigned> duration{*req, "duration", 1000ms};
|
||||
api::req_param<unsigned> capacity(*req, "capacity", 256);
|
||||
api::req_param<unsigned> list_size(*req, "list_size", 10);
|
||||
|
||||
apilog.info("toppartitions query: name={} duration={} list_size={} capacity={}",
|
||||
name, duration.param, list_size.param, capacity.param);
|
||||
name_param, duration.param, list_size.param, capacity.param);
|
||||
|
||||
return seastar::do_with(db::toppartitions_query(ctx.db, {{ks, cf}}, {}, duration.value, list_size, capacity), [&ctx] (db::toppartitions_query& q) {
|
||||
return run_toppartitions_query(q, ctx, true);
|
||||
return seastar::do_with(db::toppartitions_query(ctx.db, ks, cf, duration.value, list_size, capacity), [&ctx](auto& q) {
|
||||
return q.scatter().then([&q] {
|
||||
return sleep(q.duration()).then([&q] {
|
||||
return q.gather(q.capacity()).then([&q] (auto topk_results) {
|
||||
apilog.debug("toppartitions query: processing results");
|
||||
cf::toppartitions_query_results results;
|
||||
|
||||
results.read_cardinality = topk_results.read.size();
|
||||
results.write_cardinality = topk_results.write.size();
|
||||
|
||||
for (auto& d: topk_results.read.top(q.list_size())) {
|
||||
cf::toppartitions_record r;
|
||||
r.partition = sstring(d.item);
|
||||
r.count = d.count;
|
||||
r.error = d.error;
|
||||
results.read.push(r);
|
||||
}
|
||||
for (auto& d: topk_results.write.top(q.list_size())) {
|
||||
cf::toppartitions_record r;
|
||||
r.partition = sstring(d.item);
|
||||
r.count = d.count;
|
||||
r.error = d.error;
|
||||
results.write.push(r);
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -77,7 +77,7 @@ struct map_reduce_column_families_locally {
|
||||
future<std::unique_ptr<std::any>> operator()(database& db) const {
|
||||
auto res = seastar::make_lw_shared<std::unique_ptr<std::any>>(std::make_unique<std::any>(init));
|
||||
return do_for_each(db.get_column_families(), [res, this](const std::pair<utils::UUID, seastar::lw_shared_ptr<table>>& i) {
|
||||
*res = reducer(std::move(*res), mapper(*i.second.get()));
|
||||
*res = std::move(reducer(std::move(*res), mapper(*i.second.get())));
|
||||
}).then([res] {
|
||||
return std::move(*res);
|
||||
});
|
||||
@@ -116,7 +116,4 @@ future<json::json_return_type> get_cf_stats(http_context& ctx, const sstring& n
|
||||
future<json::json_return_type> get_cf_stats(http_context& ctx,
|
||||
int64_t column_family_stats::*f);
|
||||
|
||||
|
||||
std::tuple<sstring, sstring> parse_fully_qualified_cf_name(sstring name);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -20,11 +20,10 @@
|
||||
*/
|
||||
|
||||
#include "compaction_manager.hh"
|
||||
#include "compaction/compaction_manager.hh"
|
||||
#include "sstables/compaction_manager.hh"
|
||||
#include "api/api-doc/compaction_manager.json.hh"
|
||||
#include "db/system_keyspace.hh"
|
||||
#include "column_family.hh"
|
||||
#include "unimplemented.hh"
|
||||
#include <utility>
|
||||
|
||||
namespace api {
|
||||
@@ -59,13 +58,12 @@ void set_compaction_manager(http_context& ctx, routes& r) {
|
||||
|
||||
for (const auto& c : cm.get_compactions()) {
|
||||
cm::summary s;
|
||||
s.id = c.compaction_uuid.to_sstring();
|
||||
s.ks = c.ks_name;
|
||||
s.cf = c.cf_name;
|
||||
s.ks = c->ks_name;
|
||||
s.cf = c->cf_name;
|
||||
s.unit = "keys";
|
||||
s.task_type = sstables::compaction_name(c.type);
|
||||
s.completed = c.total_keys_written;
|
||||
s.total = c.total_partitions;
|
||||
s.task_type = sstables::compaction_name(c->type);
|
||||
s.completed = c->total_keys_written;
|
||||
s.total = c->total_partitions;
|
||||
summaries.push_back(std::move(s));
|
||||
}
|
||||
return summaries;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-present ScyllaDB
|
||||
* Copyright 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "api/config.hh"
|
||||
#include "api/api-doc/config.json.hh"
|
||||
#include "db/config.hh"
|
||||
#include "database.hh"
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
@@ -88,11 +89,11 @@ future<> get_config_swagger_entry(std::string_view name, const std::string& desc
|
||||
|
||||
namespace cs = httpd::config_json;
|
||||
|
||||
void set_config(std::shared_ptr < api_registry_builder20 > rb, http_context& ctx, routes& r, const db::config& cfg) {
|
||||
rb->register_function(r, [&cfg] (output_stream<char>& os) {
|
||||
return do_with(true, [&os, &cfg] (bool& first) {
|
||||
void set_config(std::shared_ptr < api_registry_builder20 > rb, http_context& ctx, routes& r) {
|
||||
rb->register_function(r, [&ctx] (output_stream<char>& os) {
|
||||
return do_with(true, [&os, &ctx] (bool& first) {
|
||||
auto f = make_ready_future();
|
||||
for (auto&& cfg_ref : cfg.values()) {
|
||||
for (auto&& cfg_ref : ctx.db.local().get_config().values()) {
|
||||
auto&& cfg = cfg_ref.get();
|
||||
f = f.then([&os, &first, &cfg] {
|
||||
return get_config_swagger_entry(cfg.name(), std::string(cfg.desc()), cfg.type_name(), first, os);
|
||||
@@ -102,9 +103,9 @@ void set_config(std::shared_ptr < api_registry_builder20 > rb, http_context& ctx
|
||||
});
|
||||
});
|
||||
|
||||
cs::find_config_id.set(r, [&cfg] (const_req r) {
|
||||
cs::find_config_id.set(r, [&ctx] (const_req r) {
|
||||
auto id = r.param["id"];
|
||||
for (auto&& cfg_ref : cfg.values()) {
|
||||
for (auto&& cfg_ref : ctx.db.local().get_config().values()) {
|
||||
auto&& cfg = cfg_ref.get();
|
||||
if (id == cfg.name()) {
|
||||
return cfg.value_as_json();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018-present ScyllaDB
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -26,5 +26,5 @@
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_config(std::shared_ptr<api_registry_builder20> rb, http_context& ctx, routes& r, const db::config& cfg);
|
||||
void set_config(std::shared_ptr<api_registry_builder20> rb, http_context& ctx, routes& r);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020-present ScyllaDB
|
||||
* Copyright (C) 2020 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019-present ScyllaDB
|
||||
* Copyright (C) 2019 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -28,10 +28,10 @@ namespace api {
|
||||
|
||||
namespace fd = httpd::failure_detector_json;
|
||||
|
||||
void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
fd::get_all_endpoint_states.set(r, [&g](std::unique_ptr<request> req) {
|
||||
void set_failure_detector(http_context& ctx, routes& r) {
|
||||
fd::get_all_endpoint_states.set(r, [](std::unique_ptr<request> req) {
|
||||
std::vector<fd::endpoint_state> res;
|
||||
for (auto i : g.endpoint_state_map) {
|
||||
for (auto i : gms::get_local_gossiper().endpoint_state_map) {
|
||||
fd::endpoint_state val;
|
||||
val.addrs = boost::lexical_cast<std::string>(i.first);
|
||||
val.is_alive = i.second.is_alive();
|
||||
@@ -52,14 +52,14 @@ void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
|
||||
fd::get_up_endpoint_count.set(r, [&g](std::unique_ptr<request> req) {
|
||||
return gms::get_up_endpoint_count(g).then([](int res) {
|
||||
fd::get_up_endpoint_count.set(r, [](std::unique_ptr<request> req) {
|
||||
return gms::get_up_endpoint_count().then([](int res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
fd::get_down_endpoint_count.set(r, [&g](std::unique_ptr<request> req) {
|
||||
return gms::get_down_endpoint_count(g).then([](int res) {
|
||||
fd::get_down_endpoint_count.set(r, [](std::unique_ptr<request> req) {
|
||||
return gms::get_down_endpoint_count().then([](int res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
@@ -70,8 +70,8 @@ void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
});
|
||||
});
|
||||
|
||||
fd::get_simple_states.set(r, [&g] (std::unique_ptr<request> req) {
|
||||
return gms::get_simple_states(g).then([](const std::map<sstring, sstring>& map) {
|
||||
fd::get_simple_states.set(r, [] (std::unique_ptr<request> req) {
|
||||
return gms::get_simple_states().then([](const std::map<sstring, sstring>& map) {
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value<fd::mapper>(map));
|
||||
});
|
||||
});
|
||||
@@ -83,8 +83,8 @@ void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
});
|
||||
});
|
||||
|
||||
fd::get_endpoint_state.set(r, [&g] (std::unique_ptr<request> req) {
|
||||
return get_endpoint_state(g, req->param["addr"]).then([](const sstring& state) {
|
||||
fd::get_endpoint_state.set(r, [](std::unique_ptr<request> req) {
|
||||
return gms::get_endpoint_state(req->param["addr"]).then([](const sstring& state) {
|
||||
return make_ready_future<json::json_return_type>(state);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,14 +23,8 @@
|
||||
|
||||
#include "api.hh"
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
}
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g);
|
||||
void set_failure_detector(http_context& ctx, routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -26,50 +26,50 @@
|
||||
namespace api {
|
||||
using namespace json;
|
||||
|
||||
void set_gossiper(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
httpd::gossiper_json::get_down_endpoint.set(r, [&g] (const_req req) {
|
||||
auto res = g.get_unreachable_members();
|
||||
void set_gossiper(http_context& ctx, routes& r) {
|
||||
httpd::gossiper_json::get_down_endpoint.set(r, [] (const_req req) {
|
||||
auto res = gms::get_local_gossiper().get_unreachable_members();
|
||||
return container_to_vec(res);
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_live_endpoint.set(r, [&g] (const_req req) {
|
||||
auto res = g.get_live_members();
|
||||
httpd::gossiper_json::get_live_endpoint.set(r, [] (const_req req) {
|
||||
auto res = gms::get_local_gossiper().get_live_members();
|
||||
return container_to_vec(res);
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_endpoint_downtime.set(r, [&g] (const_req req) {
|
||||
httpd::gossiper_json::get_endpoint_downtime.set(r, [] (const_req req) {
|
||||
gms::inet_address ep(req.param["addr"]);
|
||||
return g.get_endpoint_downtime(ep);
|
||||
return gms::get_local_gossiper().get_endpoint_downtime(ep);
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_current_generation_number.set(r, [&g] (std::unique_ptr<request> req) {
|
||||
httpd::gossiper_json::get_current_generation_number.set(r, [] (std::unique_ptr<request> req) {
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
return g.get_current_generation_number(ep).then([] (int res) {
|
||||
return gms::get_local_gossiper().get_current_generation_number(ep).then([] (int res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_current_heart_beat_version.set(r, [&g] (std::unique_ptr<request> req) {
|
||||
httpd::gossiper_json::get_current_heart_beat_version.set(r, [] (std::unique_ptr<request> req) {
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
return g.get_current_heart_beat_version(ep).then([] (int res) {
|
||||
return gms::get_local_gossiper().get_current_heart_beat_version(ep).then([] (int res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
httpd::gossiper_json::assassinate_endpoint.set(r, [&g](std::unique_ptr<request> req) {
|
||||
httpd::gossiper_json::assassinate_endpoint.set(r, [](std::unique_ptr<request> req) {
|
||||
if (req->get_query_param("unsafe") != "True") {
|
||||
return g.assassinate_endpoint(req->param["addr"]).then([] {
|
||||
return gms::get_local_gossiper().assassinate_endpoint(req->param["addr"]).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
}
|
||||
return g.unsafe_assassinate_endpoint(req->param["addr"]).then([] {
|
||||
return gms::get_local_gossiper().unsafe_assassinate_endpoint(req->param["addr"]).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
httpd::gossiper_json::force_remove_endpoint.set(r, [&g](std::unique_ptr<request> req) {
|
||||
httpd::gossiper_json::force_remove_endpoint.set(r, [](std::unique_ptr<request> req) {
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
return g.force_remove_endpoint(ep).then([] {
|
||||
return gms::get_local_gossiper().force_remove_endpoint(ep).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,14 +23,8 @@
|
||||
|
||||
#include "api.hh"
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
}
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_gossiper(http_context& ctx, routes& r, gms::gossiper& g);
|
||||
void set_gossiper(http_context& ctx, routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -19,93 +19,15 @@
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "hinted_handoff.hh"
|
||||
#include "api/api-doc/hinted_handoff.json.hh"
|
||||
|
||||
#include "gms/inet_address.hh"
|
||||
#include "gms/gossiper.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
using namespace json;
|
||||
namespace hh = httpd::hinted_handoff_json;
|
||||
|
||||
void set_hinted_handoff(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
hh::create_hints_sync_point.set(r, [&ctx, &g] (std::unique_ptr<request> req) -> future<json::json_return_type> {
|
||||
auto parse_hosts_list = [&g] (sstring arg) {
|
||||
std::vector<sstring> hosts_str = split(arg, ",");
|
||||
std::vector<gms::inet_address> hosts;
|
||||
hosts.reserve(hosts_str.size());
|
||||
|
||||
if (hosts_str.empty()) {
|
||||
// No target_hosts specified means that we should wait for hints for all nodes to be sent
|
||||
const auto members_set = g.get_live_members();
|
||||
std::copy(members_set.begin(), members_set.end(), std::back_inserter(hosts));
|
||||
} else {
|
||||
for (const auto& host_str : hosts_str) {
|
||||
try {
|
||||
gms::inet_address host;
|
||||
host = gms::inet_address(host_str);
|
||||
hosts.push_back(host);
|
||||
} catch (std::exception& e) {
|
||||
throw httpd::bad_param_exception(format("Failed to parse host address {}: {}", host_str, e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return hosts;
|
||||
};
|
||||
|
||||
std::vector<gms::inet_address> target_hosts = parse_hosts_list(req->get_query_param("target_hosts"));
|
||||
return ctx.sp.local().create_hint_sync_point(std::move(target_hosts)).then([] (db::hints::sync_point sync_point) {
|
||||
return json::json_return_type(sync_point.encode());
|
||||
});
|
||||
});
|
||||
|
||||
hh::get_hints_sync_point.set(r, [&ctx] (std::unique_ptr<request> req) -> future<json::json_return_type> {
|
||||
db::hints::sync_point sync_point;
|
||||
const sstring encoded = req->get_query_param("id");
|
||||
try {
|
||||
sync_point = db::hints::sync_point::decode(encoded);
|
||||
} catch (std::exception& e) {
|
||||
throw httpd::bad_param_exception(format("Failed to parse the sync point description {}: {}", encoded, e.what()));
|
||||
}
|
||||
|
||||
lowres_clock::time_point deadline;
|
||||
const sstring timeout_str = req->get_query_param("timeout");
|
||||
try {
|
||||
deadline = [&] {
|
||||
if (timeout_str.empty()) {
|
||||
// Empty string - don't wait at all, just check the status
|
||||
return lowres_clock::time_point::min();
|
||||
} else {
|
||||
const auto timeout = std::stoll(timeout_str);
|
||||
if (timeout >= 0) {
|
||||
// Wait until the point is reached, or until `timeout` seconds elapse
|
||||
return lowres_clock::now() + std::chrono::seconds(timeout);
|
||||
} else {
|
||||
// Negative value indicates infinite timeout
|
||||
return lowres_clock::time_point::max();
|
||||
}
|
||||
}
|
||||
} ();
|
||||
} catch (std::exception& e) {
|
||||
throw httpd::bad_param_exception(format("Failed to parse the timeout parameter {}: {}", timeout_str, e.what()));
|
||||
}
|
||||
|
||||
using return_type = hh::ns_get_hints_sync_point::get_hints_sync_point_return_type;
|
||||
using return_type_wrapper = hh::ns_get_hints_sync_point::return_type_wrapper;
|
||||
|
||||
return ctx.sp.local().wait_for_hint_sync_point(std::move(sync_point), deadline).then([] {
|
||||
return json::json_return_type(return_type_wrapper(return_type::DONE));
|
||||
}).handle_exception_type([] (const timed_out_error&) {
|
||||
return json::json_return_type(return_type_wrapper(return_type::IN_PROGRESS));
|
||||
});
|
||||
});
|
||||
|
||||
void set_hinted_handoff(http_context& ctx, routes& r) {
|
||||
hh::list_endpoints_pending_hints.set(r, [] (std::unique_ptr<request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
@@ -149,16 +71,5 @@ void set_hinted_handoff(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
});
|
||||
}
|
||||
|
||||
void unset_hinted_handoff(http_context& ctx, routes& r) {
|
||||
hh::create_hints_sync_point.unset(r);
|
||||
hh::get_hints_sync_point.unset(r);
|
||||
|
||||
hh::list_endpoints_pending_hints.unset(r);
|
||||
hh::truncate_all_hints.unset(r);
|
||||
hh::schedule_hint_delivery.unset(r);
|
||||
hh::pause_hints_delivery.unset(r);
|
||||
hh::get_create_hint_count.unset(r);
|
||||
hh::get_not_stored_hints_count.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,15 +23,8 @@
|
||||
|
||||
#include "api.hh"
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
}
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_hinted_handoff(http_context& ctx, routes& r, gms::gossiper& g);
|
||||
void unset_hinted_handoff(http_context& ctx, routes& r);
|
||||
void set_hinted_handoff(http_context& ctx, routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "utils/logalloc.hh"
|
||||
#include "log.hh"
|
||||
#include "database.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -96,10 +96,6 @@ void set_messaging_service(http_context& ctx, routes& r, sharded<netw::messaging
|
||||
return c.get_stats().sent_messages;
|
||||
}));
|
||||
|
||||
get_replied_messages.set(r, get_client_getter(ms, [](const shard_info& c) {
|
||||
return c.get_stats().replied;
|
||||
}));
|
||||
|
||||
get_dropped_messages.set(r, get_client_getter(ms, [](const shard_info& c) {
|
||||
// We don't have the same drop message mechanism
|
||||
// as origin has.
|
||||
@@ -159,7 +155,6 @@ void set_messaging_service(http_context& ctx, routes& r, sharded<netw::messaging
|
||||
void unset_messaging_service(http_context& ctx, routes& r) {
|
||||
get_timeout_messages.unset(r);
|
||||
get_sent_messages.unset(r);
|
||||
get_replied_messages.unset(r);
|
||||
get_dropped_messages.unset(r);
|
||||
get_exception_messages.unset(r);
|
||||
get_pending_messages.unset(r);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -193,7 +193,7 @@ sum_timer_stats_storage_proxy(distributed<proxy>& d,
|
||||
});
|
||||
}
|
||||
|
||||
void set_storage_proxy(http_context& ctx, routes& r, sharded<service::storage_service>& ss) {
|
||||
void set_storage_proxy(http_context& ctx, routes& r) {
|
||||
sp::get_total_hints.set(r, [](std::unique_ptr<request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
@@ -363,8 +363,8 @@ void set_storage_proxy(http_context& ctx, routes& r, sharded<service::storage_se
|
||||
return sum_stats_storage_proxy(ctx.sp, &service::storage_proxy_stats::stats::read_repair_repaired_background);
|
||||
});
|
||||
|
||||
sp::get_schema_versions.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().describe_schema_versions().then([] (auto result) {
|
||||
sp::get_schema_versions.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().describe_schema_versions().then([] (auto result) {
|
||||
std::vector<sp::mapper_list> res;
|
||||
for (auto e : result) {
|
||||
sp::mapper_list entry;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -21,13 +21,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
#include "api.hh"
|
||||
|
||||
namespace service { class storage_service; }
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_storage_proxy(http_context& ctx, routes& r, sharded<service::storage_service>& ss);
|
||||
void set_storage_proxy(http_context& ctx, routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,28 +23,22 @@
|
||||
#include "api/api-doc/storage_service.json.hh"
|
||||
#include "db/config.hh"
|
||||
#include "db/schema_tables.hh"
|
||||
#include "utils/hash.hh"
|
||||
#include <sstream>
|
||||
#include <optional>
|
||||
#include <time.h>
|
||||
#include <algorithm>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <boost/algorithm/string/trim_all.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include "service/storage_service.hh"
|
||||
#include "service/load_meter.hh"
|
||||
#include "db/commitlog/commitlog.hh"
|
||||
#include "gms/gossiper.hh"
|
||||
#include "db/system_keyspace.hh"
|
||||
#include "seastar/http/exception.hh"
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include "repair/repair.hh"
|
||||
#include "locator/snitch_base.hh"
|
||||
#include "column_family.hh"
|
||||
#include "log.hh"
|
||||
#include "release.hh"
|
||||
#include "compaction/compaction_manager.hh"
|
||||
#include "sstables/compaction_manager.hh"
|
||||
#include "sstables/sstables.hh"
|
||||
#include "database.hh"
|
||||
#include "db/extensions.hh"
|
||||
@@ -52,13 +46,6 @@
|
||||
#include "transport/controller.hh"
|
||||
#include "thrift/controller.hh"
|
||||
#include "locator/token_metadata.hh"
|
||||
#include "cdc/generation_service.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "locator/abstract_replication_strategy.hh"
|
||||
#include "sstables_loader.hh"
|
||||
#include "db/view/view_builder.hh"
|
||||
|
||||
extern logging::logger apilog;
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -84,7 +71,7 @@ static ss::token_range token_range_endpoints_to_json(const dht::token_range_endp
|
||||
r.rpc_endpoints = d._rpc_endpoints;
|
||||
for (auto det : d._endpoint_details) {
|
||||
ss::endpoint_detail ed;
|
||||
ed.host = boost::lexical_cast<std::string>(det._host);
|
||||
ed.host = det._host;
|
||||
ed.datacenter = det._datacenter;
|
||||
if (det._rack != "") {
|
||||
ed.rack = det._rack;
|
||||
@@ -107,59 +94,12 @@ static auto wrap_ks_cf(http_context &ctx, ks_cf_func f) {
|
||||
};
|
||||
}
|
||||
|
||||
seastar::future<json::json_return_type> run_toppartitions_query(db::toppartitions_query& q, http_context &ctx, bool legacy_request) {
|
||||
namespace cf = httpd::column_family_json;
|
||||
return q.scatter().then([&q, legacy_request] {
|
||||
return sleep(q.duration()).then([&q, legacy_request] {
|
||||
return q.gather(q.capacity()).then([&q, legacy_request] (auto topk_results) {
|
||||
apilog.debug("toppartitions query: processing results");
|
||||
cf::toppartitions_query_results results;
|
||||
|
||||
results.read_cardinality = topk_results.read.size();
|
||||
results.write_cardinality = topk_results.write.size();
|
||||
|
||||
for (auto& d: topk_results.read.top(q.list_size())) {
|
||||
cf::toppartitions_record r;
|
||||
r.partition = (legacy_request ? "" : "(" + d.item.schema->ks_name() + ":" + d.item.schema->cf_name() + ") ") + sstring(d.item);
|
||||
r.count = d.count;
|
||||
r.error = d.error;
|
||||
results.read.push(r);
|
||||
}
|
||||
for (auto& d: topk_results.write.top(q.list_size())) {
|
||||
cf::toppartitions_record r;
|
||||
r.partition = (legacy_request ? "" : "(" + d.item.schema->ks_name() + ":" + d.item.schema->cf_name() + ") ") + sstring(d.item);
|
||||
r.count = d.count;
|
||||
r.error = d.error;
|
||||
results.write.push(r);
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
future<json::json_return_type> set_tables_autocompaction(http_context& ctx, service::storage_service& ss, const sstring &keyspace, std::vector<sstring> tables, bool enabled) {
|
||||
future<json::json_return_type> set_tables_autocompaction(http_context& ctx, const sstring &keyspace, std::vector<sstring> tables, bool enabled) {
|
||||
if (tables.empty()) {
|
||||
tables = map_keys(ctx.db.local().find_keyspace(keyspace).metadata().get()->cf_meta_data());
|
||||
}
|
||||
|
||||
apilog.info("set_tables_autocompaction: enabled={} keyspace={} tables={}", enabled, keyspace, tables);
|
||||
return do_with(keyspace, std::move(tables), [&ctx, enabled] (const sstring &keyspace, const std::vector<sstring>& tables) {
|
||||
return ctx.db.invoke_on(0, [&ctx, &keyspace, &tables, enabled] (database& db) {
|
||||
auto g = database::autocompaction_toggle_guard(db);
|
||||
return ctx.db.invoke_on_all([&keyspace, &tables, enabled] (database& db) {
|
||||
return parallel_for_each(tables, [&db, &keyspace, enabled] (const sstring& table) {
|
||||
column_family& cf = db.find_column_family(keyspace, table);
|
||||
if (enabled) {
|
||||
cf.enable_auto_compaction();
|
||||
} else {
|
||||
cf.disable_auto_compaction();
|
||||
}
|
||||
return make_ready_future<>();
|
||||
});
|
||||
}).finally([g = std::move(g)] {});
|
||||
});
|
||||
}).then([] {
|
||||
return service::get_local_storage_service().set_tables_autocompaction(keyspace, tables, enabled).then([]{
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
}
|
||||
@@ -216,10 +156,10 @@ void unset_rpc_controller(http_context& ctx, routes& r) {
|
||||
ss::is_rpc_server_running.unset(r);
|
||||
}
|
||||
|
||||
void set_repair(http_context& ctx, routes& r, sharded<repair_service>& repair) {
|
||||
ss::repair_async.set(r, [&ctx, &repair](std::unique_ptr<request> req) {
|
||||
void set_repair(http_context& ctx, routes& r, sharded<netw::messaging_service>& ms) {
|
||||
ss::repair_async.set(r, [&ctx, &ms](std::unique_ptr<request> req) {
|
||||
static std::vector<sstring> options = {"primaryRange", "parallelism", "incremental",
|
||||
"jobThreads", "ranges", "columnFamilies", "dataCenters", "hosts", "ignore_nodes", "trace",
|
||||
"jobThreads", "ranges", "columnFamilies", "dataCenters", "hosts", "trace",
|
||||
"startToken", "endToken" };
|
||||
std::unordered_map<sstring, sstring> options_map;
|
||||
for (auto o : options) {
|
||||
@@ -233,7 +173,7 @@ void set_repair(http_context& ctx, routes& r, sharded<repair_service>& repair) {
|
||||
// returns immediately, not waiting for the repair to finish. The user
|
||||
// then has other mechanisms to track the ongoing repair's progress,
|
||||
// or stop it.
|
||||
return repair_start(repair, validate_keyspace(ctx, req->param),
|
||||
return repair_start(ctx.db, ms, validate_keyspace(ctx, req->param),
|
||||
options_map).then([] (int i) {
|
||||
return make_ready_future<json::json_return_type>(i);
|
||||
});
|
||||
@@ -291,14 +231,14 @@ void set_repair(http_context& ctx, routes& r, sharded<repair_service>& repair) {
|
||||
});
|
||||
});
|
||||
|
||||
ss::force_terminate_all_repair_sessions.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return repair_abort_all(ctx.db).then([] {
|
||||
ss::force_terminate_all_repair_sessions.set(r, [](std::unique_ptr<request> req) {
|
||||
return repair_abort_all(service::get_local_storage_service().db()).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::force_terminate_all_repair_sessions_new.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return repair_abort_all(ctx.db).then([] {
|
||||
ss::force_terminate_all_repair_sessions_new.set(r, [](std::unique_ptr<request> req) {
|
||||
return repair_abort_all(service::get_local_storage_service().db()).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
@@ -314,60 +254,9 @@ void unset_repair(http_context& ctx, routes& r) {
|
||||
ss::force_terminate_all_repair_sessions_new.unset(r);
|
||||
}
|
||||
|
||||
void set_sstables_loader(http_context& ctx, routes& r, sharded<sstables_loader>& sst_loader) {
|
||||
ss::load_new_ss_tables.set(r, [&ctx, &sst_loader](std::unique_ptr<request> req) {
|
||||
auto ks = validate_keyspace(ctx, req->param);
|
||||
auto cf = req->get_query_param("cf");
|
||||
auto stream = req->get_query_param("load_and_stream");
|
||||
auto primary_replica = req->get_query_param("primary_replica_only");
|
||||
boost::algorithm::to_lower(stream);
|
||||
boost::algorithm::to_lower(primary_replica);
|
||||
bool load_and_stream = stream == "true" || stream == "1";
|
||||
bool primary_replica_only = primary_replica == "true" || primary_replica == "1";
|
||||
// No need to add the keyspace, since all we want is to avoid always sending this to the same
|
||||
// CPU. Even then I am being overzealous here. This is not something that happens all the time.
|
||||
auto coordinator = std::hash<sstring>()(cf) % smp::count;
|
||||
return sst_loader.invoke_on(coordinator,
|
||||
[ks = std::move(ks), cf = std::move(cf),
|
||||
load_and_stream, primary_replica_only] (sstables_loader& loader) {
|
||||
return loader.load_new_sstables(ks, cf, load_and_stream, primary_replica_only);
|
||||
}).then_wrapped([] (auto&& f) {
|
||||
if (f.failed()) {
|
||||
auto msg = fmt::format("Failed to load new sstables: {}", f.get_exception());
|
||||
return make_exception_future<json::json_return_type>(httpd::server_error_exception(msg));
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void unset_sstables_loader(http_context& ctx, routes& r) {
|
||||
ss::load_new_ss_tables.unset(r);
|
||||
}
|
||||
|
||||
void set_view_builder(http_context& ctx, routes& r, sharded<db::view::view_builder>& vb) {
|
||||
ss::view_build_statuses.set(r, [&ctx, &vb] (std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto view = req->param["view"];
|
||||
return vb.local().view_build_statuses(std::move(keyspace), std::move(view)).then([] (std::unordered_map<sstring, sstring> status) {
|
||||
std::vector<storage_service_json::mapper> res;
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value(std::move(status), res));
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void unset_view_builder(http_context& ctx, routes& r) {
|
||||
ss::view_build_statuses.unset(r);
|
||||
}
|
||||
|
||||
static future<json::json_return_type> describe_ring_as_json(sharded<service::storage_service>& ss, sstring keyspace) {
|
||||
co_return json::json_return_type(stream_range_as_array(co_await ss.local().describe_ring(keyspace), token_range_endpoints_to_json));
|
||||
}
|
||||
|
||||
void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_service>& ss, gms::gossiper& g, sharded<cdc::generation_service>& cdc_gs) {
|
||||
void set_storage_service(http_context& ctx, routes& r) {
|
||||
ss::local_hostid.set(r, [](std::unique_ptr<request> req) {
|
||||
return db::system_keyspace::load_local_host_id().then([](const utils::UUID& id) {
|
||||
return db::system_keyspace::get_local_host_id().then([](const utils::UUID& id) {
|
||||
return make_ready_future<json::json_return_type>(id.to_sstring());
|
||||
});
|
||||
});
|
||||
@@ -389,8 +278,8 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return ctx.db.local().commitlog()->active_config().commit_log_location;
|
||||
});
|
||||
|
||||
ss::get_token_endpoint.set(r, [&ss] (std::unique_ptr<request> req) {
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(ss.local().get_token_to_endpoint_map(), [](const auto& i) {
|
||||
ss::get_token_endpoint.set(r, [] (std::unique_ptr<request> req) {
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(service::get_local_storage_service().get_token_to_endpoint_map(), [](const auto& i) {
|
||||
storage_service_json::mapper val;
|
||||
val.key = boost::lexical_cast<std::string>(i.first);
|
||||
val.value = boost::lexical_cast<std::string>(i.second);
|
||||
@@ -398,56 +287,6 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
}));
|
||||
});
|
||||
|
||||
ss::toppartitions_generic.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
bool filters_provided = false;
|
||||
|
||||
std::unordered_set<std::tuple<sstring, sstring>, utils::tuple_hash> table_filters {};
|
||||
if (req->query_parameters.contains("table_filters")) {
|
||||
filters_provided = true;
|
||||
auto filters = req->get_query_param("table_filters");
|
||||
std::stringstream ss { filters };
|
||||
std::string filter;
|
||||
while (!filters.empty() && ss.good()) {
|
||||
std::getline(ss, filter, ',');
|
||||
table_filters.emplace(parse_fully_qualified_cf_name(filter));
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<sstring> keyspace_filters {};
|
||||
if (req->query_parameters.contains("keyspace_filters")) {
|
||||
filters_provided = true;
|
||||
auto filters = req->get_query_param("keyspace_filters");
|
||||
std::stringstream ss { filters };
|
||||
std::string filter;
|
||||
while (!filters.empty() && ss.good()) {
|
||||
std::getline(ss, filter, ',');
|
||||
keyspace_filters.emplace(std::move(filter));
|
||||
}
|
||||
}
|
||||
|
||||
// when the query is empty return immediately
|
||||
if (filters_provided && table_filters.empty() && keyspace_filters.empty()) {
|
||||
apilog.debug("toppartitions query: processing results");
|
||||
httpd::column_family_json::toppartitions_query_results results;
|
||||
|
||||
results.read_cardinality = 0;
|
||||
results.write_cardinality = 0;
|
||||
|
||||
return make_ready_future<json::json_return_type>(results);
|
||||
}
|
||||
|
||||
api::req_param<std::chrono::milliseconds, unsigned> duration{*req, "duration", 1000ms};
|
||||
api::req_param<unsigned> capacity(*req, "capacity", 256);
|
||||
api::req_param<unsigned> list_size(*req, "list_size", 10);
|
||||
|
||||
apilog.info("toppartitions query: #table_filters={} #keyspace_filters={} duration={} list_size={} capacity={}",
|
||||
!table_filters.empty() ? std::to_string(table_filters.size()) : "all", !keyspace_filters.empty() ? std::to_string(keyspace_filters.size()) : "all", duration.param, list_size.param, capacity.param);
|
||||
|
||||
return seastar::do_with(db::toppartitions_query(ctx.db, std::move(table_filters), std::move(keyspace_filters), duration.value, list_size, capacity), [&ctx] (db::toppartitions_query& q) {
|
||||
return run_toppartitions_query(q, ctx);
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_leaving_nodes.set(r, [&ctx](const_req req) {
|
||||
return container_to_vec(ctx.get_token_metadata().get_leaving_endpoints());
|
||||
});
|
||||
@@ -466,15 +305,15 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return container_to_vec(addr);
|
||||
});
|
||||
|
||||
ss::get_release_version.set(r, [&ss](const_req req) {
|
||||
return ss.local().get_release_version();
|
||||
ss::get_release_version.set(r, [](const_req req) {
|
||||
return service::get_local_storage_service().get_release_version();
|
||||
});
|
||||
|
||||
ss::get_scylla_release_version.set(r, [](const_req req) {
|
||||
return scylla_version();
|
||||
});
|
||||
ss::get_schema_version.set(r, [&ss](const_req req) {
|
||||
return ss.local().get_schema_version();
|
||||
ss::get_schema_version.set(r, [](const_req req) {
|
||||
return service::get_local_storage_service().get_schema_version();
|
||||
});
|
||||
|
||||
ss::get_all_data_file_locations.set(r, [&ctx](const_req req) {
|
||||
@@ -485,11 +324,11 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return ctx.db.local().get_config().saved_caches_directory();
|
||||
});
|
||||
|
||||
ss::get_range_to_endpoint_map.set(r, [&ctx, &ss](std::unique_ptr<request> req) {
|
||||
ss::get_range_to_endpoint_map.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
std::vector<ss::maplist_mapper> res;
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(ss.local().get_range_to_address_map(keyspace),
|
||||
[](const std::pair<dht::token_range, inet_address_vector_replica_set>& entry){
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(service::get_local_storage_service().get_range_to_address_map(keyspace),
|
||||
[](const std::pair<dht::token_range, std::vector<gms::inet_address>>& entry){
|
||||
ss::maplist_mapper m;
|
||||
if (entry.first.start()) {
|
||||
m.key.push(entry.first.start().value().value().to_sstring());
|
||||
@@ -516,12 +355,13 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
|
||||
ss::describe_any_ring.set(r, [&ctx, &ss](std::unique_ptr<request> req) {
|
||||
return describe_ring_as_json(ss, "");
|
||||
ss::describe_any_ring.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(service::get_local_storage_service().describe_ring(""), token_range_endpoints_to_json));
|
||||
});
|
||||
|
||||
ss::describe_ring.set(r, [&ctx, &ss](std::unique_ptr<request> req) {
|
||||
return describe_ring_as_json(ss, validate_keyspace(ctx, req->param));
|
||||
ss::describe_ring.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(service::get_local_storage_service().describe_ring(keyspace), token_range_endpoints_to_json));
|
||||
});
|
||||
|
||||
ss::get_host_id_map.set(r, [&ctx](const_req req) {
|
||||
@@ -546,24 +386,21 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_current_generation_number.set(r, [&g](std::unique_ptr<request> req) {
|
||||
ss::get_current_generation_number.set(r, [](std::unique_ptr<request> req) {
|
||||
gms::inet_address ep(utils::fb_utilities::get_broadcast_address());
|
||||
return g.get_current_generation_number(ep).then([](int res) {
|
||||
return gms::get_local_gossiper().get_current_generation_number(ep).then([](int res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_natural_endpoints.set(r, [&ctx, &ss](const_req req) {
|
||||
ss::get_natural_endpoints.set(r, [&ctx](const_req req) {
|
||||
auto keyspace = validate_keyspace(ctx, req.param);
|
||||
return container_to_vec(ss.local().get_natural_endpoints(keyspace, req.get_query_param("cf"),
|
||||
return container_to_vec(service::get_local_storage_service().get_natural_endpoints(keyspace, req.get_query_param("cf"),
|
||||
req.get_query_param("key")));
|
||||
});
|
||||
|
||||
ss::cdc_streams_check_and_repair.set(r, [&ctx, &cdc_gs] (std::unique_ptr<request> req) {
|
||||
if (!cdc_gs.local_is_initialized()) {
|
||||
throw std::runtime_error("get_cdc_generation_service: not initialized yet");
|
||||
}
|
||||
return cdc_gs.local().check_and_repair_cdc_streams().then([] {
|
||||
ss::cdc_streams_check_and_repair.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().check_and_repair_cdc_streams().then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
@@ -574,51 +411,40 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
if (column_families.empty()) {
|
||||
column_families = map_keys(ctx.db.local().find_keyspace(keyspace).metadata().get()->cf_meta_data());
|
||||
}
|
||||
return ctx.db.invoke_on_all([keyspace, column_families] (database& db) -> future<> {
|
||||
auto table_ids = boost::copy_range<std::vector<utils::UUID>>(column_families | boost::adaptors::transformed([&] (auto& cf_name) {
|
||||
return db.find_uuid(keyspace, cf_name);
|
||||
}));
|
||||
// major compact smaller tables first, to increase chances of success if low on space.
|
||||
std::ranges::sort(table_ids, std::less<>(), [&] (const utils::UUID& id) {
|
||||
return db.find_column_family(id).get_stats().live_disk_space_used;
|
||||
});
|
||||
// as a table can be dropped during loop below, let's find it before issuing major compaction request.
|
||||
for (auto& id : table_ids) {
|
||||
co_await db.find_column_family(id).compact_all_sstables();
|
||||
return ctx.db.invoke_on_all([keyspace, column_families] (database& db) {
|
||||
std::vector<column_family*> column_families_vec;
|
||||
for (auto cf : column_families) {
|
||||
column_families_vec.push_back(&db.find_column_family(keyspace, cf));
|
||||
}
|
||||
co_return;
|
||||
return parallel_for_each(column_families_vec, [] (column_family* cf) {
|
||||
return cf->compact_all_sstables();
|
||||
});
|
||||
}).then([]{
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::force_keyspace_cleanup.set(r, [&ctx, &ss](std::unique_ptr<request> req) {
|
||||
ss::force_keyspace_cleanup.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto column_families = split_cf(req->get_query_param("cf"));
|
||||
if (column_families.empty()) {
|
||||
column_families = map_keys(ctx.db.local().find_keyspace(keyspace).metadata().get()->cf_meta_data());
|
||||
}
|
||||
return ss.local().is_cleanup_allowed(keyspace).then([&ctx, keyspace,
|
||||
return service::get_local_storage_service().is_cleanup_allowed(keyspace).then([&ctx, keyspace,
|
||||
column_families = std::move(column_families)] (bool is_cleanup_allowed) mutable {
|
||||
if (!is_cleanup_allowed) {
|
||||
return make_exception_future<json::json_return_type>(
|
||||
std::runtime_error("Can not perform cleanup operation when topology changes"));
|
||||
}
|
||||
return ctx.db.invoke_on_all([keyspace, column_families] (database& db) -> future<> {
|
||||
auto table_ids = boost::copy_range<std::vector<utils::UUID>>(column_families | boost::adaptors::transformed([&] (auto& table_name) {
|
||||
return db.find_uuid(keyspace, table_name);
|
||||
}));
|
||||
// cleanup smaller tables first, to increase chances of success if low on space.
|
||||
std::ranges::sort(table_ids, std::less<>(), [&] (const utils::UUID& id) {
|
||||
return db.find_column_family(id).get_stats().live_disk_space_used;
|
||||
});
|
||||
return ctx.db.invoke_on_all([keyspace, column_families] (database& db) {
|
||||
std::vector<column_family*> column_families_vec;
|
||||
auto& cm = db.get_compaction_manager();
|
||||
// as a table can be dropped during loop below, let's find it before issuing the cleanup request.
|
||||
for (auto& id : table_ids) {
|
||||
table& t = db.find_column_family(id);
|
||||
co_await cm.perform_cleanup(db, &t);
|
||||
for (auto cf : column_families) {
|
||||
column_families_vec.push_back(&db.find_column_family(keyspace, cf));
|
||||
}
|
||||
co_return;
|
||||
return parallel_for_each(column_families_vec, [&cm, &db] (column_family* cf) {
|
||||
return cm.perform_cleanup(db, cf);
|
||||
});
|
||||
}).then([]{
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
@@ -655,49 +481,34 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
|
||||
|
||||
ss::decommission.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().decommission().then([] {
|
||||
ss::decommission.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().decommission().then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::move.set(r, [&ss] (std::unique_ptr<request> req) {
|
||||
ss::move.set(r, [] (std::unique_ptr<request> req) {
|
||||
auto new_token = req->get_query_param("new_token");
|
||||
return ss.local().move(new_token).then([] {
|
||||
return service::get_local_storage_service().move(new_token).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::remove_node.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
ss::remove_node.set(r, [](std::unique_ptr<request> req) {
|
||||
auto host_id = req->get_query_param("host_id");
|
||||
std::vector<sstring> ignore_nodes_strs= split(req->get_query_param("ignore_nodes"), ",");
|
||||
auto ignore_nodes = std::list<gms::inet_address>();
|
||||
for (std::string n : ignore_nodes_strs) {
|
||||
try {
|
||||
std::replace(n.begin(), n.end(), '\"', ' ');
|
||||
std::replace(n.begin(), n.end(), '\'', ' ');
|
||||
boost::trim_all(n);
|
||||
if (!n.empty()) {
|
||||
auto node = gms::inet_address(n);
|
||||
ignore_nodes.push_back(node);
|
||||
}
|
||||
} catch (...) {
|
||||
throw std::runtime_error(format("Failed to parse ignore_nodes parameter: ignore_nodes={}, node={}", ignore_nodes_strs, n));
|
||||
}
|
||||
}
|
||||
return ss.local().removenode(host_id, std::move(ignore_nodes)).then([] {
|
||||
return service::get_local_storage_service().removenode(host_id).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_removal_status.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().get_removal_status().then([] (auto status) {
|
||||
ss::get_removal_status.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().get_removal_status().then([] (auto status) {
|
||||
return make_ready_future<json::json_return_type>(status);
|
||||
});
|
||||
});
|
||||
|
||||
ss::force_remove_completion.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().force_remove_completion().then([] {
|
||||
ss::force_remove_completion.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().force_remove_completion().then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
@@ -721,20 +532,20 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
|
||||
ss::get_operation_mode.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().get_operation_mode().then([] (auto mode) {
|
||||
ss::get_operation_mode.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().get_operation_mode().then([] (auto mode) {
|
||||
return make_ready_future<json::json_return_type>(mode);
|
||||
});
|
||||
});
|
||||
|
||||
ss::is_starting.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().is_starting().then([] (auto starting) {
|
||||
ss::is_starting.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().is_starting().then([] (auto starting) {
|
||||
return make_ready_future<json::json_return_type>(starting);
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_drain_progress.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.map_reduce(adder<service::storage_service::drain_progress>(), [] (auto& ss) {
|
||||
ss::get_drain_progress.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_storage_service().map_reduce(adder<service::storage_service::drain_progress>(), [] (auto& ss) {
|
||||
return ss.get_drain_progress();
|
||||
}).then([] (auto&& progress) {
|
||||
auto progress_str = format("Drained {}/{} ColumnFamilies", progress.remaining_cfs, progress.total_cfs);
|
||||
@@ -742,8 +553,8 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
});
|
||||
|
||||
ss::drain.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().drain().then([] {
|
||||
ss::drain.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().drain().then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
@@ -774,20 +585,20 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
});
|
||||
|
||||
ss::stop_gossiping.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().stop_gossiping().then([] {
|
||||
ss::stop_gossiping.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().stop_gossiping().then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::start_gossiping.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().start_gossiping().then([] {
|
||||
ss::start_gossiping.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().start_gossiping().then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::is_gossip_running.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().is_gossip_running().then([] (bool running){
|
||||
ss::is_gossip_running.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().is_gossip_running().then([] (bool running){
|
||||
return make_ready_future<json::json_return_type>(running);
|
||||
});
|
||||
});
|
||||
@@ -799,8 +610,8 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::is_initialized.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
return ss.local().is_initialized().then([] (bool initialized) {
|
||||
ss::is_initialized.set(r, [](std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().is_initialized().then([] (bool initialized) {
|
||||
return make_ready_future<json::json_return_type>(initialized);
|
||||
});
|
||||
});
|
||||
@@ -809,8 +620,8 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::is_joined.set(r, [&ss] (std::unique_ptr<request> req) {
|
||||
return make_ready_future<json::json_return_type>(ss.local().is_joined());
|
||||
ss::is_joined.set(r, [] (std::unique_ptr<request> req) {
|
||||
return make_ready_future<json::json_return_type>(service::get_local_storage_service().is_joined());
|
||||
});
|
||||
|
||||
ss::set_stream_throughput_mb_per_sec.set(r, [](std::unique_ptr<request> req) {
|
||||
@@ -838,10 +649,10 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::is_incremental_backups_enabled.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
ss::is_incremental_backups_enabled.set(r, [](std::unique_ptr<request> req) {
|
||||
// If this is issued in parallel with an ongoing change, we may see values not agreeing.
|
||||
// Reissuing is asking for trouble, so we will just return true upon seeing any true value.
|
||||
return ctx.db.map_reduce(adder<bool>(), [] (database& db) {
|
||||
return service::get_local_storage_service().db().map_reduce(adder<bool>(), [] (database& db) {
|
||||
for (auto& pair: db.get_keyspaces()) {
|
||||
auto& ks = pair.second;
|
||||
if (ks.incremental_backups_enabled()) {
|
||||
@@ -854,10 +665,10 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
});
|
||||
|
||||
ss::set_incremental_backups_enabled.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
ss::set_incremental_backups_enabled.set(r, [](std::unique_ptr<request> req) {
|
||||
auto val_str = req->get_query_param("value");
|
||||
bool value = (val_str == "True") || (val_str == "true") || (val_str == "1");
|
||||
return ctx.db.invoke_on_all([value] (database& db) {
|
||||
return service::get_local_storage_service().db().invoke_on_all([value] (database& db) {
|
||||
db.set_enable_incremental_backups(value);
|
||||
|
||||
// Change both KS and CF, so they are in sync
|
||||
@@ -875,9 +686,9 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
});
|
||||
|
||||
ss::rebuild.set(r, [&ss](std::unique_ptr<request> req) {
|
||||
ss::rebuild.set(r, [](std::unique_ptr<request> req) {
|
||||
auto source_dc = req->get_query_param("source_dc");
|
||||
return ss.local().rebuild(std::move(source_dc)).then([] {
|
||||
return service::get_local_storage_service().rebuild(std::move(source_dc)).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
@@ -902,6 +713,23 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::load_new_ss_tables.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto ks = validate_keyspace(ctx, req->param);
|
||||
auto cf = req->get_query_param("cf");
|
||||
// No need to add the keyspace, since all we want is to avoid always sending this to the same
|
||||
// CPU. Even then I am being overzealous here. This is not something that happens all the time.
|
||||
auto coordinator = std::hash<sstring>()(cf) % smp::count;
|
||||
return service::get_storage_service().invoke_on(coordinator, [ks = std::move(ks), cf = std::move(cf)] (service::storage_service& s) {
|
||||
return s.load_new_sstables(ks, cf);
|
||||
}).then_wrapped([] (auto&& f) {
|
||||
if (f.failed()) {
|
||||
auto msg = fmt::format("Failed to load new sstables: {}", f.get_exception());
|
||||
return make_exception_future<json::json_return_type>(httpd::server_error_exception(msg));
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::sample_key_range.set(r, [](std::unique_ptr<request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
@@ -912,7 +740,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
ss::reset_local_schema.set(r, [](std::unique_ptr<request> req) {
|
||||
// FIXME: We should truncate schema tables if more than one node in the cluster.
|
||||
auto& sp = service::get_storage_proxy();
|
||||
auto& fs = sp.local().features();
|
||||
auto& fs = service::get_local_storage_service().features();
|
||||
return db::schema_tables::recalculate_schema_version(sp, fs).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
@@ -948,7 +776,6 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
res.enable = tracing::tracing::get_local_tracing_instance().slow_query_tracing_enabled();
|
||||
res.ttl = tracing::tracing::get_local_tracing_instance().slow_query_record_ttl().count() ;
|
||||
res.threshold = tracing::tracing::get_local_tracing_instance().slow_query_threshold().count();
|
||||
res.fast = tracing::tracing::get_local_tracing_instance().ignore_trace_events_enabled();
|
||||
return res;
|
||||
});
|
||||
|
||||
@@ -956,9 +783,8 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
auto enable = req->get_query_param("enable");
|
||||
auto ttl = req->get_query_param("ttl");
|
||||
auto threshold = req->get_query_param("threshold");
|
||||
auto fast = req->get_query_param("fast");
|
||||
try {
|
||||
return tracing::tracing::tracing_instance().invoke_on_all([enable, ttl, threshold, fast] (auto& local_tracing) {
|
||||
return tracing::tracing::tracing_instance().invoke_on_all([enable, ttl, threshold] (auto& local_tracing) {
|
||||
if (threshold != "") {
|
||||
local_tracing.set_slow_query_threshold(std::chrono::microseconds(std::stol(threshold.c_str())));
|
||||
}
|
||||
@@ -968,9 +794,6 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
if (enable != "") {
|
||||
local_tracing.set_slow_query_enabled(strcasecmp(enable.c_str(), "true") == 0);
|
||||
}
|
||||
if (fast != "") {
|
||||
local_tracing.set_ignore_trace_events(strcasecmp(fast.c_str(), "true") == 0);
|
||||
}
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
@@ -979,18 +802,18 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
}
|
||||
});
|
||||
|
||||
ss::enable_auto_compaction.set(r, [&ctx, &ss](std::unique_ptr<request> req) {
|
||||
ss::enable_auto_compaction.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto tables = split_cf(req->get_query_param("cf"));
|
||||
|
||||
return set_tables_autocompaction(ctx, ss.local(), keyspace, tables, true);
|
||||
return set_tables_autocompaction(ctx, keyspace, tables, true);
|
||||
});
|
||||
|
||||
ss::disable_auto_compaction.set(r, [&ctx, &ss](std::unique_ptr<request> req) {
|
||||
ss::disable_auto_compaction.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto tables = split_cf(req->get_query_param("cf"));
|
||||
|
||||
return set_tables_autocompaction(ctx, ss.local(), keyspace, tables, false);
|
||||
return set_tables_autocompaction(ctx, keyspace, tables, false);
|
||||
});
|
||||
|
||||
ss::deliver_hints.set(r, [](std::unique_ptr<request> req) {
|
||||
@@ -1000,12 +823,12 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::get_cluster_name.set(r, [&g](const_req req) {
|
||||
return g.get_cluster_name();
|
||||
ss::get_cluster_name.set(r, [](const_req req) {
|
||||
return gms::get_local_gossiper().get_cluster_name();
|
||||
});
|
||||
|
||||
ss::get_partitioner_name.set(r, [&g](const_req req) {
|
||||
return g.get_partitioner_name();
|
||||
ss::get_partitioner_name.set(r, [](const_req req) {
|
||||
return gms::get_local_gossiper().get_partitioner_name();
|
||||
});
|
||||
|
||||
ss::get_tombstone_warn_threshold.set(r, [](std::unique_ptr<request> req) {
|
||||
@@ -1058,8 +881,8 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return get_cf_stats(ctx, &column_family_stats::live_disk_space_used);
|
||||
});
|
||||
|
||||
ss::get_exceptions.set(r, [&ss](const_req req) {
|
||||
return ss.local().get_exception_count();
|
||||
ss::get_exceptions.set(r, [](const_req req) {
|
||||
return service::get_local_storage_service().get_exception_count();
|
||||
});
|
||||
|
||||
ss::get_total_hints_in_progress.set(r, [](std::unique_ptr<request> req) {
|
||||
@@ -1074,21 +897,30 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
ss::get_ownership.set(r, [&ss] (std::unique_ptr<request> req) {
|
||||
return ss.local().get_ownership().then([] (auto&& ownership) {
|
||||
ss::get_ownership.set(r, [] (std::unique_ptr<request> req) {
|
||||
return service::get_local_storage_service().get_ownership().then([] (auto&& ownership) {
|
||||
std::vector<storage_service_json::mapper> res;
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value(ownership, res));
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_effective_ownership.set(r, [&ctx, &ss] (std::unique_ptr<request> req) {
|
||||
ss::get_effective_ownership.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
auto keyspace_name = req->param["keyspace"] == "null" ? "" : validate_keyspace(ctx, req->param);
|
||||
return ss.local().effective_ownership(keyspace_name).then([] (auto&& ownership) {
|
||||
return service::get_local_storage_service().effective_ownership(keyspace_name).then([] (auto&& ownership) {
|
||||
std::vector<storage_service_json::mapper> res;
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value(ownership, res));
|
||||
});
|
||||
});
|
||||
|
||||
ss::view_build_statuses.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto view = req->param["view"];
|
||||
return service::get_local_storage_service().view_build_statuses(std::move(keyspace), std::move(view)).then([] (std::unordered_map<sstring, sstring> status) {
|
||||
std::vector<storage_service_json::mapper> res;
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value(std::move(status), res));
|
||||
});
|
||||
});
|
||||
|
||||
ss::sstable_info.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
auto ks = api::req_param<sstring>(*req, "keyspace", {}).value;
|
||||
auto cf = api::req_param<sstring>(*req, "cf", {}).value;
|
||||
@@ -1098,7 +930,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
using table_sstables_list = std::vector<ss::table_sstables>;
|
||||
|
||||
return do_with(table_sstables_list{}, [ks, cf, &ctx](table_sstables_list& dst) {
|
||||
return ctx.db.map_reduce([&dst](table_sstables_list&& res) {
|
||||
return service::get_local_storage_service().db().map_reduce([&dst](table_sstables_list&& res) {
|
||||
for (auto&& t : res) {
|
||||
auto i = std::find_if(dst.begin(), dst.end(), [&t](const ss::table_sstables& t2) {
|
||||
return t.keyspace() == t2.keyspace() && t.table() == t2.table();
|
||||
@@ -1131,7 +963,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
tst.keyspace = schema->ks_name();
|
||||
tst.table = schema->cf_name();
|
||||
|
||||
for (auto sstables = t->get_sstables_including_compacted_undeleted(); auto sstable : *sstables) {
|
||||
for (auto sstable : *t->get_sstables_including_compacted_undeleted()) {
|
||||
auto ts = db_clock::to_time_t(sstable->data_file_write_time());
|
||||
::tm t;
|
||||
::gmtime_r(&ts, &t);
|
||||
@@ -1214,6 +1046,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_ctl) {
|
||||
@@ -1255,17 +1088,14 @@ void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_
|
||||
});
|
||||
|
||||
ss::take_snapshot.set(r, [&snap_ctl](std::unique_ptr<request> req) {
|
||||
apilog.debug("take_snapshot: {}", req->query_parameters);
|
||||
auto tag = req->get_query_param("tag");
|
||||
auto column_families = split(req->get_query_param("cf"), ",");
|
||||
auto sfopt = req->get_query_param("sf");
|
||||
auto sf = db::snapshot_ctl::skip_flush(strcasecmp(sfopt.c_str(), "true") == 0);
|
||||
|
||||
std::vector<sstring> keynames = split(req->get_query_param("kn"), ",");
|
||||
|
||||
auto resp = make_ready_future<>();
|
||||
if (column_families.empty()) {
|
||||
resp = snap_ctl.local().take_snapshot(tag, keynames, sf);
|
||||
resp = snap_ctl.local().take_snapshot(tag, keynames);
|
||||
} else {
|
||||
if (keynames.empty()) {
|
||||
throw httpd::bad_param_exception("The keyspace of column families must be specified");
|
||||
@@ -1273,7 +1103,7 @@ void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_
|
||||
if (keynames.size() > 1) {
|
||||
throw httpd::bad_param_exception("Only one keyspace allowed when specifying a column family");
|
||||
}
|
||||
resp = snap_ctl.local().take_column_family_snapshot(keynames[0], column_families, tag, sf);
|
||||
resp = snap_ctl.local().take_column_family_snapshot(keynames[0], column_families, tag);
|
||||
}
|
||||
return resp.then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
@@ -1297,28 +1127,7 @@ void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_
|
||||
});
|
||||
|
||||
ss::scrub.set(r, wrap_ks_cf(ctx, [&snap_ctl] (http_context& ctx, std::unique_ptr<request> req, sstring keyspace, std::vector<sstring> column_families) {
|
||||
auto scrub_mode = sstables::compaction_type_options::scrub::mode::abort;
|
||||
|
||||
const sstring scrub_mode_str = req_param<sstring>(*req, "scrub_mode", "");
|
||||
if (scrub_mode_str == "") {
|
||||
const auto skip_corrupted = req_param<bool>(*req, "skip_corrupted", false);
|
||||
|
||||
if (skip_corrupted) {
|
||||
scrub_mode = sstables::compaction_type_options::scrub::mode::skip;
|
||||
}
|
||||
} else {
|
||||
if (scrub_mode_str == "ABORT") {
|
||||
scrub_mode = sstables::compaction_type_options::scrub::mode::abort;
|
||||
} else if (scrub_mode_str == "SKIP") {
|
||||
scrub_mode = sstables::compaction_type_options::scrub::mode::skip;
|
||||
} else if (scrub_mode_str == "SEGREGATE") {
|
||||
scrub_mode = sstables::compaction_type_options::scrub::mode::segregate;
|
||||
} else if (scrub_mode_str == "VALIDATE") {
|
||||
scrub_mode = sstables::compaction_type_options::scrub::mode::validate;
|
||||
} else {
|
||||
throw std::invalid_argument(fmt::format("Unknown argument for 'scrub_mode' parameter: {}", scrub_mode_str));
|
||||
}
|
||||
}
|
||||
const auto skip_corrupted = req_param<bool>(*req, "skip_corrupted", false);
|
||||
|
||||
auto f = make_ready_future<>();
|
||||
if (!req_param<bool>(*req, "disable_snapshot", false)) {
|
||||
@@ -1328,12 +1137,12 @@ void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_
|
||||
});
|
||||
}
|
||||
|
||||
return f.then([&ctx, keyspace, column_families, scrub_mode] {
|
||||
return f.then([&ctx, keyspace, column_families, skip_corrupted] {
|
||||
return ctx.db.invoke_on_all([=] (database& db) {
|
||||
return do_for_each(column_families, [=, &db](sstring cfname) {
|
||||
auto& cm = db.get_compaction_manager();
|
||||
auto& cf = db.find_column_family(keyspace, cfname);
|
||||
return cm.perform_sstable_scrub(&cf, scrub_mode);
|
||||
return cm.perform_sstable_scrub(&cf, skip_corrupted);
|
||||
});
|
||||
});
|
||||
}).then([]{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -23,35 +23,16 @@
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
#include "api.hh"
|
||||
#include "db/data_listeners.hh"
|
||||
|
||||
namespace cql_transport { class controller; }
|
||||
class thrift_controller;
|
||||
namespace db {
|
||||
class snapshot_ctl;
|
||||
namespace view {
|
||||
class view_builder;
|
||||
}
|
||||
}
|
||||
namespace db { class snapshot_ctl; }
|
||||
namespace netw { class messaging_service; }
|
||||
class repair_service;
|
||||
namespace cdc { class generation_service; }
|
||||
class sstables_loader;
|
||||
|
||||
namespace gms {
|
||||
|
||||
class gossiper;
|
||||
|
||||
}
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_service>& ss, gms::gossiper& g, sharded<cdc::generation_service>& cdc_gs);
|
||||
void set_sstables_loader(http_context& ctx, routes& r, sharded<sstables_loader>& sst_loader);
|
||||
void unset_sstables_loader(http_context& ctx, routes& r);
|
||||
void set_view_builder(http_context& ctx, routes& r, sharded<db::view::view_builder>& vb);
|
||||
void unset_view_builder(http_context& ctx, routes& r);
|
||||
void set_repair(http_context& ctx, routes& r, sharded<repair_service>& repair);
|
||||
void set_storage_service(http_context& ctx, routes& r);
|
||||
void set_repair(http_context& ctx, routes& r, sharded<netw::messaging_service>& ms);
|
||||
void unset_repair(http_context& ctx, routes& r);
|
||||
void set_transport_controller(http_context& ctx, routes& r, cql_transport::controller& ctl);
|
||||
void unset_transport_controller(http_context& ctx, routes& r);
|
||||
@@ -59,6 +40,5 @@ void set_rpc_controller(http_context& ctx, routes& r, thrift_controller& ctl);
|
||||
void unset_rpc_controller(http_context& ctx, routes& r);
|
||||
void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_ctl);
|
||||
void unset_snapshot(http_context& ctx, routes& r);
|
||||
seastar::future<json::json_return_type> run_toppartitions_query(db::toppartitions_query& q, http_context &ctx, bool legacy_request = false);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -25,9 +25,6 @@
|
||||
#include <seastar/core/reactor.hh>
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "log.hh"
|
||||
#include "database.hh"
|
||||
|
||||
extern logging::logger apilog;
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -73,16 +70,6 @@ void set_system(http_context& ctx, routes& r) {
|
||||
}
|
||||
return json::json_void();
|
||||
});
|
||||
|
||||
hs::drop_sstable_caches.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
apilog.info("Dropping sstable caches");
|
||||
return ctx.db.invoke_on_all([] (database& db) {
|
||||
return db.drop_caches();
|
||||
}).then([] {
|
||||
apilog.info("Caches dropped");
|
||||
return json::json_return_type(json::json_void());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
208
atomic_cell.cc
208
atomic_cell.cc
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018-present ScyllaDB
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -24,125 +24,142 @@
|
||||
#include "counters.hh"
|
||||
#include "types.hh"
|
||||
|
||||
/// LSA mirator for cells with irrelevant type
|
||||
///
|
||||
///
|
||||
const data::type_imr_descriptor& no_type_imr_descriptor() {
|
||||
static thread_local data::type_imr_descriptor state(data::type_info::make_variable_size());
|
||||
return state;
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time) {
|
||||
return atomic_cell_type::make_dead(timestamp, deletion_time);
|
||||
auto& imr_data = no_type_imr_descriptor();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_dead(timestamp, deletion_time), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value, atomic_cell::collection_member cm) {
|
||||
return atomic_cell_type::make_live(timestamp, single_fragment_range(value));
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, managed_bytes_view value, atomic_cell::collection_member cm) {
|
||||
return atomic_cell_type::make_live(timestamp, fragment_range(value));
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value, atomic_cell::collection_member cm) {
|
||||
return atomic_cell_type::make_live(timestamp, value);
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value, collection_member cm)
|
||||
{
|
||||
return atomic_cell_type::make_live(timestamp, value);
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, atomic_cell::collection_member cm) {
|
||||
return atomic_cell_type::make_live(timestamp, single_fragment_range(value), expiry, ttl);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, managed_bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, atomic_cell::collection_member cm) {
|
||||
return atomic_cell_type::make_live(timestamp, fragment_range(value), expiry, ttl);
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, expiry, ttl, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, atomic_cell::collection_member cm) {
|
||||
return atomic_cell_type::make_live(timestamp, value, expiry, ttl);
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, expiry, ttl, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member cm)
|
||||
{
|
||||
return atomic_cell_type::make_live(timestamp, value, expiry, ttl);
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, expiry, ttl, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live_counter_update(api::timestamp_type timestamp, int64_t value) {
|
||||
return atomic_cell_type::make_live_counter_update(timestamp, value);
|
||||
auto& imr_data = no_type_imr_descriptor();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live_counter_update(timestamp, value), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live_uninitialized(const abstract_type& type, api::timestamp_type timestamp, size_t size) {
|
||||
return atomic_cell_type::make_live_uninitialized(timestamp, size);
|
||||
auto& imr_data = no_type_imr_descriptor();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live_uninitialized(imr_data.type_info(), timestamp, size), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
static imr::utils::object<data::cell::structure> copy_cell(const data::type_imr_descriptor& imr_data, const uint8_t* ptr)
|
||||
{
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
|
||||
// If the cell doesn't own any memory it is trivial and can be copied with
|
||||
// memcpy.
|
||||
auto f = data::cell::structure::get_member<data::cell::tags::flags>(ptr);
|
||||
if (!f.template get<data::cell::tags::external_data>()) {
|
||||
data::cell::context ctx(f, imr_data.type_info());
|
||||
// XXX: We may be better off storing the total cell size in memory. Measure!
|
||||
auto size = data::cell::structure::serialized_object_size(ptr, ctx);
|
||||
return imr_object_type::make_raw(size, [&] (uint8_t* dst) noexcept {
|
||||
std::copy_n(ptr, size, dst);
|
||||
}, &imr_data.lsa_migrator());
|
||||
}
|
||||
|
||||
return imr_object_type::make(data::cell::copy_fn(imr_data.type_info(), ptr), &imr_data.lsa_migrator());
|
||||
}
|
||||
|
||||
atomic_cell::atomic_cell(const abstract_type& type, atomic_cell_view other)
|
||||
: _data(other._view) {
|
||||
set_view(_data);
|
||||
}
|
||||
|
||||
// Based on:
|
||||
// - org.apache.cassandra.db.AbstractCell#reconcile()
|
||||
// - org.apache.cassandra.db.BufferExpiringCell#reconcile()
|
||||
// - org.apache.cassandra.db.BufferDeletedCell#reconcile()
|
||||
std::strong_ordering
|
||||
compare_atomic_cell_for_merge(atomic_cell_view left, atomic_cell_view right) {
|
||||
if (left.timestamp() != right.timestamp()) {
|
||||
return left.timestamp() <=> right.timestamp();
|
||||
}
|
||||
if (left.is_live() != right.is_live()) {
|
||||
return left.is_live() ? std::strong_ordering::less : std::strong_ordering::greater;
|
||||
}
|
||||
if (left.is_live()) {
|
||||
auto c = compare_unsigned(left.value(), right.value()) <=> 0;
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
if (left.is_live_and_has_ttl() != right.is_live_and_has_ttl()) {
|
||||
// prefer expiring cells.
|
||||
return left.is_live_and_has_ttl() ? std::strong_ordering::greater : std::strong_ordering::less;
|
||||
}
|
||||
if (left.is_live_and_has_ttl()) {
|
||||
if (left.expiry() != right.expiry()) {
|
||||
return left.expiry() <=> right.expiry();
|
||||
} else {
|
||||
// prefer the cell that was written later,
|
||||
// so it survives longer after it expires, until purged.
|
||||
return right.ttl() <=> left.ttl();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Both are deleted
|
||||
|
||||
// Origin compares big-endian serialized deletion time. That's because it
|
||||
// delegates to AbstractCell.reconcile() which compares values after
|
||||
// comparing timestamps, which in case of deleted cells will hold
|
||||
// serialized expiry.
|
||||
return (uint64_t) left.deletion_time().time_since_epoch().count()
|
||||
<=> (uint64_t) right.deletion_time().time_since_epoch().count();
|
||||
}
|
||||
return std::strong_ordering::equal;
|
||||
}
|
||||
: atomic_cell(type.imr_state().type_info(),
|
||||
copy_cell(type.imr_state(), other._view.raw_pointer()))
|
||||
{ }
|
||||
|
||||
atomic_cell_or_collection atomic_cell_or_collection::copy(const abstract_type& type) const {
|
||||
if (_data.empty()) {
|
||||
if (!_data.get()) {
|
||||
return atomic_cell_or_collection();
|
||||
}
|
||||
return atomic_cell_or_collection(managed_bytes(_data));
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell_or_collection(
|
||||
copy_cell(imr_data, _data.get())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell_or_collection::atomic_cell_or_collection(const abstract_type& type, atomic_cell_view acv)
|
||||
: _data(acv._view)
|
||||
: _data(copy_cell(type.imr_state(), acv._view.raw_pointer()))
|
||||
{
|
||||
}
|
||||
|
||||
bool atomic_cell_or_collection::equals(const abstract_type& type, const atomic_cell_or_collection& other) const
|
||||
{
|
||||
if (_data.empty() || other._data.empty()) {
|
||||
return _data.empty() && other._data.empty();
|
||||
auto ptr_a = _data.get();
|
||||
auto ptr_b = other._data.get();
|
||||
|
||||
if (!ptr_a || !ptr_b) {
|
||||
return !ptr_a && !ptr_b;
|
||||
}
|
||||
|
||||
if (type.is_atomic()) {
|
||||
auto a = atomic_cell_view::from_bytes(type, _data);
|
||||
auto b = atomic_cell_view::from_bytes(type, other._data);
|
||||
auto a = atomic_cell_view::from_bytes(type.imr_state().type_info(), _data);
|
||||
auto b = atomic_cell_view::from_bytes(type.imr_state().type_info(), other._data);
|
||||
if (a.timestamp() != b.timestamp()) {
|
||||
return false;
|
||||
}
|
||||
@@ -174,24 +191,44 @@ bool atomic_cell_or_collection::equals(const abstract_type& type, const atomic_c
|
||||
|
||||
size_t atomic_cell_or_collection::external_memory_usage(const abstract_type& t) const
|
||||
{
|
||||
return _data.external_memory_usage();
|
||||
if (!_data.get()) {
|
||||
return 0;
|
||||
}
|
||||
auto ctx = data::cell::context(_data.get(), t.imr_state().type_info());
|
||||
|
||||
auto view = data::cell::structure::make_view(_data.get(), ctx);
|
||||
auto flags = view.get<data::cell::tags::flags>();
|
||||
|
||||
size_t external_value_size = 0;
|
||||
if (flags.get<data::cell::tags::external_data>()) {
|
||||
if (flags.get<data::cell::tags::collection>()) {
|
||||
external_value_size = as_collection_mutation().data.size_bytes();
|
||||
} else {
|
||||
auto cell_view = data::cell::atomic_cell_view(t.imr_state().type_info(), view);
|
||||
external_value_size = cell_view.value_size();
|
||||
}
|
||||
// Add overhead of chunk headers. The last one is a special case.
|
||||
external_value_size += (external_value_size - 1) / data::cell::effective_external_chunk_length * data::cell::external_chunk_overhead;
|
||||
external_value_size += data::cell::external_last_chunk_overhead;
|
||||
}
|
||||
return data::cell::structure::serialized_object_size(_data.get(), ctx)
|
||||
+ imr_object_type::size_overhead + external_value_size;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const atomic_cell_view& acv) {
|
||||
if (acv.is_live()) {
|
||||
fmt::print(os, "atomic_cell{{{},ts={:d},expiry={:d},ttl={:d}}}",
|
||||
return fmt_print(os, "atomic_cell{{{},ts={:d},expiry={:d},ttl={:d}}}",
|
||||
acv.is_counter_update()
|
||||
? "counter_update_value=" + to_sstring(acv.counter_update_value())
|
||||
: to_hex(to_bytes(acv.value())),
|
||||
: to_hex(acv.value().linearize()),
|
||||
acv.timestamp(),
|
||||
acv.is_live_and_has_ttl() ? acv.expiry().time_since_epoch().count() : -1,
|
||||
acv.is_live_and_has_ttl() ? acv.ttl().count() : 0);
|
||||
} else {
|
||||
fmt::print(os, "atomic_cell{{DEAD,ts={:d},deletion_time={:d}}}",
|
||||
return fmt_print(os, "atomic_cell{{DEAD,ts={:d},deletion_time={:d}}}",
|
||||
acv.timestamp(), acv.deletion_time().time_since_epoch().count());
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
@@ -210,22 +247,22 @@ operator<<(std::ostream& os, const atomic_cell_view::printer& acvp) {
|
||||
cell_value_string_builder << "counter_update_value=" << acv.counter_update_value();
|
||||
} else {
|
||||
cell_value_string_builder << "shards: ";
|
||||
auto ccv = counter_cell_view(acv);
|
||||
cell_value_string_builder << ::join(", ", ccv.shards());
|
||||
counter_cell_view::with_linearized(acv, [&cell_value_string_builder] (counter_cell_view& ccv) {
|
||||
cell_value_string_builder << ::join(", ", ccv.shards());
|
||||
});
|
||||
}
|
||||
} else {
|
||||
cell_value_string_builder << type.to_string(to_bytes(acv.value()));
|
||||
cell_value_string_builder << type.to_string(acv.value().linearize());
|
||||
}
|
||||
fmt::print(os, "atomic_cell{{{},ts={:d},expiry={:d},ttl={:d}}}",
|
||||
return fmt_print(os, "atomic_cell{{{},ts={:d},expiry={:d},ttl={:d}}}",
|
||||
cell_value_string_builder.str(),
|
||||
acv.timestamp(),
|
||||
acv.is_live_and_has_ttl() ? acv.expiry().time_since_epoch().count() : -1,
|
||||
acv.is_live_and_has_ttl() ? acv.ttl().count() : 0);
|
||||
} else {
|
||||
fmt::print(os, "atomic_cell{{DEAD,ts={:d},deletion_time={:d}}}",
|
||||
return fmt_print(os, "atomic_cell{{DEAD,ts={:d},deletion_time={:d}}}",
|
||||
acv.timestamp(), acv.deletion_time().time_since_epoch().count());
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
@@ -234,11 +271,12 @@ operator<<(std::ostream& os, const atomic_cell::printer& acp) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const atomic_cell_or_collection::printer& p) {
|
||||
if (p._cell._data.empty()) {
|
||||
if (!p._cell._data.get()) {
|
||||
return os << "{ null atomic_cell_or_collection }";
|
||||
}
|
||||
using dc = data::cell;
|
||||
os << "{ ";
|
||||
if (p._cdef.type->is_multi_cell()) {
|
||||
if (dc::structure::get_member<dc::tags::flags>(p._cell._data.get()).get<dc::tags::collection>()) {
|
||||
os << "collection ";
|
||||
auto cmv = p._cell.as_collection_mutation();
|
||||
os << collection_mutation_view::printer(*p._cdef.type, cmv);
|
||||
|
||||
287
atomic_cell.hh
287
atomic_cell.hh
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -27,10 +27,11 @@
|
||||
#include "gc_clock.hh"
|
||||
#include "utils/managed_bytes.hh"
|
||||
#include <seastar/net//byteorder.hh>
|
||||
#include <seastar/util/bool_class.hh>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <concepts>
|
||||
#include "data/cell.hh"
|
||||
#include "data/schema_info.hh"
|
||||
#include "imr/utils.hh"
|
||||
#include "utils/fragmented_temporary_buffer.hh"
|
||||
|
||||
#include "serializer.hh"
|
||||
@@ -39,191 +40,41 @@ class abstract_type;
|
||||
class collection_type_impl;
|
||||
class atomic_cell_or_collection;
|
||||
|
||||
using atomic_cell_value = managed_bytes;
|
||||
template <mutable_view is_mutable>
|
||||
using atomic_cell_value_basic_view = managed_bytes_basic_view<is_mutable>;
|
||||
using atomic_cell_value_view = atomic_cell_value_basic_view<mutable_view::no>;
|
||||
using atomic_cell_value_mutable_view = atomic_cell_value_basic_view<mutable_view::yes>;
|
||||
|
||||
template <typename T>
|
||||
requires std::is_trivial_v<T>
|
||||
static void set_field(atomic_cell_value_mutable_view& out, unsigned offset, T val) {
|
||||
auto out_view = managed_bytes_mutable_view(out);
|
||||
out_view.remove_prefix(offset);
|
||||
write<T>(out_view, val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_trivial_v<T>
|
||||
static void set_field(atomic_cell_value& out, unsigned offset, T val) {
|
||||
auto out_view = atomic_cell_value_mutable_view(out);
|
||||
set_field(out_view, offset, val);
|
||||
}
|
||||
|
||||
template <FragmentRange Buffer>
|
||||
static void set_value(managed_bytes& b, unsigned value_offset, const Buffer& value) {
|
||||
auto v = managed_bytes_mutable_view(b).substr(value_offset, value.size_bytes());
|
||||
for (auto frag : value) {
|
||||
write_fragmented(v, single_fragmented_view(frag));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, FragmentedView Input>
|
||||
requires std::is_trivial_v<T>
|
||||
static T get_field(Input in, unsigned offset = 0) {
|
||||
in.remove_prefix(offset);
|
||||
return read_simple<T>(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents atomic cell layout. Works on serialized form.
|
||||
*
|
||||
* Layout:
|
||||
*
|
||||
* <live> := <int8_t:flags><int64_t:timestamp>(<int64_t:expiry><int32_t:ttl>)?<value>
|
||||
* <dead> := <int8_t: 0><int64_t:timestamp><int64_t:deletion_time>
|
||||
*/
|
||||
class atomic_cell_type final {
|
||||
private:
|
||||
static constexpr int8_t LIVE_FLAG = 0x01;
|
||||
static constexpr int8_t EXPIRY_FLAG = 0x02; // When present, expiry field is present. Set only for live cells
|
||||
static constexpr int8_t COUNTER_UPDATE_FLAG = 0x08; // Cell is a counter update.
|
||||
static constexpr unsigned flags_size = 1;
|
||||
static constexpr unsigned timestamp_offset = flags_size;
|
||||
static constexpr unsigned timestamp_size = 8;
|
||||
static constexpr unsigned expiry_offset = timestamp_offset + timestamp_size;
|
||||
static constexpr unsigned expiry_size = 8;
|
||||
static constexpr unsigned deletion_time_offset = timestamp_offset + timestamp_size;
|
||||
static constexpr unsigned deletion_time_size = 8;
|
||||
static constexpr unsigned ttl_offset = expiry_offset + expiry_size;
|
||||
static constexpr unsigned ttl_size = 4;
|
||||
friend class counter_cell_builder;
|
||||
private:
|
||||
static bool is_counter_update(atomic_cell_value_view cell) {
|
||||
return cell.front() & COUNTER_UPDATE_FLAG;
|
||||
}
|
||||
static bool is_live(atomic_cell_value_view cell) {
|
||||
return cell.front() & LIVE_FLAG;
|
||||
}
|
||||
static bool is_live_and_has_ttl(atomic_cell_value_view cell) {
|
||||
return cell.front() & EXPIRY_FLAG;
|
||||
}
|
||||
static bool is_dead(atomic_cell_value_view cell) {
|
||||
return !is_live(cell);
|
||||
}
|
||||
// Can be called on live and dead cells
|
||||
static api::timestamp_type timestamp(atomic_cell_value_view cell) {
|
||||
return get_field<api::timestamp_type>(cell, timestamp_offset);
|
||||
}
|
||||
static void set_timestamp(atomic_cell_value_mutable_view& cell, api::timestamp_type ts) {
|
||||
set_field(cell, timestamp_offset, ts);
|
||||
}
|
||||
// Can be called on live cells only
|
||||
private:
|
||||
template <mutable_view is_mutable>
|
||||
static managed_bytes_basic_view<is_mutable> do_get_value(managed_bytes_basic_view<is_mutable> cell) {
|
||||
auto expiry_field_size = bool(cell.front() & EXPIRY_FLAG) * (expiry_size + ttl_size);
|
||||
auto value_offset = flags_size + timestamp_size + expiry_field_size;
|
||||
cell.remove_prefix(value_offset);
|
||||
return cell;
|
||||
}
|
||||
public:
|
||||
static atomic_cell_value_view value(managed_bytes_view cell) {
|
||||
return do_get_value(cell);
|
||||
}
|
||||
static atomic_cell_value_mutable_view value(managed_bytes_mutable_view cell) {
|
||||
return do_get_value(cell);
|
||||
}
|
||||
// Can be called on live counter update cells only
|
||||
static int64_t counter_update_value(atomic_cell_value_view cell) {
|
||||
return get_field<int64_t>(cell, flags_size + timestamp_size);
|
||||
}
|
||||
// Can be called only when is_dead() is true.
|
||||
static gc_clock::time_point deletion_time(atomic_cell_value_view cell) {
|
||||
assert(is_dead(cell));
|
||||
return gc_clock::time_point(gc_clock::duration(get_field<int64_t>(cell, deletion_time_offset)));
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl() is true.
|
||||
static gc_clock::time_point expiry(atomic_cell_value_view cell) {
|
||||
assert(is_live_and_has_ttl(cell));
|
||||
auto expiry = get_field<int64_t>(cell, expiry_offset);
|
||||
return gc_clock::time_point(gc_clock::duration(expiry));
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl() is true.
|
||||
static gc_clock::duration ttl(atomic_cell_value_view cell) {
|
||||
assert(is_live_and_has_ttl(cell));
|
||||
return gc_clock::duration(get_field<int32_t>(cell, ttl_offset));
|
||||
}
|
||||
static managed_bytes make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time) {
|
||||
managed_bytes b(managed_bytes::initialized_later(), flags_size + timestamp_size + deletion_time_size);
|
||||
b[0] = 0;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_field(b, deletion_time_offset, static_cast<int64_t>(deletion_time.time_since_epoch().count()));
|
||||
return b;
|
||||
}
|
||||
template <FragmentRange Buffer>
|
||||
static managed_bytes make_live(api::timestamp_type timestamp, const Buffer& value) {
|
||||
auto value_offset = flags_size + timestamp_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + value.size_bytes());
|
||||
b[0] = LIVE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_value(b, value_offset, value);
|
||||
return b;
|
||||
}
|
||||
static managed_bytes make_live_counter_update(api::timestamp_type timestamp, int64_t value) {
|
||||
auto value_offset = flags_size + timestamp_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + sizeof(value));
|
||||
b[0] = LIVE_FLAG | COUNTER_UPDATE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_field(b, value_offset, value);
|
||||
return b;
|
||||
}
|
||||
template <FragmentRange Buffer>
|
||||
static managed_bytes make_live(api::timestamp_type timestamp, const Buffer& value, gc_clock::time_point expiry, gc_clock::duration ttl) {
|
||||
auto value_offset = flags_size + timestamp_size + expiry_size + ttl_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + value.size_bytes());
|
||||
b[0] = EXPIRY_FLAG | LIVE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_field(b, expiry_offset, static_cast<int64_t>(expiry.time_since_epoch().count()));
|
||||
set_field(b, ttl_offset, static_cast<int32_t>(ttl.count()));
|
||||
set_value(b, value_offset, value);
|
||||
return b;
|
||||
}
|
||||
static managed_bytes make_live_uninitialized(api::timestamp_type timestamp, size_t size) {
|
||||
auto value_offset = flags_size + timestamp_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + size);
|
||||
b[0] = LIVE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
return b;
|
||||
}
|
||||
template <mutable_view is_mutable>
|
||||
friend class basic_atomic_cell_view;
|
||||
friend class atomic_cell;
|
||||
};
|
||||
using atomic_cell_value_view = data::value_view;
|
||||
using atomic_cell_value_mutable_view = data::value_mutable_view;
|
||||
|
||||
/// View of an atomic cell
|
||||
template<mutable_view is_mutable>
|
||||
class basic_atomic_cell_view {
|
||||
protected:
|
||||
managed_bytes_basic_view<is_mutable> _view;
|
||||
friend class atomic_cell;
|
||||
data::cell::basic_atomic_cell_view<is_mutable> _view;
|
||||
friend class atomic_cell;
|
||||
public:
|
||||
using pointer_type = std::conditional_t<is_mutable == mutable_view::no, const uint8_t*, uint8_t*>;
|
||||
protected:
|
||||
void set_view(managed_bytes_basic_view<is_mutable> v) {
|
||||
_view = v;
|
||||
}
|
||||
basic_atomic_cell_view() = default;
|
||||
explicit basic_atomic_cell_view(managed_bytes_basic_view<is_mutable> v) : _view(std::move(v)) { }
|
||||
explicit basic_atomic_cell_view(data::cell::basic_atomic_cell_view<is_mutable> v)
|
||||
: _view(std::move(v)) { }
|
||||
|
||||
basic_atomic_cell_view(const data::type_info& ti, pointer_type ptr)
|
||||
: _view(data::cell::make_atomic_cell_view(ti, ptr))
|
||||
{ }
|
||||
|
||||
friend class atomic_cell_or_collection;
|
||||
public:
|
||||
operator basic_atomic_cell_view<mutable_view::no>() const noexcept {
|
||||
return basic_atomic_cell_view<mutable_view::no>(_view);
|
||||
}
|
||||
|
||||
void swap(basic_atomic_cell_view& other) noexcept {
|
||||
using std::swap;
|
||||
swap(_view, other._view);
|
||||
}
|
||||
|
||||
bool is_counter_update() const {
|
||||
return atomic_cell_type::is_counter_update(_view);
|
||||
return _view.is_counter_update();
|
||||
}
|
||||
bool is_live() const {
|
||||
return atomic_cell_type::is_live(_view);
|
||||
return _view.is_live();
|
||||
}
|
||||
bool is_live(tombstone t, bool is_counter) const {
|
||||
return is_live() && !is_covered_by(t, is_counter);
|
||||
@@ -232,69 +83,73 @@ public:
|
||||
return is_live() && !is_covered_by(t, is_counter) && !has_expired(now);
|
||||
}
|
||||
bool is_live_and_has_ttl() const {
|
||||
return atomic_cell_type::is_live_and_has_ttl(_view);
|
||||
return _view.is_expiring();
|
||||
}
|
||||
bool is_dead(gc_clock::time_point now) const {
|
||||
return atomic_cell_type::is_dead(_view) || has_expired(now);
|
||||
return !is_live() || has_expired(now);
|
||||
}
|
||||
bool is_covered_by(tombstone t, bool is_counter) const {
|
||||
return timestamp() <= t.timestamp || (is_counter && t.timestamp != api::missing_timestamp);
|
||||
}
|
||||
// Can be called on live and dead cells
|
||||
api::timestamp_type timestamp() const {
|
||||
return atomic_cell_type::timestamp(_view);
|
||||
return _view.timestamp();
|
||||
}
|
||||
void set_timestamp(api::timestamp_type ts) {
|
||||
atomic_cell_type::set_timestamp(_view, ts);
|
||||
_view.set_timestamp(ts);
|
||||
}
|
||||
// Can be called on live cells only
|
||||
atomic_cell_value_basic_view<is_mutable> value() const {
|
||||
return atomic_cell_type::value(_view);
|
||||
data::basic_value_view<is_mutable> value() const {
|
||||
return _view.value();
|
||||
}
|
||||
// Can be called on live cells only
|
||||
size_t value_size() const {
|
||||
return atomic_cell_type::value(_view).size();
|
||||
return _view.value_size();
|
||||
}
|
||||
bool is_value_fragmented() const {
|
||||
return _view.is_value_fragmented();
|
||||
}
|
||||
// Can be called on live counter update cells only
|
||||
int64_t counter_update_value() const {
|
||||
return atomic_cell_type::counter_update_value(_view);
|
||||
return _view.counter_update_value();
|
||||
}
|
||||
// Can be called only when is_dead(gc_clock::time_point)
|
||||
gc_clock::time_point deletion_time() const {
|
||||
return !is_live() ? atomic_cell_type::deletion_time(_view) : expiry() - ttl();
|
||||
return !is_live() ? _view.deletion_time() : expiry() - ttl();
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl()
|
||||
gc_clock::time_point expiry() const {
|
||||
return atomic_cell_type::expiry(_view);
|
||||
return _view.expiry();
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl()
|
||||
gc_clock::duration ttl() const {
|
||||
return atomic_cell_type::ttl(_view);
|
||||
return _view.ttl();
|
||||
}
|
||||
// Can be called on live and dead cells
|
||||
bool has_expired(gc_clock::time_point now) const {
|
||||
return is_live_and_has_ttl() && expiry() <= now;
|
||||
}
|
||||
|
||||
managed_bytes_view serialize() const {
|
||||
return _view;
|
||||
bytes_view serialize() const {
|
||||
return _view.serialize();
|
||||
}
|
||||
};
|
||||
|
||||
class atomic_cell_view final : public basic_atomic_cell_view<mutable_view::no> {
|
||||
atomic_cell_view(managed_bytes_view v)
|
||||
: basic_atomic_cell_view(v) {}
|
||||
atomic_cell_view(const data::type_info& ti, const uint8_t* data)
|
||||
: basic_atomic_cell_view<mutable_view::no>(ti, data) {}
|
||||
|
||||
template<mutable_view is_mutable>
|
||||
atomic_cell_view(basic_atomic_cell_view<is_mutable> view)
|
||||
: basic_atomic_cell_view<mutable_view::no>(view) {}
|
||||
atomic_cell_view(data::cell::basic_atomic_cell_view<is_mutable> view)
|
||||
: basic_atomic_cell_view<mutable_view::no>(view) { }
|
||||
friend class atomic_cell;
|
||||
public:
|
||||
static atomic_cell_view from_bytes(const abstract_type& t, managed_bytes_view v) {
|
||||
return atomic_cell_view(v);
|
||||
static atomic_cell_view from_bytes(const data::type_info& ti, const imr::utils::object<data::cell::structure>& data) {
|
||||
return atomic_cell_view(ti, data.get());
|
||||
}
|
||||
static atomic_cell_view from_bytes(const abstract_type& t, bytes_view v) {
|
||||
return atomic_cell_view(managed_bytes_view(v));
|
||||
|
||||
static atomic_cell_view from_bytes(const data::type_info& ti, bytes_view bv) {
|
||||
return atomic_cell_view(ti, reinterpret_cast<const uint8_t*>(bv.begin()));
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const atomic_cell_view& acv);
|
||||
@@ -309,11 +164,11 @@ public:
|
||||
};
|
||||
|
||||
class atomic_cell_mutable_view final : public basic_atomic_cell_view<mutable_view::yes> {
|
||||
atomic_cell_mutable_view(managed_bytes_mutable_view data)
|
||||
: basic_atomic_cell_view(data) {}
|
||||
atomic_cell_mutable_view(const data::type_info& ti, uint8_t* data)
|
||||
: basic_atomic_cell_view<mutable_view::yes>(ti, data) {}
|
||||
public:
|
||||
static atomic_cell_mutable_view from_bytes(const abstract_type& t, managed_bytes_mutable_view v) {
|
||||
return atomic_cell_mutable_view(v);
|
||||
static atomic_cell_mutable_view from_bytes(const data::type_info& ti, imr::utils::object<data::cell::structure>& data) {
|
||||
return atomic_cell_mutable_view(ti, data.get());
|
||||
}
|
||||
|
||||
friend class atomic_cell;
|
||||
@@ -322,31 +177,26 @@ public:
|
||||
using atomic_cell_ref = atomic_cell_mutable_view;
|
||||
|
||||
class atomic_cell final : public basic_atomic_cell_view<mutable_view::yes> {
|
||||
managed_bytes _data;
|
||||
atomic_cell(managed_bytes b) : _data(std::move(b)) {
|
||||
set_view(_data);
|
||||
}
|
||||
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
imr_object_type _data;
|
||||
atomic_cell(const data::type_info& ti, imr::utils::object<data::cell::structure>&& data)
|
||||
: basic_atomic_cell_view<mutable_view::yes>(ti, data.get()), _data(std::move(data)) {}
|
||||
public:
|
||||
class collection_member_tag;
|
||||
using collection_member = bool_class<collection_member_tag>;
|
||||
|
||||
atomic_cell(atomic_cell&& o) noexcept : _data(std::move(o._data)) {
|
||||
set_view(_data);
|
||||
}
|
||||
atomic_cell(atomic_cell&&) = default;
|
||||
atomic_cell& operator=(const atomic_cell&) = delete;
|
||||
atomic_cell& operator=(atomic_cell&& o) {
|
||||
_data = std::move(o._data);
|
||||
set_view(_data);
|
||||
return *this;
|
||||
atomic_cell& operator=(atomic_cell&&) = default;
|
||||
void swap(atomic_cell& other) noexcept {
|
||||
basic_atomic_cell_view<mutable_view::yes>::swap(other);
|
||||
_data.swap(other._data);
|
||||
}
|
||||
operator atomic_cell_view() const { return atomic_cell_view(managed_bytes_view(_data)); }
|
||||
operator atomic_cell_view() const { return atomic_cell_view(_view); }
|
||||
atomic_cell(const abstract_type& t, atomic_cell_view other);
|
||||
static atomic_cell make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value,
|
||||
collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, managed_bytes_view value,
|
||||
collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value,
|
||||
collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value,
|
||||
@@ -358,8 +208,6 @@ public:
|
||||
static atomic_cell make_live_counter_update(api::timestamp_type timestamp, int64_t value);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, managed_bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value,
|
||||
@@ -376,13 +224,6 @@ public:
|
||||
return make_live(type, timestamp, value, gc_clock::now() + *ttl, *ttl, cm);
|
||||
}
|
||||
}
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, const managed_bytes_view& value, ttl_opt ttl, collection_member cm = collection_member::no) {
|
||||
if (!ttl) {
|
||||
return make_live(type, timestamp, value, cm);
|
||||
} else {
|
||||
return make_live(type, timestamp, value, gc_clock::now() + *ttl, *ttl, cm);
|
||||
}
|
||||
}
|
||||
static atomic_cell make_live_uninitialized(const abstract_type& type, api::timestamp_type timestamp, size_t size);
|
||||
friend class atomic_cell_or_collection;
|
||||
friend std::ostream& operator<<(std::ostream& os, const atomic_cell& ac);
|
||||
@@ -396,7 +237,7 @@ public:
|
||||
|
||||
class column_definition;
|
||||
|
||||
std::strong_ordering compare_atomic_cell_for_merge(atomic_cell_view left, atomic_cell_view right);
|
||||
int compare_atomic_cell_for_merge(atomic_cell_view left, atomic_cell_view right);
|
||||
void merge_column(const abstract_type& def,
|
||||
atomic_cell_or_collection& old,
|
||||
const atomic_cell_or_collection& neww);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -52,7 +52,9 @@ struct appending_hash<atomic_cell_view> {
|
||||
feed_hash(h, cell.timestamp());
|
||||
if (cell.is_live()) {
|
||||
if (cdef.is_counter()) {
|
||||
::feed_hash(h, counter_cell_view(cell));
|
||||
counter_cell_view::with_linearized(cell, [&] (counter_cell_view ccv) {
|
||||
::feed_hash(h, ccv);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (cell.is_live_and_has_ttl()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
* Copyright (C) 2015 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -24,15 +24,22 @@
|
||||
#include "atomic_cell.hh"
|
||||
#include "collection_mutation.hh"
|
||||
#include "schema.hh"
|
||||
#include "hashing.hh"
|
||||
|
||||
#include "imr/utils.hh"
|
||||
|
||||
// A variant type that can hold either an atomic_cell, or a serialized collection.
|
||||
// Which type is stored is determined by the schema.
|
||||
// Has an "empty" state.
|
||||
// Objects moved-from are left in an empty state.
|
||||
class atomic_cell_or_collection final {
|
||||
managed_bytes _data;
|
||||
// FIXME: This has made us lose small-buffer optimisation. Unfortunately,
|
||||
// due to the changed cell format it would be less effective now, anyway.
|
||||
// Measure the actual impact because any attempts to fix this will become
|
||||
// irrelevant once rows are converted to the IMR as well, so maybe we can
|
||||
// live with this like that.
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
imr_object_type _data;
|
||||
private:
|
||||
atomic_cell_or_collection(managed_bytes&& data) : _data(std::move(data)) {}
|
||||
atomic_cell_or_collection(imr::utils::object<data::cell::structure>&& data) : _data(std::move(data)) {}
|
||||
public:
|
||||
atomic_cell_or_collection() = default;
|
||||
atomic_cell_or_collection(atomic_cell_or_collection&&) = default;
|
||||
@@ -42,16 +49,20 @@ public:
|
||||
atomic_cell_or_collection(atomic_cell ac) : _data(std::move(ac._data)) {}
|
||||
atomic_cell_or_collection(const abstract_type& at, atomic_cell_view acv);
|
||||
static atomic_cell_or_collection from_atomic_cell(atomic_cell data) { return { std::move(data._data) }; }
|
||||
atomic_cell_view as_atomic_cell(const column_definition& cdef) const { return atomic_cell_view::from_bytes(*cdef.type, _data); }
|
||||
atomic_cell_mutable_view as_mutable_atomic_cell(const column_definition& cdef) { return atomic_cell_mutable_view::from_bytes(*cdef.type, _data); }
|
||||
atomic_cell_view as_atomic_cell(const column_definition& cdef) const { return atomic_cell_view::from_bytes(cdef.type->imr_state().type_info(), _data); }
|
||||
atomic_cell_ref as_atomic_cell_ref(const column_definition& cdef) { return atomic_cell_mutable_view::from_bytes(cdef.type->imr_state().type_info(), _data); }
|
||||
atomic_cell_mutable_view as_mutable_atomic_cell(const column_definition& cdef) { return atomic_cell_mutable_view::from_bytes(cdef.type->imr_state().type_info(), _data); }
|
||||
atomic_cell_or_collection(collection_mutation cm) : _data(std::move(cm._data)) { }
|
||||
atomic_cell_or_collection copy(const abstract_type&) const;
|
||||
explicit operator bool() const {
|
||||
return !_data.empty();
|
||||
return bool(_data);
|
||||
}
|
||||
static constexpr bool can_use_mutable_view() {
|
||||
return true;
|
||||
}
|
||||
void swap(atomic_cell_or_collection& other) noexcept {
|
||||
_data.swap(other._data);
|
||||
}
|
||||
static atomic_cell_or_collection from_collection_mutation(collection_mutation data) { return std::move(data._data); }
|
||||
collection_mutation_view as_collection_mutation() const;
|
||||
bytes_view serialize() const;
|
||||
@@ -71,3 +82,12 @@ public:
|
||||
};
|
||||
friend std::ostream& operator<<(std::ostream&, const printer&);
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
inline void swap(atomic_cell_or_collection& a, atomic_cell_or_collection& b) noexcept
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017-present ScyllaDB
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017-present ScyllaDB
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017-present ScyllaDB
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017-present ScyllaDB
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018-present ScyllaDB
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018-present ScyllaDB
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <seastar/core/enum.hh>
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/sstring.hh>
|
||||
@@ -74,8 +75,6 @@ class authenticated_user;
|
||||
///
|
||||
class authenticator {
|
||||
public:
|
||||
using ptr_type = std::unique_ptr<authenticator>;
|
||||
|
||||
///
|
||||
/// The name of the key to be used for the user-name part of password authentication with \ref authenticate.
|
||||
///
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
@@ -91,8 +91,6 @@ public:
|
||||
///
|
||||
class authorizer {
|
||||
public:
|
||||
using ptr_type = std::unique_ptr<authorizer>;
|
||||
|
||||
virtual ~authorizer() = default;
|
||||
|
||||
virtual future<> start() = 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017-present ScyllaDB
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -45,13 +45,15 @@ static logging::logger auth_log("auth");
|
||||
// Func must support being invoked more than once.
|
||||
future<> do_after_system_ready(seastar::abort_source& as, seastar::noncopyable_function<future<>()> func) {
|
||||
struct empty_state { };
|
||||
return exponential_backoff_retry::do_until_value(1s, 1min, as, [func = std::move(func)] {
|
||||
return func().then_wrapped([] (auto&& f) -> std::optional<empty_state> {
|
||||
if (f.failed()) {
|
||||
auth_log.debug("Auth task failed with error, rescheduling: {}", f.get_exception());
|
||||
return { };
|
||||
}
|
||||
return { empty_state() };
|
||||
return delay_until_system_ready(as).then([&as, func = std::move(func)] () mutable {
|
||||
return exponential_backoff_retry::do_until_value(1s, 1min, as, [func = std::move(func)] {
|
||||
return func().then_wrapped([] (auto&& f) -> std::optional<empty_state> {
|
||||
if (f.failed()) {
|
||||
auth_log.debug("Auth task failed with error, rescheduling: {}", f.get_exception());
|
||||
return { };
|
||||
}
|
||||
return { empty_state() };
|
||||
});
|
||||
});
|
||||
}).discard_result();
|
||||
}
|
||||
@@ -106,7 +108,7 @@ future<> wait_for_schema_agreement(::service::migration_manager& mm, const datab
|
||||
});
|
||||
}
|
||||
|
||||
::service::query_state& internal_distributed_query_state() noexcept {
|
||||
const timeout_config& internal_distributed_timeout_config() noexcept {
|
||||
#ifdef DEBUG
|
||||
// Give the much slower debug tests more headroom for completing auth queries.
|
||||
static const auto t = 30s;
|
||||
@@ -114,9 +116,7 @@ future<> wait_for_schema_agreement(::service::migration_manager& mm, const datab
|
||||
static const auto t = 5s;
|
||||
#endif
|
||||
static const timeout_config tc{t, t, t, t, t, t, t};
|
||||
static thread_local ::service::client_state cs(::service::client_state::internal_tag{}, tc);
|
||||
static thread_local ::service::query_state qs(cs, empty_service_permit());
|
||||
return qs;
|
||||
return tc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017-present ScyllaDB
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "log.hh"
|
||||
#include "seastarx.hh"
|
||||
#include "utils/exponential_backoff_retry.hh"
|
||||
#include "service/query_state.hh"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -70,6 +69,10 @@ future<> once_among_shards(Task&& f) {
|
||||
return make_ready_future<>();
|
||||
}
|
||||
|
||||
inline future<> delay_until_system_ready(seastar::abort_source& as) {
|
||||
return sleep_abortable(15s, as);
|
||||
}
|
||||
|
||||
// Func must support being invoked more than once.
|
||||
future<> do_after_system_ready(seastar::abort_source& as, seastar::noncopyable_function<future<>()> func);
|
||||
|
||||
@@ -84,6 +87,6 @@ future<> wait_for_schema_agreement(::service::migration_manager&, const database
|
||||
///
|
||||
/// Time-outs for internal, non-local CQL queries.
|
||||
///
|
||||
::service::query_state& internal_distributed_query_state() noexcept;
|
||||
const timeout_config& internal_distributed_timeout_config() noexcept;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
@@ -62,7 +62,6 @@ extern "C" {
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "log.hh"
|
||||
#include "database.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -104,6 +103,7 @@ future<bool> default_authorizer::any_granted() const {
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::LOCAL_ONE,
|
||||
infinite_timeout_config,
|
||||
{},
|
||||
true).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
return !results->empty();
|
||||
@@ -116,7 +116,8 @@ future<> default_authorizer::migrate_legacy_metadata() const {
|
||||
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::LOCAL_ONE).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
db::consistency_level::LOCAL_ONE,
|
||||
infinite_timeout_config).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
return do_for_each(*results, [this](const cql3::untyped_result_set_row& row) {
|
||||
return do_with(
|
||||
row.get_as<sstring>("username"),
|
||||
@@ -135,13 +136,13 @@ future<> default_authorizer::migrate_legacy_metadata() const {
|
||||
}
|
||||
|
||||
future<> default_authorizer::start() {
|
||||
static const sstring create_table = fmt::format(
|
||||
"CREATE TABLE {}.{} ("
|
||||
"{} text,"
|
||||
"{} text,"
|
||||
"{} set<text>,"
|
||||
"PRIMARY KEY({}, {})"
|
||||
") WITH gc_grace_seconds={}",
|
||||
static const sstring create_table = sprint(
|
||||
"CREATE TABLE %s.%s ("
|
||||
"%s text,"
|
||||
"%s text,"
|
||||
"%s set<text>,"
|
||||
"PRIMARY KEY(%s, %s)"
|
||||
") WITH gc_grace_seconds=%d",
|
||||
meta::AUTH_KS,
|
||||
PERMISSIONS_CF,
|
||||
ROLE_NAME,
|
||||
@@ -196,6 +197,7 @@ default_authorizer::authorize(const role_or_anonymous& maybe_role, const resourc
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::LOCAL_ONE,
|
||||
infinite_timeout_config,
|
||||
{*maybe_role.name, r.name()}).then([](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
if (results->empty()) {
|
||||
return permissions::NONE;
|
||||
@@ -224,7 +226,7 @@ default_authorizer::modify(
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::ONE,
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{permissions::to_strings(set), sstring(role_name), resource.name()}).discard_result();
|
||||
});
|
||||
}
|
||||
@@ -249,7 +251,7 @@ future<std::vector<permission_details>> default_authorizer::list_all() const {
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::ONE,
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{},
|
||||
true).then([](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
std::vector<permission_details> all_details;
|
||||
@@ -276,7 +278,7 @@ future<> default_authorizer::revoke_all(std::string_view role_name) const {
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::ONE,
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name)}).discard_result().handle_exception([role_name](auto ep) {
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
@@ -296,6 +298,7 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::LOCAL_ONE,
|
||||
infinite_timeout_config,
|
||||
{resource.name()}).then_wrapped([this, resource](future<::shared_ptr<cql3::untyped_result_set>> f) {
|
||||
try {
|
||||
auto res = f.get0();
|
||||
@@ -312,6 +315,7 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::LOCAL_ONE,
|
||||
infinite_timeout_config,
|
||||
{r.get_as<sstring>(ROLE_NAME), resource.name()}).discard_result().handle_exception(
|
||||
[resource](auto ep) {
|
||||
try {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
@@ -46,14 +46,9 @@
|
||||
#include <seastar/core/abort_source.hh>
|
||||
|
||||
#include "auth/authorizer.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "service/migration_manager.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
class query_processor;
|
||||
|
||||
} // namespace cql3
|
||||
|
||||
namespace auth {
|
||||
|
||||
class default_authorizer : public authorizer {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-present ScyllaDB
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
@@ -59,7 +59,6 @@
|
||||
#include "service/migration_manager.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
#include "database.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -115,7 +114,7 @@ future<> password_authenticator::migrate_legacy_metadata() const {
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_query_state()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
internal_distributed_timeout_config()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
return do_for_each(*results, [this](const cql3::untyped_result_set_row& row) {
|
||||
auto username = row.get_as<sstring>("username");
|
||||
auto salted_hash = row.get_as<sstring>(SALTED_HASH);
|
||||
@@ -123,7 +122,7 @@ future<> password_authenticator::migrate_legacy_metadata() const {
|
||||
return _qp.execute_internal(
|
||||
update_row_query(),
|
||||
consistency_for_user(username),
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{std::move(salted_hash), username}).discard_result();
|
||||
}).finally([results] {});
|
||||
}).then([] {
|
||||
@@ -140,7 +139,7 @@ future<> password_authenticator::create_default_if_missing() const {
|
||||
return _qp.execute_internal(
|
||||
update_row_query(),
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{passwords::hash(DEFAULT_USER_PASSWORD, rng_for_salt), DEFAULT_USER_NAME}).then([](auto&&) {
|
||||
plogger.info("Created default superuser authentication record.");
|
||||
});
|
||||
@@ -237,7 +236,7 @@ future<authenticated_user> password_authenticator::authenticate(
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
consistency_for_user(username),
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{username},
|
||||
true);
|
||||
}).then_wrapped([=](future<::shared_ptr<cql3::untyped_result_set>> f) {
|
||||
@@ -271,7 +270,7 @@ future<> password_authenticator::create(std::string_view role_name, const authen
|
||||
return _qp.execute_internal(
|
||||
update_row_query(),
|
||||
consistency_for_user(role_name),
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result();
|
||||
}
|
||||
|
||||
@@ -288,7 +287,7 @@ future<> password_authenticator::alter(std::string_view role_name, const authent
|
||||
return _qp.execute_internal(
|
||||
query,
|
||||
consistency_for_user(role_name),
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result();
|
||||
}
|
||||
|
||||
@@ -300,7 +299,7 @@ future<> password_authenticator::drop(std::string_view name) const {
|
||||
|
||||
return _qp.execute_internal(
|
||||
query, consistency_for_user(name),
|
||||
internal_distributed_query_state(),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(name)}).discard_result();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user