Compare commits
1 Commits
branch-6.2
...
annastuchl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
933192a0bb |
225
.clang-format
225
.clang-format
@@ -1,225 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveShortCaseStatements:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCaseColons: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BitFieldColonSpacing: Both
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterExternBlock: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakAfterAttributes: Never
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakArrays: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 160
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: false
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertNewlineAtEOF: true
|
||||
InsertTrailingCommas: None
|
||||
IntegerLiteralSeparator:
|
||||
Binary: 0
|
||||
BinaryMinDigits: 0
|
||||
Decimal: 0
|
||||
DecimalMinDigits: 0
|
||||
Hex: 0
|
||||
HexMinDigits: 0
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
KeepEmptyLinesAtEOF: false
|
||||
LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
PackConstructorInitializers: NextLine
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Left
|
||||
PPIndentWidth: -1
|
||||
QualifierAlignment: Leave
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RemoveParentheses: Leave
|
||||
RemoveSemicolon: false
|
||||
RequiresClausePosition: OwnLine
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: LexicographicNumeric
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeJsonColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParens: Never
|
||||
SpacesInParensOptions:
|
||||
InCStyleCasts: false
|
||||
InConditionalStatements: false
|
||||
InEmptyParentheses: false
|
||||
Other: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Latest
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
VerilogBreakBetweenInstancePorts: true
|
||||
WhitespaceSensitiveMacros:
|
||||
- BOOST_PP_STRINGIZE
|
||||
- CF_SWIFT_NAME
|
||||
- NS_SWIFT_NAME
|
||||
- PP_STRINGIZE
|
||||
- STRINGIZE
|
||||
...
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,4 +1,3 @@
|
||||
*.cc diff=cpp
|
||||
*.hh diff=cpp
|
||||
*.svg binary
|
||||
docs/_static/api/js/* binary
|
||||
|
||||
26
.github/CODEOWNERS
vendored
26
.github/CODEOWNERS
vendored
@@ -1,5 +1,5 @@
|
||||
# AUTH
|
||||
auth/* @nuivall @ptrsmrn @KrzaQ
|
||||
auth/* @elcallio @vladzcloudius
|
||||
|
||||
# CACHE
|
||||
row_cache* @tgrabiec
|
||||
@@ -7,9 +7,9 @@ row_cache* @tgrabiec
|
||||
test/boost/mvcc* @tgrabiec
|
||||
|
||||
# CDC
|
||||
cdc/* @kbr-scylla @elcallio @piodul
|
||||
test/cql/cdc_* @kbr-scylla @elcallio @piodul
|
||||
test/boost/cdc_* @kbr-scylla @elcallio @piodul
|
||||
cdc/* @kbr- @elcallio @piodul @jul-stas
|
||||
test/cql/cdc_* @kbr- @elcallio @piodul @jul-stas
|
||||
test/boost/cdc_* @kbr- @elcallio @piodul @jul-stas
|
||||
|
||||
# COMMITLOG / BATCHLOG
|
||||
db/commitlog/* @elcallio @eliransin
|
||||
@@ -25,18 +25,18 @@ compaction/* @raphaelsc
|
||||
transport/*
|
||||
|
||||
# CQL QUERY LANGUAGE
|
||||
cql3/* @tgrabiec @nuivall @ptrsmrn @KrzaQ
|
||||
cql3/* @tgrabiec
|
||||
|
||||
# COUNTERS
|
||||
counters* @nuivall @ptrsmrn @KrzaQ
|
||||
tests/counter_test* @nuivall @ptrsmrn @KrzaQ
|
||||
counters* @jul-stas
|
||||
tests/counter_test* @jul-stas
|
||||
|
||||
# DOCS
|
||||
docs/* @annastuchlik @tzach
|
||||
docs/alternator @annastuchlik @tzach @nyh @nuivall @ptrsmrn @KrzaQ
|
||||
docs/alternator @annastuchlik @tzach @nyh @havaker @nuivall
|
||||
|
||||
# GOSSIP
|
||||
gms/* @tgrabiec @asias @kbr-scylla
|
||||
gms/* @tgrabiec @asias
|
||||
|
||||
# DOCKER
|
||||
dist/docker/*
|
||||
@@ -74,8 +74,8 @@ streaming/* @tgrabiec @asias
|
||||
service/storage_service.* @tgrabiec @asias
|
||||
|
||||
# ALTERNATOR
|
||||
alternator/* @nyh @nuivall @ptrsmrn @KrzaQ
|
||||
test/alternator/* @nyh @nuivall @ptrsmrn @KrzaQ
|
||||
alternator/* @havaker @nuivall
|
||||
test/alternator/* @havaker @nuivall
|
||||
|
||||
# HINTED HANDOFF
|
||||
db/hints/* @piodul @vladzcloudius @eliransin
|
||||
@@ -94,8 +94,8 @@ test/boost/querier_cache_test.cc @denesb
|
||||
test/cql-pytest/* @nyh
|
||||
|
||||
# RAFT
|
||||
raft/* @kbr-scylla @gleb-cloudius @kostja
|
||||
test/raft/* @kbr-scylla @gleb-cloudius @kostja
|
||||
raft/* @kbr- @gleb-cloudius @kostja
|
||||
test/raft/* @kbr- @gleb-cloudius @kostja
|
||||
|
||||
# HEAT-WEIGHTED LOAD BALANCING
|
||||
db/heat_load_balance.* @nyh @gleb-cloudius
|
||||
|
||||
20
.github/clang-include-cleaner.json
vendored
20
.github/clang-include-cleaner.json
vendored
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "clang-include-cleaner",
|
||||
"severity": "error",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\-\\+].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^(-\\s+[^\\s]+)\\s+@Line:(\\d+)$",
|
||||
"line": 2,
|
||||
"message": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.github/clang-matcher.json
vendored
18
.github/clang-matcher.json
vendored
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "clang",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^:]+):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*?)\\s+\\[(.*?)\\]$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5,
|
||||
"code": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
33
.github/mergify.yml
vendored
33
.github/mergify.yml
vendored
@@ -15,7 +15,7 @@ pull_request_rules:
|
||||
- closed
|
||||
actions:
|
||||
delete_head_branch:
|
||||
- name: Automate backport pull request 6.1
|
||||
- name: Automate backport pull request 5.2
|
||||
conditions:
|
||||
- or:
|
||||
- closed
|
||||
@@ -23,11 +23,11 @@ pull_request_rules:
|
||||
- or:
|
||||
- base=master
|
||||
- base=next
|
||||
- label=backport/6.1 # The PR must have this label to trigger the backport
|
||||
- label=backport/5.2 # The PR must have this label to trigger the backport
|
||||
- label=promoted-to-master
|
||||
actions:
|
||||
copy:
|
||||
title: "[Backport 6.1] {{ title }}"
|
||||
title: "[Backport 5.2] {{ title }}"
|
||||
body: |
|
||||
{{ body }}
|
||||
|
||||
@@ -37,7 +37,7 @@ pull_request_rules:
|
||||
|
||||
Refs #{{number}}
|
||||
branches:
|
||||
- branch-6.1
|
||||
- branch-5.2
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
- name: Automate backport pull request 5.4
|
||||
@@ -65,28 +65,3 @@ pull_request_rules:
|
||||
- branch-5.4
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
- name: Automate backport pull request 6.0
|
||||
conditions:
|
||||
- or:
|
||||
- closed
|
||||
- merged
|
||||
- or:
|
||||
- base=master
|
||||
- base=next
|
||||
- label=backport/6.0 # The PR must have this label to trigger the backport
|
||||
- label=promoted-to-master
|
||||
actions:
|
||||
copy:
|
||||
title: "[Backport 6.0] {{ title }}"
|
||||
body: |
|
||||
{{ body }}
|
||||
|
||||
{% for c in commits %}
|
||||
(cherry picked from commit {{ c.sha }})
|
||||
{% endfor %}
|
||||
|
||||
Refs #{{number}}
|
||||
branches:
|
||||
- branch-6.0
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -1 +0,0 @@
|
||||
**Please replace this line with justification for the backport/\* labels added to this PR**
|
||||
186
.github/scripts/auto-backport.py
vendored
186
.github/scripts/auto-backport.py
vendored
@@ -1,186 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
import logging
|
||||
|
||||
from github import Github, GithubException
|
||||
from git import Repo, GitCommandError
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
try:
|
||||
github_token = os.environ["GITHUB_TOKEN"]
|
||||
except KeyError:
|
||||
print("Please set the 'GITHUB_TOKEN' environment variable")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def is_pull_request():
|
||||
return '--pull-request' in sys.argv[1:]
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--repo', type=str, required=True, help='Github repository name')
|
||||
parser.add_argument('--base-branch', type=str, default='refs/heads/master', help='Base branch')
|
||||
parser.add_argument('--commits', default=None, type=str, help='Range of promoted commits.')
|
||||
parser.add_argument('--pull-request', type=int, help='Pull request number to be backported')
|
||||
parser.add_argument('--head-commit', type=str, required=is_pull_request(), help='The HEAD of target branch after the pull request specified by --pull-request is merged')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def create_pull_request(repo, new_branch_name, base_branch_name, pr, backport_pr_title, commits, is_draft=False):
|
||||
pr_body = f'{pr.body}\n\n'
|
||||
for commit in commits:
|
||||
pr_body += f'- (cherry picked from commit {commit})\n\n'
|
||||
pr_body += f'Parent PR: #{pr.number}'
|
||||
try:
|
||||
backport_pr = repo.create_pull(
|
||||
title=backport_pr_title,
|
||||
body=pr_body,
|
||||
head=f'scylladbbot:{new_branch_name}',
|
||||
base=base_branch_name,
|
||||
draft=is_draft
|
||||
)
|
||||
logging.info(f"Pull request created: {backport_pr.html_url}")
|
||||
backport_pr.add_to_assignees(pr.user)
|
||||
if is_draft:
|
||||
backport_pr.add_to_labels("conflicts")
|
||||
pr_comment = f"@{pr.user} - This PR was marked as draft because it has conflicts\n"
|
||||
pr_comment += "Please resolve them and mark this PR as ready for review"
|
||||
backport_pr.create_issue_comment(pr_comment)
|
||||
logging.info(f"Assigned PR to original author: {pr.user}")
|
||||
return backport_pr
|
||||
except GithubException as e:
|
||||
if 'A pull request already exists' in str(e):
|
||||
logging.warning(f'A pull request already exists for {pr.user}:{new_branch_name}')
|
||||
else:
|
||||
logging.error(f'Failed to create PR: {e}')
|
||||
|
||||
|
||||
def get_pr_commits(repo, pr, stable_branch, start_commit=None):
|
||||
commits = []
|
||||
if pr.merged:
|
||||
merge_commit = repo.get_commit(pr.merge_commit_sha)
|
||||
if len(merge_commit.parents) > 1: # Check if this merge commit includes multiple commits
|
||||
commits.append(pr.merge_commit_sha)
|
||||
else:
|
||||
if start_commit:
|
||||
promoted_commits = repo.compare(start_commit, stable_branch).commits
|
||||
else:
|
||||
promoted_commits = repo.get_commits(sha=stable_branch)
|
||||
for commit in pr.get_commits():
|
||||
for promoted_commit in promoted_commits:
|
||||
commit_title = commit.commit.message.splitlines()[0]
|
||||
# In Scylla-pkg and scylla-dtest, for example,
|
||||
# we don't create a merge commit for a PR with multiple commits,
|
||||
# according to the GitHub API, the last commit will be the merge commit,
|
||||
# which is not what we need when backporting (we need all the commits).
|
||||
# So here, we are validating the correct SHA for each commit so we can cherry-pick
|
||||
if promoted_commit.commit.message.startswith(commit_title):
|
||||
commits.append(promoted_commit.sha)
|
||||
|
||||
elif pr.state == 'closed':
|
||||
events = pr.get_issue_events()
|
||||
for event in events:
|
||||
if event.event == 'closed':
|
||||
commits.append(event.commit_id)
|
||||
return commits
|
||||
|
||||
|
||||
def create_pr_comment_and_remove_label(pr, comment_body):
|
||||
labels = pr.get_labels()
|
||||
pattern = re.compile(r"backport/\d+\.\d+$")
|
||||
for label in labels:
|
||||
if pattern.match(label.name):
|
||||
print(f"Removing label: {label.name}")
|
||||
comment_body += f'- {label.name}\n'
|
||||
pr.remove_from_labels(label)
|
||||
pr.create_issue_comment(comment_body)
|
||||
|
||||
|
||||
def backport(repo, pr, version, commits, backport_base_branch):
|
||||
new_branch_name = f'backport/{pr.number}/to-{version}'
|
||||
backport_pr_title = f'[Backport {version}] {pr.title}'
|
||||
repo_url = f'https://scylladbbot:{github_token}@github.com/{repo.full_name}.git'
|
||||
fork_repo = f'https://scylladbbot:{github_token}@github.com/scylladbbot/{repo.name}.git'
|
||||
with (tempfile.TemporaryDirectory() as local_repo_path):
|
||||
try:
|
||||
repo_local = Repo.clone_from(repo_url, local_repo_path, branch=backport_base_branch)
|
||||
repo_local.git.checkout(b=new_branch_name)
|
||||
is_draft = False
|
||||
for commit in commits:
|
||||
try:
|
||||
repo_local.git.cherry_pick(commit, '-m1', '-x')
|
||||
except GitCommandError as e:
|
||||
logging.warning(f'Cherry-pick conflict on commit {commit}: {e}')
|
||||
is_draft = True
|
||||
repo_local.git.add(A=True)
|
||||
repo_local.git.cherry_pick('--continue')
|
||||
if not repo.private and not repo.has_in_collaborators(pr.user.login):
|
||||
repo.add_to_collaborators(pr.user.login, permission="push")
|
||||
comment = f':warning: @{pr.user.login} you have been added as collaborator to scylladbbot fork '
|
||||
comment += f'Please check your inbox and approve the invitation, once it is done, please add the backport labels again'
|
||||
create_pr_comment_and_remove_label(pr, comment)
|
||||
return
|
||||
repo_local.git.push(fork_repo, new_branch_name, force=True)
|
||||
create_pull_request(repo, new_branch_name, backport_base_branch, pr, backport_pr_title, commits,
|
||||
is_draft=is_draft)
|
||||
|
||||
except GitCommandError as e:
|
||||
logging.warning(f"GitCommandError: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
base_branch = args.base_branch.split('/')[2]
|
||||
promoted_label = 'promoted-to-master'
|
||||
repo_name = args.repo
|
||||
if 'scylla-enterprise' in args.repo:
|
||||
promoted_label = 'promoted-to-enterprise'
|
||||
stable_branch = base_branch
|
||||
backport_branch = 'branch-'
|
||||
|
||||
backport_label_pattern = re.compile(r'backport/\d+\.\d+$')
|
||||
|
||||
g = Github(github_token)
|
||||
repo = g.get_repo(repo_name)
|
||||
closed_prs = []
|
||||
start_commit = None
|
||||
|
||||
if args.commits:
|
||||
start_commit, end_commit = args.commits.split('..')
|
||||
commits = repo.compare(start_commit, end_commit).commits
|
||||
for commit in commits:
|
||||
match = re.search(rf"Closes .*#([0-9]+)", commit.commit.message, re.IGNORECASE)
|
||||
if match:
|
||||
pr_number = int(match.group(1))
|
||||
pr = repo.get_pull(pr_number)
|
||||
closed_prs.append(pr)
|
||||
if args.pull_request:
|
||||
start_commit = args.head_commit
|
||||
pr = repo.get_pull(args.pull_request)
|
||||
closed_prs = [pr]
|
||||
|
||||
for pr in closed_prs:
|
||||
labels = [label.name for label in pr.labels]
|
||||
backport_labels = [label for label in labels if backport_label_pattern.match(label)]
|
||||
if promoted_label not in labels:
|
||||
print(f'no {promoted_label} label: {pr.number}')
|
||||
continue
|
||||
if not backport_labels:
|
||||
print(f'no backport label: {pr.number}')
|
||||
continue
|
||||
commits = get_pr_commits(repo, pr, stable_branch, start_commit)
|
||||
logging.info(f"Found PR #{pr.number} with commit {commits} and the following labels: {backport_labels}")
|
||||
for backport_label in backport_labels:
|
||||
version = backport_label.replace('backport/', '')
|
||||
backport_base_branch = backport_label.replace('backport/', backport_branch)
|
||||
backport(repo, pr, version, commits, backport_base_branch)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
82
.github/scripts/label_promoted_commits.py
vendored
82
.github/scripts/label_promoted_commits.py
vendored
@@ -1,9 +1,9 @@
|
||||
import requests
|
||||
from github import Github
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
from github import Github
|
||||
from github.GithubException import UnknownObjectException
|
||||
|
||||
try:
|
||||
github_token = os.environ["GITHUB_TOKEN"]
|
||||
@@ -16,71 +16,43 @@ def parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--repository', type=str, required=True,
|
||||
help='Github repository name (e.g., scylladb/scylladb)')
|
||||
parser.add_argument('--commits', type=str, required=True, help='Range of promoted commits.')
|
||||
parser.add_argument('--label', type=str, default='promoted-to-master', help='Label to use')
|
||||
parser.add_argument('--ref', type=str, required=True, help='PR target branch')
|
||||
parser.add_argument('--commit_before_merge', type=str, required=True, help='Git commit ID to start labeling from ('
|
||||
'newest commit).')
|
||||
parser.add_argument('--commit_after_merge', type=str, required=True,
|
||||
help='Git commit ID to end labeling at (oldest '
|
||||
'commit, exclusive).')
|
||||
parser.add_argument('--update_issue', type=bool, default=False, help='Set True to update issues when backport was '
|
||||
'done')
|
||||
parser.add_argument('--label', type=str, required=True, help='Label to use')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def add_comment_and_close_pr(pr, comment):
|
||||
if pr.state == 'open':
|
||||
pr.create_issue_comment(comment)
|
||||
pr.edit(state="closed")
|
||||
|
||||
|
||||
def mark_backport_done(repo, ref_pr_number, branch):
|
||||
pr = repo.get_pull(int(ref_pr_number))
|
||||
label_to_remove = f'backport/{branch}'
|
||||
label_to_add = f'{label_to_remove}-done'
|
||||
current_labels = [label.name for label in pr.get_labels()]
|
||||
if label_to_remove in current_labels:
|
||||
pr.remove_from_labels(label_to_remove)
|
||||
if label_to_add not in current_labels:
|
||||
pr.add_to_labels(label_to_add)
|
||||
|
||||
|
||||
def main():
|
||||
# This script is triggered by a push event to either the master branch or a branch named branch-x.y (where x and y represent version numbers). Based on the pushed branch, the script performs the following actions:
|
||||
# - When ref branch is `master`, it will add the `promoted-to-master` label, which we need later for the auto backport process
|
||||
# - When ref branch is `branch-x.y` (which means we backported a patch), it will replace in the original PR the `backport/x.y` label with `backport/x.y-done` and will close the backport PR (Since GitHub close only the one referring to default branch)
|
||||
args = parser()
|
||||
pr_pattern = re.compile(r'Closes .*#([0-9]+)')
|
||||
target_branch = re.search(r'branch-(\d+\.\d+)', args.ref)
|
||||
g = Github(github_token)
|
||||
repo = g.get_repo(args.repository, lazy=False)
|
||||
start_commit, end_commit = args.commits.split('..')
|
||||
commits = repo.compare(start_commit, end_commit).commits
|
||||
processed_prs = set()
|
||||
|
||||
commits = repo.compare(head=args.commit_after_merge, base=args.commit_before_merge)
|
||||
# Print commit information
|
||||
for commit in commits:
|
||||
print(f'Commit sha is: {commit.sha}')
|
||||
for commit in commits.commits:
|
||||
print(commit.sha)
|
||||
match = pr_pattern.search(commit.commit.message)
|
||||
if match:
|
||||
pr_number = int(match.group(1))
|
||||
if pr_number in processed_prs:
|
||||
continue
|
||||
if target_branch:
|
||||
pr = repo.get_pull(pr_number)
|
||||
branch_name = target_branch[1]
|
||||
refs_pr = re.findall(r'Parent PR: (?:#|https.*?)(\d+)', pr.body)
|
||||
if refs_pr:
|
||||
print(f'branch-{target_branch.group(1)}, pr number is: {pr_number}')
|
||||
# 1. change the backport label of the parent PR to note that
|
||||
# we've merged the corresponding backport PR
|
||||
# 2. close the backport PR and leave a comment on it to note
|
||||
# that it has been merged with a certain git commit.
|
||||
ref_pr_number = refs_pr[0]
|
||||
mark_backport_done(repo, ref_pr_number, branch_name)
|
||||
comment = f'Closed via {commit.sha}'
|
||||
add_comment_and_close_pr(pr, comment)
|
||||
pr_number = match.group(1)
|
||||
url = f'https://api.github.com/repos/{args.repository}/issues/{pr_number}/labels'
|
||||
data = {
|
||||
"labels": [f'{args.label}']
|
||||
}
|
||||
headers = {
|
||||
"Authorization": f"token {github_token}",
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if response.ok:
|
||||
print(f"Label added successfully to {url}")
|
||||
else:
|
||||
try:
|
||||
pr = repo.get_pull(pr_number)
|
||||
pr.add_to_labels('promoted-to-master')
|
||||
print(f'master branch, pr number is: {pr_number}')
|
||||
except UnknownObjectException:
|
||||
print(f'{pr_number} is not a PR but an issue, no need to add label')
|
||||
processed_prs.add(pr_number)
|
||||
print(f"No label was added to {url}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
55
.github/workflows/add-label-when-promoted.yaml
vendored
55
.github/workflows/add-label-when-promoted.yaml
vendored
@@ -4,11 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- branch-*.*
|
||||
- enterprise
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
branches: [master, next, enterprise]
|
||||
|
||||
jobs:
|
||||
check-commit:
|
||||
@@ -17,55 +12,15 @@ jobs:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Set Default Branch
|
||||
id: set_branch
|
||||
run: |
|
||||
if [[ "${{ github.repository }}" == *enterprise* ]]; then
|
||||
echo "DEFAULT_BRANCH=enterprise" >> $GITHUB_ENV
|
||||
else
|
||||
echo "DEFAULT_BRANCH=master" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
ref: ${{ env.DEFAULT_BRANCH }}
|
||||
token: ${{ secrets.AUTO_BACKPORT_TOKEN }}
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- name: Set up Git identity
|
||||
run: |
|
||||
git config --global user.name "GitHub Action"
|
||||
git config --global user.email "action@github.com"
|
||||
git config --global merge.conflictstyle diff3
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install -y python3-github python3-git
|
||||
run: sudo apt-get install -y python3-github
|
||||
|
||||
- name: Run python script
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.AUTO_BACKPORT_TOKEN }}
|
||||
run: python .github/scripts/label_promoted_commits.py --commits ${{ github.event.before }}..${{ github.sha }} --repository ${{ github.repository }} --ref ${{ github.ref }}
|
||||
- name: Run auto-backport.py when promotion completed
|
||||
if: ${{ github.event_name == 'push' && github.ref == format('refs/heads/{0}', env.DEFAULT_BRANCH) }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.AUTO_BACKPORT_TOKEN }}
|
||||
run: python .github/scripts/auto-backport.py --repo ${{ github.repository }} --base-branch ${{ github.ref }} --commits ${{ github.event.before }}..${{ github.sha }}
|
||||
- name: Check if label starts with 'backport/' and contains digits
|
||||
id: check_label
|
||||
run: |
|
||||
label_name="${{ github.event.label.name }}"
|
||||
if [[ "$label_name" =~ ^backport/[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Label matches backport/X.X pattern."
|
||||
echo "backport_label=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Label does not match the required pattern."
|
||||
echo "backport_label=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Run auto-backport.py when label was added
|
||||
if: ${{ github.event_name == 'pull_request_target' && steps.check_label.outputs.backport_label == 'true' && github.event.pull_request.state == 'closed' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.AUTO_BACKPORT_TOKEN }}
|
||||
run: python .github/scripts/auto-backport.py --repo ${{ github.repository }} --base-branch ${{ github.ref }} --pull-request ${{ github.event.pull_request.number }} --head-commit ${{ github.event.pull_request.base.sha }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: python .github/scripts/label_promoted_commits.py --commit_before_merge ${{ github.event.before }} --commit_after_merge ${{ github.event.after }} --repository ${{ github.repository }} --label promoted-to-master
|
||||
|
||||
@@ -22,12 +22,5 @@ jobs:
|
||||
const regex = new RegExp(pattern);
|
||||
|
||||
if (!regex.test(body)) {
|
||||
const error = "PR body does not contain a valid 'Fixes' reference.";
|
||||
core.setFailed(error);
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `:warning: ${error}`
|
||||
});
|
||||
core.setFailed("PR body does not contain a valid 'Fixes' reference.");
|
||||
}
|
||||
|
||||
39
.github/workflows/build-scylla.yaml
vendored
39
.github/workflows/build-scylla.yaml
vendored
@@ -1,39 +0,0 @@
|
||||
name: Build Scylla
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_mode:
|
||||
description: 'the build mode'
|
||||
type: string
|
||||
required: true
|
||||
outputs:
|
||||
md5sum:
|
||||
description: 'the md5sum for scylla executable'
|
||||
value: ${{ jobs.build.outputs.md5sum }}
|
||||
|
||||
jobs:
|
||||
read-toolchain:
|
||||
uses: ./.github/workflows/read-toolchain.yaml
|
||||
build:
|
||||
if: github.repository == 'scylladb/scylladb'
|
||||
needs:
|
||||
- read-toolchain
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ needs.read-toolchain.outputs.image }}
|
||||
outputs:
|
||||
md5sum: ${{ steps.checksum.outputs.md5sum }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Generate the building system
|
||||
run: |
|
||||
git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
./configure.py --mode ${{ inputs.build_mode }} --with scylla
|
||||
- run: |
|
||||
ninja build/${{ inputs.build_mode }}/scylla
|
||||
- id: checksum
|
||||
run: |
|
||||
checksum=$(md5sum build/${{ inputs.build_mode }}/scylla | cut -c -32)
|
||||
echo "md5sum=$checksum" >> $GITHUB_OUTPUT
|
||||
66
.github/workflows/clang-nightly.yaml
vendored
66
.github/workflows/clang-nightly.yaml
vendored
@@ -1,66 +0,0 @@
|
||||
name: clang-nightly
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# only at 5AM Saturday
|
||||
- cron: '0 5 * * SAT'
|
||||
|
||||
env:
|
||||
# use the development branch explicitly
|
||||
CLANG_VERSION: 20
|
||||
BUILD_DIR: build
|
||||
|
||||
permissions: {}
|
||||
|
||||
# cancel the in-progress run upon a repush
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
clang-dev:
|
||||
name: Build with clang nightly
|
||||
if: github.repository == 'scylladb/scylladb'
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:40
|
||||
strategy:
|
||||
matrix:
|
||||
build_type:
|
||||
- Debug
|
||||
- RelWithDebInfo
|
||||
- Dev
|
||||
steps:
|
||||
- run: |
|
||||
sudo dnf -y install git
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
# use the copr repo for llvm snapshot builds, see
|
||||
# https://copr.fedorainfracloud.org/coprs/g/fedora-llvm-team/llvm-snapshots/
|
||||
sudo dnf -y install 'dnf-command(copr)'
|
||||
sudo dnf copr enable -y @fedora-llvm-team/llvm-snapshots
|
||||
# do not install java dependencies, which is not only not used here
|
||||
sed -i.orig \
|
||||
-e '/tools\/.*\/install-dependencies.sh/d' \
|
||||
-e 's/(minio_download_jobs)/(true)/' \
|
||||
./install-dependencies.sh
|
||||
sudo ./install-dependencies.sh
|
||||
sudo dnf -y install lld
|
||||
- name: Generate the building system
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||
-DCMAKE_C_COMPILER=clang-$CLANG_VERSION \
|
||||
-DCMAKE_CXX_COMPILER=clang++-$CLANG_VERSION \
|
||||
-G Ninja \
|
||||
-B $BUILD_DIR \
|
||||
-S .
|
||||
# see https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md
|
||||
- run: |
|
||||
echo "::add-matcher::.github/clang-matcher.json"
|
||||
- run: |
|
||||
cmake --build $BUILD_DIR --target scylla
|
||||
- run: |
|
||||
echo "::remove-matcher owner=clang::"
|
||||
64
.github/workflows/clang-tidy.yaml
vendored
64
.github/workflows/clang-tidy.yaml
vendored
@@ -1,64 +0,0 @@
|
||||
name: clang-tidy
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**/*.rst'
|
||||
- '**/*.md'
|
||||
- 'docs/**'
|
||||
- '.github/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
BUILD_DIR: build
|
||||
CLANG_TIDY_CHECKS: '-*,bugprone-use-after-move'
|
||||
|
||||
permissions: {}
|
||||
|
||||
# cancel the in-progress run upon a repush
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
read-toolchain:
|
||||
uses: ./.github/workflows/read-toolchain.yaml
|
||||
clang-tidy:
|
||||
name: Run clang-tidy
|
||||
needs:
|
||||
- read-toolchain
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ needs.read-toolchain.outputs.image }}
|
||||
steps:
|
||||
- env:
|
||||
IMAGE: ${{ needs.read-toolchain.image }}
|
||||
run: |
|
||||
echo ${{ needs.read-toolchain.image }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: |
|
||||
sudo dnf -y install clang-tools-extra
|
||||
- name: Generate the building system
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DScylla_USE_LINKER=ld.lld \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DCMAKE_CXX_CLANG_TIDY="clang-tidy;--checks=$CLANG_TIDY_CHECKS" \
|
||||
-G Ninja \
|
||||
-B $BUILD_DIR \
|
||||
-S .
|
||||
# see https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md
|
||||
- run: |
|
||||
echo "::add-matcher::.github/clang-matcher.json"
|
||||
- name: Build with clang-tidy enabled
|
||||
run: |
|
||||
cmake --build $BUILD_DIR --target scylla
|
||||
- run: |
|
||||
echo "::remove-matcher owner=clang::"
|
||||
2
.github/workflows/codespell.yaml
vendored
2
.github/workflows/codespell.yaml
vendored
@@ -14,4 +14,4 @@ jobs:
|
||||
with:
|
||||
only_warn: 1
|
||||
ignore_words_list: "ans,datas,fo,ser,ue,crate,nd,reenable,strat,stap,te,raison"
|
||||
skip: "./.git,./build,./tools,*.js,*.lock,./test,./licenses,./redis/lolwut.cc,*.svg"
|
||||
skip: "./.git,./build,./tools,*.js,*.thrift,*.lock,./test,./licenses,./redis/lolwut.cc,*.svg"
|
||||
|
||||
3
.github/workflows/docs-pr.yaml
vendored
3
.github/workflows/docs-pr.yaml
vendored
@@ -12,8 +12,7 @@ on:
|
||||
- enterprise
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "db/config.hh"
|
||||
- "db/config.cc"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
80
.github/workflows/iwyu.yaml
vendored
80
.github/workflows/iwyu.yaml
vendored
@@ -1,80 +0,0 @@
|
||||
name: iwyu
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
BUILD_DIR: build
|
||||
CLEANER_OUTPUT_PATH: build/clang-include-cleaner.log
|
||||
CLEANER_DIRS: test/unit exceptions alternator api auth cdc compaction
|
||||
|
||||
permissions: {}
|
||||
|
||||
# cancel the in-progress run upon a repush
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
read-toolchain:
|
||||
uses: ./.github/workflows/read-toolchain.yaml
|
||||
clang-include-cleaner:
|
||||
name: "Analyze #includes in source files"
|
||||
needs:
|
||||
- read-toolchain
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ needs.read-toolchain.outputs.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: |
|
||||
sudo dnf -y install clang-tools-extra
|
||||
- name: Generate compilation database
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-G Ninja \
|
||||
-B $BUILD_DIR \
|
||||
-S .
|
||||
- name: Build headers
|
||||
run: |
|
||||
swagger_targets=''
|
||||
for f in api/api-doc/*.json; do
|
||||
if test "${f#*.}" = json; then
|
||||
name=$(basename "$f" .json)
|
||||
if test $name != swagger20_header; then
|
||||
swagger_targets+=" scylla_swagger_gen_$name"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
cmake \
|
||||
--build build \
|
||||
--target seastar_http_request_parser \
|
||||
--target idl-sources \
|
||||
--target $swagger_targets
|
||||
- run: |
|
||||
echo "::add-matcher::.github/clang-include-cleaner.json"
|
||||
- name: clang-include-cleaner
|
||||
run: |
|
||||
for d in $CLEANER_DIRS; do
|
||||
find $d -name '*.cc' -o -name '*.hh' \
|
||||
-exec echo {} \; \
|
||||
-exec clang-include-cleaner \
|
||||
--ignore-headers=seastarx.hh \
|
||||
--print=changes \
|
||||
-p $BUILD_DIR \
|
||||
{} \; | tee --append $CLEANER_OUTPUT_PATH
|
||||
done
|
||||
- run: |
|
||||
echo "::remove-matcher owner=clang-include-cleaner::"
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Logs (clang-include-cleaner)
|
||||
path: "./${{ env.CLEANER_OUTPUT_PATH }}"
|
||||
27
.github/workflows/make-pr-ready-for-review.yaml
vendored
27
.github/workflows/make-pr-ready-for-review.yaml
vendored
@@ -1,27 +0,0 @@
|
||||
name: Mark PR as Ready When Conflicts Label is Removed
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- unlabeled
|
||||
|
||||
env:
|
||||
DEFAULT_BRANCH: 'master'
|
||||
|
||||
jobs:
|
||||
mark-ready:
|
||||
if: github.event.label.name == 'conflicts'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
ref: ${{ env.DEFAULT_BRANCH }}
|
||||
token: ${{ secrets.AUTO_BACKPORT_TOKEN }}
|
||||
fetch-depth: 1
|
||||
- name: Mark pull request as ready for review
|
||||
run: gh pr ready "${{ github.event.pull_request.number }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.AUTO_BACKPORT_TOKEN }}
|
||||
23
.github/workflows/read-toolchain.yaml
vendored
23
.github/workflows/read-toolchain.yaml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Read Toolchain
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
image:
|
||||
description: "the toolchain docker image"
|
||||
value: ${{ jobs.read-toolchain.outputs.image }}
|
||||
|
||||
jobs:
|
||||
read-toolchain:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image: ${{ steps.read.outputs.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: tools/toolchain/image
|
||||
sparse-checkout-cone-mode: false
|
||||
- id: read
|
||||
run: |
|
||||
image=$(cat tools/toolchain/image)
|
||||
echo "image=$image" >> $GITHUB_OUTPUT
|
||||
35
.github/workflows/reproducible-build.yaml
vendored
35
.github/workflows/reproducible-build.yaml
vendored
@@ -1,35 +0,0 @@
|
||||
name: Check Reproducible Build
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 5AM every friday
|
||||
- cron: '0 5 * * FRI'
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
BUILD_MODE: release
|
||||
jobs:
|
||||
build-a:
|
||||
uses: ./.github/workflows/build-scylla.yaml
|
||||
with:
|
||||
build_mode: release
|
||||
build-b:
|
||||
uses: ./.github/workflows/build-scylla.yaml
|
||||
with:
|
||||
build_mode: release
|
||||
compare-checksum:
|
||||
if: github.repository == 'scylladb/scylladb'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-a
|
||||
- build-b
|
||||
steps:
|
||||
- env:
|
||||
CHECKSUM_A: ${{needs.build-a.outputs.md5sum}}
|
||||
CHECKSUM_B: ${{needs.build-b.outputs.md5sum}}
|
||||
run: |
|
||||
if [ $CHECKSUM_A != $CHECKSUM_B ]; then \
|
||||
echo "::error::mismatched checksums: $CHECKSUM_A != $CHECKSUM_B"; \
|
||||
exit 1; \
|
||||
fi
|
||||
50
.github/workflows/seastar.yaml
vendored
50
.github/workflows/seastar.yaml
vendored
@@ -1,50 +0,0 @@
|
||||
name: Build with the latest Seastar
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 5AM everyday
|
||||
- cron: '0 5 * * *'
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
|
||||
jobs:
|
||||
build-with-the-latest-seastar:
|
||||
runs-on: ubuntu-latest
|
||||
# be consistent with tools/toolchain/image
|
||||
container: scylladb/scylla-toolchain:fedora-40-20240621
|
||||
strategy:
|
||||
matrix:
|
||||
build_type:
|
||||
- Debug
|
||||
- RelWithDebInfo
|
||||
- Dev
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: |
|
||||
rm -rf seastar
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: scylladb/seastar
|
||||
submodules: true
|
||||
path: seastar
|
||||
- name: Generate the building system
|
||||
run: |
|
||||
git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-G Ninja \
|
||||
-B $BUILD_DIR \
|
||||
-S .
|
||||
- run: |
|
||||
cmake --build $BUILD_DIR --target scylla
|
||||
6
.github/workflows/sync-labels.yaml
vendored
6
.github/workflows/sync-labels.yaml
vendored
@@ -16,10 +16,6 @@ jobs:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -37,7 +33,7 @@ jobs:
|
||||
run: python .github/scripts/sync_labels.py --repo ${{ github.repository }} --number ${{ github.event.number }} --action ${{ github.event.action }}
|
||||
|
||||
- name: Pull request labeled or unlabeled event
|
||||
if: github.event_name == 'pull_request_target' && startsWith(github.event.label.name, 'backport/')
|
||||
if: github.event_name == 'pull_request' && startsWith(github.event.label.name, 'backport/')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: python .github/scripts/sync_labels.py --repo ${{ github.repository }} --number ${{ github.event.number }} --action ${{ github.event.action }} --label ${{ github.event.label.name }}
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,8 +3,6 @@
|
||||
.settings
|
||||
build
|
||||
build.ninja
|
||||
cmake-build-*
|
||||
build.ninja.new
|
||||
cscope.*
|
||||
/debian/
|
||||
dist/ami/files/*.rpm
|
||||
@@ -14,14 +12,13 @@ dist/ami/scylla_deploy.sh
|
||||
Cql.tokens
|
||||
.kdev4
|
||||
*.kdev4
|
||||
.idea
|
||||
CMakeLists.txt.user
|
||||
.cache
|
||||
.tox
|
||||
*.egg-info
|
||||
__pycache__CMakeLists.txt.user
|
||||
.gdbinit
|
||||
/resources
|
||||
resources
|
||||
.pytest_cache
|
||||
/expressions.tokens
|
||||
tags
|
||||
@@ -33,4 +30,3 @@ compile_commands.json
|
||||
.ccls-cache/
|
||||
.mypy_cache
|
||||
.envrc
|
||||
clang_build
|
||||
|
||||
8
.gitmodules
vendored
8
.gitmodules
vendored
@@ -1,14 +1,14 @@
|
||||
[submodule "seastar"]
|
||||
path = seastar
|
||||
url = ../scylla-seastar
|
||||
url = ../seastar
|
||||
ignore = dirty
|
||||
[submodule "swagger-ui"]
|
||||
path = swagger-ui
|
||||
url = ../scylla-swagger-ui
|
||||
ignore = dirty
|
||||
[submodule "abseil"]
|
||||
path = abseil
|
||||
url = ../abseil-cpp
|
||||
[submodule "scylla-jmx"]
|
||||
path = tools/jmx
|
||||
url = ../scylla-jmx
|
||||
[submodule "scylla-tools"]
|
||||
path = tools/java
|
||||
url = ../scylla-tools-java
|
||||
|
||||
@@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.27)
|
||||
|
||||
project(scylla)
|
||||
|
||||
include(CTest)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/seastar/cmake)
|
||||
@@ -40,50 +42,21 @@ else()
|
||||
COMMENT "List configured modes")
|
||||
endif()
|
||||
|
||||
add_compile_definitions(
|
||||
FMT_DEPRECATED_OSTREAM)
|
||||
include(limit_jobs)
|
||||
# Configure Seastar compile options to align with Scylla
|
||||
set(CMAKE_CXX_STANDARD "23" CACHE INTERNAL "")
|
||||
set(CMAKE_CXX_STANDARD "20" CACHE INTERNAL "")
|
||||
set(CMAKE_CXX_EXTENSIONS ON CACHE INTERNAL "")
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES OFF CACHE INTERNAL "")
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
set(Seastar_TESTING ON CACHE BOOL "" FORCE)
|
||||
set(Seastar_API_LEVEL 7 CACHE STRING "" FORCE)
|
||||
set(Seastar_DEPRECATED_OSTREAM_FORMATTERS OFF CACHE BOOL "" FORCE)
|
||||
set(Seastar_APPS ON CACHE BOOL "" FORCE)
|
||||
set(Seastar_EXCLUDE_APPS_FROM_ALL ON CACHE BOOL "" FORCE)
|
||||
set(Seastar_EXCLUDE_TESTS_FROM_ALL ON CACHE BOOL "" FORCE)
|
||||
set(Seastar_IO_URING OFF CACHE BOOL "" FORCE)
|
||||
set(Seastar_SCHEDULING_GROUPS_COUNT 16 CACHE STRING "" FORCE)
|
||||
set(Seastar_UNUSED_RESULT_ERROR ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(seastar)
|
||||
set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE)
|
||||
|
||||
find_package(Sanitizers QUIET)
|
||||
set(sanitizer_cxx_flags
|
||||
$<$<CONFIG:Debug,Sanitize>:$<TARGET_PROPERTY:Sanitizers::address,INTERFACE_COMPILE_OPTIONS>;$<TARGET_PROPERTY:Sanitizers::undefined_behavior,INTERFACE_COMPILE_OPTIONS>>)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(ABSL_GCC_FLAGS ${sanitizer_cxx_flags})
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(ABSL_LLVM_FLAGS ${sanitizer_cxx_flags})
|
||||
endif()
|
||||
set(ABSL_DEFAULT_LINKOPTS
|
||||
$<$<CONFIG:Debug,Sanitize>:$<TARGET_PROPERTY:Sanitizers::address,INTERFACE_LINK_LIBRARIES>;$<TARGET_PROPERTY:Sanitizers::undefined_behavior,INTERFACE_LINK_LIBRARIES>>)
|
||||
add_subdirectory(abseil)
|
||||
add_library(absl-headers INTERFACE)
|
||||
target_include_directories(absl-headers SYSTEM INTERFACE
|
||||
"${PROJECT_SOURCE_DIR}/abseil")
|
||||
add_library(absl::headers ALIAS absl-headers)
|
||||
|
||||
# Exclude absl::strerror from the default "all" target since it's not
|
||||
# used in Scylla build and, moreover, makes use of deprecated glibc APIs,
|
||||
# such as sys_nerr, which are not exposed from "stdio.h" since glibc 2.32,
|
||||
# which happens to be the case for recent Fedora distribution versions.
|
||||
#
|
||||
# Need to use the internal "absl_strerror" target name instead of namespaced
|
||||
# variant because `set_target_properties` does not understand the latter form,
|
||||
# unfortunately.
|
||||
set_target_properties(absl_strerror PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
# System libraries dependencies
|
||||
find_package(Boost REQUIRED
|
||||
@@ -95,13 +68,13 @@ target_link_libraries(Boost::regex
|
||||
find_package(Lua REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(ICU COMPONENTS uc i18n REQUIRED)
|
||||
find_package(fmt 10.0.0 REQUIRED)
|
||||
find_package(absl COMPONENTS hash raw_hash_set REQUIRED)
|
||||
find_package(libdeflate REQUIRED)
|
||||
find_package(libxcrypt REQUIRED)
|
||||
find_package(Snappy REQUIRED)
|
||||
find_package(RapidJSON REQUIRED)
|
||||
find_package(Thrift REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
find_package(zstd REQUIRED)
|
||||
|
||||
set(scylla_gen_build_dir "${CMAKE_BINARY_DIR}/gen")
|
||||
file(MAKE_DIRECTORY "${scylla_gen_build_dir}")
|
||||
@@ -109,14 +82,6 @@ file(MAKE_DIRECTORY "${scylla_gen_build_dir}")
|
||||
include(add_version_library)
|
||||
generate_scylla_version()
|
||||
|
||||
add_library(scylla-zstd STATIC
|
||||
zstd.cc)
|
||||
target_link_libraries(scylla-zstd
|
||||
PRIVATE
|
||||
db
|
||||
Seastar::seastar
|
||||
zstd::libzstd)
|
||||
|
||||
add_library(scylla-main STATIC)
|
||||
target_sources(scylla-main
|
||||
PRIVATE
|
||||
@@ -138,7 +103,6 @@ target_sources(scylla-main
|
||||
keys.cc
|
||||
multishard_mutation_query.cc
|
||||
mutation_query.cc
|
||||
node_ops/task_manager_module.cc
|
||||
partition_slice_builder.cc
|
||||
querier.cc
|
||||
query.cc
|
||||
@@ -152,18 +116,15 @@ target_sources(scylla-main
|
||||
serializer.cc
|
||||
sstables_loader.cc
|
||||
table_helper.cc
|
||||
tasks/task_handler.cc
|
||||
tasks/task_manager.cc
|
||||
timeout_config.cc
|
||||
unimplemented.cc
|
||||
validation.cc
|
||||
vint-serialization.cc)
|
||||
vint-serialization.cc
|
||||
zstd.cc)
|
||||
target_link_libraries(scylla-main
|
||||
PRIVATE
|
||||
"$<LINK_LIBRARY:WHOLE_ARCHIVE,scylla-zstd>"
|
||||
db
|
||||
absl::headers
|
||||
absl::btree
|
||||
absl::hash
|
||||
absl::raw_hash_set
|
||||
Seastar::seastar
|
||||
@@ -196,8 +157,6 @@ include(check_headers)
|
||||
check_headers(check-headers scylla-main
|
||||
GLOB ${CMAKE_CURRENT_SOURCE_DIR}/*.hh)
|
||||
|
||||
add_custom_target(compiler-training)
|
||||
|
||||
add_subdirectory(api)
|
||||
add_subdirectory(alternator)
|
||||
add_subdirectory(db)
|
||||
@@ -210,6 +169,7 @@ add_subdirectory(dht)
|
||||
add_subdirectory(gms)
|
||||
add_subdirectory(idl)
|
||||
add_subdirectory(index)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(lang)
|
||||
add_subdirectory(locator)
|
||||
add_subdirectory(message)
|
||||
@@ -227,6 +187,7 @@ add_subdirectory(service)
|
||||
add_subdirectory(sstables)
|
||||
add_subdirectory(streaming)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(thrift)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(tracing)
|
||||
add_subdirectory(transport)
|
||||
@@ -267,6 +228,7 @@ target_link_libraries(scylla PRIVATE
|
||||
sstables
|
||||
streaming
|
||||
test-perf
|
||||
thrift
|
||||
tools
|
||||
tracing
|
||||
transport
|
||||
@@ -275,16 +237,10 @@ target_link_libraries(scylla PRIVATE
|
||||
|
||||
target_link_libraries(scylla PRIVATE
|
||||
seastar
|
||||
absl::headers
|
||||
Boost::program_options)
|
||||
|
||||
target_include_directories(scylla PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${scylla_gen_build_dir}")
|
||||
|
||||
add_custom_target(maybe-scylla
|
||||
DEPENDS $<$<CONFIG:Dev>:$<TARGET_FILE:scylla>>)
|
||||
add_dependencies(compiler-training
|
||||
maybe-scylla)
|
||||
|
||||
add_subdirectory(dist)
|
||||
|
||||
21
HACKING.md
21
HACKING.md
@@ -19,18 +19,18 @@ $ git submodule update --init --recursive
|
||||
### Dependencies
|
||||
|
||||
Scylla is fairly fussy about its build environment, requiring a very recent
|
||||
version of the C++23 compiler and numerous tools and libraries to build.
|
||||
version of the C++20 compiler and numerous tools and libraries to build.
|
||||
|
||||
Run `./install-dependencies.sh` (as root) to use your Linux distributions's
|
||||
package manager to install the appropriate packages on your build machine.
|
||||
However, this will only work on very recent distributions. For example,
|
||||
currently Fedora users must upgrade to Fedora 32 otherwise the C++ compiler
|
||||
will be too old, and not support the new C++23 standard that Scylla uses.
|
||||
will be too old, and not support the new C++20 standard that Scylla uses.
|
||||
|
||||
Alternatively, to avoid having to upgrade your build machine or install
|
||||
various packages on it, we provide another option - the **frozen toolchain**.
|
||||
This is a script, `./tools/toolchain/dbuild`, that can execute build or run
|
||||
commands inside a container that contains exactly the right build tools and
|
||||
commands inside a Docker image that contains exactly the right build tools and
|
||||
libraries. The `dbuild` technique is useful for beginners, but is also the way
|
||||
in which ScyllaDB produces official releases, so it is highly recommended.
|
||||
|
||||
@@ -43,12 +43,6 @@ $ ./tools/toolchain/dbuild ninja build/release/scylla
|
||||
$ ./tools/toolchain/dbuild ./build/release/scylla --developer-mode 1
|
||||
```
|
||||
|
||||
Note: do not mix environemtns - either perform all your work with dbuild, or natively on the host.
|
||||
Note2: you can get to an interactive shell within dbuild by running it without any parameters:
|
||||
```bash
|
||||
$ ./tools/toolchain/dbuild
|
||||
```
|
||||
|
||||
### Build system
|
||||
|
||||
**Note**: Compiling Scylla requires, conservatively, 2 GB of memory per native
|
||||
@@ -122,13 +116,6 @@ Run all tests through the test execution wrapper with
|
||||
$ ./test.py --mode={debug,release}
|
||||
```
|
||||
|
||||
or, if you are using `dbuild`, you need to build the code and the tests and then you can run them at will:
|
||||
|
||||
```bash
|
||||
$ ./tools/toolchain/dbuild ninja {debug,release,dev}-build
|
||||
$ ./tools/toolchain/dbuild ./test.py --mode {debug,release,dev}
|
||||
```
|
||||
|
||||
The `--name` argument can be specified to run a particular test.
|
||||
|
||||
Alternatively, you can execute the test executable directly. For example,
|
||||
@@ -212,7 +199,7 @@ The `scylla.yaml` file in the repository by default writes all database data to
|
||||
|
||||
Scylla has a number of requirements for the file-system and operating system to operate ideally and at peak performance. However, during development, these requirements can be relaxed with the `--developer-mode` flag.
|
||||
|
||||
Additionally, when running on under-powered platforms like portable laptops, the `--overprovisioned` flag is useful.
|
||||
Additionally, when running on under-powered platforms like portable laptops, the `--overprovisined` flag is useful.
|
||||
|
||||
On a development machine, one might run Scylla as
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -15,7 +15,7 @@ For more information, please see the [ScyllaDB web site].
|
||||
## Build Prerequisites
|
||||
|
||||
Scylla is fairly fussy about its build environment, requiring very recent
|
||||
versions of the C++23 compiler and of many libraries to build. The document
|
||||
versions of the C++20 compiler and of many libraries to build. The document
|
||||
[HACKING.md](HACKING.md) includes detailed information on building and
|
||||
developing Scylla, but to get Scylla building quickly on (almost) any build
|
||||
machine, Scylla offers a [frozen toolchain](tools/toolchain/README.md),
|
||||
@@ -65,13 +65,11 @@ $ ./tools/toolchain/dbuild ./build/release/scylla --help
|
||||
|
||||
## Testing
|
||||
|
||||
[](https://github.com/scylladb/scylladb/actions/workflows/seastar.yaml) [](https://github.com/scylladb/scylladb/actions/workflows/reproducible-build.yaml) [](https://github.com/scylladb/scylladb/actions/workflows/clang-nightly.yaml)
|
||||
|
||||
See [test.py manual](docs/dev/testing.md).
|
||||
|
||||
## Scylla APIs and compatibility
|
||||
By default, Scylla is compatible with Apache Cassandra and its API - CQL.
|
||||
There is also support for the API of Amazon DynamoDB™,
|
||||
By default, Scylla is compatible with Apache Cassandra and its APIs - CQL and
|
||||
Thrift. There is also support for the API of Amazon DynamoDB™,
|
||||
which needs to be enabled and configured in order to be used. For more
|
||||
information on how to enable the DynamoDB™ API in Scylla,
|
||||
and the current compatibility of this feature as well as Scylla-specific extensions, see
|
||||
@@ -84,11 +82,11 @@ Documentation can be found [here](docs/dev/README.md).
|
||||
Seastar documentation can be found [here](http://docs.seastar.io/master/index.html).
|
||||
User documentation can be found [here](https://docs.scylladb.com/).
|
||||
|
||||
## Training
|
||||
## Training
|
||||
|
||||
Training material and online courses can be found at [Scylla University](https://university.scylladb.com/).
|
||||
The courses are free, self-paced and include hands-on examples. They cover a variety of topics including Scylla data modeling,
|
||||
administration, architecture, basic NoSQL concepts, using drivers for application development, Scylla setup, failover, compactions,
|
||||
Training material and online courses can be found at [Scylla University](https://university.scylladb.com/).
|
||||
The courses are free, self-paced and include hands-on examples. They cover a variety of topics including Scylla data modeling,
|
||||
administration, architecture, basic NoSQL concepts, using drivers for application development, Scylla setup, failover, compactions,
|
||||
multi-datacenters and how Scylla integrates with third-party applications.
|
||||
|
||||
## Contributing to Scylla
|
||||
|
||||
@@ -78,7 +78,7 @@ fi
|
||||
|
||||
# Default scylla product/version tags
|
||||
PRODUCT=scylla
|
||||
VERSION=6.2.4
|
||||
VERSION=5.5.0-dev
|
||||
|
||||
if test -f version
|
||||
then
|
||||
@@ -88,13 +88,10 @@ else
|
||||
SCYLLA_VERSION=$VERSION
|
||||
if [ -z "$SCYLLA_RELEASE" ]; then
|
||||
GIT_COMMIT=$(git -C "$SCRIPT_DIR" log --pretty=format:'%h' -n 1 --abbrev=12)
|
||||
# For custom package builds, replace "0" with "counter.yourname",
|
||||
# For custom package builds, replace "0" with "counter.your_name",
|
||||
# where counter starts at 1 and increments for successive versions.
|
||||
# This ensures that the package manager will select your custom
|
||||
# package over the standard release.
|
||||
# Do not use any special characters like - or _ in the name above!
|
||||
# These characters either have special meaning or are illegal in
|
||||
# version strings.
|
||||
SCYLLA_BUILD=0
|
||||
SCYLLA_RELEASE=$SCYLLA_BUILD.$DATE.$GIT_COMMIT
|
||||
elif [ -f "$OUTPUT_DIR/SCYLLA-RELEASE-FILE" ]; then
|
||||
@@ -104,7 +101,7 @@ else
|
||||
fi
|
||||
|
||||
if [ -f "$OUTPUT_DIR/SCYLLA-RELEASE-FILE" ]; then
|
||||
GIT_COMMIT_FILE=$(cat "$OUTPUT_DIR/SCYLLA-RELEASE-FILE" | rev | cut -d . -f 1 | rev)
|
||||
GIT_COMMIT_FILE=$(cat "$OUTPUT_DIR/SCYLLA-RELEASE-FILE" |cut -d . -f 3)
|
||||
if [ "$GIT_COMMIT" = "$GIT_COMMIT_FILE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
1
abseil
1
abseil
Submodule abseil deleted from d7aaad83b4
@@ -27,8 +27,7 @@ target_link_libraries(alternator
|
||||
cql3
|
||||
idl
|
||||
Seastar::seastar
|
||||
xxHash::xxhash
|
||||
absl::headers)
|
||||
xxHash::xxhash)
|
||||
|
||||
check_headers(check-headers alternator
|
||||
GLOB_RECURSE ${CMAKE_CURRENT_SOURCE_DIR}/*.hh)
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "alternator/executor.hh"
|
||||
#include "cql3/selection/selection.hh"
|
||||
#include "cql3/result_set.hh"
|
||||
#include "types/types.hh"
|
||||
#include <seastar/core/coroutine.hh>
|
||||
|
||||
namespace alternator {
|
||||
@@ -32,12 +31,11 @@ future<std::string> get_key_from_roles(service::storage_proxy& proxy, auth::serv
|
||||
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"));
|
||||
const column_definition* can_login_col = schema->get_column_definition(bytes("can_login"));
|
||||
if (!salted_hash_col || !can_login_col) {
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(fmt::format("Credentials cannot be fetched for: {}", username)));
|
||||
if (!salted_hash_col) {
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(format("Credentials cannot be fetched for: {}", username)));
|
||||
}
|
||||
auto selection = cql3::selection::selection::for_columns(schema, {salted_hash_col, can_login_col});
|
||||
auto partition_slice = query::partition_slice(std::move(bounds), {}, query::column_id_vector{salted_hash_col->id, can_login_col->id}, selection->get_query_options());
|
||||
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), query::tombstone_limit(proxy.get_tombstone_limit()));
|
||||
auto cl = auth::password_authenticator::consistency_for_user(username);
|
||||
@@ -51,18 +49,11 @@ future<std::string> get_key_from_roles(service::storage_proxy& proxy, auth::serv
|
||||
|
||||
auto result_set = builder.build();
|
||||
if (result_set->empty()) {
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(fmt::format("User not found: {}", username)));
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(format("User not found: {}", username)));
|
||||
}
|
||||
const auto& result = result_set->rows().front();
|
||||
bool can_login = result[1] && value_cast<bool>(boolean_type->deserialize(*result[1]));
|
||||
if (!can_login) {
|
||||
// This is a valid role name, but has "login=False" so should not be
|
||||
// usable for authentication (see #19735).
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(fmt::format("Role {} has login=false so cannot be used for login", username)));
|
||||
}
|
||||
const managed_bytes_opt& salted_hash = result.front();
|
||||
const managed_bytes_opt& salted_hash = result_set->rows().front().front(); // We only asked for 1 row and 1 column
|
||||
if (!salted_hash) {
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(fmt::format("No password found for user: {}", username)));
|
||||
co_await coroutine::return_exception(api_error::unrecognized_client(format("No password found for user: {}", username)));
|
||||
}
|
||||
co_return value_cast<sstring>(utf8_type->deserialize(*salted_hash));
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@ comparison_operator_type get_comparison_operator(const rjson::value& comparison_
|
||||
{"NOT_CONTAINS", comparison_operator_type::NOT_CONTAINS},
|
||||
};
|
||||
if (!comparison_operator.IsString()) {
|
||||
throw api_error::validation(fmt::format("Invalid comparison operator definition {}", rjson::print(comparison_operator)));
|
||||
throw api_error::validation(format("Invalid comparison operator definition {}", rjson::print(comparison_operator)));
|
||||
}
|
||||
std::string op = comparison_operator.GetString();
|
||||
auto it = ops.find(op);
|
||||
if (it == ops.end()) {
|
||||
throw api_error::validation(fmt::format("Unsupported comparison operator {}", op));
|
||||
throw api_error::validation(format("Unsupported comparison operator {}", op));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
@@ -429,7 +429,7 @@ static bool check_BETWEEN(const T& v, const T& lb, const T& ub, bool bounds_from
|
||||
if (cmp_lt()(ub, lb)) {
|
||||
if (bounds_from_query) {
|
||||
throw api_error::validation(
|
||||
fmt::format("BETWEEN operator requires lower_bound <= upper_bound, but {} > {}", lb, ub));
|
||||
format("BETWEEN operator requires lower_bound <= upper_bound, but {} > {}", lb, ub));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -613,7 +613,7 @@ conditional_operator_type get_conditional_operator(const rjson::value& req) {
|
||||
return conditional_operator_type::OR;
|
||||
} else {
|
||||
throw api_error::validation(
|
||||
fmt::format("'ConditionalOperator' parameter must be AND, OR or missing. Found {}.", s));
|
||||
format("'ConditionalOperator' parameter must be AND, OR or missing. Found {}.", s));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,8 @@ controller::controller(
|
||||
sharded<service::memory_limiter>& memory_limiter,
|
||||
sharded<auth::service>& auth_service,
|
||||
sharded<qos::service_level_controller>& sl_controller,
|
||||
const db::config& config,
|
||||
seastar::scheduling_group sg)
|
||||
: protocol_server(sg)
|
||||
, _gossiper(gossiper)
|
||||
const db::config& config)
|
||||
: _gossiper(gossiper)
|
||||
, _proxy(proxy)
|
||||
, _mm(mm)
|
||||
, _sys_dist_ks(sys_dist_ks)
|
||||
@@ -64,9 +62,7 @@ std::vector<socket_address> controller::listen_addresses() const {
|
||||
}
|
||||
|
||||
future<> controller::start_server() {
|
||||
seastar::thread_attributes attr;
|
||||
attr.sched_group = _sched_group;
|
||||
return seastar::async(std::move(attr), [this] {
|
||||
return seastar::async([this] {
|
||||
_listen_addresses.clear();
|
||||
|
||||
auto preferred = _config.listen_interface_prefer_ipv6() ? std::make_optional(net::inet_address::family::INET6) : std::nullopt;
|
||||
@@ -160,9 +156,7 @@ future<> controller::stop_server() {
|
||||
}
|
||||
|
||||
future<> controller::request_stop_server() {
|
||||
return with_scheduling_group(_sched_group, [this] {
|
||||
return stop_server();
|
||||
});
|
||||
return stop_server();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,8 +80,7 @@ public:
|
||||
sharded<service::memory_limiter>& memory_limiter,
|
||||
sharded<auth::service>& auth_service,
|
||||
sharded<qos::service_level_controller>& sl_controller,
|
||||
const db::config& config,
|
||||
seastar::scheduling_group sg);
|
||||
const db::config& config);
|
||||
|
||||
virtual sstring name() const override;
|
||||
virtual sstring protocol() const override;
|
||||
|
||||
@@ -6,17 +6,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
#include <seastar/core/sleep.hh>
|
||||
#include "alternator/executor.hh"
|
||||
#include "auth/permission.hh"
|
||||
#include "cdc/log.hh"
|
||||
#include "auth/service.hh"
|
||||
#include "db/config.hh"
|
||||
#include "log.hh"
|
||||
#include "schema/schema_builder.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "service/client_state.hh"
|
||||
#include "timestamp.hh"
|
||||
#include "types/map.hh"
|
||||
#include "schema/schema.hh"
|
||||
@@ -33,7 +28,6 @@
|
||||
#include "conditions.hh"
|
||||
#include "cql3/util.hh"
|
||||
#include <optional>
|
||||
#include "utils/assert.hh"
|
||||
#include "utils/overloaded_functor.hh"
|
||||
#include <seastar/json/json_elements.hh>
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
@@ -89,7 +83,7 @@ static map_type attrs_type() {
|
||||
|
||||
static const column_definition& attrs_column(const schema& schema) {
|
||||
const column_definition* cdef = schema.get_column_definition(bytes(executor::ATTRS_COLUMN_NAME));
|
||||
SCYLLA_ASSERT(cdef);
|
||||
assert(cdef);
|
||||
return *cdef;
|
||||
}
|
||||
|
||||
@@ -120,7 +114,6 @@ json::json_return_type make_streamed(rjson::value&& value) {
|
||||
elogger.error("Exception during streaming HTTP response: {}", ex);
|
||||
}
|
||||
co_await los.close();
|
||||
co_await rjson::destroy_gently(std::move(*lrs));
|
||||
if (ex) {
|
||||
co_await coroutine::return_exception_ptr(std::move(ex));
|
||||
}
|
||||
@@ -195,12 +188,12 @@ static std::string view_name(const std::string& table_name, std::string_view ind
|
||||
}
|
||||
if (!valid_table_name_chars(index_name)) {
|
||||
throw api_error::validation(
|
||||
fmt::format("IndexName '{}' must satisfy regular expression pattern: [a-zA-Z0-9_.-]+", index_name));
|
||||
format("IndexName '{}' must satisfy regular expression pattern: [a-zA-Z0-9_.-]+", index_name));
|
||||
}
|
||||
std::string ret = table_name + delim + std::string(index_name);
|
||||
if (ret.length() > max_table_name_length) {
|
||||
throw api_error::validation(
|
||||
fmt::format("The total length of TableName ('{}') and IndexName ('{}') cannot exceed {} characters",
|
||||
format("The total length of TableName ('{}') and IndexName ('{}') cannot exceed {} characters",
|
||||
table_name, index_name, max_table_name_length - delim.size()));
|
||||
}
|
||||
return ret;
|
||||
@@ -255,7 +248,7 @@ schema_ptr executor::find_table(service::storage_proxy& proxy, const rjson::valu
|
||||
validate_table_name(table_name.value());
|
||||
|
||||
throw api_error::resource_not_found(
|
||||
fmt::format("Requested resource not found: Table: {} not found", *table_name));
|
||||
format("Requested resource not found: Table: {} not found", *table_name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +302,7 @@ get_table_or_view(service::storage_proxy& proxy, const rjson::value& request) {
|
||||
validate_table_name(table_name);
|
||||
|
||||
throw api_error::resource_not_found(
|
||||
fmt::format("Requested resource not found: Internal table: {}.{} not found", internal_ks_name, internal_table_name));
|
||||
format("Requested resource not found: Internal table: {}.{} not found", internal_ks_name, internal_table_name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +316,7 @@ get_table_or_view(service::storage_proxy& proxy, const rjson::value& request) {
|
||||
type = table_or_view_type::gsi;
|
||||
} else {
|
||||
throw api_error::validation(
|
||||
fmt::format("Non-string IndexName '{}'", rjson::to_string_view(*index_name)));
|
||||
format("Non-string IndexName '{}'", rjson::to_string_view(*index_name)));
|
||||
}
|
||||
// If no tables for global indexes were found, the index may be local
|
||||
if (!proxy.data_dictionary().has_schema(keyspace_name, table_name)) {
|
||||
@@ -341,14 +334,14 @@ get_table_or_view(service::storage_proxy& proxy, const rjson::value& request) {
|
||||
// does exist but the index does not (ValidationException).
|
||||
if (proxy.data_dictionary().has_schema(keyspace_name, orig_table_name)) {
|
||||
throw api_error::validation(
|
||||
fmt::format("Requested resource not found: Index '{}' for table '{}'", index_name->GetString(), orig_table_name));
|
||||
format("Requested resource not found: Index '{}' for table '{}'", index_name->GetString(), orig_table_name));
|
||||
} else {
|
||||
throw api_error::resource_not_found(
|
||||
fmt::format("Requested resource not found: Table: {} not found", orig_table_name));
|
||||
format("Requested resource not found: Table: {} not found", orig_table_name));
|
||||
}
|
||||
} else {
|
||||
throw api_error::resource_not_found(
|
||||
fmt::format("Requested resource not found: Table: {} not found", table_name));
|
||||
format("Requested resource not found: Table: {} not found", table_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,7 +354,7 @@ static std::string get_string_attribute(const rjson::value& value, std::string_v
|
||||
if (!attribute_value)
|
||||
return default_return;
|
||||
if (!attribute_value->IsString()) {
|
||||
throw api_error::validation(fmt::format("Expected string value for attribute {}, got: {}",
|
||||
throw api_error::validation(format("Expected string value for attribute {}, got: {}",
|
||||
attribute_name, value));
|
||||
}
|
||||
return std::string(attribute_value->GetString(), attribute_value->GetStringLength());
|
||||
@@ -376,7 +369,7 @@ static bool get_bool_attribute(const rjson::value& value, std::string_view attri
|
||||
return default_return;
|
||||
}
|
||||
if (!attribute_value->IsBool()) {
|
||||
throw api_error::validation(fmt::format("Expected boolean value for attribute {}, got: {}",
|
||||
throw api_error::validation(format("Expected boolean value for attribute {}, got: {}",
|
||||
attribute_name, value));
|
||||
}
|
||||
return attribute_value->GetBool();
|
||||
@@ -390,7 +383,7 @@ static std::optional<int> get_int_attribute(const rjson::value& value, std::stri
|
||||
if (!attribute_value)
|
||||
return {};
|
||||
if (!attribute_value->IsInt()) {
|
||||
throw api_error::validation(fmt::format("Expected integer value for attribute {}, got: {}",
|
||||
throw api_error::validation(format("Expected integer value for attribute {}, got: {}",
|
||||
attribute_name, value));
|
||||
}
|
||||
return attribute_value->GetInt();
|
||||
@@ -438,7 +431,7 @@ static rjson::value generate_arn_for_table(const schema& schema) {
|
||||
}
|
||||
|
||||
static rjson::value generate_arn_for_index(const schema& schema, std::string_view index_name) {
|
||||
return rjson::from_string(fmt::format(
|
||||
return rjson::from_string(format(
|
||||
"arn:scylla:alternator:{}:scylla:table/{}/index/{}",
|
||||
schema.ks_name(), schema.cf_name(), index_name));
|
||||
}
|
||||
@@ -551,53 +544,6 @@ future<executor::request_return_type> executor::describe_table(client_state& cli
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(response)));
|
||||
}
|
||||
|
||||
// Check CQL's Role-Based Access Control (RBAC) permission_to_check (MODIFY,
|
||||
// SELECT, DROP, etc.) on the given table. When permission is denied an
|
||||
// appropriate user-readable api_error::access_denied is thrown.
|
||||
future<> verify_permission(
|
||||
const service::client_state& client_state,
|
||||
const schema_ptr& schema,
|
||||
auth::permission permission_to_check) {
|
||||
// Using exceptions for errors makes this function faster in the success
|
||||
// path (when the operation is allowed). Using a continuation instead of
|
||||
// co_await makes it faster (no allocation) in the happy path where
|
||||
// permissions are cached and check_has_permissions() doesn't yield.
|
||||
return client_state.check_has_permission(auth::command_desc(
|
||||
permission_to_check,
|
||||
auth::make_data_resource(schema->ks_name(), schema->cf_name()))).then(
|
||||
[permission_to_check, &schema, &client_state] (bool allowed) {
|
||||
if (!allowed) {
|
||||
sstring username = "anonymous";
|
||||
if (client_state.user() && client_state.user()->name) {
|
||||
username = client_state.user()->name.value();
|
||||
}
|
||||
throw api_error::access_denied(format(
|
||||
"{} access on table {}.{} is denied to role {}",
|
||||
auth::permissions::to_string(permission_to_check),
|
||||
schema->ks_name(), schema->cf_name(), username));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Similar to verify_permission() above, but just for CREATE operations.
|
||||
// Those do not operate on any specific table, so require permissions on
|
||||
// ALL KEYSPACES instead of any specific table.
|
||||
future<> verify_create_permission(const service::client_state& client_state) {
|
||||
return client_state.check_has_permission(auth::command_desc(
|
||||
auth::permission::CREATE,
|
||||
auth::resource(auth::resource_kind::data))).then(
|
||||
[&client_state] (bool allowed) {
|
||||
if (!allowed) {
|
||||
sstring username = "anonymous";
|
||||
if (client_state.user() && client_state.user()->name) {
|
||||
username = client_state.user()->name.value();
|
||||
}
|
||||
throw api_error::access_denied(format(
|
||||
"CREATE access on ALL KEYSPACES is denied to role {}", username));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
future<executor::request_return_type> executor::delete_table(client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value request) {
|
||||
_stats.api_operations.delete_table++;
|
||||
elogger.trace("Deleting table {}", request);
|
||||
@@ -613,15 +559,14 @@ future<executor::request_return_type> executor::delete_table(client_state& clien
|
||||
|
||||
schema_ptr schema = get_table(_proxy, request);
|
||||
rjson::value table_description = fill_table_description(schema, table_status::deleting, _proxy);
|
||||
co_await verify_permission(client_state, schema, auth::permission::DROP);
|
||||
co_await _mm.container().invoke_on(0, [&, cs = client_state.move_to_other_shard()] (service::migration_manager& mm) -> future<> {
|
||||
|
||||
co_await _mm.container().invoke_on(0, [&] (service::migration_manager& mm) -> future<> {
|
||||
// FIXME: the following needs to be in a loop. If mm.announce() below
|
||||
// fails, we need to retry the whole thing.
|
||||
auto group0_guard = co_await mm.start_group0_operation();
|
||||
|
||||
std::optional<data_dictionary::table> tbl = p.local().data_dictionary().try_find_table(keyspace_name, table_name);
|
||||
if (!tbl) {
|
||||
throw api_error::resource_not_found(fmt::format("Requested resource not found: Table: {} not found", table_name));
|
||||
if (!p.local().data_dictionary().has_schema(keyspace_name, table_name)) {
|
||||
throw api_error::resource_not_found(format("Requested resource not found: Table: {} not found", table_name));
|
||||
}
|
||||
|
||||
auto m = co_await service::prepare_column_family_drop_announcement(_proxy, keyspace_name, table_name, group0_guard.write_timestamp(), service::drop_views::yes);
|
||||
@@ -629,29 +574,7 @@ future<executor::request_return_type> executor::delete_table(client_state& clien
|
||||
|
||||
std::move(m2.begin(), m2.end(), std::back_inserter(m));
|
||||
|
||||
// When deleting a table and its views, we need to remove this role's
|
||||
// special permissions in those tables (undoing the "auto-grant" done
|
||||
// by CreateTable). If we didn't do this, if a second role later
|
||||
// recreates a table with the same name, the first role would still
|
||||
// have permissions over the new table.
|
||||
// To make things more robust we just remove *all* permissions for
|
||||
// the deleted table (CQL's drop_table_statement also does this).
|
||||
// Unfortunately, there is an API mismatch between this code (which
|
||||
// uses separate group0_guard and vector<mutation>) and the function
|
||||
// revoke_all() which uses a combined "group0_batch" structure - so
|
||||
// we need to do some ugly back-and-forth conversions between the pair
|
||||
// to the group0_batch and back to the pair :-(
|
||||
service::group0_batch mc(std::move(group0_guard));
|
||||
mc.add_mutations(std::move(m));
|
||||
co_await auth::revoke_all(*cs.get().get_auth_service(),
|
||||
auth::make_data_resource(schema->ks_name(), schema->cf_name()), mc);
|
||||
for (const view_ptr& v : tbl->views()) {
|
||||
co_await auth::revoke_all(*cs.get().get_auth_service(),
|
||||
auth::make_data_resource(v->ks_name(), v->cf_name()), mc);
|
||||
}
|
||||
std::tie(m, group0_guard) = co_await std::move(mc).extract();
|
||||
|
||||
co_await mm.announce(std::move(m), std::move(group0_guard), fmt::format("alternator-executor: delete {} table", table_name));
|
||||
co_await mm.announce(std::move(m), std::move(group0_guard), format("alternator-executor: delete {} table", table_name));
|
||||
});
|
||||
|
||||
rjson::value response = rjson::empty_object();
|
||||
@@ -671,7 +594,7 @@ static data_type parse_key_type(const std::string& type) {
|
||||
}
|
||||
}
|
||||
throw api_error::validation(
|
||||
fmt::format("Invalid key type '{}', can only be S, B or N.", type));
|
||||
format("Invalid key type '{}', can only be S, B or N.", type));
|
||||
}
|
||||
|
||||
|
||||
@@ -681,7 +604,7 @@ static void add_column(schema_builder& builder, const std::string& name, const r
|
||||
// second column with the same name. We should fix this, by renaming
|
||||
// some column names which we want to reserve.
|
||||
if (name == executor::ATTRS_COLUMN_NAME) {
|
||||
throw api_error::validation(fmt::format("Column name '{}' is currently reserved. FIXME.", name));
|
||||
throw api_error::validation(format("Column name '{}' is currently reserved. FIXME.", name));
|
||||
}
|
||||
for (auto it = attribute_definitions.Begin(); it != attribute_definitions.End(); ++it) {
|
||||
const rjson::value& attribute_info = *it;
|
||||
@@ -692,7 +615,7 @@ static void add_column(schema_builder& builder, const std::string& name, const r
|
||||
}
|
||||
}
|
||||
throw api_error::validation(
|
||||
fmt::format("KeySchema key '{}' missing in AttributeDefinitions", name));
|
||||
format("KeySchema key '{}' missing in AttributeDefinitions", name));
|
||||
}
|
||||
|
||||
// Parse the KeySchema request attribute, which specifies the column names
|
||||
@@ -760,7 +683,7 @@ static schema_ptr get_table_from_arn(service::storage_proxy& proxy, std::string_
|
||||
// A table name cannot contain a '/' - if it does, it's not a
|
||||
// table ARN, it may be an index. DynamoDB returns a
|
||||
// ValidationException in that case - see #10786.
|
||||
throw api_error::validation(fmt::format("ResourceArn '{}' is not a valid table ARN", table_name));
|
||||
throw api_error::validation(format("ResourceArn '{}' is not a valid table ARN", table_name));
|
||||
}
|
||||
// FIXME: remove sstring creation once find_schema gains a view-based interface
|
||||
return proxy.data_dictionary().find_schema(sstring(keyspace_name), sstring(table_name));
|
||||
@@ -795,7 +718,7 @@ static void validate_tags(const std::map<sstring, sstring>& tags) {
|
||||
std::string_view value = it->second;
|
||||
if (!allowed_write_isolation_values.contains(value)) {
|
||||
throw api_error::validation(
|
||||
fmt::format("Incorrect write isolation tag {}. Allowed values: {}", value, allowed_write_isolation_values));
|
||||
format("Incorrect write isolation tag {}. Allowed values: {}", value, allowed_write_isolation_values));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -830,7 +753,7 @@ void rmw_operation::set_default_write_isolation(std::string_view value) {
|
||||
"See docs/alternator/alternator.md for instructions.");
|
||||
}
|
||||
if (!allowed_write_isolation_values.contains(value)) {
|
||||
throw std::runtime_error(fmt::format("Invalid --alternator-write-isolation "
|
||||
throw std::runtime_error(format("Invalid --alternator-write-isolation "
|
||||
"setting '{}'. Allowed values: {}.",
|
||||
value, allowed_write_isolation_values));
|
||||
}
|
||||
@@ -907,7 +830,6 @@ future<executor::request_return_type> executor::tag_resource(client_state& clien
|
||||
if (tags->Size() < 1) {
|
||||
co_return api_error::validation("The number of tags must be at least 1") ;
|
||||
}
|
||||
co_await verify_permission(client_state, schema, auth::permission::ALTER);
|
||||
co_await db::modify_tags(_mm, schema->ks_name(), schema->cf_name(), [tags](std::map<sstring, sstring>& tags_map) {
|
||||
update_tags_map(*tags, tags_map, update_tags_action::add_tags);
|
||||
});
|
||||
@@ -927,7 +849,7 @@ future<executor::request_return_type> executor::untag_resource(client_state& cli
|
||||
}
|
||||
|
||||
schema_ptr schema = get_table_from_arn(_proxy, rjson::to_string_view(*arn));
|
||||
co_await verify_permission(client_state, schema, auth::permission::ALTER);
|
||||
|
||||
co_await db::modify_tags(_mm, schema->ks_name(), schema->cf_name(), [tags](std::map<sstring, sstring>& tags_map) {
|
||||
update_tags_map(*tags, tags_map, update_tags_action::delete_tags);
|
||||
});
|
||||
@@ -978,10 +900,7 @@ static void verify_billing_mode(const rjson::value& request) {
|
||||
// throws user-facing api_error::validation if it's not.
|
||||
// In particular, verify that the same AttributeName doesn't appear more than
|
||||
// once (Issue #13870).
|
||||
// Return the set of attribute names defined in AttributeDefinitions - this
|
||||
// set is useful for later verifying that all of them are used by some
|
||||
// KeySchema (issue #19784)
|
||||
static std::unordered_set<std::string> validate_attribute_definitions(const rjson::value& attribute_definitions){
|
||||
static void validate_attribute_definitions(const rjson::value& attribute_definitions){
|
||||
if (!attribute_definitions.IsArray()) {
|
||||
throw api_error::validation("AttributeDefinitions must be an array");
|
||||
}
|
||||
@@ -996,7 +915,7 @@ static std::unordered_set<std::string> validate_attribute_definitions(const rjso
|
||||
}
|
||||
auto [it2, added] = seen_attribute_names.emplace(rjson::to_string_view(*attribute_name));
|
||||
if (!added) {
|
||||
throw api_error::validation(fmt::format("Duplicate AttributeName={} in AttributeDefinitions",
|
||||
throw api_error::validation(format("Duplicate AttributeName={} in AttributeDefinitions",
|
||||
rjson::to_string_view(*attribute_name)));
|
||||
}
|
||||
const rjson::value* attribute_type = rjson::find(*it, "AttributeType");
|
||||
@@ -1007,11 +926,10 @@ static std::unordered_set<std::string> validate_attribute_definitions(const rjso
|
||||
throw api_error::validation("AttributeType in AttributeDefinitions must be a string");
|
||||
}
|
||||
}
|
||||
return seen_attribute_names;
|
||||
}
|
||||
|
||||
static future<executor::request_return_type> create_table_on_shard0(service::client_state&& client_state, tracing::trace_state_ptr trace_state, rjson::value request, service::storage_proxy& sp, service::migration_manager& mm, gms::gossiper& gossiper) {
|
||||
SCYLLA_ASSERT(this_shard_id() == 0);
|
||||
static future<executor::request_return_type> create_table_on_shard0(tracing::trace_state_ptr trace_state, rjson::value request, service::storage_proxy& sp, service::migration_manager& mm, gms::gossiper& gossiper) {
|
||||
assert(this_shard_id() == 0);
|
||||
|
||||
// We begin by parsing and validating the content of the CreateTable
|
||||
// command. We can't inspect the current database schema at this point
|
||||
@@ -1021,26 +939,19 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
validate_table_name(table_name);
|
||||
|
||||
if (table_name.find(executor::INTERNAL_TABLE_PREFIX) == 0) {
|
||||
co_return api_error::validation(fmt::format("Prefix {} is reserved for accessing internal tables", executor::INTERNAL_TABLE_PREFIX));
|
||||
co_return api_error::validation(format("Prefix {} is reserved for accessing internal tables", executor::INTERNAL_TABLE_PREFIX));
|
||||
}
|
||||
std::string keyspace_name = executor::KEYSPACE_NAME_PREFIX + table_name;
|
||||
const rjson::value& attribute_definitions = request["AttributeDefinitions"];
|
||||
// Save the list of AttributeDefinitions in unused_attribute_definitions,
|
||||
// and below remove each one as we see it in a KeySchema of the table or
|
||||
// any of its GSIs or LSIs. If anything remains in this set at the end of
|
||||
// this function, it's an error.
|
||||
std::unordered_set<std::string> unused_attribute_definitions =
|
||||
validate_attribute_definitions(attribute_definitions);
|
||||
validate_attribute_definitions(attribute_definitions);
|
||||
|
||||
tracing::add_table_name(trace_state, keyspace_name, table_name);
|
||||
|
||||
schema_builder builder(keyspace_name, table_name);
|
||||
auto [hash_key, range_key] = parse_key_schema(request);
|
||||
add_column(builder, hash_key, attribute_definitions, column_kind::partition_key);
|
||||
unused_attribute_definitions.erase(hash_key);
|
||||
if (!range_key.empty()) {
|
||||
add_column(builder, range_key, attribute_definitions, column_kind::clustering_key);
|
||||
unused_attribute_definitions.erase(range_key);
|
||||
}
|
||||
builder.with_column(bytes(executor::ATTRS_COLUMN_NAME), attrs_type(), column_kind::regular_column);
|
||||
|
||||
@@ -1053,6 +964,7 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
// any table.
|
||||
const rjson::value* gsi = rjson::find(request, "GlobalSecondaryIndexes");
|
||||
std::vector<schema_builder> view_builders;
|
||||
std::vector<sstring> where_clauses;
|
||||
std::unordered_set<std::string> index_names;
|
||||
if (gsi) {
|
||||
if (!gsi->IsArray()) {
|
||||
@@ -1066,7 +978,7 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
std::string_view index_name = rjson::to_string_view(*index_name_v);
|
||||
auto [it, added] = index_names.emplace(index_name);
|
||||
if (!added) {
|
||||
co_return api_error::validation(fmt::format("Duplicate IndexName '{}', ", index_name));
|
||||
co_return api_error::validation(format("Duplicate IndexName '{}', ", index_name));
|
||||
}
|
||||
std::string vname(view_name(table_name, index_name));
|
||||
elogger.trace("Adding GSI {}", index_name);
|
||||
@@ -1080,7 +992,6 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
add_column(builder, view_hash_key, attribute_definitions, column_kind::regular_column);
|
||||
}
|
||||
add_column(view_builder, view_hash_key, attribute_definitions, column_kind::partition_key);
|
||||
unused_attribute_definitions.erase(view_hash_key);
|
||||
if (!view_range_key.empty()) {
|
||||
if (partial_schema->get_column_definition(to_bytes(view_range_key)) == nullptr) {
|
||||
// A column that exists in a global secondary index is upgraded from being a map entry
|
||||
@@ -1093,7 +1004,6 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
add_column(builder, view_range_key, attribute_definitions, column_kind::regular_column);
|
||||
}
|
||||
add_column(view_builder, view_range_key, attribute_definitions, column_kind::clustering_key);
|
||||
unused_attribute_definitions.erase(view_range_key);
|
||||
}
|
||||
// Base key columns which aren't part of the index's key need to
|
||||
// be added to the view nonetheless, as (additional) clustering
|
||||
@@ -1106,6 +1016,12 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
}
|
||||
// GSIs have no tags:
|
||||
view_builder.add_extension(db::tags_extension::NAME, ::make_shared<db::tags_extension>());
|
||||
sstring where_clause = format("{} IS NOT NULL", cql3::util::maybe_quote(view_hash_key));
|
||||
if (!view_range_key.empty()) {
|
||||
where_clause = format("{} AND {} IS NOT NULL", where_clause,
|
||||
cql3::util::maybe_quote(view_range_key));
|
||||
}
|
||||
where_clauses.push_back(std::move(where_clause));
|
||||
view_builders.emplace_back(std::move(view_builder));
|
||||
}
|
||||
}
|
||||
@@ -1123,7 +1039,7 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
std::string_view index_name = rjson::to_string_view(*index_name_v);
|
||||
auto [it, added] = index_names.emplace(index_name);
|
||||
if (!added) {
|
||||
co_return api_error::validation(fmt::format("Duplicate IndexName '{}', ", index_name));
|
||||
co_return api_error::validation(format("Duplicate IndexName '{}', ", index_name));
|
||||
}
|
||||
std::string vname(lsi_name(table_name, index_name));
|
||||
elogger.trace("Adding LSI {}", index_name);
|
||||
@@ -1138,11 +1054,9 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
co_return api_error::validation("LocalSecondaryIndex hash key must match the base table hash key");
|
||||
}
|
||||
add_column(view_builder, view_hash_key, attribute_definitions, column_kind::partition_key);
|
||||
unused_attribute_definitions.erase(view_hash_key);
|
||||
if (view_range_key.empty()) {
|
||||
co_return api_error::validation("LocalSecondaryIndex must specify a sort key");
|
||||
}
|
||||
unused_attribute_definitions.erase(view_range_key);
|
||||
if (view_range_key == hash_key) {
|
||||
co_return api_error::validation("LocalSecondaryIndex sort key cannot be the same as hash key");
|
||||
}
|
||||
@@ -1160,6 +1074,12 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
// Note above we don't need to add virtual columns, as all
|
||||
// base columns were copied to view. TODO: reconsider the need
|
||||
// for virtual columns when we support Projection.
|
||||
sstring where_clause = format("{} IS NOT NULL", cql3::util::maybe_quote(view_hash_key));
|
||||
if (!view_range_key.empty()) {
|
||||
where_clause = format("{} AND {} IS NOT NULL", where_clause,
|
||||
cql3::util::maybe_quote(view_range_key));
|
||||
}
|
||||
where_clauses.push_back(std::move(where_clause));
|
||||
// LSIs have no tags, but Scylla's "synchronous_updates" feature
|
||||
// (which an LSIs need), is actually implemented as a tag so we
|
||||
// need to add it here:
|
||||
@@ -1169,12 +1089,6 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
}
|
||||
}
|
||||
|
||||
if (!unused_attribute_definitions.empty()) {
|
||||
co_return api_error::validation(format(
|
||||
"AttributeDefinitions defines spurious attributes not used by any KeySchema: {}",
|
||||
unused_attribute_definitions));
|
||||
}
|
||||
|
||||
// We don't yet support configuring server-side encryption (SSE) via the
|
||||
// SSESpecifiction attribute, but an SSESpecification with Enabled=false
|
||||
// is simply the default, and should be accepted:
|
||||
@@ -1204,9 +1118,8 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
}
|
||||
builder.add_extension(db::tags_extension::NAME, ::make_shared<db::tags_extension>(tags_map));
|
||||
|
||||
co_await verify_create_permission(client_state);
|
||||
|
||||
schema_ptr schema = builder.build();
|
||||
auto where_clause_it = where_clauses.begin();
|
||||
for (auto& view_builder : view_builders) {
|
||||
// Note below we don't need to add virtual columns, as all
|
||||
// base columns were copied to view. TODO: reconsider the need
|
||||
@@ -1217,7 +1130,8 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
}
|
||||
}
|
||||
const bool include_all_columns = true;
|
||||
view_builder.with_view_info(*schema, include_all_columns, ""/*where clause*/);
|
||||
view_builder.with_view_info(*schema, include_all_columns, *where_clause_it);
|
||||
++where_clause_it;
|
||||
}
|
||||
|
||||
// FIXME: the following needs to be in a loop. If mm.announce() below
|
||||
@@ -1242,7 +1156,7 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
schema_mutations = service::prepare_new_keyspace_announcement(sp.local_db(), ksm, ts);
|
||||
} catch (exceptions::already_exists_exception&) {
|
||||
if (sp.data_dictionary().has_schema(keyspace_name, table_name)) {
|
||||
co_return api_error::resource_in_use(fmt::format("Table {} already exists", table_name));
|
||||
co_return api_error::resource_in_use(format("Table {} already exists", table_name));
|
||||
}
|
||||
}
|
||||
if (sp.data_dictionary().try_find_table(schema->id())) {
|
||||
@@ -1264,27 +1178,7 @@ static future<executor::request_return_type> create_table_on_shard0(service::cli
|
||||
});
|
||||
|
||||
}
|
||||
// If a role is allowed to create a table, we must give it permissions to
|
||||
// use (and eventually delete) the specific table it just created (and
|
||||
// also the view tables). This is known as "auto-grant".
|
||||
// Unfortunately, there is an API mismatch between this code (which uses
|
||||
// separate group0_guard and vector<mutation>) and the function
|
||||
// grant_applicable_permissions() which uses a combined "group0_batch"
|
||||
// structure - so we need to do some ugly back-and-forth conversions
|
||||
// between the pair to the group0_batch and back to the pair :-(
|
||||
service::group0_batch mc(std::move(group0_guard));
|
||||
mc.add_mutations(std::move(schema_mutations));
|
||||
co_await auth::grant_applicable_permissions(
|
||||
*client_state.get_auth_service(), *client_state.user(),
|
||||
auth::make_data_resource(schema->ks_name(), schema->cf_name()), mc);
|
||||
for (const schema_builder& view_builder : view_builders) {
|
||||
co_await auth::grant_applicable_permissions(
|
||||
*client_state.get_auth_service(), *client_state.user(),
|
||||
auth::make_data_resource(view_builder.ks_name(), view_builder.cf_name()), mc);
|
||||
}
|
||||
std::tie(schema_mutations, group0_guard) = co_await std::move(mc).extract();
|
||||
|
||||
co_await mm.announce(std::move(schema_mutations), std::move(group0_guard), fmt::format("alternator-executor: create {} table", table_name));
|
||||
co_await mm.announce(std::move(schema_mutations), std::move(group0_guard), format("alternator-executor: create {} table", table_name));
|
||||
|
||||
co_await mm.wait_for_schema_agreement(sp.local_db(), db::timeout_clock::now() + 10s, nullptr);
|
||||
rjson::value status = rjson::empty_object();
|
||||
@@ -1297,9 +1191,9 @@ future<executor::request_return_type> executor::create_table(client_state& clien
|
||||
_stats.api_operations.create_table++;
|
||||
elogger.trace("Creating table {}", request);
|
||||
|
||||
co_return co_await _mm.container().invoke_on(0, [&, tr = tracing::global_trace_state_ptr(trace_state), request = std::move(request), &sp = _proxy.container(), &g = _gossiper.container(), client_state_other_shard = client_state.move_to_other_shard()]
|
||||
co_return co_await _mm.container().invoke_on(0, [&, tr = tracing::global_trace_state_ptr(trace_state), request = std::move(request), &sp = _proxy.container(), &g = _gossiper.container()]
|
||||
(service::migration_manager& mm) mutable -> future<executor::request_return_type> {
|
||||
co_return co_await create_table_on_shard0(client_state_other_shard.get(), tr, std::move(request), sp.local(), mm, g.local());
|
||||
co_return co_await create_table_on_shard0(tr, std::move(request), sp.local(), mm, g.local());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1324,7 +1218,7 @@ future<executor::request_return_type> executor::update_table(client_state& clien
|
||||
verify_billing_mode(request);
|
||||
}
|
||||
|
||||
co_return co_await _mm.container().invoke_on(0, [&p = _proxy.container(), request = std::move(request), gt = tracing::global_trace_state_ptr(std::move(trace_state)), client_state_other_shard = client_state.move_to_other_shard()]
|
||||
co_return co_await _mm.container().invoke_on(0, [&p = _proxy.container(), request = std::move(request), gt = tracing::global_trace_state_ptr(std::move(trace_state))]
|
||||
(service::migration_manager& mm) mutable -> future<executor::request_return_type> {
|
||||
// FIXME: the following needs to be in a loop. If mm.announce() below
|
||||
// fails, we need to retry the whole thing.
|
||||
@@ -1337,7 +1231,7 @@ future<executor::request_return_type> executor::update_table(client_state& clien
|
||||
// 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) {
|
||||
co_await coroutine::return_exception(api_error::validation(fmt::format("Prefix {} is reserved for accessing internal tables", INTERNAL_TABLE_PREFIX)));
|
||||
co_await coroutine::return_exception(api_error::validation(format("Prefix {} is reserved for accessing internal tables", INTERNAL_TABLE_PREFIX)));
|
||||
}
|
||||
|
||||
schema_builder builder(tab);
|
||||
@@ -1355,8 +1249,8 @@ future<executor::request_return_type> executor::update_table(client_state& clien
|
||||
}
|
||||
|
||||
auto schema = builder.build();
|
||||
co_await verify_permission(client_state_other_shard.get(), schema, auth::permission::ALTER);
|
||||
auto m = co_await service::prepare_column_family_update_announcement(p.local(), schema, std::vector<view_ptr>(), group0_guard.write_timestamp());
|
||||
|
||||
auto m = co_await service::prepare_column_family_update_announcement(p.local(), schema, false, std::vector<view_ptr>(), group0_guard.write_timestamp());
|
||||
|
||||
co_await mm.announce(std::move(m), std::move(group0_guard), format("alternator-executor: update {} table", tab->cf_name()));
|
||||
|
||||
@@ -1444,7 +1338,7 @@ void validate_value(const rjson::value& v, const char* caller) {
|
||||
}
|
||||
} else if (type != "L" && type != "M" && type != "BOOL" && type != "NULL") {
|
||||
// TODO: can do more sanity checks on the content of the above types.
|
||||
throw api_error::validation(fmt::format("{}: unknown type {} for value {}", caller, type, v));
|
||||
throw api_error::validation(format("{}: unknown type {} for value {}", caller, type, v));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1638,7 +1532,7 @@ rmw_operation::returnvalues rmw_operation::parse_returnvalues(const rjson::value
|
||||
} else if (s == "UPDATED_NEW") {
|
||||
return rmw_operation::returnvalues::UPDATED_NEW;
|
||||
} else {
|
||||
throw api_error::validation(fmt::format("Unrecognized value for ReturnValues: {}", s));
|
||||
throw api_error::validation(format("Unrecognized value for ReturnValues: {}", s));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1657,7 +1551,7 @@ rmw_operation::parse_returnvalues_on_condition_check_failure(const rjson::value&
|
||||
} else if (s == "ALL_OLD") {
|
||||
return rmw_operation::returnvalues_on_condition_check_failure::ALL_OLD;
|
||||
} else {
|
||||
throw api_error::validation(fmt::format("Unrecognized value for ReturnValuesOnConditionCheckFailure: {}", s));
|
||||
throw api_error::validation(format("Unrecognized value for ReturnValuesOnConditionCheckFailure: {}", s));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1781,7 +1675,7 @@ future<executor::request_return_type> rmw_operation::execute(service::storage_pr
|
||||
}
|
||||
} else if (_write_isolation != write_isolation::LWT_ALWAYS) {
|
||||
std::optional<mutation> m = apply(nullptr, api::new_timestamp());
|
||||
SCYLLA_ASSERT(m); // !needs_read_before_write, so apply() did not check a condition
|
||||
assert(m); // !needs_read_before_write, so apply() did not check a condition
|
||||
return proxy.mutate(std::vector<mutation>{std::move(*m)}, db::consistency_level::LOCAL_QUORUM, executor::default_timeout(), trace_state, std::move(permit), db::allow_per_partition_rate_limit::yes).then([this] () mutable {
|
||||
return rmw_operation_return(std::move(_return_attributes));
|
||||
});
|
||||
@@ -1913,13 +1807,10 @@ future<executor::request_return_type> executor::put_item(client_state& client_st
|
||||
auto op = make_shared<put_item_operation>(_proxy, std::move(request));
|
||||
tracing::add_table_name(trace_state, op->schema()->ks_name(), op->schema()->cf_name());
|
||||
const bool needs_read_before_write = op->needs_read_before_write();
|
||||
|
||||
co_await verify_permission(client_state, op->schema(), auth::permission::MODIFY);
|
||||
|
||||
if (auto shard = op->shard_for_execute(needs_read_before_write); shard) {
|
||||
_stats.api_operations.put_item--; // uncount on this shard, will be counted in other shard
|
||||
_stats.shard_bounce_for_lwt++;
|
||||
co_return co_await container().invoke_on(*shard, _ssg,
|
||||
return container().invoke_on(*shard, _ssg,
|
||||
[request = std::move(*op).move_request(), cs = client_state.move_to_other_shard(), gt = tracing::global_trace_state_ptr(trace_state), permit = std::move(permit)]
|
||||
(executor& e) mutable {
|
||||
return do_with(cs.get(), [&e, request = std::move(request), trace_state = tracing::trace_state_ptr(gt)]
|
||||
@@ -1932,7 +1823,7 @@ future<executor::request_return_type> executor::put_item(client_state& client_st
|
||||
});
|
||||
});
|
||||
}
|
||||
co_return co_await op->execute(_proxy, client_state, trace_state, std::move(permit), needs_read_before_write, _stats).finally([op, start_time, this] {
|
||||
return op->execute(_proxy, client_state, trace_state, std::move(permit), needs_read_before_write, _stats).finally([op, start_time, this] {
|
||||
_stats.api_operations.put_item_latency.mark(std::chrono::steady_clock::now() - start_time);
|
||||
});
|
||||
}
|
||||
@@ -2005,13 +1896,10 @@ future<executor::request_return_type> executor::delete_item(client_state& client
|
||||
auto op = make_shared<delete_item_operation>(_proxy, std::move(request));
|
||||
tracing::add_table_name(trace_state, op->schema()->ks_name(), op->schema()->cf_name());
|
||||
const bool needs_read_before_write = op->needs_read_before_write();
|
||||
|
||||
co_await verify_permission(client_state, op->schema(), auth::permission::MODIFY);
|
||||
|
||||
if (auto shard = op->shard_for_execute(needs_read_before_write); shard) {
|
||||
_stats.api_operations.delete_item--; // uncount on this shard, will be counted in other shard
|
||||
_stats.shard_bounce_for_lwt++;
|
||||
co_return co_await container().invoke_on(*shard, _ssg,
|
||||
return container().invoke_on(*shard, _ssg,
|
||||
[request = std::move(*op).move_request(), cs = client_state.move_to_other_shard(), gt = tracing::global_trace_state_ptr(trace_state), permit = std::move(permit)]
|
||||
(executor& e) mutable {
|
||||
return do_with(cs.get(), [&e, request = std::move(request), trace_state = tracing::trace_state_ptr(gt)]
|
||||
@@ -2024,7 +1912,7 @@ future<executor::request_return_type> executor::delete_item(client_state& client
|
||||
});
|
||||
});
|
||||
}
|
||||
co_return co_await op->execute(_proxy, client_state, trace_state, std::move(permit), needs_read_before_write, _stats).finally([op, start_time, this] {
|
||||
return op->execute(_proxy, client_state, trace_state, std::move(permit), needs_read_before_write, _stats).finally([op, start_time, this] {
|
||||
_stats.api_operations.delete_item_latency.mark(std::chrono::steady_clock::now() - start_time);
|
||||
});
|
||||
}
|
||||
@@ -2189,11 +2077,10 @@ static future<> do_batch_write(service::storage_proxy& proxy,
|
||||
future<executor::request_return_type> executor::batch_write_item(client_state& client_state, tracing::trace_state_ptr trace_state, service_permit permit, rjson::value request) {
|
||||
_stats.api_operations.batch_write_item++;
|
||||
rjson::value& request_items = request["RequestItems"];
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
std::vector<std::pair<schema_ptr, put_or_delete_item>> mutation_builders;
|
||||
mutation_builders.reserve(request_items.MemberCount());
|
||||
uint batch_size = 0;
|
||||
|
||||
for (auto it = request_items.MemberBegin(); it != request_items.MemberEnd(); ++it) {
|
||||
schema_ptr schema = get_table_from_batch_request(_proxy, it);
|
||||
tracing::add_table_name(trace_state, schema->ks_name(), schema->cf_name());
|
||||
@@ -2201,7 +2088,7 @@ future<executor::request_return_type> executor::batch_write_item(client_state& c
|
||||
1, primary_key_hash{schema}, primary_key_equal{schema});
|
||||
for (auto& request : it->value.GetArray()) {
|
||||
if (!request.IsObject() || request.MemberCount() != 1) {
|
||||
co_return api_error::validation(format("Invalid BatchWriteItem request: {}", request));
|
||||
return make_ready_future<request_return_type>(api_error::validation(format("Invalid BatchWriteItem request: {}", request)));
|
||||
}
|
||||
auto r = request.MemberBegin();
|
||||
const std::string r_name = r->name.GetString();
|
||||
@@ -2212,10 +2099,9 @@ future<executor::request_return_type> executor::batch_write_item(client_state& c
|
||||
item, schema, put_or_delete_item::put_item{}));
|
||||
auto mut_key = std::make_pair(mutation_builders.back().second.pk(), mutation_builders.back().second.ck());
|
||||
if (used_keys.contains(mut_key)) {
|
||||
co_return api_error::validation("Provided list of item keys contains duplicates");
|
||||
return make_ready_future<request_return_type>(api_error::validation("Provided list of item keys contains duplicates"));
|
||||
}
|
||||
used_keys.insert(std::move(mut_key));
|
||||
batch_size++;
|
||||
} else if (r_name == "DeleteRequest") {
|
||||
const rjson::value& key = (r->value)["Key"];
|
||||
mutation_builders.emplace_back(schema, put_or_delete_item(
|
||||
@@ -2223,28 +2109,21 @@ future<executor::request_return_type> executor::batch_write_item(client_state& c
|
||||
auto mut_key = std::make_pair(mutation_builders.back().second.pk(),
|
||||
mutation_builders.back().second.ck());
|
||||
if (used_keys.contains(mut_key)) {
|
||||
co_return api_error::validation("Provided list of item keys contains duplicates");
|
||||
return make_ready_future<request_return_type>(api_error::validation("Provided list of item keys contains duplicates"));
|
||||
}
|
||||
used_keys.insert(std::move(mut_key));
|
||||
batch_size++;
|
||||
} else {
|
||||
co_return api_error::validation(fmt::format("Unknown BatchWriteItem request type: {}", r_name));
|
||||
return make_ready_future<request_return_type>(api_error::validation(format("Unknown BatchWriteItem request type: {}", r_name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& b : mutation_builders) {
|
||||
co_await verify_permission(client_state, b.first, auth::permission::MODIFY);
|
||||
}
|
||||
|
||||
_stats.api_operations.batch_write_item_batch_total += batch_size;
|
||||
co_return co_await do_batch_write(_proxy, _ssg, std::move(mutation_builders), client_state, trace_state, std::move(permit), _stats).then([start_time, this] () {
|
||||
return do_batch_write(_proxy, _ssg, std::move(mutation_builders), client_state, trace_state, std::move(permit), _stats).then([] () {
|
||||
// FIXME: Issue #5650: If we failed writing some of the updates,
|
||||
// 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());
|
||||
_stats.api_operations.batch_write_item_latency.mark(std::chrono::steady_clock::now() - start_time);
|
||||
return make_ready_future<executor::request_return_type>(make_jsonable(std::move(ret)));
|
||||
});
|
||||
}
|
||||
@@ -2351,13 +2230,13 @@ void attribute_path_map_add(const char* source, attribute_path_map<T>& map, cons
|
||||
} else if(!p.has_operators()) {
|
||||
// If p is top-level and we already have it or a part of it
|
||||
// in map, it's a forbidden overlapping path.
|
||||
throw api_error::validation(fmt::format(
|
||||
throw api_error::validation(format(
|
||||
"Invalid {}: two document paths overlap at {}", source, p.root()));
|
||||
} else if (it->second.has_value()) {
|
||||
// If we're here, it != map.end() && p.has_operators && it->second.has_value().
|
||||
// This means the top-level attribute already has a value, and we're
|
||||
// trying to add a non-top-level value. It's an overlap.
|
||||
throw api_error::validation(fmt::format("Invalid {}: two document paths overlap at {}", source, p.root()));
|
||||
throw api_error::validation(format("Invalid {}: two document paths overlap at {}", source, p.root()));
|
||||
}
|
||||
node* h = &it->second;
|
||||
// The second step is to walk h from the top-level node to the inner node
|
||||
@@ -2417,7 +2296,7 @@ void attribute_path_map_add(const char* source, attribute_path_map<T>& map, cons
|
||||
if (it == map.end()) {
|
||||
map.emplace(attr, node {std::move(value)});
|
||||
} else {
|
||||
throw api_error::validation(fmt::format(
|
||||
throw api_error::validation(format(
|
||||
"Invalid {}: Duplicate attribute: {}", source, attr));
|
||||
}
|
||||
}
|
||||
@@ -2470,7 +2349,7 @@ static select_type parse_select(const rjson::value& request, table_or_view_type
|
||||
}
|
||||
return select_type::projection;
|
||||
}
|
||||
throw api_error::validation(fmt::format("Unknown Select value '{}'. Allowed choices: ALL_ATTRIBUTES, SPECIFIC_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, COUNT",
|
||||
throw api_error::validation(format("Unknown Select value '{}'. Allowed choices: ALL_ATTRIBUTES, SPECIFIC_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, COUNT",
|
||||
select));
|
||||
}
|
||||
|
||||
@@ -2839,12 +2718,12 @@ static std::optional<rjson::value> action_result(
|
||||
std::string v1_type = get_item_type_string(v1);
|
||||
if (v1_type == "N") {
|
||||
if (get_item_type_string(v2) != "N") {
|
||||
throw api_error::validation(fmt::format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
|
||||
throw api_error::validation(format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
|
||||
}
|
||||
result = number_add(v1, v2);
|
||||
} else if (v1_type == "SS" || v1_type == "NS" || v1_type == "BS") {
|
||||
if (get_item_type_string(v2) != v1_type) {
|
||||
throw api_error::validation(fmt::format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
|
||||
throw api_error::validation(format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
|
||||
}
|
||||
result = set_sum(v1, v2);
|
||||
} else {
|
||||
@@ -2930,7 +2809,7 @@ static bool hierarchy_actions(
|
||||
} else if (h.has_members()) {
|
||||
if (type[0] != 'M' || !v.IsObject()) {
|
||||
// A .something on a non-map doesn't work.
|
||||
throw api_error::validation(fmt::format("UpdateExpression: document paths not valid for this item:{}", h));
|
||||
throw api_error::validation(format("UpdateExpression: document paths not valid for this item:{}", h));
|
||||
}
|
||||
for (const auto& member : h.get_members()) {
|
||||
std::string attr = member.first;
|
||||
@@ -3117,7 +2996,7 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
std::string column_name = actions.first;
|
||||
const column_definition* cdef = _schema->get_column_definition(to_bytes(column_name));
|
||||
if (cdef && cdef->is_primary_key()) {
|
||||
throw api_error::validation(fmt::format("UpdateItem cannot update key column {}", column_name));
|
||||
throw api_error::validation(format("UpdateItem cannot update key column {}", column_name));
|
||||
}
|
||||
if (actions.second.has_value()) {
|
||||
// An action on a top-level attribute column_name. The single
|
||||
@@ -3141,7 +3020,7 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
}
|
||||
const rjson::value *toplevel = rjson::find(*previous_item, column_name);
|
||||
if (!toplevel) {
|
||||
throw api_error::validation(fmt::format("UpdateItem cannot update document path: missing attribute {}",
|
||||
throw api_error::validation(format("UpdateItem cannot update document path: missing attribute {}",
|
||||
column_name));
|
||||
}
|
||||
rjson::value result = rjson::copy(*toplevel);
|
||||
@@ -3178,7 +3057,7 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
validate_value(v2, "AttributeUpdates");
|
||||
std::string v2_type = get_item_type_string(v2);
|
||||
if (v2_type != "SS" && v2_type != "NS" && v2_type != "BS") {
|
||||
throw api_error::validation(fmt::format("AttributeUpdates DELETE operation with Value only valid for sets, got type {}", v2_type));
|
||||
throw api_error::validation(format("AttributeUpdates DELETE operation with Value only valid for sets, got type {}", v2_type));
|
||||
}
|
||||
if (v1) {
|
||||
std::optional<rjson::value> result = set_diff(*v1, v2);
|
||||
@@ -3224,7 +3103,7 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
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(fmt::format("Operand type mismatch in AttributeUpdates ADD. Expected {}, got {}", v1_type, v2_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));
|
||||
@@ -3243,7 +3122,7 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
|
||||
}
|
||||
} else {
|
||||
throw api_error::validation(
|
||||
fmt::format("Unknown Action value '{}' in AttributeUpdates", action));
|
||||
format("Unknown Action value '{}' in AttributeUpdates", action));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3281,13 +3160,10 @@ future<executor::request_return_type> executor::update_item(client_state& client
|
||||
auto op = make_shared<update_item_operation>(_proxy, std::move(request));
|
||||
tracing::add_table_name(trace_state, op->schema()->ks_name(), op->schema()->cf_name());
|
||||
const bool needs_read_before_write = op->needs_read_before_write();
|
||||
|
||||
co_await verify_permission(client_state, op->schema(), auth::permission::MODIFY);
|
||||
|
||||
if (auto shard = op->shard_for_execute(needs_read_before_write); shard) {
|
||||
_stats.api_operations.update_item--; // uncount on this shard, will be counted in other shard
|
||||
_stats.shard_bounce_for_lwt++;
|
||||
co_return co_await container().invoke_on(*shard, _ssg,
|
||||
return container().invoke_on(*shard, _ssg,
|
||||
[request = std::move(*op).move_request(), cs = client_state.move_to_other_shard(), gt = tracing::global_trace_state_ptr(trace_state), permit = std::move(permit)]
|
||||
(executor& e) mutable {
|
||||
return do_with(cs.get(), [&e, request = std::move(request), trace_state = tracing::trace_state_ptr(gt)]
|
||||
@@ -3300,7 +3176,7 @@ future<executor::request_return_type> executor::update_item(client_state& client
|
||||
});
|
||||
});
|
||||
}
|
||||
co_return co_await op->execute(_proxy, client_state, trace_state, std::move(permit), needs_read_before_write, _stats).finally([op, start_time, this] {
|
||||
return op->execute(_proxy, client_state, trace_state, std::move(permit), needs_read_before_write, _stats).finally([op, start_time, this] {
|
||||
_stats.api_operations.update_item_latency.mark(std::chrono::steady_clock::now() - start_time);
|
||||
});
|
||||
}
|
||||
@@ -3381,8 +3257,8 @@ future<executor::request_return_type> executor::get_item(client_state& client_st
|
||||
auto attrs_to_get = calculate_attrs_to_get(request, used_attribute_names);
|
||||
const rjson::value* expression_attribute_names = rjson::find(request, "ExpressionAttributeNames");
|
||||
verify_all_are_used(expression_attribute_names, used_attribute_names, "ExpressionAttributeNames", "GetItem");
|
||||
co_await verify_permission(client_state, schema, auth::permission::SELECT);
|
||||
co_return co_await _proxy.query(schema, std::move(command), std::move(partition_ranges), cl,
|
||||
|
||||
return _proxy.query(schema, std::move(command), std::move(partition_ranges), cl,
|
||||
service::storage_proxy::coordinator_query_options(executor::default_timeout(), std::move(permit), client_state, trace_state)).then(
|
||||
[this, schema, partition_slice = std::move(partition_slice), selection = std::move(selection), attrs_to_get = std::move(attrs_to_get), start_time = std::move(start_time)] (service::storage_proxy::coordinator_query_result qr) mutable {
|
||||
_stats.api_operations.get_item_latency.mark(std::chrono::steady_clock::now() - start_time);
|
||||
@@ -3451,7 +3327,7 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
// the response size, as DynamoDB does.
|
||||
_stats.api_operations.batch_get_item++;
|
||||
rjson::value& request_items = request["RequestItems"];
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
// We need to validate all the parameters before starting any asynchronous
|
||||
// query, and fail the entire request on any parse error. So we parse all
|
||||
// the input into our own vector "requests", each element a table_requests
|
||||
@@ -3484,7 +3360,7 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
}
|
||||
};
|
||||
std::vector<table_requests> requests;
|
||||
uint batch_size = 0;
|
||||
|
||||
for (auto it = request_items.MemberBegin(); it != request_items.MemberEnd(); ++it) {
|
||||
table_requests rs(get_table_from_batch_request(_proxy, it));
|
||||
tracing::add_table_name(trace_state, sstring(executor::KEYSPACE_NAME_PREFIX) + rs.schema->cf_name(), rs.schema->cf_name());
|
||||
@@ -3498,15 +3374,9 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
rs.add(key);
|
||||
check_key(key, rs.schema);
|
||||
}
|
||||
batch_size += rs.requests.size();
|
||||
requests.emplace_back(std::move(rs));
|
||||
}
|
||||
|
||||
for (const table_requests& tr : requests) {
|
||||
co_await verify_permission(client_state, tr.schema, auth::permission::SELECT);
|
||||
}
|
||||
|
||||
_stats.api_operations.batch_get_item_batch_total += batch_size;
|
||||
// If we got here, all "requests" are valid, so let's start the
|
||||
// requests for the different partitions all in parallel.
|
||||
std::vector<future<std::vector<rjson::value>>> response_futures;
|
||||
@@ -3596,7 +3466,6 @@ future<executor::request_return_type> executor::batch_get_item(client_state& cli
|
||||
if (!some_succeeded && eptr) {
|
||||
co_await coroutine::return_exception_ptr(std::move(eptr));
|
||||
}
|
||||
_stats.api_operations.batch_get_item_latency.mark(std::chrono::steady_clock::now() - start_time);
|
||||
if (is_big(response)) {
|
||||
co_return make_streamed(std::move(response));
|
||||
} else {
|
||||
@@ -3906,7 +3775,7 @@ static rjson::value encode_paging_state(const schema& schema, const service::pag
|
||||
}
|
||||
|
||||
static future<executor::request_return_type> do_query(service::storage_proxy& proxy,
|
||||
schema_ptr table_schema,
|
||||
schema_ptr schema,
|
||||
const rjson::value* exclusive_start_key,
|
||||
dht::partition_range_vector partition_ranges,
|
||||
std::vector<query::clustering_range> ck_bounds,
|
||||
@@ -3923,50 +3792,33 @@ static future<executor::request_return_type> do_query(service::storage_proxy& pr
|
||||
|
||||
tracing::trace(trace_state, "Performing a database query");
|
||||
|
||||
// Reverse the schema and the clustering bounds as the underlying code expects
|
||||
// reversed queries in the native reversed format.
|
||||
auto query_schema = table_schema;
|
||||
const bool reversed = custom_opts.contains<query::partition_slice::option::reversed>();
|
||||
if (reversed) {
|
||||
query_schema = table_schema->get_reversed();
|
||||
|
||||
std::reverse(ck_bounds.begin(), ck_bounds.end());
|
||||
for (auto& bound : ck_bounds) {
|
||||
bound = query::reverse(bound);
|
||||
}
|
||||
}
|
||||
|
||||
if (exclusive_start_key) {
|
||||
partition_key pk = pk_from_json(*exclusive_start_key, table_schema);
|
||||
partition_key pk = pk_from_json(*exclusive_start_key, schema);
|
||||
auto pos = position_in_partition::for_partition_start();
|
||||
if (table_schema->clustering_key_size() > 0) {
|
||||
pos = pos_from_json(*exclusive_start_key, table_schema);
|
||||
if (schema->clustering_key_size() > 0) {
|
||||
pos = pos_from_json(*exclusive_start_key, schema);
|
||||
}
|
||||
old_paging_state = make_lw_shared<service::pager::paging_state>(pk, pos, query::max_partitions, query_id::create_null_id(), service::pager::paging_state::replicas_per_token_range{}, std::nullopt, 0);
|
||||
}
|
||||
|
||||
co_await verify_permission(client_state, table_schema, auth::permission::SELECT);
|
||||
|
||||
auto regular_columns = boost::copy_range<query::column_id_vector>(
|
||||
table_schema->regular_columns() | boost::adaptors::transformed([] (const column_definition& cdef) { return cdef.id; }));
|
||||
schema->regular_columns() | boost::adaptors::transformed([] (const column_definition& cdef) { return cdef.id; }));
|
||||
auto static_columns = boost::copy_range<query::column_id_vector>(
|
||||
table_schema->static_columns() | boost::adaptors::transformed([] (const column_definition& cdef) { return cdef.id; }));
|
||||
auto selection = cql3::selection::selection::wildcard(table_schema);
|
||||
schema->static_columns() | boost::adaptors::transformed([] (const column_definition& cdef) { return cdef.id; }));
|
||||
auto selection = cql3::selection::selection::wildcard(schema);
|
||||
query::partition_slice::option_set opts = selection->get_query_options();
|
||||
opts.add(custom_opts);
|
||||
auto partition_slice = query::partition_slice(std::move(ck_bounds), std::move(static_columns), std::move(regular_columns), opts);
|
||||
auto command = ::make_lw_shared<query::read_command>(query_schema->id(), query_schema->version(), partition_slice, proxy.get_max_result_size(partition_slice),
|
||||
auto command = ::make_lw_shared<query::read_command>(schema->id(), schema->version(), partition_slice, proxy.get_max_result_size(partition_slice),
|
||||
query::tombstone_limit(proxy.get_tombstone_limit()));
|
||||
|
||||
elogger.trace("Executing read query (reversed {}): table schema {}, query schema {}", partition_slice.is_reversed(), table_schema->version(), query_schema->version());
|
||||
|
||||
auto query_state_ptr = std::make_unique<service::query_state>(client_state, trace_state, std::move(permit));
|
||||
|
||||
// FIXME: should be moved above, set on opts, so get_max_result_size knows it?
|
||||
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>{});
|
||||
query_options = std::make_unique<cql3::query_options>(std::move(query_options), std::move(old_paging_state));
|
||||
auto p = service::pager::query_pagers::pager(proxy, query_schema, selection, *query_state_ptr, *query_options, command, std::move(partition_ranges), nullptr);
|
||||
auto p = service::pager::query_pagers::pager(proxy, schema, selection, *query_state_ptr, *query_options, command, std::move(partition_ranges), nullptr);
|
||||
|
||||
std::unique_ptr<cql3::result_set> rs = co_await p->fetch_page(limit, gc_clock::now(), executor::default_timeout());
|
||||
if (!p->is_exhausted()) {
|
||||
@@ -3976,7 +3828,7 @@ static future<executor::request_return_type> do_query(service::storage_proxy& pr
|
||||
bool has_filter = filter;
|
||||
auto [items, size] = co_await describe_items(*selection, std::move(rs), std::move(attrs_to_get), std::move(filter));
|
||||
if (paging_state) {
|
||||
rjson::add(items, "LastEvaluatedKey", encode_paging_state(*table_schema, *paging_state));
|
||||
rjson::add(items, "LastEvaluatedKey", encode_paging_state(*schema, *paging_state));
|
||||
}
|
||||
if (has_filter){
|
||||
cql_stats.filtered_rows_read_total += p->stats().rows_read_total;
|
||||
@@ -3990,7 +3842,7 @@ static future<executor::request_return_type> do_query(service::storage_proxy& pr
|
||||
}
|
||||
|
||||
static dht::token token_for_segment(int segment, int total_segments) {
|
||||
SCYLLA_ASSERT(total_segments > 1 && segment >= 0 && segment < total_segments);
|
||||
assert(total_segments > 1 && segment >= 0 && segment < total_segments);
|
||||
uint64_t delta = std::numeric_limits<uint64_t>::max() / total_segments;
|
||||
return dht::token::from_int64(std::numeric_limits<int64_t>::min() + delta * segment);
|
||||
}
|
||||
@@ -4148,7 +4000,7 @@ static query::clustering_range calculate_ck_bound(schema_ptr schema, const colum
|
||||
// NOTICE(sarna): A range starting with given prefix and ending (non-inclusively) with a string "incremented" by a single
|
||||
// character at the end. Throws for NUMBER instances.
|
||||
if (!ck_cdef.type->is_compatible_with(*utf8_type)) {
|
||||
throw api_error::validation(fmt::format("BEGINS_WITH operator cannot be applied to type {}", type_to_string(ck_cdef.type)));
|
||||
throw api_error::validation(format("BEGINS_WITH operator cannot be applied to type {}", type_to_string(ck_cdef.type)));
|
||||
}
|
||||
return get_clustering_range_for_begins_with(std::move(raw_value), ck, schema, ck_cdef.type);
|
||||
}
|
||||
@@ -4229,13 +4081,13 @@ static std::string_view get_toplevel(const parsed::value& v,
|
||||
used_attribute_names.emplace(column_name);
|
||||
if (!expression_attribute_names) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeNames missing, entry '{}' required by KeyConditionExpression",
|
||||
format("ExpressionAttributeNames missing, entry '{}' required by KeyConditionExpression",
|
||||
column_name));
|
||||
}
|
||||
const rjson::value* value = rjson::find(*expression_attribute_names, column_name);
|
||||
if (!value || !value->IsString()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeNames missing entry '{}' required by KeyConditionExpression",
|
||||
format("ExpressionAttributeNames missing entry '{}' required by KeyConditionExpression",
|
||||
column_name));
|
||||
}
|
||||
column_name = rjson::to_string_view(*value);
|
||||
@@ -4353,7 +4205,7 @@ calculate_bounds_condition_expression(schema_ptr schema,
|
||||
}
|
||||
if (f->_function_name != "begins_with") {
|
||||
throw api_error::validation(
|
||||
fmt::format("KeyConditionExpression function '{}' not supported",f->_function_name));
|
||||
format("KeyConditionExpression function '{}' not supported",f->_function_name));
|
||||
}
|
||||
if (f->_parameters.size() != 2 || !f->_parameters[0].is_path() ||
|
||||
!f->_parameters[1].is_constant()) {
|
||||
@@ -4418,7 +4270,7 @@ calculate_bounds_condition_expression(schema_ptr schema,
|
||||
ck_bounds.push_back(query::clustering_range(ck));
|
||||
} else {
|
||||
throw api_error::validation(
|
||||
fmt::format("KeyConditionExpression condition on non-key attribute {}", key));
|
||||
format("KeyConditionExpression condition on non-key attribute {}", key));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -4426,10 +4278,10 @@ calculate_bounds_condition_expression(schema_ptr schema,
|
||||
// are allowed *only* on the clustering key:
|
||||
if (sstring(key) == pk_cdef.name_as_text()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("KeyConditionExpression only '=' condition is supported on partition key {}", key));
|
||||
format("KeyConditionExpression only '=' condition is supported on partition key {}", key));
|
||||
} else if (!ck_cdef || sstring(key) != ck_cdef->name_as_text()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("KeyConditionExpression condition on non-key attribute {}", key));
|
||||
format("KeyConditionExpression condition on non-key attribute {}", key));
|
||||
}
|
||||
if (!ck_bounds.empty()) {
|
||||
throw api_error::validation(
|
||||
@@ -4453,7 +4305,7 @@ calculate_bounds_condition_expression(schema_ptr schema,
|
||||
// begins_with() supported on bytes and strings (both stored
|
||||
// in the database as strings) but not on numbers.
|
||||
throw api_error::validation(
|
||||
fmt::format("KeyConditionExpression begins_with() not supported on type {}",
|
||||
format("KeyConditionExpression begins_with() not supported on type {}",
|
||||
type_to_string(ck_cdef->type)));
|
||||
} else if (raw_value.empty()) {
|
||||
ck_bounds.push_back(query::clustering_range::make_open_ended_both_sides());
|
||||
@@ -4586,10 +4438,8 @@ future<executor::request_return_type> executor::list_tables(client_state& client
|
||||
|
||||
auto tables = _proxy.data_dictionary().get_tables(); // hold on to temporary, table_names isn't a container, it's a view
|
||||
auto table_names = tables
|
||||
| boost::adaptors::filtered([this] (data_dictionary::table t) {
|
||||
return t.schema()->ks_name().find(KEYSPACE_NAME_PREFIX) == 0 &&
|
||||
!t.schema()->is_view() &&
|
||||
!cdc::is_log_for_some_table(_proxy.local_db(), t.schema()->ks_name(), t.schema()->cf_name());
|
||||
| boost::adaptors::filtered([] (data_dictionary::table t) {
|
||||
return t.schema()->ks_name().find(KEYSPACE_NAME_PREFIX) == 0 && !t.schema()->is_view();
|
||||
})
|
||||
| boost::adaptors::transformed([] (data_dictionary::table t) {
|
||||
return t.schema()->cf_name();
|
||||
@@ -4681,7 +4531,7 @@ future<executor::request_return_type> executor::describe_continuous_backups(clie
|
||||
validate_table_name(table_name);
|
||||
|
||||
throw api_error::table_not_found(
|
||||
fmt::format("Table {} not found", table_name));
|
||||
format("Table {} not found", table_name));
|
||||
}
|
||||
rjson::value desc = rjson::empty_object();
|
||||
rjson::add(desc, "ContinuousBackupsStatus", "DISABLED");
|
||||
@@ -4725,7 +4575,7 @@ static lw_shared_ptr<keyspace_metadata> create_keyspace_metadata(std::string_vie
|
||||
// used by default on new Alternator tables. Change this initialization
|
||||
// to 0 enable tablets by default, with automatic number of tablets.
|
||||
std::optional<unsigned> initial_tablets;
|
||||
if (sp.get_db().local().get_config().enable_tablets()) {
|
||||
if (sp.get_db().local().get_config().check_experimental(db::experimental_features_t::feature::TABLETS)) {
|
||||
auto it = tags_map.find(INITIAL_TABLETS_TAG_KEY);
|
||||
if (it != tags_map.end()) {
|
||||
// Tag set. If it's a valid number, use it. If not - e.g., it's
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/http/httpd.hh>
|
||||
#include "seastarx.hh"
|
||||
#include <seastar/json/json_elements.hh>
|
||||
#include <seastar/core/sharded.hh>
|
||||
@@ -262,9 +263,4 @@ public:
|
||||
// add more than a couple of levels in its own output construction.
|
||||
bool is_big(const rjson::value& val, int big_size = 100'000);
|
||||
|
||||
// Check CQL's Role-Based Access Control (RBAC) permission (MODIFY,
|
||||
// SELECT, DROP, etc.) on the given table. When permission is denied an
|
||||
// appropriate user-readable api_error::access_denied is thrown.
|
||||
future<> verify_permission(const service::client_state&, const schema_ptr&, auth::permission);
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace alternator {
|
||||
|
||||
template <typename Func, typename Result = std::invoke_result_t<Func, expressionsParser&>>
|
||||
template <typename Func, typename Result = std::result_of_t<Func(expressionsParser&)>>
|
||||
static Result do_with_parser(std::string_view input, Func&& f) {
|
||||
expressionsLexer::InputStreamType input_stream{
|
||||
reinterpret_cast<const ANTLR_UINT8*>(input.data()),
|
||||
@@ -43,7 +43,7 @@ static Result do_with_parser(std::string_view input, Func&& f) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Func, typename Result = std::invoke_result_t<Func, expressionsParser&>>
|
||||
template <typename Func, typename Result = std::result_of_t<Func(expressionsParser&)>>
|
||||
static Result parse(const char* input_name, std::string_view input, Func&& f) {
|
||||
if (input.length() > 4096) {
|
||||
throw expressions_syntax_error(format("{} expression size {} exceeds allowed maximum 4096.",
|
||||
@@ -57,10 +57,10 @@ static Result parse(const char* input_name, std::string_view input, Func&& f) {
|
||||
// TODO: displayRecognitionError could set a position inside the
|
||||
// expressions_syntax_error in throws, and we could use it here to
|
||||
// mark the broken position in 'input'.
|
||||
throw expressions_syntax_error(fmt::format("Failed parsing {} '{}': {}",
|
||||
throw expressions_syntax_error(format("Failed parsing {} '{}': {}",
|
||||
input_name, input, e.what()));
|
||||
} catch (...) {
|
||||
throw expressions_syntax_error(fmt::format("Failed parsing {} '{}': {}",
|
||||
throw expressions_syntax_error(format("Failed parsing {} '{}': {}",
|
||||
input_name, input, std::current_exception()));
|
||||
}
|
||||
}
|
||||
@@ -160,12 +160,12 @@ static std::optional<std::string> resolve_path_component(const std::string& colu
|
||||
if (column_name.size() > 0 && column_name.front() == '#') {
|
||||
if (!expression_attribute_names) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeNames missing, entry '{}' required by expression", column_name));
|
||||
format("ExpressionAttributeNames missing, entry '{}' required by expression", column_name));
|
||||
}
|
||||
const rjson::value* value = rjson::find(*expression_attribute_names, column_name);
|
||||
if (!value || !value->IsString()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeNames missing entry '{}' required by expression", column_name));
|
||||
format("ExpressionAttributeNames missing entry '{}' required by expression", column_name));
|
||||
}
|
||||
used_attribute_names.emplace(column_name);
|
||||
return std::string(rjson::to_string_view(*value));
|
||||
@@ -202,16 +202,16 @@ static void resolve_constant(parsed::constant& c,
|
||||
[&] (const std::string& valref) {
|
||||
if (!expression_attribute_values) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeValues missing, entry '{}' required by expression", valref));
|
||||
format("ExpressionAttributeValues missing, entry '{}' required by expression", valref));
|
||||
}
|
||||
const rjson::value* value = rjson::find(*expression_attribute_values, valref);
|
||||
if (!value) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeValues missing entry '{}' required by expression", valref));
|
||||
format("ExpressionAttributeValues missing entry '{}' required by expression", valref));
|
||||
}
|
||||
if (value->IsNull()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("ExpressionAttributeValues null value for entry '{}' required by expression", valref));
|
||||
format("ExpressionAttributeValues null value for entry '{}' required by expression", valref));
|
||||
}
|
||||
validate_value(*value, "ExpressionAttributeValues");
|
||||
used_attribute_values.emplace(valref);
|
||||
@@ -708,7 +708,7 @@ rjson::value calculate_value(const parsed::value& v,
|
||||
auto function_it = function_handlers.find(std::string_view(f._function_name));
|
||||
if (function_it == function_handlers.end()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("{}: unknown function '{}' called.", caller, f._function_name));
|
||||
format("{}: unknown function '{}' called.", caller, f._function_name));
|
||||
}
|
||||
return function_it->second(caller, previous_item, f);
|
||||
},
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
std::vector<std::variant<std::string, unsigned>>& operators() {
|
||||
return _operators;
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream&, const path&);
|
||||
};
|
||||
|
||||
// When an expression is first parsed, all constants are references, like
|
||||
@@ -255,6 +256,6 @@ public:
|
||||
} // namespace parsed
|
||||
} // namespace alternator
|
||||
|
||||
template <> struct fmt::formatter<alternator::parsed::path> : fmt::formatter<string_view> {
|
||||
template <> struct fmt::formatter<alternator::parsed::path> : fmt::formatter<std::string_view> {
|
||||
auto format(const alternator::parsed::path&, fmt::format_context& ctx) const -> decltype(ctx.out());
|
||||
};
|
||||
|
||||
@@ -143,17 +143,17 @@ static big_decimal parse_and_validate_number(std::string_view s) {
|
||||
big_decimal ret(s);
|
||||
auto [magnitude, precision] = internal::get_magnitude_and_precision(s);
|
||||
if (magnitude > 125) {
|
||||
throw api_error::validation(fmt::format("Number overflow: {}. Attempting to store a number with magnitude larger than supported range.", s));
|
||||
throw api_error::validation(format("Number overflow: {}. Attempting to store a number with magnitude larger than supported range.", s));
|
||||
}
|
||||
if (magnitude < -130) {
|
||||
throw api_error::validation(fmt::format("Number underflow: {}. Attempting to store a number with magnitude lower than supported range.", s));
|
||||
throw api_error::validation(format("Number underflow: {}. Attempting to store a number with magnitude lower than supported range.", s));
|
||||
}
|
||||
if (precision > 38) {
|
||||
throw api_error::validation(fmt::format("Number too precise: {}. Attempting to store a number with more significant digits than supported.", s));
|
||||
throw api_error::validation(format("Number too precise: {}. Attempting to store a number with more significant digits than supported.", s));
|
||||
}
|
||||
return ret;
|
||||
} catch (const marshal_exception& e) {
|
||||
throw api_error::validation(fmt::format("The parameter cannot be converted to a numeric value: {}", s));
|
||||
throw api_error::validation(format("The parameter cannot be converted to a numeric value: {}", s));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -265,7 +265,7 @@ bytes get_key_column_value(const rjson::value& item, const column_definition& co
|
||||
std::string column_name = column.name_as_text();
|
||||
const rjson::value* key_typed_value = rjson::find(item, column_name);
|
||||
if (!key_typed_value) {
|
||||
throw api_error::validation(fmt::format("Key column {} not found", column_name));
|
||||
throw api_error::validation(format("Key column {} not found", column_name));
|
||||
}
|
||||
return get_key_from_typed_value(*key_typed_value, column);
|
||||
}
|
||||
@@ -277,26 +277,19 @@ bytes get_key_column_value(const rjson::value& item, const column_definition& co
|
||||
// mentioned in the exception message).
|
||||
// If the type does match, a reference to the encoded value is returned.
|
||||
static const rjson::value& get_typed_value(const rjson::value& key_typed_value, std::string_view type_str, std::string_view name, std::string_view value_name) {
|
||||
if (!key_typed_value.IsObject() || key_typed_value.MemberCount() != 1) {
|
||||
if (!key_typed_value.IsObject() || key_typed_value.MemberCount() != 1 ||
|
||||
!key_typed_value.MemberBegin()->value.IsString()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("Malformed value object for {} {}: {}",
|
||||
format("Malformed value object for {} {}: {}",
|
||||
value_name, name, key_typed_value));
|
||||
}
|
||||
|
||||
auto it = key_typed_value.MemberBegin();
|
||||
if (rjson::to_string_view(it->name) != type_str) {
|
||||
throw api_error::validation(
|
||||
fmt::format("Type mismatch: expected type {} for {} {}, got type {}",
|
||||
format("Type mismatch: expected type {} for {} {}, got type {}",
|
||||
type_str, value_name, name, it->name));
|
||||
}
|
||||
// We assume this function is called just for key types (S, B, N), and
|
||||
// all of those always have a string value in the JSON.
|
||||
if (!it->value.IsString()) {
|
||||
throw api_error::validation(
|
||||
fmt::format("Malformed value object for {} {}: {}",
|
||||
value_name, name, key_typed_value));
|
||||
|
||||
}
|
||||
return it->value;
|
||||
}
|
||||
|
||||
@@ -402,16 +395,16 @@ position_in_partition pos_from_json(const rjson::value& item, schema_ptr schema)
|
||||
|
||||
big_decimal unwrap_number(const rjson::value& v, std::string_view diagnostic) {
|
||||
if (!v.IsObject() || v.MemberCount() != 1) {
|
||||
throw api_error::validation(fmt::format("{}: invalid number object", diagnostic));
|
||||
throw api_error::validation(format("{}: invalid number object", diagnostic));
|
||||
}
|
||||
auto it = v.MemberBegin();
|
||||
if (it->name != "N") {
|
||||
throw api_error::validation(fmt::format("{}: expected number, found type '{}'", diagnostic, it->name));
|
||||
throw api_error::validation(format("{}: expected number, found type '{}'", diagnostic, it->name));
|
||||
}
|
||||
if (!it->value.IsString()) {
|
||||
// We shouldn't reach here. Callers normally validate their input
|
||||
// earlier with validate_value().
|
||||
throw api_error::validation(fmt::format("{}: improperly formatted number constant", diagnostic));
|
||||
throw api_error::validation(format("{}: improperly formatted number constant", diagnostic));
|
||||
}
|
||||
big_decimal ret = parse_and_validate_number(rjson::to_string_view(it->value));
|
||||
return ret;
|
||||
@@ -492,7 +485,7 @@ rjson::value set_sum(const rjson::value& v1, const rjson::value& v2) {
|
||||
auto [set1_type, set1] = unwrap_set(v1);
|
||||
auto [set2_type, set2] = unwrap_set(v2);
|
||||
if (set1_type != set2_type) {
|
||||
throw api_error::validation(fmt::format("Mismatched set types: {} and {}", set1_type, set2_type));
|
||||
throw api_error::validation(format("Mismatched set types: {} and {}", set1_type, set2_type));
|
||||
}
|
||||
if (!set1 || !set2) {
|
||||
throw api_error::validation("UpdateExpression: ADD operation for sets must be given sets as arguments");
|
||||
@@ -520,7 +513,7 @@ std::optional<rjson::value> set_diff(const rjson::value& v1, const rjson::value&
|
||||
auto [set1_type, set1] = unwrap_set(v1);
|
||||
auto [set2_type, set2] = unwrap_set(v2);
|
||||
if (set1_type != set2_type) {
|
||||
throw api_error::validation(fmt::format("Set DELETE type mismatch: {} and {}", set1_type, set2_type));
|
||||
throw api_error::validation(format("Set DELETE type mismatch: {} and {}", set1_type, set2_type));
|
||||
}
|
||||
if (!set1 || !set2) {
|
||||
throw api_error::validation("UpdateExpression: DELETE operation can only be performed on a set");
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "alternator/server.hh"
|
||||
#include "log.hh"
|
||||
#include <fmt/ranges.h>
|
||||
#include <seastar/http/function_handlers.hh>
|
||||
#include <seastar/http/short_streams.hh>
|
||||
#include <seastar/core/coroutine.hh>
|
||||
@@ -17,15 +16,10 @@
|
||||
#include <seastar/util/short_streams.hh>
|
||||
#include "seastarx.hh"
|
||||
#include "error.hh"
|
||||
#include "service/client_state.hh"
|
||||
#include "service/qos/service_level_controller.hh"
|
||||
#include "utils/assert.hh"
|
||||
#include "timeout_config.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include "auth.hh"
|
||||
#include <cctype>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "gms/gossiper.hh"
|
||||
#include "utils/overloaded_functor.hh"
|
||||
@@ -39,6 +33,8 @@ using reply = http::reply;
|
||||
|
||||
namespace alternator {
|
||||
|
||||
static constexpr auto TARGET = "X-Amz-Target";
|
||||
|
||||
inline std::vector<std::string_view> split(std::string_view text, char separator) {
|
||||
std::vector<std::string_view> tokens;
|
||||
if (text == "") {
|
||||
@@ -214,13 +210,8 @@ protected:
|
||||
sstring local_dc = topology.get_datacenter();
|
||||
std::unordered_set<gms::inet_address> local_dc_nodes = topology.get_datacenter_endpoints().at(local_dc);
|
||||
for (auto& ip : local_dc_nodes) {
|
||||
// Note that it's not enough for the node to be is_alive() - a
|
||||
// node joining the cluster is also "alive" but not responsive to
|
||||
// requests. We alive *and* normal. See #19694, #21538.
|
||||
if (_gossiper.is_alive(ip) && _gossiper.is_normal(ip)) {
|
||||
// Use the gossiped broadcast_rpc_address if available instead
|
||||
// of the internal IP address "ip". See discussion in #18711.
|
||||
rjson::push_back(results, rjson::from_string(_gossiper.get_rpc_address(ip)));
|
||||
if (_gossiper.is_alive(ip)) {
|
||||
rjson::push_back(results, rjson::from_string(ip.to_sstring()));
|
||||
}
|
||||
}
|
||||
rep->set_status(reply::status_type::ok);
|
||||
@@ -263,7 +254,7 @@ future<std::string> server::verify_signature(const request& req, const chunked_c
|
||||
std::string_view authorization_header = authorization_it->second;
|
||||
auto pos = authorization_header.find_first_of(' ');
|
||||
if (pos == std::string_view::npos || authorization_header.substr(0, pos) != "AWS4-HMAC-SHA256") {
|
||||
throw api_error::invalid_signature(fmt::format("Authorization header must use AWS4-HMAC-SHA256 algorithm: {}", authorization_header));
|
||||
throw api_error::invalid_signature(format("Authorization header must use AWS4-HMAC-SHA256 algorithm: {}", authorization_header));
|
||||
}
|
||||
authorization_header.remove_prefix(pos+1);
|
||||
std::string credential;
|
||||
@@ -298,7 +289,7 @@ future<std::string> server::verify_signature(const request& req, const chunked_c
|
||||
|
||||
std::vector<std::string_view> credential_split = split(credential, '/');
|
||||
if (credential_split.size() != 5) {
|
||||
throw api_error::validation(fmt::format("Incorrect credential information format: {}", credential));
|
||||
throw api_error::validation(format("Incorrect credential information format: {}", credential));
|
||||
}
|
||||
std::string user(credential_split[0]);
|
||||
std::string datestamp(credential_split[1]);
|
||||
@@ -383,7 +374,7 @@ static tracing::trace_state_ptr maybe_trace_query(service::client_state& client_
|
||||
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, seastar::format("Alternator {}", op), client_state.get_client_address());
|
||||
tracing::begin(trace_state, format("Alternator {}", op), client_state.get_client_address());
|
||||
if (!username.empty()) {
|
||||
tracing::set_username(trace_state, auth::authenticated_user(username));
|
||||
}
|
||||
@@ -393,10 +384,10 @@ static tracing::trace_state_ptr maybe_trace_query(service::client_state& client_
|
||||
|
||||
future<executor::request_return_type> server::handle_api_request(std::unique_ptr<request> req) {
|
||||
_executor._stats.total_operations++;
|
||||
sstring target = req->get_header("X-Amz-Target");
|
||||
// target is DynamoDB API version followed by a dot '.' and operation type (e.g. CreateTable)
|
||||
auto dot = target.find('.');
|
||||
std::string_view op = (dot == sstring::npos) ? std::string_view() : std::string_view(target).substr(dot+1);
|
||||
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
|
||||
@@ -408,7 +399,7 @@ future<executor::request_return_type> server::handle_api_request(std::unique_ptr
|
||||
++_executor._stats.requests_blocked_memory;
|
||||
}
|
||||
auto units = co_await std::move(units_fut);
|
||||
SCYLLA_ASSERT(req->content_stream);
|
||||
assert(req->content_stream);
|
||||
chunked_content content = co_await util::read_entire_stream(*req->content_stream);
|
||||
auto username = co_await verify_signature(*req, content);
|
||||
|
||||
@@ -419,7 +410,7 @@ future<executor::request_return_type> server::handle_api_request(std::unique_ptr
|
||||
auto callback_it = _callbacks.find(op);
|
||||
if (callback_it == _callbacks.end()) {
|
||||
_executor._stats.unsupported_operations++;
|
||||
co_return api_error::unknown_operation(fmt::format("Unsupported operation {}", op));
|
||||
co_return api_error::unknown_operation(format("Unsupported operation {}", op));
|
||||
}
|
||||
if (_pending_requests.get_count() >= _max_concurrent_requests) {
|
||||
_executor._stats.requests_shed++;
|
||||
@@ -427,11 +418,11 @@ future<executor::request_return_type> server::handle_api_request(std::unique_ptr
|
||||
}
|
||||
_pending_requests.enter();
|
||||
auto leave = defer([this] () noexcept { _pending_requests.leave(); });
|
||||
executor::client_state client_state(service::client_state::external_tag(),
|
||||
_auth_service, &_sl_controller, _timeout_config.current_values(), req->get_client_address());
|
||||
if (!username.empty()) {
|
||||
client_state.set_login(auth::authenticated_user(username));
|
||||
}
|
||||
//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 = username.empty()
|
||||
? service::client_state{service::client_state::internal_tag()}
|
||||
: service::client_state{service::client_state::internal_tag(), _auth_service, _sl_controller, username};
|
||||
co_await client_state.maybe_update_per_service_level_params();
|
||||
|
||||
tracing::trace_state_ptr trace_state = maybe_trace_query(client_state, username, op, content);
|
||||
@@ -478,7 +469,6 @@ server::server(executor& exec, service::storage_proxy& proxy, gms::gossiper& gos
|
||||
, _enforce_authorization(false)
|
||||
, _enabled_servers{}
|
||||
, _pending_requests{}
|
||||
, _timeout_config(_proxy.data_dictionary().get_config())
|
||||
, _callbacks{
|
||||
{"CreateTable", [] (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.create_table(client_state, std::move(trace_state), std::move(permit), std::move(json_request));
|
||||
@@ -643,7 +633,7 @@ future<> server::json_parser::stop() {
|
||||
|
||||
const char* api_error::what() const noexcept {
|
||||
if (_what_string.empty()) {
|
||||
_what_string = fmt::format("{} {}: {}", std::to_underlying(_http_code), _type, _msg);
|
||||
_what_string = format("{} {}: {}", static_cast<int>(_http_code), _type, _msg);
|
||||
}
|
||||
return _what_string.c_str();
|
||||
}
|
||||
|
||||
@@ -42,11 +42,6 @@ class server {
|
||||
bool _enforce_authorization;
|
||||
utils::small_vector<std::reference_wrapper<seastar::httpd::http_server>, 2> _enabled_servers;
|
||||
gate _pending_requests;
|
||||
// In some places we will need a CQL updateable_timeout_config object even
|
||||
// though it isn't really relevant for Alternator which defines its own
|
||||
// timeouts separately. We can create this object only once.
|
||||
updateable_timeout_config _timeout_config;
|
||||
|
||||
alternator_callbacks_map _callbacks;
|
||||
|
||||
semaphore* _memory_limiter;
|
||||
|
||||
@@ -67,8 +67,6 @@ stats::stats() : api_operations{} {
|
||||
OPERATION_LATENCY(get_item_latency, "GetItem")
|
||||
OPERATION_LATENCY(delete_item_latency, "DeleteItem")
|
||||
OPERATION_LATENCY(update_item_latency, "UpdateItem")
|
||||
OPERATION_LATENCY(batch_write_item_latency, "BatchWriteItem")
|
||||
OPERATION_LATENCY(batch_get_item_latency, "BatchGetItem")
|
||||
OPERATION(list_streams, "ListStreams")
|
||||
OPERATION(describe_stream, "DescribeStream")
|
||||
OPERATION(get_shard_iterator, "GetShardIterator")
|
||||
@@ -96,10 +94,6 @@ stats::stats() : api_operations{} {
|
||||
seastar::metrics::description("number of rows read and matched during filtering operations")),
|
||||
seastar::metrics::make_total_operations("filtered_rows_dropped_total", [this] { return cql_stats.filtered_rows_read_total - cql_stats.filtered_rows_matched_total; },
|
||||
seastar::metrics::description("number of rows read and dropped during filtering operations")),
|
||||
seastar::metrics::make_counter("batch_item_count", seastar::metrics::description("The total number of items processed across all batches"),{op("BatchWriteItem")},
|
||||
api_operations.batch_write_item_batch_total).set_skip_when_empty(),
|
||||
seastar::metrics::make_counter("batch_item_count", seastar::metrics::description("The total number of items processed across all batches"),{op("BatchGetItem")},
|
||||
api_operations.batch_get_item_batch_total).set_skip_when_empty(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <seastar/core/metrics_registration.hh>
|
||||
#include "utils/estimated_histogram.hh"
|
||||
#include "utils/histogram.hh"
|
||||
#include "cql3/stats.hh"
|
||||
|
||||
@@ -26,8 +27,6 @@ public:
|
||||
struct {
|
||||
uint64_t batch_get_item = 0;
|
||||
uint64_t batch_write_item = 0;
|
||||
uint64_t batch_get_item_batch_total = 0;
|
||||
uint64_t batch_write_item_batch_total = 0;
|
||||
uint64_t create_backup = 0;
|
||||
uint64_t create_global_table = 0;
|
||||
uint64_t create_table = 0;
|
||||
@@ -71,8 +70,6 @@ public:
|
||||
utils::timed_rate_moving_average_summary_and_histogram get_item_latency;
|
||||
utils::timed_rate_moving_average_summary_and_histogram delete_item_latency;
|
||||
utils::timed_rate_moving_average_summary_and_histogram update_item_latency;
|
||||
utils::timed_rate_moving_average_summary_and_histogram batch_write_item_latency;
|
||||
utils::timed_rate_moving_average_summary_and_histogram batch_get_item_latency;
|
||||
utils::timed_rate_moving_average_summary_and_histogram get_records_latency;
|
||||
} api_operations;
|
||||
// Miscellaneous event counters
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include <seastar/json/formatter.hh>
|
||||
|
||||
#include "auth/permission.hh"
|
||||
#include "db/config.hh"
|
||||
|
||||
#include "cdc/log.hh"
|
||||
@@ -234,8 +233,11 @@ struct shard_id {
|
||||
|
||||
// dynamo specifies shardid as max 65 chars.
|
||||
friend std::ostream& operator<<(std::ostream& os, const shard_id& id) {
|
||||
fmt::print(os, "{} {:x}:{}", marker, id.time.time_since_epoch().count(), id.id.to_bytes());
|
||||
return os;
|
||||
boost::io::ios_flags_saver fs(os);
|
||||
return os << marker << std::hex
|
||||
<< id.time.time_since_epoch().count()
|
||||
<< ':' << id.id.to_bytes()
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -777,7 +779,7 @@ struct event_id {
|
||||
cdc::stream_id stream;
|
||||
utils::UUID timestamp;
|
||||
|
||||
static constexpr auto marker = 'E';
|
||||
static const auto marker = 'E';
|
||||
|
||||
event_id(cdc::stream_id s, utils::UUID ts)
|
||||
: stream(s)
|
||||
@@ -785,8 +787,10 @@ struct event_id {
|
||||
{}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const event_id& id) {
|
||||
fmt::print(os, "{}{}:{}", marker, id.stream.to_bytes(), id.timestamp);
|
||||
return os;
|
||||
boost::io::ios_flags_saver fs(os);
|
||||
return os << marker << std::hex << id.stream.to_bytes()
|
||||
<< ':' << id.timestamp
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -819,13 +823,11 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
}
|
||||
|
||||
if (!schema || !base || !is_alternator_keyspace(schema->ks_name())) {
|
||||
co_return api_error::resource_not_found(fmt::to_string(iter.table));
|
||||
throw api_error::resource_not_found(fmt::to_string(iter.table));
|
||||
}
|
||||
|
||||
tracing::add_table_name(trace_state, schema->ks_name(), schema->cf_name());
|
||||
|
||||
co_await verify_permission(client_state, schema, auth::permission::SELECT);
|
||||
|
||||
db::consistency_level cl = db::consistency_level::LOCAL_QUORUM;
|
||||
partition_key pk = iter.shard.id.to_partition_key(*schema);
|
||||
|
||||
@@ -890,7 +892,7 @@ future<executor::request_return_type> executor::get_records(client_state& client
|
||||
auto command = ::make_lw_shared<query::read_command>(schema->id(), schema->version(), partition_slice, _proxy.get_max_result_size(partition_slice),
|
||||
query::tombstone_limit(_proxy.get_tombstone_limit()), query::row_limit(limit * mul));
|
||||
|
||||
co_return co_await _proxy.query(schema, std::move(command), std::move(partition_ranges), cl, service::storage_proxy::coordinator_query_options(default_timeout(), std::move(permit), client_state)).then(
|
||||
return _proxy.query(schema, std::move(command), std::move(partition_ranges), cl, service::storage_proxy::coordinator_query_options(default_timeout(), std::move(permit), client_state)).then(
|
||||
[this, schema, partition_slice = std::move(partition_slice), selection = std::move(selection), start_time = std::move(start_time), limit, key_names = std::move(key_names), attr_names = std::move(attr_names), type, iter, high_ts] (service::storage_proxy::coordinator_query_result qr) mutable {
|
||||
cql3::selection::result_set_builder builder(*selection, gc_clock::now());
|
||||
query::result_view::consume(*qr.query_result, partition_slice, cql3::selection::result_set_builder::visitor(builder, *schema, *selection));
|
||||
@@ -1055,6 +1057,9 @@ void executor::add_stream_options(const rjson::value& stream_specification, sche
|
||||
if (stream_enabled->GetBool()) {
|
||||
auto db = sp.data_dictionary();
|
||||
|
||||
if (!db.features().cdc) {
|
||||
throw api_error::validation("StreamSpecification: streams (CDC) feature not enabled in cluster.");
|
||||
}
|
||||
if (!db.features().alternator_streams) {
|
||||
throw api_error::validation("StreamSpecification: alternator streams feature not enabled in cluster.");
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "log.hh"
|
||||
#include "gc_clock.hh"
|
||||
#include "replica/database.hh"
|
||||
#include "service/client_state.hh"
|
||||
#include "service_permit.hh"
|
||||
#include "timestamp.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
@@ -36,7 +35,6 @@
|
||||
#include "mutation/mutation.hh"
|
||||
#include "types/types.hh"
|
||||
#include "types/map.hh"
|
||||
#include "utils/assert.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include "utils/big_decimal.hh"
|
||||
#include "cql3/selection/selection.hh"
|
||||
@@ -99,7 +97,6 @@ future<executor::request_return_type> executor::update_time_to_live(client_state
|
||||
}
|
||||
sstring attribute_name(v->GetString(), v->GetStringLength());
|
||||
|
||||
co_await verify_permission(client_state, schema, auth::permission::ALTER);
|
||||
co_await db::modify_tags(_mm, schema->ks_name(), schema->cf_name(), [&](std::map<sstring, sstring>& tags_map) {
|
||||
if (enabled) {
|
||||
if (tags_map.contains(TTL_TAG_KEY)) {
|
||||
@@ -315,7 +312,7 @@ static size_t random_offset(size_t min, size_t max) {
|
||||
// this range's primary node is down. For this we need to return not just
|
||||
// a list of this node's secondary ranges - but also the primary owner of
|
||||
// each of those ranges.
|
||||
static future<std::vector<std::pair<dht::token_range, gms::inet_address>>> get_secondary_ranges(
|
||||
static std::vector<std::pair<dht::token_range, gms::inet_address>> get_secondary_ranges(
|
||||
const locator::effective_replication_map_ptr& erm,
|
||||
gms::inet_address ep) {
|
||||
const auto& tm = *erm->get_token_metadata_ptr();
|
||||
@@ -326,7 +323,6 @@ static future<std::vector<std::pair<dht::token_range, gms::inet_address>>> get_s
|
||||
}
|
||||
auto prev_tok = sorted_tokens.back();
|
||||
for (const auto& tok : sorted_tokens) {
|
||||
co_await coroutine::maybe_yield();
|
||||
inet_address_vector_replica_set eps = erm->get_natural_endpoints(tok);
|
||||
if (eps.size() <= 1 || eps[1] != ep) {
|
||||
prev_tok = tok;
|
||||
@@ -354,7 +350,7 @@ static future<std::vector<std::pair<dht::token_range, gms::inet_address>>> get_s
|
||||
}
|
||||
prev_tok = tok;
|
||||
}
|
||||
co_return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -387,66 +383,63 @@ static future<std::vector<std::pair<dht::token_range, gms::inet_address>>> get_s
|
||||
// the chances of covering all ranges during a scan when restarts occur.
|
||||
// A more deterministic way would be to regularly persist the scanning state,
|
||||
// but that incurs overhead that we want to avoid if not needed.
|
||||
//
|
||||
// FIXME: Check if this algorithm is safe with tablet migration.
|
||||
// https://github.com/scylladb/scylladb/issues/16567
|
||||
|
||||
// ranges_holder_primary holds just the primary ranges themselves
|
||||
class ranges_holder_primary {
|
||||
dht::token_range_vector _token_ranges;
|
||||
public:
|
||||
explicit ranges_holder_primary(dht::token_range_vector token_ranges) : _token_ranges(std::move(token_ranges)) {}
|
||||
static future<ranges_holder_primary> make(const locator::vnode_effective_replication_map_ptr& erm, gms::inet_address ep) {
|
||||
co_return ranges_holder_primary(co_await erm->get_primary_ranges(ep));
|
||||
}
|
||||
std::size_t size() const { return _token_ranges.size(); }
|
||||
const dht::token_range& operator[](std::size_t i) const {
|
||||
return _token_ranges[i];
|
||||
}
|
||||
bool should_skip(std::size_t i) const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// ranges_holder<secondary> holds the secondary token ranges plus each
|
||||
// range's primary owner, needed to implement should_skip().
|
||||
class ranges_holder_secondary {
|
||||
std::vector<std::pair<dht::token_range, gms::inet_address>> _token_ranges;
|
||||
const gms::gossiper& _gossiper;
|
||||
public:
|
||||
explicit ranges_holder_secondary(std::vector<std::pair<dht::token_range, gms::inet_address>> token_ranges, const gms::gossiper& g)
|
||||
: _token_ranges(std::move(token_ranges))
|
||||
, _gossiper(g) {}
|
||||
static future<ranges_holder_secondary> make(const locator::effective_replication_map_ptr& erm, gms::inet_address ep, const gms::gossiper& g) {
|
||||
co_return ranges_holder_secondary(co_await get_secondary_ranges(erm, ep), g);
|
||||
}
|
||||
std::size_t size() const { return _token_ranges.size(); }
|
||||
const dht::token_range& operator[](std::size_t i) const {
|
||||
return _token_ranges[i].first;
|
||||
}
|
||||
// range i should be skipped if its primary owner is alive.
|
||||
bool should_skip(std::size_t i) const {
|
||||
return _gossiper.is_alive(_token_ranges[i].second);
|
||||
}
|
||||
};
|
||||
|
||||
template<class primary_or_secondary_t>
|
||||
enum primary_or_secondary_t {primary, secondary};
|
||||
template<primary_or_secondary_t primary_or_secondary>
|
||||
class token_ranges_owned_by_this_shard {
|
||||
// ranges_holder_primary holds just the primary ranges themselves
|
||||
class ranges_holder_primary {
|
||||
const dht::token_range_vector _token_ranges;
|
||||
public:
|
||||
ranges_holder_primary(const locator::vnode_effective_replication_map_ptr& erm, gms::gossiper& g, gms::inet_address ep)
|
||||
: _token_ranges(erm->get_primary_ranges(ep)) {}
|
||||
std::size_t size() const { return _token_ranges.size(); }
|
||||
const dht::token_range& operator[](std::size_t i) const {
|
||||
return _token_ranges[i];
|
||||
}
|
||||
bool should_skip(std::size_t i) const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// ranges_holder<secondary> holds the secondary token ranges plus each
|
||||
// range's primary owner, needed to implement should_skip().
|
||||
class ranges_holder_secondary {
|
||||
std::vector<std::pair<dht::token_range, gms::inet_address>> _token_ranges;
|
||||
gms::gossiper& _gossiper;
|
||||
public:
|
||||
ranges_holder_secondary(const locator::effective_replication_map_ptr& erm, gms::gossiper& g, gms::inet_address ep)
|
||||
: _token_ranges(get_secondary_ranges(erm, ep))
|
||||
, _gossiper(g) {}
|
||||
std::size_t size() const { return _token_ranges.size(); }
|
||||
const dht::token_range& operator[](std::size_t i) const {
|
||||
return _token_ranges[i].first;
|
||||
}
|
||||
// range i should be skipped if its primary owner is alive.
|
||||
bool should_skip(std::size_t i) const {
|
||||
return _gossiper.is_alive(_token_ranges[i].second);
|
||||
}
|
||||
};
|
||||
|
||||
schema_ptr _s;
|
||||
locator::effective_replication_map_ptr _erm;
|
||||
// _token_ranges will contain a list of token ranges owned by this node.
|
||||
// We'll further need to split each such range to the pieces owned by
|
||||
// the current shard, using _intersecter.
|
||||
const primary_or_secondary_t _token_ranges;
|
||||
using ranges_holder = std::conditional_t<
|
||||
primary_or_secondary == primary_or_secondary_t::primary,
|
||||
ranges_holder_primary,
|
||||
ranges_holder_secondary>;
|
||||
const ranges_holder _token_ranges;
|
||||
// NOTICE: _range_idx is used modulo _token_ranges size when accessing
|
||||
// the data to ensure that it doesn't go out of bounds
|
||||
size_t _range_idx;
|
||||
size_t _end_idx;
|
||||
std::optional<dht::selective_token_range_sharder> _intersecter;
|
||||
public:
|
||||
token_ranges_owned_by_this_shard(schema_ptr s, primary_or_secondary_t token_ranges)
|
||||
token_ranges_owned_by_this_shard(replica::database& db, gms::gossiper& g, schema_ptr s)
|
||||
: _s(s)
|
||||
, _erm(s->table().get_effective_replication_map())
|
||||
, _token_ranges(std::move(token_ranges))
|
||||
, _token_ranges(db.find_keyspace(s->ks_name()).get_vnode_effective_replication_map(),
|
||||
g, _erm->get_topology().my_address())
|
||||
, _range_idx(random_offset(0, _token_ranges.size() - 1))
|
||||
, _end_idx(_range_idx + _token_ranges.size())
|
||||
{
|
||||
@@ -502,7 +495,6 @@ struct scan_ranges_context {
|
||||
bytes column_name;
|
||||
std::optional<std::string> member;
|
||||
|
||||
service::client_state internal_client_state;
|
||||
::shared_ptr<cql3::selection::selection> selection;
|
||||
std::unique_ptr<service::query_state> query_state_ptr;
|
||||
std::unique_ptr<cql3::query_options> query_options;
|
||||
@@ -512,7 +504,6 @@ struct scan_ranges_context {
|
||||
: s(s)
|
||||
, column_name(column_name)
|
||||
, member(member)
|
||||
, internal_client_state(service::client_state::internal_tag())
|
||||
{
|
||||
// FIXME: don't read the entire items - read only parts of it.
|
||||
// We must read the key columns (to be able to delete) and also
|
||||
@@ -531,9 +522,10 @@ struct scan_ranges_context {
|
||||
std::vector<query::clustering_range> ck_bounds{query::clustering_range::make_open_ended_both_sides()};
|
||||
auto partition_slice = query::partition_slice(std::move(ck_bounds), {}, std::move(regular_columns), opts);
|
||||
command = ::make_lw_shared<query::read_command>(s->id(), s->version(), partition_slice, proxy.get_max_result_size(partition_slice), query::tombstone_limit(proxy.get_tombstone_limit()));
|
||||
executor::client_state client_state{executor::client_state::internal_tag()};
|
||||
tracing::trace_state_ptr trace_state;
|
||||
// NOTICE: empty_service_permit is used because the TTL service has fixed parallelism
|
||||
query_state_ptr = std::make_unique<service::query_state>(internal_client_state, trace_state, empty_service_permit());
|
||||
query_state_ptr = std::make_unique<service::query_state>(client_state, trace_state, empty_service_permit());
|
||||
// FIXME: What should we do on multi-DC? Will we run the expiration on the same ranges on all
|
||||
// DCs or only once for each range? If the latter, we need to change the CLs in the
|
||||
// scanner and deleter.
|
||||
@@ -556,7 +548,7 @@ static future<> scan_table_ranges(
|
||||
expiration_service::stats& expiration_stats)
|
||||
{
|
||||
const schema_ptr& s = scan_ctx.s;
|
||||
SCYLLA_ASSERT (partition_ranges.size() == 1); // otherwise issue #9167 will cause incorrect results.
|
||||
assert (partition_ranges.size() == 1); // otherwise issue #9167 will cause incorrect results.
|
||||
auto p = service::pager::query_pagers::pager(proxy, s, scan_ctx.selection, *scan_ctx.query_state_ptr,
|
||||
*scan_ctx.query_options, scan_ctx.command, std::move(partition_ranges), nullptr);
|
||||
while (!p->is_exhausted()) {
|
||||
@@ -729,9 +721,7 @@ static future<bool> scan_table(
|
||||
expiration_stats.scan_table++;
|
||||
// FIXME: need to pace the scan, not do it all at once.
|
||||
scan_ranges_context scan_ctx{s, proxy, std::move(column_name), std::move(member)};
|
||||
auto erm = db.real_database().find_keyspace(s->ks_name()).get_vnode_effective_replication_map();
|
||||
auto my_address = erm->get_topology().my_address();
|
||||
token_ranges_owned_by_this_shard my_ranges(s, co_await ranges_holder_primary::make(erm, my_address));
|
||||
token_ranges_owned_by_this_shard<primary> my_ranges(db.real_database(), gossiper, s);
|
||||
while (std::optional<dht::partition_range> range = my_ranges.next_partition_range()) {
|
||||
// Note that because of issue #9167 we need to run a separate
|
||||
// query on each partition range, and can't pass several of
|
||||
@@ -751,7 +741,7 @@ static future<bool> scan_table(
|
||||
// by tasking another node to take over scanning of the dead node's primary
|
||||
// ranges. What we do here is that this node will also check expiration
|
||||
// on its *secondary* ranges - but only those whose primary owner is down.
|
||||
token_ranges_owned_by_this_shard my_secondary_ranges(s, co_await ranges_holder_secondary::make(erm, my_address, gossiper));
|
||||
token_ranges_owned_by_this_shard<secondary> my_secondary_ranges(db.real_database(), gossiper, s);
|
||||
while (std::optional<dht::partition_range> range = my_secondary_ranges.next_partition_range()) {
|
||||
expiration_stats.secondary_ranges_scanned++;
|
||||
dht::partition_range_vector partition_ranges;
|
||||
|
||||
@@ -7,7 +7,6 @@ set(swagger_files
|
||||
api-doc/commitlog.json
|
||||
api-doc/compaction_manager.json
|
||||
api-doc/config.json
|
||||
api-doc/cql_server_test.json
|
||||
api-doc/endpoint_snitch_info.json
|
||||
api-doc/error_injection.json
|
||||
api-doc/failure_detector.json
|
||||
@@ -47,7 +46,6 @@ target_sources(api
|
||||
commitlog.cc
|
||||
compaction_manager.cc
|
||||
config.cc
|
||||
cql_server_test.cc
|
||||
endpoint_snitch.cc
|
||||
error_injection.cc
|
||||
authorization_cache.cc
|
||||
@@ -74,8 +72,7 @@ target_link_libraries(api
|
||||
idl
|
||||
wasmtime_bindings
|
||||
Seastar::seastar
|
||||
xxHash::xxhash
|
||||
absl::headers)
|
||||
xxHash::xxhash)
|
||||
|
||||
check_headers(check-headers api
|
||||
GLOB_RECURSE ${CMAKE_CURRENT_SOURCE_DIR}/*.hh)
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
"parameters":[
|
||||
{
|
||||
"name":"pluginid",
|
||||
"description":"The plugin ID, describe the component the metric belongs to. Examples are cache and alternator, etc'. Regex are supported.",
|
||||
"description":"The plugin ID, describe the component the metric belongs to. Examples are cache, thrift, etc'. Regex are supported.The plugin ID, describe the component the metric belong to. Examples are: cache, thrift etc'. regex are supported",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
@@ -199,4 +199,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,14 +92,6 @@
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"consider_only_existing_data",
|
||||
"description":"Set to \"true\" to flush all memtables and force tombstone garbage collection to check only the sstables being compacted (false by default). The memtable, commitlog and other uncompacted sstables will not be checked during tombstone garbage collection.",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"split_output",
|
||||
"description":"true if the output of the major compaction should be split in several sstables",
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"apiVersion":"0.0.1",
|
||||
"swaggerVersion":"1.2",
|
||||
"basePath":"{{Protocol}}://{{Host}}",
|
||||
"resourcePath":"/cql_server_test",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"apis":[
|
||||
{
|
||||
"path":"/cql_server_test/connections_params",
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Get service level params of each CQL connection",
|
||||
"type":"connections_service_level_params",
|
||||
"nickname":"connections_params",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -63,28 +63,6 @@
|
||||
"paramType":"path"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Read the state of an injection from all shards",
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"error_injection_info"
|
||||
},
|
||||
"nickname":"read_injection",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"injection",
|
||||
"description":"injection name",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"path"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -174,39 +152,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"models":{
|
||||
"mapper":{
|
||||
"id":"mapper",
|
||||
"description":"A key value mapping",
|
||||
"properties":{
|
||||
"key":{
|
||||
"type":"string",
|
||||
"description":"The key"
|
||||
},
|
||||
"value":{
|
||||
"type":"string",
|
||||
"description":"The value"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error_injection_info":{
|
||||
"id":"error_injection_info",
|
||||
"description":"Information about an error injection",
|
||||
"properties":{
|
||||
"enabled":{
|
||||
"type":"boolean",
|
||||
"description":"Is the error injection enabled"
|
||||
},
|
||||
"parameters":{
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"mapper"
|
||||
},
|
||||
"description":"The parameter values"
|
||||
}
|
||||
},
|
||||
"required":["enabled"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,38 +62,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/raft/read_barrier",
|
||||
"operations": [
|
||||
{
|
||||
"method": "POST",
|
||||
"summary": "Triggers read barrier for the given Raft group to wait for previously committed commands in this group to be applied locally. For example, can be used on group 0 to wait for the node to obtain latest schema changes.",
|
||||
"type": "string",
|
||||
"nickname": "read_barrier",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "group_id",
|
||||
"description": "The ID of the group. When absent, group0 is used.",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"type": "string",
|
||||
"paramType": "query"
|
||||
},
|
||||
{
|
||||
"name": "timeout",
|
||||
"description": "Timeout in seconds after which the endpoint returns a failure. If not provided, 60s is used.",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"type": "long",
|
||||
"paramType": "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Returns a list of the tokens endpoint mapping, provide keyspace and cf param to get tablet mapping",
|
||||
"summary":"Returns a list of the tokens endpoint mapping",
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"mapper"
|
||||
@@ -100,22 +100,6 @@
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"keyspace",
|
||||
"description":"The keyspace to provide the tablet mapping for",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"cf",
|
||||
"description":"The table to provide the tablet mapping for",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -741,123 +725,11 @@
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"consider_only_existing_data",
|
||||
"description":"Set to \"true\" to flush all memtables and force tombstone garbage collection to check only the sstables being compacted (false by default). The memtable, commitlog and other uncompacted sstables will not be checked during tombstone garbage collection.",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/backup",
|
||||
"operations":[
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"Starts copying SSTables from a specified keyspace to a designated bucket in object storage",
|
||||
"type":"string",
|
||||
"nickname":"start_backup",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"endpoint",
|
||||
"description":"ID of the configured object storage endpoint to copy sstables to",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"bucket",
|
||||
"description":"Name of the bucket to backup sstables to",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"keyspace",
|
||||
"description":"Name of a keyspace to copy sstables from",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"snapshot",
|
||||
"description":"Name of a snapshot to copy sstables from",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/restore",
|
||||
"operations":[
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"Starts copying SSTables from a designated bucket in object storage to a specified keyspace",
|
||||
"type":"string",
|
||||
"nickname":"start_restore",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"endpoint",
|
||||
"description":"ID of the configured object storage endpoint to copy SSTables from",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"bucket",
|
||||
"description":"Name of the bucket to read SSTables from",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"snapshot",
|
||||
"description":"Name of a snapshot to copy SSTables from",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"keyspace",
|
||||
"description":"Name of a keyspace to copy SSTables to",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"table",
|
||||
"description":"Name of a table to copy SSTables to",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/keyspace_compaction/{keyspace}",
|
||||
"operations":[
|
||||
@@ -893,14 +765,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"consider_only_existing_data",
|
||||
"description":"Set to \"true\" to flush all memtables and force tombstone garbage collection to check only the sstables being compacted (false by default). The memtable, commitlog and other uncompacted sstables will not be checked during tombstone garbage collection.",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1809,11 +1673,33 @@
|
||||
{
|
||||
"path":"/storage_service/rpc_server",
|
||||
"operations":[
|
||||
{
|
||||
"method":"DELETE",
|
||||
"summary":"Allows a user to disable thrift",
|
||||
"type":"void",
|
||||
"nickname":"stop_rpc_server",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
]
|
||||
},
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"allows a user to re-enable thrift",
|
||||
"type":"void",
|
||||
"nickname":"start_rpc_server",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
]
|
||||
},
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Determine if thrift is running",
|
||||
"type":"boolean",
|
||||
"nickname":"is_thrift_server_running",
|
||||
"nickname":"is_rpc_server_running",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
@@ -2011,14 +1897,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"force",
|
||||
"description":"Enforce the source_dc option, even if it unsafe to use for rebuild",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"boolean",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2176,7 +2054,7 @@
|
||||
"operations":[
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"Enables/Disables tracing for the whole system.",
|
||||
"summary":"Enables/Disables tracing for the whole system. Only thrift requests can start tracing currently",
|
||||
"type":"void",
|
||||
"nickname":"set_trace_probability",
|
||||
"produces":[
|
||||
@@ -2848,22 +2726,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/quiesce_topology",
|
||||
"operations":[
|
||||
{
|
||||
"nickname":"quiesce_topology",
|
||||
"method":"POST",
|
||||
"summary":"Waits until there are no ongoing topology operations. Guarantees that topology operations which started before the call are finished after the call. This doesn't consider requested but not started operations. Such operations may start after the call succeeds.",
|
||||
"type":"void",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/metrics/total_hints",
|
||||
"operations":[
|
||||
|
||||
@@ -194,21 +194,6 @@
|
||||
"parameters":[]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/system/highest_supported_sstable_version",
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Get highest supported sstable version",
|
||||
"type":"string",
|
||||
"nickname":"get_highest_supported_sstable_version",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
"parameters":[
|
||||
{
|
||||
"name":"task_id",
|
||||
"description":"The uuid of a task to abort; if the task is not abortable, 403 status code is returned",
|
||||
"description":"The uuid of a task to abort",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
@@ -144,14 +144,6 @@
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"path"
|
||||
},
|
||||
{
|
||||
"name":"timeout",
|
||||
"description":"Timeout for waiting; if times out, 408 status code is returned",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"long",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -205,60 +197,11 @@
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Get current ttl value",
|
||||
"type":"long",
|
||||
"nickname":"get_ttl",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/task_manager/drain/{module}",
|
||||
"operations":[
|
||||
{
|
||||
"method":"POST",
|
||||
"summary":"Drain finished local tasks",
|
||||
"type":"void",
|
||||
"nickname":"drain_tasks",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"module",
|
||||
"description":"The module to drain",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"path"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models":{
|
||||
"task_identity":{
|
||||
"id": "task_identity",
|
||||
"description":"Id and node of a task",
|
||||
"properties":{
|
||||
"task_id":{
|
||||
"type":"string",
|
||||
"description":"The uuid of a task"
|
||||
},
|
||||
"node":{
|
||||
"type":"string",
|
||||
"description":"Address of a server on which a task is created"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task_stats" :{
|
||||
"id": "task_stats",
|
||||
"description":"A task statistics object",
|
||||
@@ -281,14 +224,6 @@
|
||||
"type":"string",
|
||||
"description":"The description of the task"
|
||||
},
|
||||
"kind":{
|
||||
"type":"string",
|
||||
"enum":[
|
||||
"node",
|
||||
"cluster"
|
||||
],
|
||||
"description":"The kind of a task"
|
||||
},
|
||||
"scope":{
|
||||
"type":"string",
|
||||
"description":"The scope of the task"
|
||||
@@ -323,14 +258,6 @@
|
||||
"type":"string",
|
||||
"description":"The description of the task"
|
||||
},
|
||||
"kind":{
|
||||
"type":"string",
|
||||
"enum":[
|
||||
"node",
|
||||
"cluster"
|
||||
],
|
||||
"description":"The kind of a task"
|
||||
},
|
||||
"scope":{
|
||||
"type":"string",
|
||||
"description":"The scope of the task"
|
||||
@@ -400,9 +327,9 @@
|
||||
"children_ids":{
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"task_identity"
|
||||
"type":"string"
|
||||
},
|
||||
"description":"Task identities of children of this task"
|
||||
"description":"Task IDs of children of this task"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"items":{
|
||||
"type":"double"
|
||||
},
|
||||
"description":"One, five and fifteen minutes rates"
|
||||
"description":"One, five and fifteen mintues rates"
|
||||
},
|
||||
"mean_rate": {
|
||||
"type":"double",
|
||||
|
||||
71
api/api.cc
71
api/api.cc
@@ -10,7 +10,6 @@
|
||||
#include <seastar/http/file_handler.hh>
|
||||
#include <seastar/http/transformers.hh>
|
||||
#include <seastar/http/api_docs.hh>
|
||||
#include "cql_server_test.hh"
|
||||
#include "storage_service.hh"
|
||||
#include "token_metadata.hh"
|
||||
#include "commitlog.hh"
|
||||
@@ -72,10 +71,6 @@ future<> set_server_init(http_context& ctx) {
|
||||
rb->register_function(r, "error_injection",
|
||||
"The error injection API");
|
||||
set_error_injection(ctx, r);
|
||||
rb->register_function(r, "storage_proxy",
|
||||
"The storage proxy API");
|
||||
rb->register_function(r, "storage_service",
|
||||
"The storage service API");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,10 +81,6 @@ future<> set_server_config(http_context& ctx, const db::config& cfg) {
|
||||
});
|
||||
}
|
||||
|
||||
future<> unset_server_config(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_config(ctx, r); });
|
||||
}
|
||||
|
||||
static future<> register_api(http_context& ctx, const sstring& api_name,
|
||||
const sstring api_desc,
|
||||
std::function<void(http_context& ctx, routes& r)> f) {
|
||||
@@ -109,16 +100,16 @@ future<> unset_transport_controller(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_transport_controller(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_thrift_controller(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { set_thrift_controller(ctx, r); });
|
||||
future<> set_rpc_controller(http_context& ctx, thrift_controller& ctl) {
|
||||
return ctx.http_server.set_routes([&ctx, &ctl] (routes& r) { set_rpc_controller(ctx, r, ctl); });
|
||||
}
|
||||
|
||||
future<> unset_thrift_controller(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_thrift_controller(ctx, r); });
|
||||
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, service::raft_group0_client& group0_client) {
|
||||
return ctx.http_server.set_routes([&ctx, &ss, &group0_client] (routes& r) {
|
||||
return register_api(ctx, "storage_service", "The storage service API", [&ss, &group0_client] (http_context& ctx, routes& r) {
|
||||
set_storage_service(ctx, r, ss, group0_client);
|
||||
});
|
||||
}
|
||||
@@ -127,14 +118,6 @@ future<> unset_server_storage_service(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_storage_service(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_load_meter(http_context& ctx, service::load_meter& lm) {
|
||||
return ctx.http_server.set_routes([&ctx, &lm] (routes& r) { set_load_meter(ctx, r, lm); });
|
||||
}
|
||||
|
||||
future<> unset_load_meter(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_load_meter(ctx, r); });
|
||||
}
|
||||
|
||||
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); });
|
||||
}
|
||||
@@ -197,21 +180,10 @@ future<> unset_server_snitch(http_context& ctx) {
|
||||
}
|
||||
|
||||
future<> set_server_gossip(http_context& ctx, sharded<gms::gossiper>& g) {
|
||||
co_await register_api(ctx, "gossiper",
|
||||
return register_api(ctx, "gossiper",
|
||||
"The gossiper API", [&g] (http_context& ctx, routes& r) {
|
||||
set_gossiper(ctx, r, g.local());
|
||||
});
|
||||
co_await register_api(ctx, "failure_detector",
|
||||
"The failure detector API", [&g] (http_context& ctx, routes& r) {
|
||||
set_failure_detector(ctx, r, g.local());
|
||||
});
|
||||
}
|
||||
|
||||
future<> unset_server_gossip(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) {
|
||||
unset_gossiper(ctx, r);
|
||||
unset_failure_detector(ctx, r);
|
||||
});
|
||||
}
|
||||
|
||||
future<> set_server_column_family(http_context& ctx, sharded<db::system_keyspace>& sys_ks) {
|
||||
@@ -236,7 +208,10 @@ future<> unset_server_messaging_service(http_context& ctx) {
|
||||
}
|
||||
|
||||
future<> set_server_storage_proxy(http_context& ctx, sharded<service::storage_proxy>& proxy) {
|
||||
return ctx.http_server.set_routes([&ctx, &proxy] (routes& r) { set_storage_proxy(ctx, r, proxy); });
|
||||
return register_api(ctx, "storage_proxy",
|
||||
"The storage proxy API", [&proxy] (http_context& ctx, routes& r) {
|
||||
set_storage_proxy(ctx, r, proxy);
|
||||
});
|
||||
}
|
||||
|
||||
future<> unset_server_storage_proxy(http_context& ctx) {
|
||||
@@ -259,10 +234,6 @@ future<> set_server_cache(http_context& ctx) {
|
||||
"The cache service API", set_cache_service);
|
||||
}
|
||||
|
||||
future<> unset_server_cache(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_cache_service(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_hinted_handoff(http_context& ctx, sharded<service::storage_proxy>& proxy) {
|
||||
return register_api(ctx, "hinted_handoff",
|
||||
"The hinted handoff API", [&proxy] (http_context& ctx, routes& r) {
|
||||
@@ -274,6 +245,16 @@ 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) {
|
||||
auto rb = std::make_shared < api_registry_builder > (ctx.api_doc);
|
||||
|
||||
@@ -330,16 +311,6 @@ future<> unset_server_task_manager_test(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_task_manager_test(ctx, r); });
|
||||
}
|
||||
|
||||
future<> set_server_cql_server_test(http_context& ctx, cql_transport::controller& ctl) {
|
||||
return register_api(ctx, "cql_server_test", "The CQL server test API", [&ctl] (http_context& ctx, routes& r) {
|
||||
set_cql_server_test(ctx, r, ctl);
|
||||
});
|
||||
}
|
||||
|
||||
future<> unset_server_cql_server_test(http_context& ctx) {
|
||||
return ctx.http_server.set_routes([&ctx] (routes& r) { unset_cql_server_test(ctx, r); });
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
future<> set_server_tasks_compaction_module(http_context& ctx, sharded<service::storage_service>& ss, sharded<db::snapshot_ctl>& snap_ctl) {
|
||||
@@ -375,7 +346,7 @@ void req_params::process(const request& req) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ent.value = req.get_path_param(name);
|
||||
ent.value = req.param[name];
|
||||
} catch (std::out_of_range&) {
|
||||
throw httpd::bad_param_exception(fmt::format("Mandatory parameter '{}' was not provided", name));
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ public:
|
||||
value = T{boost::lexical_cast<Base>(param)};
|
||||
}
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
throw httpd::bad_param_exception(fmt::format("{} ({}): type error - should be {}", name, param, boost::units::detail::demangle(typeid(Base).name())));
|
||||
throw httpd::bad_param_exception(format("{} ({}): type error - should be {}", name, param, boost::units::detail::demangle(typeid(Base).name())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class snitch_ptr;
|
||||
} // namespace locator
|
||||
|
||||
namespace cql_transport { class controller; }
|
||||
class thrift_controller;
|
||||
namespace db {
|
||||
class snapshot_ctl;
|
||||
class config;
|
||||
@@ -76,16 +77,17 @@ struct http_context {
|
||||
sstring api_doc;
|
||||
httpd::http_server_control http_server;
|
||||
distributed<replica::database>& db;
|
||||
service::load_meter& lmeter;
|
||||
|
||||
http_context(distributed<replica::database>& _db)
|
||||
: db(_db)
|
||||
http_context(distributed<replica::database>& _db,
|
||||
service::load_meter& _lm)
|
||||
: db(_db), lmeter(_lm)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
future<> set_server_init(http_context& ctx);
|
||||
future<> set_server_config(http_context& ctx, const db::config& cfg);
|
||||
future<> unset_server_config(http_context& ctx);
|
||||
future<> set_server_snitch(http_context& ctx, sharded<locator::snitch_ptr>& snitch);
|
||||
future<> unset_server_snitch(http_context& ctx);
|
||||
future<> set_server_storage_service(http_context& ctx, sharded<service::storage_service>& ss, service::raft_group0_client&);
|
||||
@@ -98,8 +100,8 @@ future<> set_server_repair(http_context& ctx, sharded<repair_service>& repair);
|
||||
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);
|
||||
future<> set_thrift_controller(http_context& ctx);
|
||||
future<> unset_thrift_controller(http_context& ctx);
|
||||
future<> set_rpc_controller(http_context& ctx, thrift_controller& ctl);
|
||||
future<> unset_rpc_controller(http_context& ctx);
|
||||
future<> set_server_authorization_cache(http_context& ctx, sharded<auth::service> &auth_service);
|
||||
future<> unset_server_authorization_cache(http_context& ctx);
|
||||
future<> set_server_snapshot(http_context& ctx, sharded<db::snapshot_ctl>& snap_ctl);
|
||||
@@ -107,7 +109,6 @@ future<> unset_server_snapshot(http_context& ctx);
|
||||
future<> set_server_token_metadata(http_context& ctx, sharded<locator::shared_token_metadata>& tm);
|
||||
future<> unset_server_token_metadata(http_context& ctx);
|
||||
future<> set_server_gossip(http_context& ctx, sharded<gms::gossiper>& g);
|
||||
future<> unset_server_gossip(http_context& ctx);
|
||||
future<> set_server_column_family(http_context& ctx, sharded<db::system_keyspace>& sys_ks);
|
||||
future<> unset_server_column_family(http_context& ctx);
|
||||
future<> set_server_messaging_service(http_context& ctx, sharded<netw::messaging_service>& ms);
|
||||
@@ -118,8 +119,8 @@ future<> set_server_stream_manager(http_context& ctx, sharded<streaming::stream_
|
||||
future<> unset_server_stream_manager(http_context& ctx);
|
||||
future<> set_hinted_handoff(http_context& ctx, sharded<service::storage_proxy>& p);
|
||||
future<> unset_hinted_handoff(http_context& ctx);
|
||||
future<> set_server_gossip_settle(http_context& ctx, sharded<gms::gossiper>& g);
|
||||
future<> set_server_cache(http_context& ctx);
|
||||
future<> unset_server_cache(http_context& ctx);
|
||||
future<> set_server_compaction_manager(http_context& ctx);
|
||||
future<> set_server_done(http_context& ctx);
|
||||
future<> set_server_task_manager(http_context& ctx, sharded<tasks::task_manager>& tm, lw_shared_ptr<db::config> cfg);
|
||||
@@ -130,9 +131,5 @@ future<> set_server_tasks_compaction_module(http_context& ctx, sharded<service::
|
||||
future<> unset_server_tasks_compaction_module(http_context& ctx);
|
||||
future<> set_server_raft(http_context&, sharded<service::raft_group_registry>&);
|
||||
future<> unset_server_raft(http_context&);
|
||||
future<> set_load_meter(http_context& ctx, service::load_meter& lm);
|
||||
future<> unset_load_meter(http_context& ctx);
|
||||
future<> set_server_cql_server_test(http_context& ctx, cql_transport::controller& ctl);
|
||||
future<> unset_server_cql_server_test(http_context& ctx);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,20 +8,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
|
||||
namespace auth {
|
||||
class service;
|
||||
}
|
||||
#include "api.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct http_context;
|
||||
void set_authorization_cache(http_context& ctx, seastar::httpd::routes& r, seastar::sharded<auth::service> &auth_service);
|
||||
void unset_authorization_cache(http_context& ctx, seastar::httpd::routes& r);
|
||||
void set_authorization_cache(http_context& ctx, httpd::routes& r, sharded<auth::service> &auth_service);
|
||||
void unset_authorization_cache(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include "cache_service.hh"
|
||||
#include "api/api.hh"
|
||||
#include "api/api-doc/cache_service.json.hh"
|
||||
#include "column_family.hh"
|
||||
|
||||
@@ -196,9 +195,9 @@ void set_cache_service(http_context& ctx, routes& r) {
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
cs::get_row_capacity.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
return seastar::map_reduce(smp::all_cpus(), [] (int cpu) {
|
||||
return make_ready_future<uint64_t>(memory::stats().total_memory());
|
||||
cs::get_row_capacity.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return ctx.db.map_reduce0([](replica::database& db) -> uint64_t {
|
||||
return memory::stats().total_memory();
|
||||
}, uint64_t(0), std::plus<uint64_t>()).then([](const int64_t& res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
@@ -320,50 +319,5 @@ void set_cache_service(http_context& ctx, routes& r) {
|
||||
});
|
||||
}
|
||||
|
||||
void unset_cache_service(http_context& ctx, routes& r) {
|
||||
cs::get_row_cache_save_period_in_seconds.unset(r);
|
||||
cs::set_row_cache_save_period_in_seconds.unset(r);
|
||||
cs::get_key_cache_save_period_in_seconds.unset(r);
|
||||
cs::set_key_cache_save_period_in_seconds.unset(r);
|
||||
cs::get_counter_cache_save_period_in_seconds.unset(r);
|
||||
cs::set_counter_cache_save_period_in_seconds.unset(r);
|
||||
cs::get_row_cache_keys_to_save.unset(r);
|
||||
cs::set_row_cache_keys_to_save.unset(r);
|
||||
cs::get_key_cache_keys_to_save.unset(r);
|
||||
cs::set_key_cache_keys_to_save.unset(r);
|
||||
cs::get_counter_cache_keys_to_save.unset(r);
|
||||
cs::set_counter_cache_keys_to_save.unset(r);
|
||||
cs::invalidate_key_cache.unset(r);
|
||||
cs::invalidate_counter_cache.unset(r);
|
||||
cs::set_row_cache_capacity_in_mb.unset(r);
|
||||
cs::set_key_cache_capacity_in_mb.unset(r);
|
||||
cs::set_counter_cache_capacity_in_mb.unset(r);
|
||||
cs::save_caches.unset(r);
|
||||
cs::get_key_capacity.unset(r);
|
||||
cs::get_key_hits.unset(r);
|
||||
cs::get_key_requests.unset(r);
|
||||
cs::get_key_hit_rate.unset(r);
|
||||
cs::get_key_hits_moving_avrage.unset(r);
|
||||
cs::get_key_requests_moving_avrage.unset(r);
|
||||
cs::get_key_size.unset(r);
|
||||
cs::get_key_entries.unset(r);
|
||||
cs::get_row_capacity.unset(r);
|
||||
cs::get_row_hits.unset(r);
|
||||
cs::get_row_requests.unset(r);
|
||||
cs::get_row_hit_rate.unset(r);
|
||||
cs::get_row_hits_moving_avrage.unset(r);
|
||||
cs::get_row_requests_moving_avrage.unset(r);
|
||||
cs::get_row_size.unset(r);
|
||||
cs::get_row_entries.unset(r);
|
||||
cs::get_counter_capacity.unset(r);
|
||||
cs::get_counter_hits.unset(r);
|
||||
cs::get_counter_requests.unset(r);
|
||||
cs::get_counter_hit_rate.unset(r);
|
||||
cs::get_counter_hits_moving_avrage.unset(r);
|
||||
cs::get_counter_requests_moving_avrage.unset(r);
|
||||
cs::get_counter_size.unset(r);
|
||||
cs::get_counter_entries.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
#include "api.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct http_context;
|
||||
void set_cache_service(http_context& ctx, seastar::httpd::routes& r);
|
||||
void unset_cache_service(http_context& ctx, seastar::httpd::routes& r);
|
||||
void set_cache_service(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ static const char* str_to_regex(const sstring& v) {
|
||||
void set_collectd(http_context& ctx, routes& r) {
|
||||
cd::get_collectd.set(r, [](std::unique_ptr<request> req) {
|
||||
|
||||
auto id = ::make_shared<scollectd::type_instance_id>(req->get_path_param("pluginid"),
|
||||
auto id = ::make_shared<scollectd::type_instance_id>(req->param["pluginid"],
|
||||
req->get_query_param("instance"), req->get_query_param("type"),
|
||||
req->get_query_param("type_instance"));
|
||||
|
||||
@@ -91,7 +91,7 @@ void set_collectd(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
cd::enable_collectd.set(r, [](std::unique_ptr<request> req) -> future<json::json_return_type> {
|
||||
std::regex plugin(req->get_path_param("pluginid").c_str());
|
||||
std::regex plugin(req->param["pluginid"].c_str());
|
||||
std::regex instance(str_to_regex(req->get_query_param("instance")));
|
||||
std::regex type(str_to_regex(req->get_query_param("type")));
|
||||
std::regex type_instance(str_to_regex(req->get_query_param("type_instance")));
|
||||
|
||||
@@ -6,16 +6,13 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
#include "column_family.hh"
|
||||
#include "api/api.hh"
|
||||
#include "api/api-doc/column_family.json.hh"
|
||||
#include "api/api-doc/storage_service.json.hh"
|
||||
#include <vector>
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "sstables/sstables.hh"
|
||||
#include "sstables/metadata_collector.hh"
|
||||
#include "utils/assert.hh"
|
||||
#include "utils/estimated_histogram.hh"
|
||||
#include <algorithm>
|
||||
#include "db/system_keyspace.hh"
|
||||
@@ -31,7 +28,6 @@ using namespace httpd;
|
||||
|
||||
using namespace json;
|
||||
namespace cf = httpd::column_family_json;
|
||||
namespace ss = httpd::storage_service_json;
|
||||
|
||||
std::tuple<sstring, sstring> parse_fully_qualified_cf_name(sstring name) {
|
||||
auto pos = name.find("%3A");
|
||||
@@ -83,65 +79,6 @@ future<json::json_return_type> get_cf_stats(http_context& ctx,
|
||||
}, std::plus<int64_t>());
|
||||
}
|
||||
|
||||
static future<json::json_return_type> set_tables(http_context& ctx, const sstring& keyspace, std::vector<sstring> tables, std::function<future<>(replica::table&)> set) {
|
||||
if (tables.empty()) {
|
||||
tables = map_keys(ctx.db.local().find_keyspace(keyspace).metadata().get()->cf_meta_data());
|
||||
}
|
||||
|
||||
return do_with(keyspace, std::move(tables), [&ctx, set] (const sstring& keyspace, const std::vector<sstring>& tables) {
|
||||
return ctx.db.invoke_on_all([&keyspace, &tables, set] (replica::database& db) {
|
||||
return parallel_for_each(tables, [&db, &keyspace, set] (const sstring& table) {
|
||||
replica::table& t = db.find_column_family(keyspace, table);
|
||||
return set(t);
|
||||
});
|
||||
});
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
}
|
||||
|
||||
class autocompaction_toggle_guard {
|
||||
replica::database& _db;
|
||||
public:
|
||||
autocompaction_toggle_guard(replica::database& db) : _db(db) {
|
||||
SCYLLA_ASSERT(this_shard_id() == 0);
|
||||
if (!_db._enable_autocompaction_toggle) {
|
||||
throw std::runtime_error("Autocompaction toggle is busy");
|
||||
}
|
||||
_db._enable_autocompaction_toggle = false;
|
||||
}
|
||||
autocompaction_toggle_guard(const autocompaction_toggle_guard&) = delete;
|
||||
autocompaction_toggle_guard(autocompaction_toggle_guard&&) = default;
|
||||
~autocompaction_toggle_guard() {
|
||||
SCYLLA_ASSERT(this_shard_id() == 0);
|
||||
_db._enable_autocompaction_toggle = true;
|
||||
}
|
||||
};
|
||||
|
||||
static future<json::json_return_type> set_tables_autocompaction(http_context& ctx, const sstring &keyspace, std::vector<sstring> tables, bool enabled) {
|
||||
apilog.info("set_tables_autocompaction: enabled={} keyspace={} tables={}", enabled, keyspace, tables);
|
||||
|
||||
return ctx.db.invoke_on(0, [&ctx, keyspace, tables = std::move(tables), enabled] (replica::database& db) {
|
||||
auto g = autocompaction_toggle_guard(db);
|
||||
return set_tables(ctx, keyspace, tables, [enabled] (replica::table& cf) {
|
||||
if (enabled) {
|
||||
cf.enable_auto_compaction();
|
||||
} else {
|
||||
return cf.disable_auto_compaction();
|
||||
}
|
||||
return make_ready_future<>();
|
||||
}).finally([g = std::move(g)] {});
|
||||
});
|
||||
}
|
||||
|
||||
static future<json::json_return_type> set_tables_tombstone_gc(http_context& ctx, const sstring &keyspace, std::vector<sstring> tables, bool enabled) {
|
||||
apilog.info("set_tables_tombstone_gc: enabled={} keyspace={} tables={}", enabled, keyspace, tables);
|
||||
return set_tables(ctx, keyspace, std::move(tables), [enabled] (replica::table& t) {
|
||||
t.set_tombstone_gc_enabled(enabled);
|
||||
return make_ready_future<>();
|
||||
});
|
||||
}
|
||||
|
||||
static future<json::json_return_type> get_cf_stats_count(http_context& ctx, const sstring& name,
|
||||
utils::timed_rate_moving_average_summary_and_histogram replica::column_family_stats::*f) {
|
||||
return map_reduce_cf(ctx, name, int64_t(0), [f](const replica::column_family& cf) {
|
||||
@@ -367,14 +304,6 @@ ratio_holder filter_recent_false_positive_as_ratio_holder(const sstables::shared
|
||||
return ratio_holder(f + sst->filter_get_recent_true_positive(), f);
|
||||
}
|
||||
|
||||
uint64_t accumulate_on_active_memtables(replica::table& t, noncopyable_function<uint64_t(replica::memtable& mt)> action) {
|
||||
uint64_t ret = 0;
|
||||
t.for_each_active_memtable([&] (replica::memtable& mt) {
|
||||
ret += action(mt);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace>& sys_ks) {
|
||||
cf::get_column_family_name.set(r, [&ctx] (const_req req){
|
||||
std::vector<sstring> res;
|
||||
@@ -409,14 +338,14 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_memtable_columns_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), uint64_t{0}, [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, std::mem_fn(&replica::memtable::partition_count));
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t{0}, [](replica::column_family& cf) {
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed(std::mem_fn(&replica::memtable::partition_count)), uint64_t(0));
|
||||
}, std::plus<>());
|
||||
});
|
||||
|
||||
cf::get_all_memtable_columns_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, uint64_t{0}, [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, std::mem_fn(&replica::memtable::partition_count));
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed(std::mem_fn(&replica::memtable::partition_count)), uint64_t(0));
|
||||
}, std::plus<>());
|
||||
});
|
||||
|
||||
@@ -429,34 +358,34 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_memtable_off_heap_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, [] (replica::memtable& active_memtable) {
|
||||
return active_memtable.region().occupancy().total_space();
|
||||
});
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](replica::column_family& cf) {
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed([] (replica::memtable* active_memtable) {
|
||||
return active_memtable->region().occupancy().total_space();
|
||||
}), uint64_t(0));
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
|
||||
cf::get_all_memtable_off_heap_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, int64_t(0), [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, [] (replica::memtable& active_memtable) {
|
||||
return active_memtable.region().occupancy().total_space();
|
||||
});
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed([] (replica::memtable* active_memtable) {
|
||||
return active_memtable->region().occupancy().total_space();
|
||||
}), uint64_t(0));
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
|
||||
cf::get_memtable_live_data_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, [] (replica::memtable& active_memtable) {
|
||||
return active_memtable.region().occupancy().used_space();
|
||||
});
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](replica::column_family& cf) {
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed([] (replica::memtable* active_memtable) {
|
||||
return active_memtable->region().occupancy().used_space();
|
||||
}), uint64_t(0));
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
|
||||
cf::get_all_memtable_live_data_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, int64_t(0), [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, [] (replica::memtable& active_memtable) {
|
||||
return active_memtable.region().occupancy().used_space();
|
||||
});
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed([] (replica::memtable* active_memtable) {
|
||||
return active_memtable->region().occupancy().used_space();
|
||||
}), uint64_t(0));
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
|
||||
@@ -470,7 +399,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
cf::get_cf_all_memtables_off_heap_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
warn(unimplemented::cause::INDEXES);
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](replica::column_family& cf) {
|
||||
return cf.occupancy().total_space();
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
@@ -486,7 +415,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
cf::get_cf_all_memtables_live_data_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
warn(unimplemented::cause::INDEXES);
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](replica::column_family& cf) {
|
||||
return cf.occupancy().used_space();
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
@@ -494,14 +423,14 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
cf::get_all_cf_all_memtables_live_data_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
warn(unimplemented::cause::INDEXES);
|
||||
return map_reduce_cf(ctx, int64_t(0), [](replica::column_family& cf) {
|
||||
return accumulate_on_active_memtables(cf, [] (replica::memtable& active_memtable) {
|
||||
return active_memtable.region().occupancy().used_space();
|
||||
});
|
||||
return boost::accumulate(cf.active_memtables() | boost::adaptors::transformed([] (replica::memtable* active_memtable) {
|
||||
return active_memtable->region().occupancy().used_space();
|
||||
}), uint64_t(0));
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
|
||||
cf::get_memtable_switch_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats(ctx,req->get_path_param("name") ,&replica::column_family_stats::memtable_switch_count);
|
||||
return get_cf_stats(ctx,req->param["name"] ,&replica::column_family_stats::memtable_switch_count);
|
||||
});
|
||||
|
||||
cf::get_all_memtable_switch_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -510,7 +439,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_estimated_row_size_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), utils::estimated_histogram(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](replica::column_family& cf) {
|
||||
utils::estimated_histogram res(0);
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
res.merge(i->get_stats_metadata().estimated_partition_size);
|
||||
@@ -522,7 +451,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_estimated_row_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](replica::column_family& cf) {
|
||||
uint64_t res = 0;
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
res += i->get_stats_metadata().estimated_partition_size.count();
|
||||
@@ -533,7 +462,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_estimated_column_count_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), utils::estimated_histogram(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](replica::column_family& cf) {
|
||||
utils::estimated_histogram res(0);
|
||||
for (auto sstables = cf.get_sstables(); auto& i : *sstables) {
|
||||
res.merge(i->get_stats_metadata().estimated_cells_count);
|
||||
@@ -550,7 +479,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_pending_flushes.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats(ctx,req->get_path_param("name") ,&replica::column_family_stats::pending_flushes);
|
||||
return get_cf_stats(ctx,req->param["name"] ,&replica::column_family_stats::pending_flushes);
|
||||
});
|
||||
|
||||
cf::get_all_pending_flushes.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -558,7 +487,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_read.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats_count(ctx,req->get_path_param("name") ,&replica::column_family_stats::reads);
|
||||
return get_cf_stats_count(ctx,req->param["name"] ,&replica::column_family_stats::reads);
|
||||
});
|
||||
|
||||
cf::get_all_read.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -566,7 +495,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_write.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats_count(ctx, req->get_path_param("name") ,&replica::column_family_stats::writes);
|
||||
return get_cf_stats_count(ctx, req->param["name"] ,&replica::column_family_stats::writes);
|
||||
});
|
||||
|
||||
cf::get_all_write.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -574,19 +503,19 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_read_latency_histogram_depricated.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_histogram(ctx, req->get_path_param("name"), &replica::column_family_stats::reads);
|
||||
return get_cf_histogram(ctx, req->param["name"], &replica::column_family_stats::reads);
|
||||
});
|
||||
|
||||
cf::get_read_latency_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_rate_and_histogram(ctx, req->get_path_param("name"), &replica::column_family_stats::reads);
|
||||
return get_cf_rate_and_histogram(ctx, req->param["name"], &replica::column_family_stats::reads);
|
||||
});
|
||||
|
||||
cf::get_read_latency.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats_sum(ctx,req->get_path_param("name") ,&replica::column_family_stats::reads);
|
||||
return get_cf_stats_sum(ctx,req->param["name"] ,&replica::column_family_stats::reads);
|
||||
});
|
||||
|
||||
cf::get_write_latency.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats_sum(ctx, req->get_path_param("name") ,&replica::column_family_stats::writes);
|
||||
return get_cf_stats_sum(ctx, req->param["name"] ,&replica::column_family_stats::writes);
|
||||
});
|
||||
|
||||
cf::get_all_read_latency_histogram_depricated.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -598,11 +527,11 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_write_latency_histogram_depricated.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_histogram(ctx, req->get_path_param("name"), &replica::column_family_stats::writes);
|
||||
return get_cf_histogram(ctx, req->param["name"], &replica::column_family_stats::writes);
|
||||
});
|
||||
|
||||
cf::get_write_latency_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_rate_and_histogram(ctx, req->get_path_param("name"), &replica::column_family_stats::writes);
|
||||
return get_cf_rate_and_histogram(ctx, req->param["name"], &replica::column_family_stats::writes);
|
||||
});
|
||||
|
||||
cf::get_all_write_latency_histogram_depricated.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -614,7 +543,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_pending_compactions.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](replica::column_family& cf) {
|
||||
return cf.estimate_pending_compactions();
|
||||
}, std::plus<int64_t>());
|
||||
});
|
||||
@@ -626,7 +555,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_live_ss_table_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats(ctx, req->get_path_param("name"), &replica::column_family_stats::live_sstable_count);
|
||||
return get_cf_stats(ctx, req->param["name"], &replica::column_family_stats::live_sstable_count);
|
||||
});
|
||||
|
||||
cf::get_all_live_ss_table_count.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -634,11 +563,11 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_unleveled_sstables.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_unleveled_sstables(ctx, req->get_path_param("name"));
|
||||
return get_cf_unleveled_sstables(ctx, req->param["name"]);
|
||||
});
|
||||
|
||||
cf::get_live_disk_space_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return sum_sstable(ctx, req->get_path_param("name"), false);
|
||||
return sum_sstable(ctx, req->param["name"], false);
|
||||
});
|
||||
|
||||
cf::get_all_live_disk_space_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -646,7 +575,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_total_disk_space_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return sum_sstable(ctx, req->get_path_param("name"), true);
|
||||
return sum_sstable(ctx, req->param["name"], true);
|
||||
});
|
||||
|
||||
cf::get_all_total_disk_space_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
@@ -655,7 +584,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_min_row_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), INT64_MAX, min_partition_size, min_int64);
|
||||
return map_reduce_cf(ctx, req->param["name"], INT64_MAX, min_partition_size, min_int64);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
@@ -665,7 +594,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_max_row_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), int64_t(0), max_partition_size, max_int64);
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), max_partition_size, max_int64);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
@@ -676,7 +605,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_mean_row_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
// Cassandra 3.x mean values are truncated as integrals.
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), integral_ratio_holder(), mean_partition_size, std::plus<integral_ratio_holder>());
|
||||
return map_reduce_cf(ctx, req->param["name"], integral_ratio_holder(), mean_partition_size, std::plus<integral_ratio_holder>());
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
@@ -686,7 +615,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), uint64_t(0), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (replica::column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_get_false_positive();
|
||||
@@ -704,7 +633,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_recent_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), uint64_t(0), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (replica::column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_get_recent_false_positive();
|
||||
@@ -722,7 +651,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_bloom_filter_false_ratio.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), ratio_holder(), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], ratio_holder(), [] (replica::column_family& cf) {
|
||||
return boost::accumulate(*cf.get_sstables() | boost::adaptors::transformed(filter_false_positive_as_ratio_holder), ratio_holder());
|
||||
}, std::plus<>());
|
||||
});
|
||||
@@ -734,7 +663,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_recent_bloom_filter_false_ratio.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), ratio_holder(), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], ratio_holder(), [] (replica::column_family& cf) {
|
||||
return boost::accumulate(*cf.get_sstables() | boost::adaptors::transformed(filter_recent_false_positive_as_ratio_holder), ratio_holder());
|
||||
}, std::plus<>());
|
||||
});
|
||||
@@ -746,7 +675,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_bloom_filter_disk_space_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), uint64_t(0), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (replica::column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_size();
|
||||
@@ -764,7 +693,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_bloom_filter_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), uint64_t(0), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (replica::column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->filter_memory_size();
|
||||
@@ -782,7 +711,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_index_summary_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), uint64_t(0), [] (replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (replica::column_family& cf) {
|
||||
auto sstables = cf.get_sstables();
|
||||
return std::accumulate(sstables->begin(), sstables->end(), uint64_t(0), [](uint64_t s, auto& sst) {
|
||||
return s + sst->get_summary().memory_footprint();
|
||||
@@ -805,7 +734,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
// We are missing the off heap memory calculation
|
||||
// Return 0 is the wrong value. It's a work around
|
||||
// until the memory calculation will be available
|
||||
//auto id = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
//auto id = get_uuid(req->param["name"], ctx.db.local());
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
@@ -818,7 +747,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
cf::get_speculative_retries.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
//auto id = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
//auto id = get_uuid(req->param["name"], ctx.db.local());
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
@@ -831,14 +760,32 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
cf::get_key_cache_hit_rate.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
//auto id = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
//auto id = get_uuid(req->param["name"], ctx.db.local());
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
cf::get_true_snapshots_size.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
auto uuid = get_uuid(req->param["name"], ctx.db.local());
|
||||
return ctx.db.local().find_column_family(uuid).get_snapshot_details().then([](
|
||||
const std::unordered_map<sstring, replica::column_family::snapshot_details>& sd) {
|
||||
int64_t res = 0;
|
||||
for (auto i : sd) {
|
||||
res += i.second.total;
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_all_true_snapshots_size.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
cf::get_row_cache_hit_out_of_range.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
//auto id = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
//auto id = get_uuid(req->param["name"], ctx.db.local());
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
@@ -849,7 +796,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_row_cache_hit.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_raw(ctx, req->get_path_param("name"), utils::rate_moving_average(), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_raw(ctx, req->param["name"], utils::rate_moving_average(), [](const replica::column_family& cf) {
|
||||
return cf.get_row_cache().stats().hits.rate();
|
||||
}, std::plus<utils::rate_moving_average>()).then([](const utils::rate_moving_average& m) {
|
||||
return make_ready_future<json::json_return_type>(meter_to_json(m));
|
||||
@@ -865,7 +812,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_row_cache_miss.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_raw(ctx, req->get_path_param("name"), utils::rate_moving_average(), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_raw(ctx, req->param["name"], utils::rate_moving_average(), [](const replica::column_family& cf) {
|
||||
return cf.get_row_cache().stats().misses.rate();
|
||||
}, std::plus<utils::rate_moving_average>()).then([](const utils::rate_moving_average& m) {
|
||||
return make_ready_future<json::json_return_type>(meter_to_json(m));
|
||||
@@ -882,120 +829,102 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_cas_prepare.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->get_path_param("name"), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->param["name"], [](const replica::column_family& cf) {
|
||||
return cf.get_stats().cas_prepare.histogram();
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_cas_propose.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->get_path_param("name"), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->param["name"], [](const replica::column_family& cf) {
|
||||
return cf.get_stats().cas_accept.histogram();
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_cas_commit.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->get_path_param("name"), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->param["name"], [](const replica::column_family& cf) {
|
||||
return cf.get_stats().cas_learn.histogram();
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_sstables_per_read_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf(ctx, req->get_path_param("name"), utils::estimated_histogram(0), [](replica::column_family& cf) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](replica::column_family& cf) {
|
||||
return cf.get_stats().estimated_sstable_per_read;
|
||||
},
|
||||
utils::estimated_histogram_merge, utils_json::estimated_histogram());
|
||||
});
|
||||
|
||||
cf::get_tombstone_scanned_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_histogram(ctx, req->get_path_param("name"), &replica::column_family_stats::tombstone_scanned);
|
||||
return get_cf_histogram(ctx, req->param["name"], &replica::column_family_stats::tombstone_scanned);
|
||||
});
|
||||
|
||||
cf::get_live_scanned_histogram.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return get_cf_histogram(ctx, req->get_path_param("name"), &replica::column_family_stats::live_scanned);
|
||||
return get_cf_histogram(ctx, req->param["name"], &replica::column_family_stats::live_scanned);
|
||||
});
|
||||
|
||||
cf::get_col_update_time_delta_histogram.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
//auto id = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
//auto id = get_uuid(req->param["name"], ctx.db.local());
|
||||
std::vector<double> res;
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
|
||||
cf::get_auto_compaction.set(r, [&ctx] (const_req req) {
|
||||
auto uuid = get_uuid(req.get_path_param("name"), ctx.db.local());
|
||||
auto uuid = get_uuid(req.param["name"], ctx.db.local());
|
||||
replica::column_family& cf = ctx.db.local().find_column_family(uuid);
|
||||
return !cf.is_auto_compaction_disabled_by_user();
|
||||
});
|
||||
|
||||
cf::enable_auto_compaction.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
apilog.info("column_family/enable_auto_compaction: name={}", req->get_path_param("name"));
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(req->get_path_param("name"));
|
||||
validate_table(ctx, ks, cf);
|
||||
return set_tables_autocompaction(ctx, ks, {std::move(cf)}, true);
|
||||
apilog.info("column_family/enable_auto_compaction: name={}", req->param["name"]);
|
||||
return ctx.db.invoke_on(0, [&ctx, req = std::move(req)] (replica::database& db) {
|
||||
auto g = replica::database::autocompaction_toggle_guard(db);
|
||||
return foreach_column_family(ctx, req->param["name"], [](replica::column_family &cf) {
|
||||
cf.enable_auto_compaction();
|
||||
}).then([g = std::move(g)] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cf::disable_auto_compaction.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
apilog.info("column_family/disable_auto_compaction: name={}", req->get_path_param("name"));
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(req->get_path_param("name"));
|
||||
validate_table(ctx, ks, cf);
|
||||
return set_tables_autocompaction(ctx, ks, {std::move(cf)}, false);
|
||||
});
|
||||
|
||||
ss::enable_auto_compaction.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("enable_auto_compaction: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_autocompaction(ctx, keyspace, tables, true);
|
||||
});
|
||||
|
||||
ss::disable_auto_compaction.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("disable_auto_compaction: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_autocompaction(ctx, keyspace, tables, false);
|
||||
apilog.info("column_family/disable_auto_compaction: name={}", req->param["name"]);
|
||||
return ctx.db.invoke_on(0, [&ctx, req = std::move(req)] (replica::database& db) {
|
||||
auto g = replica::database::autocompaction_toggle_guard(db);
|
||||
return foreach_column_family(ctx, req->param["name"], [](replica::column_family &cf) {
|
||||
return cf.disable_auto_compaction();
|
||||
}).then([g = std::move(g)] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_tombstone_gc.set(r, [&ctx] (const_req req) {
|
||||
auto uuid = get_uuid(req.get_path_param("name"), ctx.db.local());
|
||||
auto uuid = get_uuid(req.param["name"], ctx.db.local());
|
||||
replica::table& t = ctx.db.local().find_column_family(uuid);
|
||||
return t.tombstone_gc_enabled();
|
||||
});
|
||||
|
||||
cf::enable_tombstone_gc.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
apilog.info("column_family/enable_tombstone_gc: name={}", req->get_path_param("name"));
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(req->get_path_param("name"));
|
||||
validate_table(ctx, ks, cf);
|
||||
return set_tables_tombstone_gc(ctx, ks, {std::move(cf)}, true);
|
||||
apilog.info("column_family/enable_tombstone_gc: name={}", req->param["name"]);
|
||||
return foreach_column_family(ctx, req->param["name"], [](replica::table& t) {
|
||||
t.set_tombstone_gc_enabled(true);
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
cf::disable_tombstone_gc.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
apilog.info("column_family/disable_tombstone_gc: name={}", req->get_path_param("name"));
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(req->get_path_param("name"));
|
||||
validate_table(ctx, ks, cf);
|
||||
return set_tables_tombstone_gc(ctx, ks, {std::move(cf)}, false);
|
||||
});
|
||||
|
||||
ss::enable_tombstone_gc.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("enable_tombstone_gc: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_tombstone_gc(ctx, keyspace, tables, true);
|
||||
});
|
||||
|
||||
ss::disable_tombstone_gc.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("disable_tombstone_gc: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_tombstone_gc(ctx, keyspace, tables, false);
|
||||
apilog.info("column_family/disable_tombstone_gc: name={}", req->param["name"]);
|
||||
return foreach_column_family(ctx, req->param["name"], [](replica::table& t) {
|
||||
t.set_tombstone_gc_enabled(false);
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_built_indexes.set(r, [&ctx, &sys_ks](std::unique_ptr<http::request> req) {
|
||||
auto ks_cf = parse_fully_qualified_cf_name(req->get_path_param("name"));
|
||||
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 sys_ks.local().load_view_build_progress().then([ks, cf_name, &ctx](const std::vector<db::system_keyspace_view_build_progress>& vb) mutable {
|
||||
@@ -1033,7 +962,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_compression_ratio.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto uuid = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
auto uuid = get_uuid(req->param["name"], ctx.db.local());
|
||||
|
||||
return ctx.db.map_reduce(sum_ratio<double>(), [uuid](replica::database& db) {
|
||||
replica::column_family& cf = db.find_column_family(uuid);
|
||||
@@ -1044,21 +973,21 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_read_latency_estimated_histogram.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->get_path_param("name"), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->param["name"], [](const replica::column_family& cf) {
|
||||
return cf.get_stats().reads.histogram();
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_write_latency_estimated_histogram.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->get_path_param("name"), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_time_histogram(ctx, req->param["name"], [](const replica::column_family& cf) {
|
||||
return cf.get_stats().writes.histogram();
|
||||
});
|
||||
});
|
||||
|
||||
cf::set_compaction_strategy_class.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
sstring strategy = req->get_query_param("class_name");
|
||||
apilog.info("column_family/set_compaction_strategy_class: name={} strategy={}", req->get_path_param("name"), strategy);
|
||||
return foreach_column_family(ctx, req->get_path_param("name"), [strategy](replica::column_family& cf) {
|
||||
apilog.info("column_family/set_compaction_strategy_class: name={} strategy={}", req->param["name"], strategy);
|
||||
return foreach_column_family(ctx, req->param["name"], [strategy](replica::column_family& cf) {
|
||||
cf.set_compaction_strategy(sstables::compaction_strategy::type(strategy));
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
@@ -1066,7 +995,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_compaction_strategy_class.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().find_column_family(get_uuid(req.get_path_param("name"), ctx.db.local())).get_compaction_strategy().name();
|
||||
return ctx.db.local().find_column_family(get_uuid(req.param["name"], ctx.db.local())).get_compaction_strategy().name();
|
||||
});
|
||||
|
||||
cf::set_compression_parameters.set(r, [](std::unique_ptr<http::request> req) {
|
||||
@@ -1082,7 +1011,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
});
|
||||
|
||||
cf::get_sstable_count_per_level.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
return map_reduce_cf_raw(ctx, req->get_path_param("name"), std::vector<uint64_t>(), [](const replica::column_family& cf) {
|
||||
return map_reduce_cf_raw(ctx, req->param["name"], std::vector<uint64_t>(), [](const replica::column_family& cf) {
|
||||
return cf.sstable_count_per_level();
|
||||
}, concat_sstable_count_per_level).then([](const std::vector<uint64_t>& res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
@@ -1091,7 +1020,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
cf::get_sstables_for_key.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto key = req->get_query_param("key");
|
||||
auto uuid = get_uuid(req->get_path_param("name"), ctx.db.local());
|
||||
auto uuid = get_uuid(req->param["name"], ctx.db.local());
|
||||
|
||||
return ctx.db.map_reduce0([key, uuid] (replica::database& db) -> future<std::unordered_set<sstring>> {
|
||||
auto sstables = co_await db.find_column_family(uuid).get_sstables_by_partition_key(key);
|
||||
@@ -1107,7 +1036,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
|
||||
cf::toppartitions.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
auto name = req->get_path_param("name");
|
||||
auto name = req->param["name"];
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(name);
|
||||
|
||||
api::req_param<std::chrono::milliseconds, unsigned> duration{*req, "duration", 1000ms};
|
||||
@@ -1126,7 +1055,6 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
auto params = req_params({
|
||||
std::pair("name", mandatory::yes),
|
||||
std::pair("flush_memtables", mandatory::no),
|
||||
std::pair("consider_only_existing_data", mandatory::no),
|
||||
std::pair("split_output", mandatory::no),
|
||||
});
|
||||
params.process(*req);
|
||||
@@ -1135,8 +1063,7 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
}
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(*params.get("name"));
|
||||
auto flush = params.get_as<bool>("flush_memtables").value_or(true);
|
||||
auto consider_only_existing_data = params.get_as<bool>("consider_only_existing_data").value_or(false);
|
||||
apilog.info("column_family/force_major_compaction: name={} flush={} consider_only_existing_data={}", req->get_path_param("name"), flush, consider_only_existing_data);
|
||||
apilog.info("column_family/force_major_compaction: name={} flush={}", req->param["name"], flush);
|
||||
|
||||
auto keyspace = validate_keyspace(ctx, ks);
|
||||
std::vector<table_info> table_infos = {table_info{
|
||||
@@ -1146,10 +1073,10 @@ void set_column_family(http_context& ctx, routes& r, sharded<db::system_keyspace
|
||||
|
||||
auto& compaction_module = ctx.db.local().get_compaction_manager().get_task_manager_module();
|
||||
std::optional<flush_mode> fmopt;
|
||||
if (!flush && !consider_only_existing_data) {
|
||||
if (!flush) {
|
||||
fmopt = flush_mode::skip;
|
||||
}
|
||||
auto task = co_await compaction_module.make_and_start_task<major_keyspace_compaction_task_impl>({}, std::move(keyspace), tasks::task_id::create_null_id(), ctx.db, std::move(table_infos), fmopt, consider_only_existing_data);
|
||||
auto task = co_await compaction_module.make_and_start_task<major_keyspace_compaction_task_impl>({}, std::move(keyspace), tasks::task_id::create_null_id(), ctx.db, std::move(table_infos), fmopt);
|
||||
co_await task->done();
|
||||
co_return json_void();
|
||||
});
|
||||
@@ -1229,6 +1156,8 @@ void unset_column_family(http_context& ctx, routes& r) {
|
||||
cf::get_speculative_retries.unset(r);
|
||||
cf::get_all_speculative_retries.unset(r);
|
||||
cf::get_key_cache_hit_rate.unset(r);
|
||||
cf::get_true_snapshots_size.unset(r);
|
||||
cf::get_all_true_snapshots_size.unset(r);
|
||||
cf::get_row_cache_hit_out_of_range.unset(r);
|
||||
cf::get_all_row_cache_hit_out_of_range.unset(r);
|
||||
cf::get_row_cache_hit.unset(r);
|
||||
@@ -1245,13 +1174,6 @@ void unset_column_family(http_context& ctx, routes& r) {
|
||||
cf::get_auto_compaction.unset(r);
|
||||
cf::enable_auto_compaction.unset(r);
|
||||
cf::disable_auto_compaction.unset(r);
|
||||
ss::enable_auto_compaction.unset(r);
|
||||
ss::disable_auto_compaction.unset(r);
|
||||
cf::get_tombstone_gc.unset(r);
|
||||
cf::enable_tombstone_gc.unset(r);
|
||||
cf::disable_tombstone_gc.unset(r);
|
||||
ss::enable_tombstone_gc.unset(r);
|
||||
ss::disable_tombstone_gc.unset(r);
|
||||
cf::get_built_indexes.unset(r);
|
||||
cf::get_compression_metadata_off_heap_memory_used.unset(r);
|
||||
cf::get_compression_parameters.unset(r);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "replica/database.hh"
|
||||
#include <seastar/core/future-util.hh>
|
||||
#include <seastar/json/json_elements.hh>
|
||||
#include <any>
|
||||
#include "api/api_init.hh"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <seastar/coroutine/exception.hh>
|
||||
|
||||
#include "compaction_manager.hh"
|
||||
#include "compaction/compaction_manager.hh"
|
||||
@@ -111,7 +110,7 @@ void set_compaction_manager(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
cm::stop_keyspace_compaction.set(r, [&ctx] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto ks_name = validate_keyspace(ctx, req);
|
||||
auto ks_name = validate_keyspace(ctx, req->param);
|
||||
auto table_names = parse_tables(ks_name, ctx, req->query_parameters, "tables");
|
||||
if (table_names.empty()) {
|
||||
table_names = map_keys(ctx.db.local().find_keyspace(ks_name).metadata().get()->cf_meta_data());
|
||||
@@ -154,13 +153,10 @@ void set_compaction_manager(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
cm::get_compaction_history.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
std::function<future<>(output_stream<char>&&)> f = [&ctx] (output_stream<char>&& out) -> future<> {
|
||||
auto s = std::move(out);
|
||||
bool first = true;
|
||||
std::exception_ptr ex;
|
||||
try {
|
||||
co_await s.write("[");
|
||||
co_await ctx.db.local().get_compaction_manager().get_compaction_history([&s, &first](const db::compaction_history_entry& entry) mutable -> future<> {
|
||||
std::function<future<>(output_stream<char>&&)> f = [&ctx](output_stream<char>&& s) {
|
||||
return do_with(output_stream<char>(std::move(s)), true, [&ctx] (output_stream<char>& s, bool& first){
|
||||
return s.write("[").then([&ctx, &s, &first] {
|
||||
return ctx.db.local().get_compaction_manager().get_compaction_history([&s, &first](const db::compaction_history_entry& entry) mutable {
|
||||
cm::history h;
|
||||
h.id = fmt::to_string(entry.id);
|
||||
h.ks = std::move(entry.ks);
|
||||
@@ -174,21 +170,18 @@ void set_compaction_manager(http_context& ctx, routes& r) {
|
||||
e.value = it.second;
|
||||
h.rows_merged.push(std::move(e));
|
||||
}
|
||||
if (!first) {
|
||||
co_await s.write(", ");
|
||||
}
|
||||
auto fut = first ? make_ready_future<>() : s.write(", ");
|
||||
first = false;
|
||||
co_await formatter::write(s, h);
|
||||
return fut.then([&s, h = std::move(h)] {
|
||||
return formatter::write(s, h);
|
||||
});
|
||||
}).then([&s] {
|
||||
return s.write("]").then([&s] {
|
||||
return s.close();
|
||||
});
|
||||
});
|
||||
co_await s.write("]");
|
||||
co_await s.flush();
|
||||
} catch (...) {
|
||||
ex = std::current_exception();
|
||||
}
|
||||
co_await s.close();
|
||||
if (ex) {
|
||||
co_await coroutine::return_exception_ptr(std::move(ex));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
return make_ready_future<json::json_return_type>(std::move(f));
|
||||
});
|
||||
|
||||
114
api/config.cc
114
api/config.cc
@@ -6,12 +6,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "api/api.hh"
|
||||
#include "api/config.hh"
|
||||
#include "api/api-doc/config.json.hh"
|
||||
#include "api/api-doc/storage_proxy.json.hh"
|
||||
#include "api/api-doc/storage_service.json.hh"
|
||||
#include "replica/database.hh"
|
||||
#include "db/config.hh"
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
@@ -19,8 +15,6 @@
|
||||
|
||||
namespace api {
|
||||
using namespace seastar::httpd;
|
||||
namespace sp = httpd::storage_proxy_json;
|
||||
namespace ss = httpd::storage_service_json;
|
||||
|
||||
template<class T>
|
||||
json::json_return_type get_json_return_type(const T& val) {
|
||||
@@ -98,7 +92,7 @@ void set_config(std::shared_ptr < api_registry_builder20 > rb, http_context& ctx
|
||||
});
|
||||
|
||||
cs::find_config_id.set(r, [&cfg] (const_req r) {
|
||||
auto id = r.get_path_param("id");
|
||||
auto id = r.param["id"];
|
||||
for (auto&& cfg_ref : cfg.values()) {
|
||||
auto&& cfg = cfg_ref.get();
|
||||
if (id == cfg.name()) {
|
||||
@@ -107,112 +101,6 @@ void set_config(std::shared_ptr < api_registry_builder20 > rb, http_context& ctx
|
||||
}
|
||||
throw bad_param_exception(sstring("No such config entry: ") + id);
|
||||
});
|
||||
|
||||
sp::get_rpc_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
sp::get_read_rpc_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.read_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_read_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
sp::get_write_rpc_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.write_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_write_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
sp::get_counter_write_rpc_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.counter_write_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_counter_write_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
sp::get_cas_contention_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.cas_contention_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_cas_contention_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
sp::get_range_rpc_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.range_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_range_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
sp::get_truncate_rpc_timeout.set(r, [&cfg](const_req req) {
|
||||
return cfg.truncate_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_truncate_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(seastar::json::json_void());
|
||||
});
|
||||
|
||||
ss::get_all_data_file_locations.set(r, [&cfg](const_req req) {
|
||||
return container_to_vec(cfg.data_file_directories());
|
||||
});
|
||||
|
||||
ss::get_saved_caches_location.set(r, [&cfg](const_req req) {
|
||||
return cfg.saved_caches_directory();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void unset_config(http_context& ctx, routes& r) {
|
||||
cs::find_config_id.unset(r);
|
||||
sp::get_rpc_timeout.unset(r);
|
||||
sp::set_rpc_timeout.unset(r);
|
||||
sp::get_read_rpc_timeout.unset(r);
|
||||
sp::set_read_rpc_timeout.unset(r);
|
||||
sp::get_write_rpc_timeout.unset(r);
|
||||
sp::set_write_rpc_timeout.unset(r);
|
||||
sp::get_counter_write_rpc_timeout.unset(r);
|
||||
sp::set_counter_write_rpc_timeout.unset(r);
|
||||
sp::get_cas_contention_timeout.unset(r);
|
||||
sp::set_cas_contention_timeout.unset(r);
|
||||
sp::get_range_rpc_timeout.unset(r);
|
||||
sp::set_range_rpc_timeout.unset(r);
|
||||
sp::get_truncate_rpc_timeout.unset(r);
|
||||
sp::set_truncate_rpc_timeout.unset(r);
|
||||
ss::get_all_data_file_locations.unset(r);
|
||||
ss::get_saved_caches_location.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,5 +14,4 @@
|
||||
namespace api {
|
||||
|
||||
void set_config(std::shared_ptr<httpd::api_registry_builder20> rb, http_context& ctx, httpd::routes& r, const db::config& cfg, bool first = false);
|
||||
void unset_config(http_context& ctx, httpd::routes& r);
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SCYLLA_BUILD_MODE_RELEASE
|
||||
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <boost/range/algorithm/transform.hpp>
|
||||
|
||||
#include "api/api-doc/cql_server_test.json.hh"
|
||||
#include "cql_server_test.hh"
|
||||
#include "transport/controller.hh"
|
||||
#include "transport/server.hh"
|
||||
#include "service/qos/qos_common.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
namespace cst = httpd::cql_server_test_json;
|
||||
using namespace json;
|
||||
using namespace seastar::httpd;
|
||||
|
||||
struct connection_sl_params : public json::json_base {
|
||||
json::json_element<sstring> _role_name;
|
||||
json::json_element<sstring> _workload_type;
|
||||
json::json_element<sstring> _timeout;
|
||||
|
||||
connection_sl_params(const sstring& role_name, const sstring& workload_type, const sstring& timeout) {
|
||||
_role_name = role_name;
|
||||
_workload_type = workload_type;
|
||||
_timeout = timeout;
|
||||
register_params();
|
||||
}
|
||||
|
||||
connection_sl_params(const connection_sl_params& params)
|
||||
: connection_sl_params(params._role_name(), params._workload_type(), params._timeout()) {}
|
||||
|
||||
void register_params() {
|
||||
add(&_role_name, "role_name");
|
||||
add(&_workload_type, "workload_type");
|
||||
add(&_timeout, "timeout");
|
||||
}
|
||||
};
|
||||
|
||||
void set_cql_server_test(http_context& ctx, seastar::httpd::routes& r, cql_transport::controller& ctl) {
|
||||
cst::connections_params.set(r, [&ctl] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto sl_params = co_await ctl.get_connections_service_level_params();
|
||||
|
||||
std::vector<connection_sl_params> result;
|
||||
boost::transform(std::move(sl_params), std::back_inserter(result), [] (const cql_transport::connection_service_level_params& params) {
|
||||
auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(params.timeout_config.read_timeout).count();
|
||||
return connection_sl_params(
|
||||
std::move(params.role_name),
|
||||
sstring(qos::service_level_options::to_string(params.workload_type)),
|
||||
to_string(cql_duration(months_counter{0}, days_counter{0}, nanoseconds_counter{nanos})));
|
||||
});
|
||||
co_return result;
|
||||
});
|
||||
}
|
||||
|
||||
void unset_cql_server_test(http_context& ctx, seastar::httpd::routes& r) {
|
||||
cst::connections_params.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SCYLLA_BUILD_MODE_RELEASE
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cql_transport {
|
||||
class controller;
|
||||
}
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
|
||||
namespace api {
|
||||
struct http_context;
|
||||
|
||||
void set_cql_server_test(http_context& ctx, seastar::httpd::routes& r, cql_transport::controller& ctl);
|
||||
void unset_cql_server_test(http_context& ctx, seastar::httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,8 +7,10 @@
|
||||
*/
|
||||
|
||||
#include "api/api-doc/error_injection.json.hh"
|
||||
#include "api/api_init.hh"
|
||||
#include "api/api.hh"
|
||||
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "log.hh"
|
||||
#include "utils/error_injection.hh"
|
||||
#include "utils/rjson.hh"
|
||||
#include <seastar/core/future-util.hh>
|
||||
@@ -22,7 +24,7 @@ namespace hf = httpd::error_injection_json;
|
||||
void set_error_injection(http_context& ctx, routes& r) {
|
||||
|
||||
hf::enable_injection.set(r, [](std::unique_ptr<request> req) {
|
||||
sstring injection = req->get_path_param("injection");
|
||||
sstring injection = req->param["injection"];
|
||||
bool one_shot = req->get_query_param("one_shot") == "True";
|
||||
auto params = req->content;
|
||||
|
||||
@@ -54,7 +56,7 @@ void set_error_injection(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
hf::disable_injection.set(r, [](std::unique_ptr<request> req) {
|
||||
sstring injection = req->get_path_param("injection");
|
||||
sstring injection = req->param["injection"];
|
||||
|
||||
auto& errinj = utils::get_local_injector();
|
||||
return errinj.disable_on_all(injection).then([] {
|
||||
@@ -62,32 +64,6 @@ void set_error_injection(http_context& ctx, routes& r) {
|
||||
});
|
||||
});
|
||||
|
||||
hf::read_injection.set(r, [](std::unique_ptr<request> req) -> future<json::json_return_type> {
|
||||
const sstring injection = req->get_path_param("injection");
|
||||
|
||||
std::vector<error_injection_json::error_injection_info> error_injection_infos(smp::count, error_injection_json::error_injection_info{});
|
||||
|
||||
co_await smp::invoke_on_all([&] {
|
||||
auto& info = error_injection_infos[this_shard_id()];
|
||||
auto& errinj = utils::get_local_injector();
|
||||
const auto enabled = errinj.is_enabled(injection);
|
||||
info.enabled = enabled;
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
std::vector<error_injection_json::mapper> parameters;
|
||||
for (const auto& p : errinj.get_injection_parameters(injection)) {
|
||||
error_injection_json::mapper param;
|
||||
param.key = p.first;
|
||||
param.value = p.second;
|
||||
parameters.push_back(std::move(param));
|
||||
}
|
||||
info.parameters = std::move(parameters);
|
||||
});
|
||||
|
||||
co_return json::json_return_type(error_injection_infos);
|
||||
});
|
||||
|
||||
hf::disable_on_all.set(r, [](std::unique_ptr<request> req) {
|
||||
auto& errinj = utils::get_local_injector();
|
||||
return errinj.disable_on_all().then([] {
|
||||
@@ -96,7 +72,7 @@ void set_error_injection(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
hf::message_injection.set(r, [](std::unique_ptr<request> req) {
|
||||
sstring injection = req->get_path_param("injection");
|
||||
sstring injection = req->param["injection"];
|
||||
auto& errinj = utils::get_local_injector();
|
||||
return errinj.receive_message_on_all(injection).then([] {
|
||||
return make_ready_future<json::json_return_type>(json::json_void());
|
||||
|
||||
@@ -66,7 +66,7 @@ void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
return g.container().invoke_on(0, [] (gms::gossiper& g) {
|
||||
std::map<sstring, sstring> nodes_status;
|
||||
g.for_each_endpoint_state([&] (const gms::inet_address& node, const gms::endpoint_state&) {
|
||||
nodes_status.emplace(fmt::to_string(node), g.is_alive(node) ? "UP" : "DOWN");
|
||||
nodes_status.emplace(node.to_sstring(), g.is_alive(node) ? "UP" : "DOWN");
|
||||
});
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value<fd::mapper>(nodes_status));
|
||||
});
|
||||
@@ -81,9 +81,9 @@ 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 g.container().invoke_on(0, [req = std::move(req)] (gms::gossiper& g) {
|
||||
auto state = g.get_endpoint_state_ptr(gms::inet_address(req->get_path_param("addr")));
|
||||
auto state = g.get_endpoint_state_ptr(gms::inet_address(req->param["addr"]));
|
||||
if (!state) {
|
||||
return make_ready_future<json::json_return_type>(format("unknown endpoint {}", req->get_path_param("addr")));
|
||||
return make_ready_future<json::json_return_type>(format("unknown endpoint {}", req->param["addr"]));
|
||||
}
|
||||
std::stringstream ss;
|
||||
g.append_endpoint_state(ss, *state);
|
||||
@@ -99,16 +99,5 @@ void set_failure_detector(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
});
|
||||
}
|
||||
|
||||
void unset_failure_detector(http_context& ctx, routes& r) {
|
||||
fd::get_all_endpoint_states.unset(r);
|
||||
fd::get_up_endpoint_count.unset(r);
|
||||
fd::get_down_endpoint_count.unset(r);
|
||||
fd::get_phi_convict_threshold.unset(r);
|
||||
fd::get_simple_states.unset(r);
|
||||
fd::set_phi_convict_threshold.unset(r);
|
||||
fd::get_endpoint_state.unset(r);
|
||||
fd::get_endpoint_phi_values.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,5 @@ class gossiper;
|
||||
namespace api {
|
||||
|
||||
void set_failure_detector(http_context& ctx, httpd::routes& r, gms::gossiper& g);
|
||||
void unset_failure_detector(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -32,21 +32,21 @@ void set_gossiper(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_endpoint_downtime.set(r, [&g] (std::unique_ptr<request> req) -> future<json::json_return_type> {
|
||||
gms::inet_address ep(req->get_path_param("addr"));
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
// synchronize unreachable_members on all shards
|
||||
co_await g.get_unreachable_members_synchronized();
|
||||
co_return g.get_endpoint_downtime(ep);
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_current_generation_number.set(r, [&g] (std::unique_ptr<http::request> req) {
|
||||
gms::inet_address ep(req->get_path_param("addr"));
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
return g.get_current_generation_number(ep).then([] (gms::generation_type res) {
|
||||
return make_ready_future<json::json_return_type>(res.value());
|
||||
});
|
||||
});
|
||||
|
||||
httpd::gossiper_json::get_current_heart_beat_version.set(r, [&g] (std::unique_ptr<http::request> req) {
|
||||
gms::inet_address ep(req->get_path_param("addr"));
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
return g.get_current_heart_beat_version(ep).then([] (gms::version_type res) {
|
||||
return make_ready_future<json::json_return_type>(res.value());
|
||||
});
|
||||
@@ -54,31 +54,21 @@ void set_gossiper(http_context& ctx, routes& r, gms::gossiper& g) {
|
||||
|
||||
httpd::gossiper_json::assassinate_endpoint.set(r, [&g](std::unique_ptr<http::request> req) {
|
||||
if (req->get_query_param("unsafe") != "True") {
|
||||
return g.assassinate_endpoint(req->get_path_param("addr")).then([] {
|
||||
return g.assassinate_endpoint(req->param["addr"]).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
}
|
||||
return g.unsafe_assassinate_endpoint(req->get_path_param("addr")).then([] {
|
||||
return g.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<http::request> req) {
|
||||
gms::inet_address ep(req->get_path_param("addr"));
|
||||
gms::inet_address ep(req->param["addr"]);
|
||||
return g.force_remove_endpoint(ep, gms::null_permit_id).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void unset_gossiper(http_context& ctx, routes& r) {
|
||||
httpd::gossiper_json::get_down_endpoint.unset(r);
|
||||
httpd::gossiper_json::get_live_endpoint.unset(r);
|
||||
httpd::gossiper_json::get_endpoint_downtime.unset(r);
|
||||
httpd::gossiper_json::get_current_generation_number.unset(r);
|
||||
httpd::gossiper_json::get_current_heart_beat_version.unset(r);
|
||||
httpd::gossiper_json::assassinate_endpoint.unset(r);
|
||||
httpd::gossiper_json::force_remove_endpoint.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,5 @@ class gossiper;
|
||||
namespace api {
|
||||
|
||||
void set_gossiper(http_context& ctx, httpd::routes& r, gms::gossiper& g);
|
||||
void unset_gossiper(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ void set_messaging_service(http_context& ctx, routes& r, sharded<netw::messaging
|
||||
});
|
||||
|
||||
hf::inject_disconnect.set(r, [&ms] (std::unique_ptr<request> req) -> future<json::json_return_type> {
|
||||
auto ip = msg_addr(req->get_path_param("ip"));
|
||||
auto ip = msg_addr(req->param["ip"]);
|
||||
co_await ms.invoke_on_all([ip] (netw::messaging_service& ms) {
|
||||
ms.remove_rpc_client(ip);
|
||||
});
|
||||
|
||||
108
api/raft.cc
108
api/raft.cc
@@ -8,10 +8,10 @@
|
||||
|
||||
#include <seastar/core/coroutine.hh>
|
||||
|
||||
#include "api/api.hh"
|
||||
#include "api/api-doc/raft.json.hh"
|
||||
|
||||
#include "service/raft/raft_group_registry.hh"
|
||||
#include "log.hh"
|
||||
|
||||
using namespace seastar::httpd;
|
||||
|
||||
@@ -19,44 +19,34 @@ extern logging::logger apilog;
|
||||
|
||||
namespace api {
|
||||
|
||||
struct http_context;
|
||||
namespace r = httpd::raft_json;
|
||||
using namespace json;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
::service::raft_timeout get_request_timeout(const http::request& req) {
|
||||
return std::invoke([timeout_str = req.get_query_param("timeout")] {
|
||||
if (timeout_str.empty()) {
|
||||
return ::service::raft_timeout{};
|
||||
}
|
||||
auto dur = std::stoll(timeout_str);
|
||||
if (dur <= 0) {
|
||||
throw bad_param_exception{"Timeout must be a positive number."};
|
||||
}
|
||||
return ::service::raft_timeout{.value = lowres_clock::now() + std::chrono::seconds{dur}};
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void set_raft(http_context&, httpd::routes& r, sharded<service::raft_group_registry>& raft_gr) {
|
||||
r::trigger_snapshot.set(r, [&raft_gr] (std::unique_ptr<http::request> req) -> future<json_return_type> {
|
||||
raft::group_id gid{utils::UUID{req->get_path_param("group_id")}};
|
||||
auto timeout = get_request_timeout(*req);
|
||||
raft::group_id gid{utils::UUID{req->param["group_id"]}};
|
||||
auto timeout_dur = std::invoke([timeout_str = req->get_query_param("timeout")] {
|
||||
if (timeout_str.empty()) {
|
||||
return std::chrono::seconds{60};
|
||||
}
|
||||
auto dur = std::stoll(timeout_str);
|
||||
if (dur <= 0) {
|
||||
throw std::runtime_error{"Timeout must be a positive number."};
|
||||
}
|
||||
return std::chrono::seconds{dur};
|
||||
});
|
||||
|
||||
std::atomic<bool> found_srv{false};
|
||||
co_await raft_gr.invoke_on_all([gid, timeout, &found_srv] (service::raft_group_registry& raft_gr) -> future<> {
|
||||
if (!raft_gr.find_server(gid)) {
|
||||
co_await raft_gr.invoke_on_all([gid, timeout_dur, &found_srv] (service::raft_group_registry& raft_gr) -> future<> {
|
||||
auto* srv = raft_gr.find_server(gid);
|
||||
if (!srv) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
found_srv = true;
|
||||
abort_on_expiry aoe(lowres_clock::now() + timeout_dur);
|
||||
apilog.info("Triggering Raft group {} snapshot", gid);
|
||||
auto srv = raft_gr.get_server_with_timeouts(gid);
|
||||
auto result = co_await srv.trigger_snapshot(nullptr, timeout);
|
||||
auto result = co_await srv->trigger_snapshot(&aoe.abort_source());
|
||||
if (result) {
|
||||
apilog.info("New snapshot for Raft group {} created", gid);
|
||||
} else {
|
||||
@@ -65,72 +55,30 @@ void set_raft(http_context&, httpd::routes& r, sharded<service::raft_group_regis
|
||||
});
|
||||
|
||||
if (!found_srv) {
|
||||
throw bad_param_exception{fmt::format("Server for group ID {} not found", gid)};
|
||||
throw std::runtime_error{fmt::format("Server for group ID {} not found", gid)};
|
||||
}
|
||||
|
||||
co_return json_void{};
|
||||
});
|
||||
r::get_leader_host.set(r, [&raft_gr] (std::unique_ptr<http::request> req) -> future<json_return_type> {
|
||||
if (!req->query_parameters.contains("group_id")) {
|
||||
const auto leader_id = co_await raft_gr.invoke_on(0, [] (service::raft_group_registry& raft_gr) {
|
||||
auto& srv = raft_gr.group0();
|
||||
return srv.current_leader();
|
||||
return smp::submit_to(0, [&] {
|
||||
auto& srv = std::invoke([&] () -> raft::server& {
|
||||
if (req->query_parameters.contains("group_id")) {
|
||||
raft::group_id id{utils::UUID{req->get_query_param("group_id")}};
|
||||
return raft_gr.local().get_server(id);
|
||||
} else {
|
||||
return raft_gr.local().group0();
|
||||
}
|
||||
});
|
||||
co_return json_return_type{leader_id.to_sstring()};
|
||||
}
|
||||
|
||||
const raft::group_id gid{utils::UUID{req->get_query_param("group_id")}};
|
||||
|
||||
std::atomic<bool> found_srv{false};
|
||||
std::atomic<raft::server_id> leader_id = raft::server_id::create_null_id();
|
||||
co_await raft_gr.invoke_on_all([gid, &found_srv, &leader_id] (service::raft_group_registry& raft_gr) {
|
||||
if (raft_gr.find_server(gid)) {
|
||||
found_srv = true;
|
||||
leader_id = raft_gr.get_server(gid).current_leader();
|
||||
}
|
||||
return make_ready_future<>();
|
||||
return json_return_type(srv.current_leader().to_sstring());
|
||||
});
|
||||
|
||||
if (!found_srv) {
|
||||
throw bad_param_exception{fmt::format("Server for group ID {} not found", gid)};
|
||||
}
|
||||
|
||||
co_return json_return_type(leader_id.load().to_sstring());
|
||||
});
|
||||
r::read_barrier.set(r, [&raft_gr] (std::unique_ptr<http::request> req) -> future<json_return_type> {
|
||||
auto timeout = get_request_timeout(*req);
|
||||
|
||||
if (!req->query_parameters.contains("group_id")) {
|
||||
// Read barrier on group 0 by default
|
||||
co_await raft_gr.invoke_on(0, [timeout] (service::raft_group_registry& raft_gr) -> future<> {
|
||||
co_await raft_gr.group0_with_timeouts().read_barrier(nullptr, timeout);
|
||||
});
|
||||
co_return json_void{};
|
||||
}
|
||||
|
||||
raft::group_id gid{utils::UUID{req->get_query_param("group_id")}};
|
||||
|
||||
std::atomic<bool> found_srv{false};
|
||||
co_await raft_gr.invoke_on_all([gid, timeout, &found_srv] (service::raft_group_registry& raft_gr) -> future<> {
|
||||
if (!raft_gr.find_server(gid)) {
|
||||
co_return;
|
||||
}
|
||||
found_srv = true;
|
||||
co_await raft_gr.get_server_with_timeouts(gid).read_barrier(nullptr, timeout);
|
||||
});
|
||||
|
||||
if (!found_srv) {
|
||||
throw bad_param_exception{fmt::format("Server for group ID {} not found", gid)};
|
||||
}
|
||||
|
||||
co_return json_void{};
|
||||
});
|
||||
}
|
||||
|
||||
void unset_raft(http_context&, httpd::routes& r) {
|
||||
r::trigger_snapshot.unset(r);
|
||||
r::get_leader_host.unset(r);
|
||||
r::read_barrier.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace api {
|
||||
|
||||
enum class scrub_status {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "api/api-doc/utils.json.hh"
|
||||
#include "db/config.hh"
|
||||
#include "utils/histogram.hh"
|
||||
#include "replica/database.hh"
|
||||
#include <seastar/core/scheduling_specific.hh>
|
||||
|
||||
namespace api {
|
||||
@@ -258,6 +259,83 @@ void set_storage_proxy(http_context& ctx, routes& r, sharded<service::storage_pr
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
sp::get_rpc_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::get_read_rpc_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().read_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_read_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::get_write_rpc_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().write_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_write_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::get_counter_write_rpc_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().counter_write_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_counter_write_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::get_cas_contention_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().cas_contention_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_cas_contention_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::get_range_rpc_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().range_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_range_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::get_truncate_rpc_timeout.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().truncate_request_timeout_in_ms()/1000.0;
|
||||
});
|
||||
|
||||
sp::set_truncate_rpc_timeout.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto enable = req->get_query_param("timeout");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
sp::reload_trigger_classes.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
@@ -438,6 +516,20 @@ void unset_storage_proxy(http_context& ctx, routes& r) {
|
||||
sp::get_max_hints_in_progress.unset(r);
|
||||
sp::set_max_hints_in_progress.unset(r);
|
||||
sp::get_hints_in_progress.unset(r);
|
||||
sp::get_rpc_timeout.unset(r);
|
||||
sp::set_rpc_timeout.unset(r);
|
||||
sp::get_read_rpc_timeout.unset(r);
|
||||
sp::set_read_rpc_timeout.unset(r);
|
||||
sp::get_write_rpc_timeout.unset(r);
|
||||
sp::set_write_rpc_timeout.unset(r);
|
||||
sp::get_counter_write_rpc_timeout.unset(r);
|
||||
sp::set_counter_write_rpc_timeout.unset(r);
|
||||
sp::get_cas_contention_timeout.unset(r);
|
||||
sp::set_cas_contention_timeout.unset(r);
|
||||
sp::get_range_rpc_timeout.unset(r);
|
||||
sp::set_range_rpc_timeout.unset(r);
|
||||
sp::get_truncate_rpc_timeout.unset(r);
|
||||
sp::set_truncate_rpc_timeout.unset(r);
|
||||
sp::reload_trigger_classes.unset(r);
|
||||
sp::get_read_repair_attempted.unset(r);
|
||||
sp::get_read_repair_repaired_blocking.unset(r);
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <boost/algorithm/string/trim_all.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <fmt/ranges.h>
|
||||
#include "service/raft/raft_group0_client.hh"
|
||||
#include "service/storage_service.hh"
|
||||
#include "service/load_meter.hh"
|
||||
@@ -36,7 +35,6 @@
|
||||
#include <seastar/http/exception.hh>
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <seastar/coroutine/parallel_for_each.hh>
|
||||
#include <seastar/coroutine/exception.hh>
|
||||
#include "repair/row_level.hh"
|
||||
#include "locator/snitch_base.hh"
|
||||
#include "column_family.hh"
|
||||
@@ -49,12 +47,12 @@
|
||||
#include "db/extensions.hh"
|
||||
#include "db/snapshot-ctl.hh"
|
||||
#include "transport/controller.hh"
|
||||
#include "thrift/controller.hh"
|
||||
#include "locator/token_metadata.hh"
|
||||
#include "cdc/generation_service.hh"
|
||||
#include "locator/abstract_replication_strategy.hh"
|
||||
#include "sstables_loader.hh"
|
||||
#include "db/view/view_builder.hh"
|
||||
#include "utils/user_provided_param.hh"
|
||||
|
||||
using namespace seastar::httpd;
|
||||
using namespace std::chrono_literals;
|
||||
@@ -65,7 +63,6 @@ namespace api {
|
||||
|
||||
namespace ss = httpd::storage_service_json;
|
||||
namespace sp = httpd::storage_proxy_json;
|
||||
namespace cf = httpd::column_family_json;
|
||||
using namespace json;
|
||||
|
||||
sstring validate_keyspace(const http_context& ctx, sstring ks_name) {
|
||||
@@ -75,15 +72,11 @@ sstring validate_keyspace(const http_context& ctx, sstring ks_name) {
|
||||
throw bad_param_exception(replica::no_such_keyspace(ks_name).what());
|
||||
}
|
||||
|
||||
sstring validate_keyspace(const http_context& ctx, const std::unique_ptr<http::request>& req) {
|
||||
return validate_keyspace(ctx, req->get_path_param("keyspace"));
|
||||
sstring validate_keyspace(const http_context& ctx, const parameters& param) {
|
||||
return validate_keyspace(ctx, param["keyspace"]);
|
||||
}
|
||||
|
||||
sstring validate_keyspace(const http_context& ctx, const http::request& req) {
|
||||
return validate_keyspace(ctx, req.get_path_param("keyspace"));
|
||||
}
|
||||
|
||||
void validate_table(const http_context& ctx, sstring ks_name, sstring table_name) {
|
||||
static void validate_table(const http_context& ctx, sstring ks_name, sstring table_name) {
|
||||
auto& db = ctx.db.local();
|
||||
try {
|
||||
db.find_column_family(ks_name, table_name);
|
||||
@@ -206,13 +199,14 @@ using ks_cf_func = std::function<future<json::json_return_type>(http_context&, s
|
||||
|
||||
static auto wrap_ks_cf(http_context &ctx, ks_cf_func f) {
|
||||
return [&ctx, f = std::move(f)](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto table_infos = parse_table_infos(keyspace, ctx, req->query_parameters, "cf");
|
||||
return f(ctx, std::move(req), std::move(keyspace), std::move(table_infos));
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -242,6 +236,47 @@ seastar::future<json::json_return_type> run_toppartitions_query(db::toppartition
|
||||
});
|
||||
}
|
||||
|
||||
static future<json::json_return_type> set_tables(http_context& ctx, const sstring& keyspace, std::vector<sstring> tables, std::function<future<>(replica::table&)> set) {
|
||||
if (tables.empty()) {
|
||||
tables = map_keys(ctx.db.local().find_keyspace(keyspace).metadata().get()->cf_meta_data());
|
||||
}
|
||||
|
||||
return do_with(keyspace, std::move(tables), [&ctx, set] (const sstring& keyspace, const std::vector<sstring>& tables) {
|
||||
return ctx.db.invoke_on_all([&keyspace, &tables, set] (replica::database& db) {
|
||||
return parallel_for_each(tables, [&db, &keyspace, set] (const sstring& table) {
|
||||
replica::table& t = db.find_column_family(keyspace, table);
|
||||
return set(t);
|
||||
});
|
||||
});
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
}
|
||||
|
||||
future<json::json_return_type> set_tables_autocompaction(http_context& ctx, const sstring &keyspace, std::vector<sstring> tables, bool enabled) {
|
||||
apilog.info("set_tables_autocompaction: enabled={} keyspace={} tables={}", enabled, keyspace, tables);
|
||||
|
||||
return ctx.db.invoke_on(0, [&ctx, keyspace, tables = std::move(tables), enabled] (replica::database& db) {
|
||||
auto g = replica::database::autocompaction_toggle_guard(db);
|
||||
return set_tables(ctx, keyspace, tables, [enabled] (replica::table& cf) {
|
||||
if (enabled) {
|
||||
cf.enable_auto_compaction();
|
||||
} else {
|
||||
return cf.disable_auto_compaction();
|
||||
}
|
||||
return make_ready_future<>();
|
||||
}).finally([g = std::move(g)] {});
|
||||
});
|
||||
}
|
||||
|
||||
future<json::json_return_type> set_tables_tombstone_gc(http_context& ctx, const sstring &keyspace, std::vector<sstring> tables, bool enabled) {
|
||||
apilog.info("set_tables_tombstone_gc: enabled={} keyspace={} tables={}", enabled, keyspace, tables);
|
||||
return set_tables(ctx, keyspace, std::move(tables), [enabled] (replica::table& t) {
|
||||
t.set_tombstone_gc_enabled(enabled);
|
||||
return make_ready_future<>();
|
||||
});
|
||||
}
|
||||
|
||||
future<scrub_info> parse_scrub_options(const http_context& ctx, sharded<db::snapshot_ctl>& snap_ctl, std::unique_ptr<http::request> req) {
|
||||
scrub_info info;
|
||||
auto rp = req_params({
|
||||
@@ -307,17 +342,21 @@ future<scrub_info> parse_scrub_options(const http_context& ctx, sharded<db::snap
|
||||
}
|
||||
|
||||
void set_transport_controller(http_context& ctx, routes& r, cql_transport::controller& ctl) {
|
||||
ss::start_native_transport.set(r, [&ctl](std::unique_ptr<http::request> req) {
|
||||
ss::start_native_transport.set(r, [&ctx, &ctl](std::unique_ptr<http::request> req) {
|
||||
return smp::submit_to(0, [&] {
|
||||
return ctl.start_server();
|
||||
return with_scheduling_group(ctx.db.local().get_statement_scheduling_group(), [&ctl] {
|
||||
return ctl.start_server();
|
||||
});
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::stop_native_transport.set(r, [&ctl](std::unique_ptr<http::request> req) {
|
||||
ss::stop_native_transport.set(r, [&ctx, &ctl](std::unique_ptr<http::request> req) {
|
||||
return smp::submit_to(0, [&] {
|
||||
return ctl.request_stop_server();
|
||||
return with_scheduling_group(ctx.db.local().get_statement_scheduling_group(), [&ctl] {
|
||||
return ctl.request_stop_server();
|
||||
});
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
@@ -338,21 +377,44 @@ void unset_transport_controller(http_context& ctx, routes& r) {
|
||||
ss::is_native_transport_running.unset(r);
|
||||
}
|
||||
|
||||
// NOTE: preserved only for backward compatibility
|
||||
void set_thrift_controller(http_context& ctx, routes& r) {
|
||||
ss::is_thrift_server_running.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
return smp::submit_to(0, [] {
|
||||
return make_ready_future<json::json_return_type>(false);
|
||||
void set_rpc_controller(http_context& ctx, routes& r, thrift_controller& ctl) {
|
||||
ss::stop_rpc_server.set(r, [&ctx, &ctl](std::unique_ptr<http::request> req) {
|
||||
return smp::submit_to(0, [&] {
|
||||
return with_scheduling_group(ctx.db.local().get_statement_scheduling_group(), [&ctl] {
|
||||
return ctl.request_stop_server();
|
||||
});
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::start_rpc_server.set(r, [&ctx, &ctl](std::unique_ptr<http::request> req) {
|
||||
return smp::submit_to(0, [&] {
|
||||
return with_scheduling_group(ctx.db.local().get_statement_scheduling_group(), [&ctl] {
|
||||
return ctl.start_server();
|
||||
});
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::is_rpc_server_running.set(r, [&ctl] (std::unique_ptr<http::request> req) {
|
||||
return smp::submit_to(0, [&] {
|
||||
return !ctl.listen_addresses().empty();
|
||||
}).then([] (bool running) {
|
||||
return make_ready_future<json::json_return_type>(running);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void unset_thrift_controller(http_context& ctx, routes& r) {
|
||||
ss::is_thrift_server_running.unset(r);
|
||||
void unset_rpc_controller(http_context& ctx, routes& r) {
|
||||
ss::stop_rpc_server.unset(r);
|
||||
ss::start_rpc_server.unset(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<http::request> req) -> future<json::json_return_type> {
|
||||
ss::repair_async.set(r, [&ctx, &repair](std::unique_ptr<http::request> req) {
|
||||
static std::unordered_set<sstring> options = {"primaryRange", "parallelism", "incremental",
|
||||
"jobThreads", "ranges", "columnFamilies", "dataCenters", "hosts", "ignore_nodes", "trace",
|
||||
"startToken", "endToken", "ranges_parallelism", "small_table_optimization"};
|
||||
@@ -365,7 +427,8 @@ void set_repair(http_context& ctx, routes& r, sharded<repair_service>& repair) {
|
||||
continue;
|
||||
}
|
||||
if (!options.contains(x.first)) {
|
||||
throw httpd::bad_param_exception(format("option {} is not supported", x.first));
|
||||
return make_exception_future<json::json_return_type>(
|
||||
httpd::bad_param_exception(format("option {} is not supported", x.first)));
|
||||
}
|
||||
}
|
||||
std::unordered_map<sstring, sstring> options_map;
|
||||
@@ -380,14 +443,10 @@ 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.
|
||||
try {
|
||||
int res = co_await repair_start(repair, validate_keyspace(ctx, req), options_map);
|
||||
co_return json::json_return_type(res);
|
||||
} catch (const std::invalid_argument& e) {
|
||||
// if the option is not sane, repair_start() throws immediately, so
|
||||
// convert the exception to an HTTP error
|
||||
throw httpd::bad_param_exception(e.what());
|
||||
}
|
||||
return repair_start(repair, validate_keyspace(ctx, req->param),
|
||||
options_map).then([] (int i) {
|
||||
return make_ready_future<json::json_return_type>(i);
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_active_repair_async.set(r, [&repair] (std::unique_ptr<http::request> req) {
|
||||
@@ -467,7 +526,7 @@ void unset_repair(http_context& ctx, routes& 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<http::request> req) {
|
||||
auto ks = validate_keyspace(ctx, 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");
|
||||
@@ -490,33 +549,16 @@ void set_sstables_loader(http_context& ctx, routes& r, sharded<sstables_loader>&
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
|
||||
ss::start_restore.set(r, [&sst_loader] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto endpoint = req->get_query_param("endpoint");
|
||||
auto keyspace = req->get_query_param("keyspace");
|
||||
auto table = req->get_query_param("table");
|
||||
auto bucket = req->get_query_param("bucket");
|
||||
auto snapshot_name = req->get_query_param("snapshot");
|
||||
if (table.empty()) {
|
||||
// TODO: If missing, should restore all tables
|
||||
throw httpd::bad_param_exception("The table name must be specified");
|
||||
}
|
||||
|
||||
auto task_id = co_await sst_loader.local().download_new_sstables(keyspace, table, endpoint, bucket, snapshot_name);
|
||||
co_return json::json_return_type(fmt::to_string(task_id));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void unset_sstables_loader(http_context& ctx, routes& r) {
|
||||
ss::load_new_ss_tables.unset(r);
|
||||
ss::start_restore.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<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto view = req->get_path_param("view");
|
||||
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));
|
||||
@@ -542,24 +584,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, [&ctx, &ss] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
const auto keyspace_name = req->get_query_param("keyspace");
|
||||
const auto table_name = req->get_query_param("cf");
|
||||
|
||||
std::map<dht::token, gms::inet_address> token_endpoints;
|
||||
if (keyspace_name.empty() && table_name.empty()) {
|
||||
token_endpoints = ss.local().get_token_to_endpoint_map();
|
||||
} else if (!keyspace_name.empty() && !table_name.empty()) {
|
||||
auto& db = ctx.db.local();
|
||||
if (!db.has_schema(keyspace_name, table_name)) {
|
||||
throw bad_param_exception(fmt::format("Failed to find table {}.{}", keyspace_name, table_name));
|
||||
}
|
||||
token_endpoints = co_await ss.local().get_tablet_to_endpoint_map(db.find_schema(keyspace_name, table_name)->id());
|
||||
} else {
|
||||
throw bad_param_exception("Either provide both keyspace and table (for tablet table) or neither (for vnodes)");
|
||||
}
|
||||
|
||||
co_return json::json_return_type(stream_range_as_array(token_endpoints, [](const auto& i) {
|
||||
ss::get_token_endpoint.set(r, [&ss] (std::unique_ptr<http::request> req) {
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(ss.local().get_token_to_endpoint_map(), [](const auto& i) {
|
||||
storage_service_json::mapper val;
|
||||
val.key = fmt::to_string(i.first);
|
||||
val.value = fmt::to_string(i.second);
|
||||
@@ -628,8 +654,16 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
return ss.local().get_schema_version();
|
||||
});
|
||||
|
||||
ss::get_all_data_file_locations.set(r, [&ctx](const_req req) {
|
||||
return container_to_vec(ctx.db.local().get_config().data_file_directories());
|
||||
});
|
||||
|
||||
ss::get_saved_caches_location.set(r, [&ctx](const_req req) {
|
||||
return ctx.db.local().get_config().saved_caches_directory();
|
||||
});
|
||||
|
||||
ss::get_range_to_endpoint_map.set(r, [&ctx, &ss](std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto table = req->get_query_param("cf");
|
||||
|
||||
auto erm = std::invoke([&]() -> locator::effective_replication_map_ptr {
|
||||
@@ -660,7 +694,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
m.key.push("");
|
||||
}
|
||||
for (const gms::inet_address& address : entry.second) {
|
||||
m.value.push(fmt::to_string(address));
|
||||
m.value.push(address.to_sstring());
|
||||
}
|
||||
return m;
|
||||
});
|
||||
@@ -669,7 +703,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
ss::get_pending_range_to_endpoint_map.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
std::vector<ss::maplist_mapper> res;
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
@@ -678,19 +712,32 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
if (!req->param.exists("keyspace")) {
|
||||
throw bad_param_exception("The keyspace param is not provided");
|
||||
}
|
||||
auto keyspace = req->get_path_param("keyspace");
|
||||
auto keyspace = req->param["keyspace"];
|
||||
auto table = req->get_query_param("table");
|
||||
if (!table.empty()) {
|
||||
validate_table(ctx, keyspace, table);
|
||||
return describe_ring_as_json_for_table(ss, keyspace, table);
|
||||
}
|
||||
return describe_ring_as_json(ss, validate_keyspace(ctx, req));
|
||||
return describe_ring_as_json(ss, validate_keyspace(ctx, req->param));
|
||||
});
|
||||
|
||||
ss::get_load.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
return get_cf_stats(ctx, &replica::column_family_stats::live_disk_space_used);
|
||||
});
|
||||
|
||||
ss::get_load_map.set(r, [&ctx] (std::unique_ptr<http::request> req) {
|
||||
return ctx.lmeter.get_load_map().then([] (auto&& load_map) {
|
||||
std::vector<ss::map_string_double> res;
|
||||
for (auto i : load_map) {
|
||||
ss::map_string_double val;
|
||||
val.key = i.first;
|
||||
val.value = i.second;
|
||||
res.push_back(val);
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_current_generation_number.set(r, [&ss](std::unique_ptr<http::request> req) {
|
||||
auto ep = ss.local().get_token_metadata().get_topology().my_address();
|
||||
return ss.local().gossiper().get_current_generation_number(ep).then([](gms::generation_type res) {
|
||||
@@ -699,7 +746,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
|
||||
ss::get_natural_endpoints.set(r, [&ctx, &ss](const_req req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req.param);
|
||||
return container_to_vec(ss.local().get_natural_endpoints(keyspace, req.get_query_param("cf"),
|
||||
req.get_query_param("key")));
|
||||
});
|
||||
@@ -716,19 +763,17 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
auto& db = ctx.db;
|
||||
auto params = req_params({
|
||||
std::pair("flush_memtables", mandatory::no),
|
||||
std::pair("consider_only_existing_data", mandatory::no),
|
||||
});
|
||||
params.process(*req);
|
||||
auto flush = params.get_as<bool>("flush_memtables").value_or(true);
|
||||
auto consider_only_existing_data = params.get_as<bool>("consider_only_existing_data").value_or(false);
|
||||
apilog.info("force_compaction: flush={} consider_only_existing_data={}", flush, consider_only_existing_data);
|
||||
apilog.info("force_compaction: flush={}", flush);
|
||||
|
||||
auto& compaction_module = db.local().get_compaction_manager().get_task_manager_module();
|
||||
std::optional<flush_mode> fmopt;
|
||||
if (!flush && !consider_only_existing_data) {
|
||||
if (!flush) {
|
||||
fmopt = flush_mode::skip;
|
||||
}
|
||||
auto task = co_await compaction_module.make_and_start_task<global_major_compaction_task_impl>({}, db, fmopt, consider_only_existing_data);
|
||||
auto task = co_await compaction_module.make_and_start_task<global_major_compaction_task_impl>({}, db, fmopt);
|
||||
try {
|
||||
co_await task->done();
|
||||
} catch (...) {
|
||||
@@ -745,21 +790,19 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
std::pair("keyspace", mandatory::yes),
|
||||
std::pair("cf", mandatory::no),
|
||||
std::pair("flush_memtables", mandatory::no),
|
||||
std::pair("consider_only_existing_data", mandatory::no),
|
||||
});
|
||||
params.process(*req);
|
||||
auto keyspace = validate_keyspace(ctx, *params.get("keyspace"));
|
||||
auto table_infos = parse_table_infos(keyspace, ctx, params.get("cf").value_or(""));
|
||||
auto flush = params.get_as<bool>("flush_memtables").value_or(true);
|
||||
auto consider_only_existing_data = params.get_as<bool>("consider_only_existing_data").value_or(false);
|
||||
apilog.info("force_keyspace_compaction: keyspace={} tables={}, flush={} consider_only_existing_data={}", keyspace, table_infos, flush, consider_only_existing_data);
|
||||
apilog.debug("force_keyspace_compaction: keyspace={} tables={}, flush={}", keyspace, table_infos, flush);
|
||||
|
||||
auto& compaction_module = db.local().get_compaction_manager().get_task_manager_module();
|
||||
std::optional<flush_mode> fmopt;
|
||||
if (!flush && !consider_only_existing_data) {
|
||||
if (!flush) {
|
||||
fmopt = flush_mode::skip;
|
||||
}
|
||||
auto task = co_await compaction_module.make_and_start_task<major_keyspace_compaction_task_impl>({}, std::move(keyspace), tasks::task_id::create_null_id(), db, table_infos, fmopt, consider_only_existing_data);
|
||||
auto task = co_await compaction_module.make_and_start_task<major_keyspace_compaction_task_impl>({}, std::move(keyspace), tasks::task_id::create_null_id(), db, table_infos, fmopt);
|
||||
try {
|
||||
co_await task->done();
|
||||
} catch (...) {
|
||||
@@ -772,7 +815,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
|
||||
ss::force_keyspace_cleanup.set(r, [&ctx, &ss](std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto& db = ctx.db;
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto table_infos = parse_table_infos(keyspace, ctx, req->query_parameters, "cf");
|
||||
const auto& rs = db.local().find_keyspace(keyspace).get_replication_strategy();
|
||||
if (rs.get_type() == locator::replication_strategy_type::local || !rs.is_vnode_based()) {
|
||||
@@ -867,7 +910,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
|
||||
ss::force_keyspace_flush.set(r, [&ctx](std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto column_families = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
apilog.info("perform_keyspace_flush: keyspace={} tables={}", keyspace, column_families);
|
||||
auto& db = ctx.db;
|
||||
@@ -898,8 +941,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
auto host_id = validate_host_id(req->get_query_param("host_id"));
|
||||
std::vector<sstring> ignore_nodes_strs = utils::split_comma_separated_list(req->get_query_param("ignore_nodes"));
|
||||
apilog.info("remove_node: host_id={} ignore_nodes={}", host_id, ignore_nodes_strs);
|
||||
locator::host_id_or_endpoint_list ignore_nodes;
|
||||
ignore_nodes.reserve(ignore_nodes_strs.size());
|
||||
auto ignore_nodes = std::list<locator::host_id_or_endpoint>();
|
||||
for (const sstring& n : ignore_nodes_strs) {
|
||||
try {
|
||||
auto hoep = locator::host_id_or_endpoint(n);
|
||||
@@ -908,7 +950,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
}
|
||||
ignore_nodes.push_back(std::move(hoep));
|
||||
} catch (...) {
|
||||
throw std::runtime_error(fmt::format("Failed to parse ignore_nodes parameter: ignore_nodes={}, node={}: {}", ignore_nodes_strs, n, std::current_exception()));
|
||||
throw std::runtime_error(format("Failed to parse ignore_nodes parameter: ignore_nodes={}, node={}: {}", ignore_nodes_strs, n, std::current_exception()));
|
||||
}
|
||||
}
|
||||
return ss.local().removenode(host_id, std::move(ignore_nodes)).then([] {
|
||||
@@ -977,7 +1019,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
ss::truncate.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto column_family = req->get_query_param("cf");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
@@ -1063,7 +1105,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
|
||||
ss::get_compaction_throughput_mb_per_sec.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
int value = ctx.db.local().get_compaction_manager().throughput_mbs();
|
||||
int value = ctx.db.local().get_config().compaction_throughput_mb_per_sec();
|
||||
return make_ready_future<json::json_return_type>(value);
|
||||
});
|
||||
|
||||
@@ -1111,16 +1153,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
|
||||
ss::rebuild.set(r, [&ss](std::unique_ptr<http::request> req) {
|
||||
utils::optional_param source_dc;
|
||||
if (auto source_dc_str = req->get_query_param("source_dc"); !source_dc_str.empty()) {
|
||||
source_dc.emplace(std::move(source_dc_str)).set_user_provided();
|
||||
}
|
||||
if (auto force_str = req->get_query_param("force"); !force_str.empty() && service::loosen_constraints(validate_bool(force_str))) {
|
||||
if (!source_dc) {
|
||||
throw bad_param_exception("The `source_dc` option must be provided for using the `force` option");
|
||||
}
|
||||
source_dc.set_force();
|
||||
}
|
||||
auto source_dc = req->get_query_param("source_dc");
|
||||
apilog.info("rebuild: source_dc={}", source_dc);
|
||||
return ss.local().rebuild(std::move(source_dc)).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
@@ -1130,14 +1163,14 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
ss::bulk_load.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto path = req->get_path_param("path");
|
||||
auto path = req->param["path"];
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::bulk_load_async.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto path = req->get_path_param("path");
|
||||
auto path = req->param["path"];
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
@@ -1224,6 +1257,38 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
}
|
||||
});
|
||||
|
||||
ss::enable_auto_compaction.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("enable_auto_compaction: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_autocompaction(ctx, keyspace, tables, true);
|
||||
});
|
||||
|
||||
ss::disable_auto_compaction.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("disable_auto_compaction: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_autocompaction(ctx, keyspace, tables, false);
|
||||
});
|
||||
|
||||
ss::enable_tombstone_gc.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("enable_tombstone_gc: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_tombstone_gc(ctx, keyspace, tables, true);
|
||||
});
|
||||
|
||||
ss::disable_tombstone_gc.set(r, [&ctx](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto tables = parse_tables(keyspace, ctx, req->query_parameters, "cf");
|
||||
|
||||
apilog.info("disable_tombstone_gc: keyspace={} tables={}", keyspace, tables);
|
||||
return set_tables_tombstone_gc(ctx, keyspace, tables, false);
|
||||
});
|
||||
|
||||
ss::deliver_hints.set(r, [](std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
@@ -1317,7 +1382,7 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
});
|
||||
|
||||
ss::get_effective_ownership.set(r, [&ctx, &ss] (std::unique_ptr<http::request> req) {
|
||||
auto keyspace_name = req->get_path_param("keyspace") == "null" ? "" : validate_keyspace(ctx, req);
|
||||
auto keyspace_name = req->param["keyspace"] == "null" ? "" : validate_keyspace(ctx, req->param);
|
||||
auto table_name = req->get_query_param("cf");
|
||||
|
||||
if (!keyspace_name.empty()) {
|
||||
@@ -1463,7 +1528,12 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
ss::reload_raft_topology_state.set(r,
|
||||
[&ss, &group0_client] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
co_await ss.invoke_on(0, [&group0_client] (service::storage_service& ss) -> future<> {
|
||||
return ss.reload_raft_topology_state(group0_client);
|
||||
apilog.info("Waiting for group 0 read/apply mutex before reloading Raft topology state...");
|
||||
auto holder = co_await group0_client.hold_read_apply_mutex();
|
||||
apilog.info("Reloading Raft topology state");
|
||||
// Using topology_transition() instead of topology_state_load(), because the former notifies listeners
|
||||
co_await ss.topology_transition();
|
||||
apilog.info("Reloaded Raft topology state");
|
||||
});
|
||||
co_return json_void();
|
||||
});
|
||||
@@ -1552,11 +1622,6 @@ void set_storage_service(http_context& ctx, routes& r, sharded<service::storage_
|
||||
co_return json_void();
|
||||
});
|
||||
|
||||
ss::quiesce_topology.set(r, [&ss] (std::unique_ptr<http::request> req) -> future<json_return_type> {
|
||||
co_await ss.local().await_topology_quiesced();
|
||||
co_return json_void();
|
||||
});
|
||||
|
||||
sp::get_schema_versions.set(r, [&ss](std::unique_ptr<http::request> req) {
|
||||
return ss.local().describe_schema_versions().then([] (auto result) {
|
||||
std::vector<sp::mapper_list> res;
|
||||
@@ -1578,10 +1643,13 @@ void unset_storage_service(http_context& ctx, routes& r) {
|
||||
ss::get_release_version.unset(r);
|
||||
ss::get_scylla_release_version.unset(r);
|
||||
ss::get_schema_version.unset(r);
|
||||
ss::get_all_data_file_locations.unset(r);
|
||||
ss::get_saved_caches_location.unset(r);
|
||||
ss::get_range_to_endpoint_map.unset(r);
|
||||
ss::get_pending_range_to_endpoint_map.unset(r);
|
||||
ss::describe_ring.unset(r);
|
||||
ss::get_load.unset(r);
|
||||
ss::get_load_map.unset(r);
|
||||
ss::get_current_generation_number.unset(r);
|
||||
ss::get_natural_endpoints.unset(r);
|
||||
ss::cdc_streams_check_and_repair.unset(r);
|
||||
@@ -1629,6 +1697,10 @@ void unset_storage_service(http_context& ctx, routes& r) {
|
||||
ss::get_trace_probability.unset(r);
|
||||
ss::get_slow_query_info.unset(r);
|
||||
ss::set_slow_query.unset(r);
|
||||
ss::enable_auto_compaction.unset(r);
|
||||
ss::disable_auto_compaction.unset(r);
|
||||
ss::enable_tombstone_gc.unset(r);
|
||||
ss::disable_tombstone_gc.unset(r);
|
||||
ss::deliver_hints.unset(r);
|
||||
ss::get_cluster_name.unset(r);
|
||||
ss::get_partitioner_name.unset(r);
|
||||
@@ -1653,67 +1725,39 @@ void unset_storage_service(http_context& ctx, routes& r) {
|
||||
ss::add_tablet_replica.unset(r);
|
||||
ss::del_tablet_replica.unset(r);
|
||||
ss::tablet_balancing_enable.unset(r);
|
||||
ss::quiesce_topology.unset(r);
|
||||
sp::get_schema_versions.unset(r);
|
||||
}
|
||||
|
||||
void set_load_meter(http_context& ctx, routes& r, service::load_meter& lm) {
|
||||
ss::get_load_map.set(r, [&lm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto load_map = co_await lm.get_load_map();
|
||||
std::vector<ss::map_string_double> res;
|
||||
for (auto i : load_map) {
|
||||
ss::map_string_double val;
|
||||
val.key = i.first;
|
||||
val.value = i.second;
|
||||
res.push_back(val);
|
||||
}
|
||||
co_return res;
|
||||
});
|
||||
}
|
||||
|
||||
void unset_load_meter(http_context& ctx, routes& r) {
|
||||
ss::get_load_map.unset(r);
|
||||
}
|
||||
|
||||
void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_ctl) {
|
||||
ss::get_snapshot_details.set(r, [&snap_ctl](std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto result = co_await snap_ctl.local().get_snapshot_details();
|
||||
co_return std::function([res = std::move(result)] (output_stream<char>&& o) -> future<> {
|
||||
std::exception_ptr ex;
|
||||
auto result = std::move(res);
|
||||
output_stream<char> out = std::move(o);
|
||||
try {
|
||||
auto result = std::move(res);
|
||||
bool first = true;
|
||||
bool first = true;
|
||||
|
||||
co_await out.write("[");
|
||||
for (auto& [name, details] : result) {
|
||||
if (!first) {
|
||||
co_await out.write(", ");
|
||||
}
|
||||
std::vector<ss::snapshot> snapshot;
|
||||
for (auto& cf : details) {
|
||||
ss::snapshot snp;
|
||||
snp.ks = cf.ks;
|
||||
snp.cf = cf.cf;
|
||||
snp.live = cf.details.live;
|
||||
snp.total = cf.details.total;
|
||||
snapshot.push_back(std::move(snp));
|
||||
}
|
||||
ss::snapshots all_snapshots;
|
||||
all_snapshots.key = name;
|
||||
all_snapshots.value = std::move(snapshot);
|
||||
co_await all_snapshots.write(out);
|
||||
first = false;
|
||||
co_await out.write("[");
|
||||
for (auto&& map : result) {
|
||||
if (!first) {
|
||||
co_await out.write(", ");
|
||||
}
|
||||
co_await out.write("]");
|
||||
co_await out.flush();
|
||||
} catch (...) {
|
||||
ex = std::current_exception();
|
||||
std::vector<ss::snapshot> snapshot;
|
||||
for (auto& cf : std::get<1>(map)) {
|
||||
ss::snapshot snp;
|
||||
snp.ks = cf.ks;
|
||||
snp.cf = cf.cf;
|
||||
snp.live = cf.live;
|
||||
snp.total = cf.total;
|
||||
snapshot.push_back(std::move(snp));
|
||||
}
|
||||
ss::snapshots all_snapshots;
|
||||
all_snapshots.key = std::get<0>(map);
|
||||
all_snapshots.value = std::move(snapshot);
|
||||
co_await all_snapshots.write(out);
|
||||
first = false;
|
||||
}
|
||||
co_await out.write("]");
|
||||
co_await out.close();
|
||||
if (ex) {
|
||||
co_await coroutine::return_exception_ptr(std::move(ex));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1792,35 +1836,6 @@ void set_snapshot(http_context& ctx, routes& r, sharded<db::snapshot_ctl>& snap_
|
||||
|
||||
co_return json::json_return_type(static_cast<int>(scrub_status::successful));
|
||||
});
|
||||
|
||||
ss::start_backup.set(r, [&snap_ctl] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto endpoint = req->get_query_param("endpoint");
|
||||
auto keyspace = req->get_query_param("keyspace");
|
||||
auto bucket = req->get_query_param("bucket");
|
||||
auto snapshot_name = req->get_query_param("snapshot");
|
||||
if (snapshot_name.empty()) {
|
||||
// TODO: If missing, snapshot should be taken by scylla, then removed
|
||||
throw httpd::bad_param_exception("The snapshot name must be specified");
|
||||
}
|
||||
|
||||
auto& ctl = snap_ctl.local();
|
||||
auto task_id = co_await ctl.start_backup(std::move(endpoint), std::move(bucket), std::move(keyspace), std::move(snapshot_name));
|
||||
co_return json::json_return_type(fmt::to_string(task_id));
|
||||
});
|
||||
|
||||
cf::get_true_snapshots_size.set(r, [&snap_ctl] (std::unique_ptr<http::request> req) {
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(req->get_path_param("name"));
|
||||
return snap_ctl.local().true_snapshots_size(std::move(ks), std::move(cf)).then([] (int64_t res) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_all_true_snapshots_size.set(r, [] (std::unique_ptr<http::request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void unset_snapshot(http_context& ctx, routes& r) {
|
||||
@@ -1829,9 +1844,6 @@ void unset_snapshot(http_context& ctx, routes& r) {
|
||||
ss::del_snapshot.unset(r);
|
||||
ss::true_snapshots_size.unset(r);
|
||||
ss::scrub.unset(r);
|
||||
ss::start_backup.unset(r);
|
||||
cf::get_true_snapshots_size.unset(r);
|
||||
cf::get_all_true_snapshots_size.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "db/data_listeners.hh"
|
||||
|
||||
namespace cql_transport { class controller; }
|
||||
class thrift_controller;
|
||||
namespace db {
|
||||
class snapshot_ctl;
|
||||
namespace view {
|
||||
@@ -39,11 +40,7 @@ sstring validate_keyspace(const http_context& ctx, sstring ks_name);
|
||||
|
||||
// verify that the keyspace parameter is found, otherwise a bad_param_exception exception is thrown
|
||||
// containing the description of the respective keyspace error.
|
||||
sstring validate_keyspace(const http_context& ctx, const std::unique_ptr<http::request>& req);
|
||||
|
||||
// verify that the table parameter is found, otherwise a bad_param_exception exception is thrown
|
||||
// containing the description of the respective table error.
|
||||
void validate_table(const http_context& ctx, sstring ks_name, sstring table_name);
|
||||
sstring validate_keyspace(const http_context& ctx, const httpd::parameters& param);
|
||||
|
||||
// splits a request parameter assumed to hold a comma-separated list of table names
|
||||
// verify that the tables are found, otherwise a bad_param_exception exception is thrown
|
||||
@@ -79,12 +76,10 @@ void set_repair(http_context& ctx, httpd::routes& r, sharded<repair_service>& re
|
||||
void unset_repair(http_context& ctx, httpd::routes& r);
|
||||
void set_transport_controller(http_context& ctx, httpd::routes& r, cql_transport::controller& ctl);
|
||||
void unset_transport_controller(http_context& ctx, httpd::routes& r);
|
||||
void set_thrift_controller(http_context& ctx, httpd::routes& r);
|
||||
void unset_thrift_controller(http_context& ctx, httpd::routes& r);
|
||||
void set_rpc_controller(http_context& ctx, httpd::routes& r, thrift_controller& ctl);
|
||||
void unset_rpc_controller(http_context& ctx, httpd::routes& r);
|
||||
void set_snapshot(http_context& ctx, httpd::routes& r, sharded<db::snapshot_ctl>& snap_ctl);
|
||||
void unset_snapshot(http_context& ctx, httpd::routes& r);
|
||||
void set_load_meter(http_context& ctx, httpd::routes& r, service::load_meter& lm);
|
||||
void unset_load_meter(http_context& ctx, httpd::routes& r);
|
||||
seastar::future<json::json_return_type> run_toppartitions_query(db::toppartitions_query& q, http_context &ctx, bool legacy_request = false);
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -108,7 +108,7 @@ void set_stream_manager(http_context& ctx, routes& r, sharded<streaming::stream_
|
||||
});
|
||||
|
||||
hs::get_total_incoming_bytes.set(r, [&sm](std::unique_ptr<request> req) {
|
||||
gms::inet_address peer(req->get_path_param("peer"));
|
||||
gms::inet_address peer(req->param["peer"]);
|
||||
return sm.map_reduce0([peer](streaming::stream_manager& sm) {
|
||||
return sm.get_progress_on_all_shards(peer).then([] (auto sbytes) {
|
||||
return sbytes.bytes_received;
|
||||
@@ -129,7 +129,7 @@ void set_stream_manager(http_context& ctx, routes& r, sharded<streaming::stream_
|
||||
});
|
||||
|
||||
hs::get_total_outgoing_bytes.set(r, [&sm](std::unique_ptr<request> req) {
|
||||
gms::inet_address peer(req->get_path_param("peer"));
|
||||
gms::inet_address peer(req->param["peer"]);
|
||||
return sm.map_reduce0([peer] (streaming::stream_manager& sm) {
|
||||
return sm.get_progress_on_all_shards(peer).then([] (auto sbytes) {
|
||||
return sbytes.bytes_sent;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "api/api-doc/system.json.hh"
|
||||
#include "api/api-doc/metrics.json.hh"
|
||||
#include "replica/database.hh"
|
||||
#include "sstables/sstables_manager.hh"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <seastar/core/reactor.hh>
|
||||
@@ -123,9 +122,9 @@ void set_system(http_context& ctx, routes& r) {
|
||||
|
||||
hs::get_logger_level.set(r, [](const_req req) {
|
||||
try {
|
||||
return logging::level_name(logging::logger_registry().get_logger_level(req.get_path_param("name")));
|
||||
return logging::level_name(logging::logger_registry().get_logger_level(req.param["name"]));
|
||||
} catch (std::out_of_range& e) {
|
||||
throw bad_param_exception("Unknown logger name " + req.get_path_param("name"));
|
||||
throw bad_param_exception("Unknown logger name " + req.param["name"]);
|
||||
}
|
||||
// just to keep the compiler happy
|
||||
return sstring();
|
||||
@@ -134,9 +133,9 @@ void set_system(http_context& ctx, routes& r) {
|
||||
hs::set_logger_level.set(r, [](const_req req) {
|
||||
try {
|
||||
logging::log_level level = boost::lexical_cast<logging::log_level>(std::string(req.get_query_param("level")));
|
||||
logging::logger_registry().set_logger_level(req.get_path_param("name"), level);
|
||||
logging::logger_registry().set_logger_level(req.param["name"], level);
|
||||
} catch (std::out_of_range& e) {
|
||||
throw bad_param_exception("Unknown logger name " + req.get_path_param("name"));
|
||||
throw bad_param_exception("Unknown logger name " + req.param["name"]);
|
||||
} catch (boost::bad_lexical_cast& e) {
|
||||
throw bad_param_exception("Unknown logging level " + req.get_query_param("level"));
|
||||
}
|
||||
@@ -183,11 +182,6 @@ void set_system(http_context& ctx, routes& r) {
|
||||
apilog.info("Profile dumped to {}", profile_dest);
|
||||
return make_ready_future<json::json_return_type>(json::json_return_type(json::json_void()));
|
||||
}) ;
|
||||
|
||||
hs::get_highest_supported_sstable_version.set(r, [&ctx] (const_req req) {
|
||||
auto& table = ctx.db.local().find_column_family("system", "local");
|
||||
return seastar::to_sstring(table.get_sstables_manager().get_highest_supported_format());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,13 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
#include "api.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct http_context;
|
||||
void set_system(http_context& ctx, seastar::httpd::routes& r);
|
||||
void set_system(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -7,15 +7,12 @@
|
||||
*/
|
||||
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <seastar/coroutine/exception.hh>
|
||||
#include <seastar/http/exception.hh>
|
||||
|
||||
#include "task_manager.hh"
|
||||
#include "api/api.hh"
|
||||
#include "api/api-doc/task_manager.json.hh"
|
||||
#include "db/system_keyspace.hh"
|
||||
#include "tasks/task_handler.hh"
|
||||
#include "utils/overloaded_functor.hh"
|
||||
|
||||
#include <utility>
|
||||
#include <boost/range/adaptors.hpp>
|
||||
@@ -26,56 +23,91 @@ namespace tm = httpd::task_manager_json;
|
||||
using namespace json;
|
||||
using namespace seastar::httpd;
|
||||
|
||||
tm::task_status make_status(tasks::task_status status) {
|
||||
auto start_time = db_clock::to_time_t(status.start_time);
|
||||
auto end_time = db_clock::to_time_t(status.end_time);
|
||||
inline bool filter_tasks(tasks::task_manager::task_ptr task, std::unordered_map<sstring, sstring>& query_params) {
|
||||
return (!query_params.contains("keyspace") || query_params["keyspace"] == task->get_status().keyspace) &&
|
||||
(!query_params.contains("table") || query_params["table"] == task->get_status().table);
|
||||
}
|
||||
|
||||
struct full_task_status {
|
||||
tasks::task_manager::task::status task_status;
|
||||
std::string type;
|
||||
tasks::task_manager::task::progress progress;
|
||||
std::string module;
|
||||
tasks::task_id parent_id;
|
||||
tasks::is_abortable abortable;
|
||||
std::vector<std::string> children_ids;
|
||||
};
|
||||
|
||||
struct task_stats {
|
||||
task_stats(tasks::task_manager::task_ptr task)
|
||||
: task_id(task->id().to_sstring())
|
||||
, state(task->get_status().state)
|
||||
, type(task->type())
|
||||
, scope(task->get_status().scope)
|
||||
, keyspace(task->get_status().keyspace)
|
||||
, table(task->get_status().table)
|
||||
, entity(task->get_status().entity)
|
||||
, sequence_number(task->get_status().sequence_number)
|
||||
{ }
|
||||
|
||||
sstring task_id;
|
||||
tasks::task_manager::task_state state;
|
||||
std::string type;
|
||||
std::string scope;
|
||||
std::string keyspace;
|
||||
std::string table;
|
||||
std::string entity;
|
||||
uint64_t sequence_number;
|
||||
};
|
||||
|
||||
tm::task_status make_status(full_task_status status) {
|
||||
auto start_time = db_clock::to_time_t(status.task_status.start_time);
|
||||
auto end_time = db_clock::to_time_t(status.task_status.end_time);
|
||||
::tm st, et;
|
||||
::gmtime_r(&end_time, &et);
|
||||
::gmtime_r(&start_time, &st);
|
||||
|
||||
std::vector<tm::task_identity> tis{status.children.size()};
|
||||
boost::transform(status.children, tis.begin(), [] (const auto& child) {
|
||||
tm::task_identity ident;
|
||||
ident.task_id = child.task_id.to_sstring();
|
||||
ident.node = fmt::format("{}", child.node);
|
||||
return ident;
|
||||
});
|
||||
|
||||
tm::task_status res{};
|
||||
res.id = status.task_id.to_sstring();
|
||||
res.id = status.task_status.id.to_sstring();
|
||||
res.type = status.type;
|
||||
res.kind = status.kind;
|
||||
res.scope = status.scope;
|
||||
res.state = status.state;
|
||||
res.is_abortable = bool(status.is_abortable);
|
||||
res.scope = status.task_status.scope;
|
||||
res.state = status.task_status.state;
|
||||
res.is_abortable = bool(status.abortable);
|
||||
res.start_time = st;
|
||||
res.end_time = et;
|
||||
res.error = status.error;
|
||||
res.parent_id = status.parent_id ? status.parent_id.to_sstring() : "none";
|
||||
res.sequence_number = status.sequence_number;
|
||||
res.shard = status.shard;
|
||||
res.keyspace = status.keyspace;
|
||||
res.table = status.table;
|
||||
res.entity = status.entity;
|
||||
res.progress_units = status.progress_units;
|
||||
res.error = status.task_status.error;
|
||||
res.parent_id = status.parent_id.to_sstring();
|
||||
res.sequence_number = status.task_status.sequence_number;
|
||||
res.shard = status.task_status.shard;
|
||||
res.keyspace = status.task_status.keyspace;
|
||||
res.table = status.task_status.table;
|
||||
res.entity = status.task_status.entity;
|
||||
res.progress_units = status.task_status.progress_units;
|
||||
res.progress_total = status.progress.total;
|
||||
res.progress_completed = status.progress.completed;
|
||||
res.children_ids = std::move(tis);
|
||||
res.children_ids = std::move(status.children_ids);
|
||||
return res;
|
||||
}
|
||||
|
||||
tm::task_stats make_stats(tasks::task_stats stats) {
|
||||
tm::task_stats res{};
|
||||
res.task_id = stats.task_id.to_sstring();
|
||||
res.type = stats.type;
|
||||
res.kind = stats.kind;
|
||||
res.scope = stats.scope;
|
||||
res.state = stats.state;
|
||||
res.sequence_number = stats.sequence_number;
|
||||
res.keyspace = stats.keyspace;
|
||||
res.table = stats.table;
|
||||
res.entity = stats.entity;
|
||||
return res;
|
||||
future<full_task_status> retrieve_status(const tasks::task_manager::foreign_task_ptr& task) {
|
||||
if (task.get() == nullptr) {
|
||||
co_return coroutine::return_exception(httpd::bad_param_exception("Task not found"));
|
||||
}
|
||||
auto progress = co_await task->get_progress();
|
||||
full_task_status s;
|
||||
s.task_status = task->get_status();
|
||||
s.type = task->type();
|
||||
s.parent_id = task->get_parent_id();
|
||||
s.abortable = task->is_abortable();
|
||||
s.module = task->get_module_name();
|
||||
s.progress.completed = progress.completed;
|
||||
s.progress.total = progress.total;
|
||||
std::vector<std::string> ct{task->get_children().size()};
|
||||
boost::transform(task->get_children(), ct.begin(), [] (const auto& child) {
|
||||
return child->id().to_sstring();
|
||||
});
|
||||
s.children_ids = std::move(ct);
|
||||
co_return s;
|
||||
}
|
||||
|
||||
void set_task_manager(http_context& ctx, routes& r, sharded<tasks::task_manager>& tm, db::config& cfg) {
|
||||
@@ -85,123 +117,139 @@ void set_task_manager(http_context& ctx, routes& r, sharded<tasks::task_manager>
|
||||
});
|
||||
|
||||
tm::get_tasks.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
using chunked_stats = utils::chunked_vector<tasks::task_stats>;
|
||||
using chunked_stats = utils::chunked_vector<task_stats>;
|
||||
auto internal = tasks::is_internal{req_param<bool>(*req, "internal", false)};
|
||||
std::vector<chunked_stats> res = co_await tm.map([&req, internal] (tasks::task_manager& tm) {
|
||||
chunked_stats local_res;
|
||||
tasks::task_manager::module_ptr module;
|
||||
std::optional<std::string> keyspace = std::nullopt;
|
||||
std::optional<std::string> table = std::nullopt;
|
||||
try {
|
||||
module = tm.find_module(req->get_path_param("module"));
|
||||
module = tm.find_module(req->param["module"]);
|
||||
} catch (...) {
|
||||
throw bad_param_exception(fmt::format("{}", std::current_exception()));
|
||||
}
|
||||
|
||||
if (auto it = req->query_parameters.find("keyspace"); it != req->query_parameters.end()) {
|
||||
keyspace = it->second;
|
||||
}
|
||||
if (auto it = req->query_parameters.find("table"); it != req->query_parameters.end()) {
|
||||
table = it->second;
|
||||
}
|
||||
|
||||
return module->get_stats(internal, [keyspace = std::move(keyspace), table = std::move(table)] (std::string& ks, std::string& t) {
|
||||
return (!keyspace || keyspace == ks) && (!table || table == t);
|
||||
const auto& filtered_tasks = module->get_tasks() | boost::adaptors::filtered([¶ms = req->query_parameters, internal] (const auto& task) {
|
||||
return (internal || !task.second->is_internal()) && filter_tasks(task.second, params);
|
||||
});
|
||||
for (auto& [task_id, task] : filtered_tasks) {
|
||||
local_res.push_back(task_stats{task});
|
||||
}
|
||||
return local_res;
|
||||
});
|
||||
|
||||
std::function<future<>(output_stream<char>&&)> f = [r = std::move(res)] (output_stream<char>&& os) -> future<> {
|
||||
auto s = std::move(os);
|
||||
std::exception_ptr ex;
|
||||
try {
|
||||
auto res = std::move(r);
|
||||
co_await s.write("[");
|
||||
std::string delim = "";
|
||||
for (auto& v: res) {
|
||||
for (auto& stats: v) {
|
||||
co_await s.write(std::exchange(delim, ", "));
|
||||
tm::task_stats ts = make_stats(stats);
|
||||
co_await formatter::write(s, ts);
|
||||
}
|
||||
auto res = std::move(r);
|
||||
co_await s.write("[");
|
||||
std::string delim = "";
|
||||
for (auto& v: res) {
|
||||
for (auto& stats: v) {
|
||||
co_await s.write(std::exchange(delim, ", "));
|
||||
tm::task_stats ts;
|
||||
ts = stats;
|
||||
co_await formatter::write(s, ts);
|
||||
}
|
||||
co_await s.write("]");
|
||||
co_await s.flush();
|
||||
} catch (...) {
|
||||
ex = std::current_exception();
|
||||
}
|
||||
co_await s.write("]");
|
||||
co_await s.close();
|
||||
if (ex) {
|
||||
co_await coroutine::return_exception_ptr(std::move(ex));
|
||||
}
|
||||
};
|
||||
co_return std::move(f);
|
||||
});
|
||||
|
||||
tm::get_task_status.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto id = tasks::task_id{utils::UUID{req->get_path_param("task_id")}};
|
||||
tasks::task_status status;
|
||||
auto id = tasks::task_id{utils::UUID{req->param["task_id"]}};
|
||||
tasks::task_manager::foreign_task_ptr task;
|
||||
try {
|
||||
auto task = tasks::task_handler{tm.local(), id};
|
||||
status = co_await task.get_status();
|
||||
task = co_await tasks::task_manager::invoke_on_task(tm, id, std::function([] (tasks::task_manager::task_ptr task) -> future<tasks::task_manager::foreign_task_ptr> {
|
||||
if (task->is_complete()) {
|
||||
task->unregister_task();
|
||||
}
|
||||
co_return std::move(task);
|
||||
}));
|
||||
} catch (tasks::task_manager::task_not_found& e) {
|
||||
throw bad_param_exception(e.what());
|
||||
}
|
||||
co_return make_status(status);
|
||||
auto s = co_await retrieve_status(task);
|
||||
co_return make_status(s);
|
||||
});
|
||||
|
||||
tm::abort_task.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto id = tasks::task_id{utils::UUID{req->get_path_param("task_id")}};
|
||||
auto id = tasks::task_id{utils::UUID{req->param["task_id"]}};
|
||||
try {
|
||||
auto task = tasks::task_handler{tm.local(), id};
|
||||
co_await task.abort();
|
||||
co_await tasks::task_manager::invoke_on_task(tm, id, [] (tasks::task_manager::task_ptr task) -> future<> {
|
||||
if (!task->is_abortable()) {
|
||||
co_await coroutine::return_exception(std::runtime_error("Requested task cannot be aborted"));
|
||||
}
|
||||
co_await task->abort();
|
||||
});
|
||||
} catch (tasks::task_manager::task_not_found& e) {
|
||||
throw bad_param_exception(e.what());
|
||||
} catch (tasks::task_not_abortable& e) {
|
||||
throw httpd::base_exception{e.what(), http::reply::status_type::forbidden};
|
||||
}
|
||||
co_return json_void();
|
||||
});
|
||||
|
||||
tm::wait_task.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto id = tasks::task_id{utils::UUID{req->get_path_param("task_id")}};
|
||||
tasks::task_status status;
|
||||
std::optional<std::chrono::seconds> timeout = std::nullopt;
|
||||
if (auto it = req->query_parameters.find("timeout"); it != req->query_parameters.end()) {
|
||||
timeout = std::chrono::seconds(boost::lexical_cast<uint32_t>(it->second));
|
||||
}
|
||||
auto id = tasks::task_id{utils::UUID{req->param["task_id"]}};
|
||||
tasks::task_manager::foreign_task_ptr task;
|
||||
try {
|
||||
auto task = tasks::task_handler{tm.local(), id};
|
||||
status = co_await task.wait_for_task(timeout);
|
||||
task = co_await tasks::task_manager::invoke_on_task(tm, id, std::function([] (tasks::task_manager::task_ptr task) {
|
||||
return task->done().then_wrapped([task] (auto f) {
|
||||
task->unregister_task();
|
||||
// done() is called only because we want the task to be complete before getting its status.
|
||||
// The future should be ignored here as the result does not matter.
|
||||
f.ignore_ready_future();
|
||||
return make_foreign(task);
|
||||
});
|
||||
}));
|
||||
} catch (tasks::task_manager::task_not_found& e) {
|
||||
throw bad_param_exception(e.what());
|
||||
} catch (timed_out_error& e) {
|
||||
throw httpd::base_exception{e.what(), http::reply::status_type::request_timeout};
|
||||
}
|
||||
co_return make_status(status);
|
||||
auto s = co_await retrieve_status(task);
|
||||
co_return make_status(s);
|
||||
});
|
||||
|
||||
tm::get_task_status_recursively.set(r, [&_tm = tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto& tm = _tm;
|
||||
auto id = tasks::task_id{utils::UUID{req->get_path_param("task_id")}};
|
||||
try {
|
||||
auto task = tasks::task_handler{tm.local(), id};
|
||||
auto res = co_await task.get_status_recursively(true);
|
||||
auto id = tasks::task_id{utils::UUID{req->param["task_id"]}};
|
||||
std::queue<tasks::task_manager::foreign_task_ptr> q;
|
||||
utils::chunked_vector<full_task_status> res;
|
||||
|
||||
std::function<future<>(output_stream<char>&&)> f = [r = std::move(res)] (output_stream<char>&& os) -> future<> {
|
||||
auto s = std::move(os);
|
||||
auto res = std::move(r);
|
||||
co_await s.write("[");
|
||||
std::string delim = "";
|
||||
for (auto& status: res) {
|
||||
co_await s.write(std::exchange(delim, ", "));
|
||||
co_await formatter::write(s, make_status(status));
|
||||
tasks::task_manager::foreign_task_ptr task;
|
||||
try {
|
||||
// Get requested task.
|
||||
task = co_await tasks::task_manager::invoke_on_task(tm, id, std::function([] (tasks::task_manager::task_ptr task) -> future<tasks::task_manager::foreign_task_ptr> {
|
||||
if (task->is_complete()) {
|
||||
task->unregister_task();
|
||||
}
|
||||
co_await s.write("]");
|
||||
co_await s.close();
|
||||
};
|
||||
co_return f;
|
||||
co_return task;
|
||||
}));
|
||||
} catch (tasks::task_manager::task_not_found& e) {
|
||||
throw bad_param_exception(e.what());
|
||||
}
|
||||
|
||||
// Push children's statuses in BFS order.
|
||||
q.push(co_await task.copy()); // Task cannot be moved since we need it to be alive during whole loop execution.
|
||||
while (!q.empty()) {
|
||||
auto& current = q.front();
|
||||
res.push_back(co_await retrieve_status(current));
|
||||
for (auto& child: current->get_children()) {
|
||||
q.push(co_await child.copy());
|
||||
}
|
||||
q.pop();
|
||||
}
|
||||
|
||||
std::function<future<>(output_stream<char>&&)> f = [r = std::move(res)] (output_stream<char>&& os) -> future<> {
|
||||
auto s = std::move(os);
|
||||
auto res = std::move(r);
|
||||
co_await s.write("[");
|
||||
std::string delim = "";
|
||||
for (auto& status: res) {
|
||||
co_await s.write(std::exchange(delim, ", "));
|
||||
co_await formatter::write(s, make_status(status));
|
||||
}
|
||||
co_await s.write("]");
|
||||
co_await s.close();
|
||||
};
|
||||
co_return f;
|
||||
});
|
||||
|
||||
tm::get_and_update_ttl.set(r, [&cfg] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
@@ -213,37 +261,6 @@ void set_task_manager(http_context& ctx, routes& r, sharded<tasks::task_manager>
|
||||
}
|
||||
co_return json::json_return_type(ttl);
|
||||
});
|
||||
|
||||
tm::get_ttl.set(r, [&cfg] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
uint32_t ttl = cfg.task_ttl_seconds();
|
||||
co_return json::json_return_type(ttl);
|
||||
});
|
||||
|
||||
tm::drain_tasks.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
co_await tm.invoke_on_all([&req] (tasks::task_manager& tm) -> future<> {
|
||||
tasks::task_manager::module_ptr module;
|
||||
try {
|
||||
module = tm.find_module(req->get_path_param("module"));
|
||||
} catch (...) {
|
||||
throw bad_param_exception(fmt::format("{}", std::current_exception()));
|
||||
}
|
||||
|
||||
const auto& local_tasks = module->get_local_tasks();
|
||||
std::vector<tasks::task_id> ids;
|
||||
ids.reserve(local_tasks.size());
|
||||
std::transform(begin(local_tasks), end(local_tasks), std::back_inserter(ids), [] (const auto& task) {
|
||||
return task.second->is_complete() ? task.first : tasks::task_id::create_null_id();
|
||||
});
|
||||
|
||||
for (auto&& id : ids) {
|
||||
if (id) {
|
||||
module->unregister_task(id);
|
||||
}
|
||||
co_await maybe_yield();
|
||||
}
|
||||
});
|
||||
co_return json_void();
|
||||
});
|
||||
}
|
||||
|
||||
void unset_task_manager(http_context& ctx, routes& r) {
|
||||
@@ -254,8 +271,6 @@ void unset_task_manager(http_context& ctx, routes& r) {
|
||||
tm::wait_task.unset(r);
|
||||
tm::get_task_status_recursively.unset(r);
|
||||
tm::get_and_update_ttl.unset(r);
|
||||
tm::get_ttl.unset(r);
|
||||
tm::drain_tasks.unset(r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "task_manager_test.hh"
|
||||
#include "api/api-doc/task_manager_test.json.hh"
|
||||
#include "tasks/test_module.hh"
|
||||
#include "utils/overloaded_functor.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -62,8 +61,8 @@ void set_task_manager_test(http_context& ctx, routes& r, sharded<tasks::task_man
|
||||
auto module = tms.local().find_module("test");
|
||||
id = co_await module->make_task<tasks::test_task_impl>(shard, id, keyspace, table, entity, data);
|
||||
co_await tms.invoke_on(shard, [id] (tasks::task_manager& tm) {
|
||||
auto it = tm.get_local_tasks().find(id);
|
||||
if (it != tm.get_local_tasks().end()) {
|
||||
auto it = tm.get_all_tasks().find(id);
|
||||
if (it != tm.get_all_tasks().end()) {
|
||||
it->second->start();
|
||||
}
|
||||
});
|
||||
@@ -73,16 +72,9 @@ void set_task_manager_test(http_context& ctx, routes& r, sharded<tasks::task_man
|
||||
tmt::unregister_test_task.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto id = tasks::task_id{utils::UUID{req->query_parameters["task_id"]}};
|
||||
try {
|
||||
co_await tasks::task_manager::invoke_on_task(tm, id, [] (tasks::task_manager::task_variant task_v) -> future<> {
|
||||
return std::visit(overloaded_functor{
|
||||
[] (tasks::task_manager::task_ptr task) -> future<> {
|
||||
tasks::test_task test_task{task};
|
||||
co_await test_task.unregister_task();
|
||||
},
|
||||
[] (tasks::task_manager::virtual_task_ptr task) {
|
||||
return make_ready_future();
|
||||
}
|
||||
}, task_v);
|
||||
co_await tasks::task_manager::invoke_on_task(tm, id, [] (tasks::task_manager::task_ptr task) -> future<> {
|
||||
tasks::test_task test_task{task};
|
||||
co_await test_task.unregister_task();
|
||||
});
|
||||
} catch (tasks::task_manager::task_not_found& e) {
|
||||
throw bad_param_exception(e.what());
|
||||
@@ -91,26 +83,20 @@ void set_task_manager_test(http_context& ctx, routes& r, sharded<tasks::task_man
|
||||
});
|
||||
|
||||
tmt::finish_test_task.set(r, [&tm] (std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto id = tasks::task_id{utils::UUID{req->get_path_param("task_id")}};
|
||||
auto id = tasks::task_id{utils::UUID{req->param["task_id"]}};
|
||||
auto it = req->query_parameters.find("error");
|
||||
bool fail = it != req->query_parameters.end();
|
||||
std::string error = fail ? it->second : "";
|
||||
|
||||
try {
|
||||
co_await tasks::task_manager::invoke_on_task(tm, id, [fail, error = std::move(error)] (tasks::task_manager::task_variant task_v) -> future<> {
|
||||
return std::visit(overloaded_functor{
|
||||
[fail, error = std::move(error)] (tasks::task_manager::task_ptr task) -> future<> {
|
||||
tasks::test_task test_task{task};
|
||||
if (fail) {
|
||||
co_await test_task.finish_failed(std::make_exception_ptr(std::runtime_error(error)));
|
||||
} else {
|
||||
co_await test_task.finish();
|
||||
}
|
||||
},
|
||||
[] (tasks::task_manager::virtual_task_ptr task) {
|
||||
return make_ready_future();
|
||||
}
|
||||
}, task_v);
|
||||
co_await tasks::task_manager::invoke_on_task(tm, id, [fail, error = std::move(error)] (tasks::task_manager::task_ptr task) {
|
||||
tasks::test_task test_task{task};
|
||||
if (fail) {
|
||||
test_task.finish_failed(std::make_exception_ptr(std::runtime_error(error)));
|
||||
} else {
|
||||
test_task.finish();
|
||||
}
|
||||
return make_ready_future<>();
|
||||
});
|
||||
} catch (tasks::task_manager::task_not_found& e) {
|
||||
throw bad_param_exception(e.what());
|
||||
|
||||
@@ -11,19 +11,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
#include "api.hh"
|
||||
|
||||
namespace tasks {
|
||||
class task_manager;
|
||||
}
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
|
||||
namespace api {
|
||||
struct http_context;
|
||||
void set_task_manager_test(http_context& ctx, seastar::httpd::routes& r, seastar::sharded<tasks::task_manager>& tm);
|
||||
void unset_task_manager_test(http_context& ctx, seastar::httpd::routes& r);
|
||||
|
||||
void set_task_manager_test(http_context& ctx, httpd::routes& r, sharded<tasks::task_manager>& tm);
|
||||
void unset_task_manager_test(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include <seastar/core/coroutine.hh>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "api/api.hh"
|
||||
#include "api/storage_service.hh"
|
||||
@@ -30,7 +29,7 @@ using ks_cf_func = std::function<future<json::json_return_type>(http_context&, s
|
||||
|
||||
static auto wrap_ks_cf(http_context &ctx, ks_cf_func f) {
|
||||
return [&ctx, f = std::move(f)](std::unique_ptr<http::request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto table_infos = parse_table_infos(keyspace, ctx, req->query_parameters, "cf");
|
||||
return f(ctx, std::move(req), std::move(keyspace), std::move(table_infos));
|
||||
};
|
||||
@@ -62,7 +61,7 @@ void set_tasks_compaction_module(http_context& ctx, routes& r, sharded<service::
|
||||
|
||||
t::force_keyspace_cleanup_async.set(r, [&ctx, &ss](std::unique_ptr<http::request> req) -> future<json::json_return_type> {
|
||||
auto& db = ctx.db;
|
||||
auto keyspace = validate_keyspace(ctx, req);
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto table_infos = parse_table_infos(keyspace, ctx, req->query_parameters, "cf");
|
||||
apilog.info("force_keyspace_cleanup_async: keyspace={} tables={}", keyspace, table_infos);
|
||||
if (!co_await ss.local().is_cleanup_allowed(keyspace)) {
|
||||
|
||||
13
api/tasks.hh
13
api/tasks.hh
@@ -8,20 +8,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
#include "db/snapshot-ctl.hh"
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
|
||||
namespace service {
|
||||
class storage_service;
|
||||
}
|
||||
#include "api.hh"
|
||||
#include "db/config.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct http_context;
|
||||
void set_tasks_compaction_module(http_context& ctx, httpd::routes& r, sharded<service::storage_service>& ss, sharded<db::snapshot_ctl>& snap_ctl);
|
||||
void unset_tasks_compaction_module(http_context& ctx, httpd::routes& r);
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@ using namespace json;
|
||||
void set_token_metadata(http_context& ctx, routes& r, sharded<locator::shared_token_metadata>& tm) {
|
||||
ss::local_hostid.set(r, [&tm](std::unique_ptr<http::request> req) {
|
||||
auto id = tm.local().get()->get_my_id();
|
||||
if (!bool(id)) {
|
||||
throw not_found_exception("local host ID is not yet set");
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(id.to_sstring());
|
||||
});
|
||||
|
||||
@@ -34,7 +31,7 @@ void set_token_metadata(http_context& ctx, routes& r, sharded<locator::shared_to
|
||||
});
|
||||
|
||||
ss::get_node_tokens.set(r, [&tm] (std::unique_ptr<http::request> req) {
|
||||
gms::inet_address addr(req->get_path_param("endpoint"));
|
||||
gms::inet_address addr(req->param["endpoint"]);
|
||||
auto& local_tm = *tm.local().get();
|
||||
const auto host_id = local_tm.get_host_id_if_known(addr);
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(host_id ? local_tm.get_tokens(*host_id): std::vector<dht::token>{}, [](const dht::token& i) {
|
||||
@@ -71,7 +68,7 @@ void set_token_metadata(http_context& ctx, routes& r, sharded<locator::shared_to
|
||||
|
||||
ss::get_host_id_map.set(r, [&tm](const_req req) {
|
||||
std::vector<ss::mapper> res;
|
||||
return map_to_key_value(tm.local().get()->get_endpoint_to_host_id_map(), res);
|
||||
return map_to_key_value(tm.local().get()->get_endpoint_to_host_id_map_for_reading(), res);
|
||||
});
|
||||
|
||||
static auto host_or_broadcast = [&tm](const_req req) {
|
||||
|
||||
@@ -9,16 +9,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sharded.hh>
|
||||
|
||||
namespace seastar::httpd {
|
||||
class routes;
|
||||
}
|
||||
#include "api/api_init.hh"
|
||||
|
||||
namespace locator { class shared_token_metadata; }
|
||||
|
||||
namespace api {
|
||||
struct http_context;
|
||||
void set_token_metadata(http_context& ctx, seastar::httpd::routes& r, seastar::sharded<locator::shared_token_metadata>& tm);
|
||||
void unset_token_metadata(http_context& ctx, seastar::httpd::routes& r);
|
||||
|
||||
void set_token_metadata(http_context& ctx, httpd::routes& r, sharded<locator::shared_token_metadata>& tm);
|
||||
void unset_token_metadata(http_context& ctx, httpd::routes& r);
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ target_link_libraries(scylla_auth
|
||||
Seastar::seastar
|
||||
xxHash::xxhash
|
||||
PRIVATE
|
||||
absl::headers
|
||||
cql3
|
||||
idl
|
||||
wasmtime_bindings
|
||||
|
||||
@@ -59,15 +59,15 @@ public:
|
||||
return make_ready_future<authenticated_user>(anonymous_user());
|
||||
}
|
||||
|
||||
virtual future<> create(std::string_view, const authentication_options& options, ::service::group0_batch&) override {
|
||||
virtual future<> create(std::string_view, const authentication_options& options) override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
virtual future<> alter(std::string_view, const authentication_options& options, ::service::group0_batch&) override {
|
||||
virtual future<> alter(std::string_view, const authentication_options& options) override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
virtual future<> drop(std::string_view, ::service::group0_batch&) override {
|
||||
virtual future<> drop(std::string_view) override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "auth/authorizer.hh"
|
||||
#include <seastar/core/future.hh>
|
||||
|
||||
namespace cql3 {
|
||||
class query_processor;
|
||||
@@ -45,12 +44,12 @@ public:
|
||||
return make_ready_future<permission_set>(permissions::ALL);
|
||||
}
|
||||
|
||||
virtual future<> grant(std::string_view, permission_set, const resource&, ::service::group0_batch&) override {
|
||||
virtual future<> grant(std::string_view, permission_set, const resource&) override {
|
||||
return make_exception_future<>(
|
||||
unsupported_authorization_operation("GRANT operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
virtual future<> revoke(std::string_view, permission_set, const resource&, ::service::group0_batch&) override {
|
||||
virtual future<> revoke(std::string_view, permission_set, const resource&) override {
|
||||
return make_exception_future<>(
|
||||
unsupported_authorization_operation("REVOKE operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
@@ -61,12 +60,14 @@ public:
|
||||
"LIST PERMISSIONS operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
virtual future<> revoke_all(std::string_view, ::service::group0_batch&) override {
|
||||
return make_ready_future();
|
||||
virtual future<> revoke_all(std::string_view) override {
|
||||
return make_exception_future(
|
||||
unsupported_authorization_operation("REVOKE operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
virtual future<> revoke_all(const resource&, ::service::group0_batch&) override {
|
||||
return make_ready_future();
|
||||
virtual future<> revoke_all(const resource&) override {
|
||||
return make_exception_future(
|
||||
unsupported_authorization_operation("REVOKE operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
virtual const resource_set& protected_resources() const override {
|
||||
|
||||
@@ -50,7 +50,7 @@ inline bool is_anonymous(const authenticated_user& u) noexcept {
|
||||
/// The user name, or "anonymous".
|
||||
///
|
||||
template <>
|
||||
struct fmt::formatter<auth::authenticated_user> : fmt::formatter<string_view> {
|
||||
struct fmt::formatter<auth::authenticated_user> : fmt::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const auth::authenticated_user& u, FormatContext& ctx) const {
|
||||
if (u.name) {
|
||||
|
||||
@@ -25,25 +25,6 @@ enum class authentication_option {
|
||||
options
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<auth::authentication_option> : fmt::formatter<string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const auth::authentication_option a, FormatContext& ctx) const {
|
||||
using enum auth::authentication_option;
|
||||
switch (a) {
|
||||
case password:
|
||||
return formatter<string_view>::format("PASSWORD", ctx);
|
||||
case options:
|
||||
return formatter<string_view>::format("OPTIONS", ctx);
|
||||
}
|
||||
std::abort();
|
||||
}
|
||||
};
|
||||
|
||||
namespace auth {
|
||||
|
||||
using authentication_option_set = std::unordered_set<authentication_option>;
|
||||
|
||||
using custom_options = std::unordered_map<sstring, sstring>;
|
||||
@@ -65,3 +46,18 @@ public:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<auth::authentication_option> : fmt::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const auth::authentication_option a, FormatContext& ctx) const {
|
||||
using enum auth::authentication_option;
|
||||
switch (a) {
|
||||
case password:
|
||||
return formatter<std::string_view>::format("PASSWORD", ctx);
|
||||
case options:
|
||||
return formatter<std::string_view>::format("OPTIONS", ctx);
|
||||
}
|
||||
std::abort();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
#include <seastar/core/enum.hh>
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
|
||||
#include "auth/authentication_options.hh"
|
||||
#include "auth/resource.hh"
|
||||
#include "auth/sasl_challenge.hh"
|
||||
|
||||
#include "service/raft/raft_group0_client.hh"
|
||||
|
||||
namespace db {
|
||||
class config;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
///
|
||||
/// The options provided must be a subset of `supported_options()`.
|
||||
///
|
||||
virtual future<> create(std::string_view role_name, const authentication_options& options, ::service::group0_batch& mc) = 0;
|
||||
virtual future<> create(std::string_view role_name, const authentication_options& options) = 0;
|
||||
|
||||
///
|
||||
/// Alter the authentication record of an existing user.
|
||||
@@ -115,12 +115,12 @@ public:
|
||||
///
|
||||
/// Callers must ensure that the specification of `alterable_options()` is adhered to.
|
||||
///
|
||||
virtual future<> alter(std::string_view role_name, const authentication_options& options, ::service::group0_batch& mc) = 0;
|
||||
virtual future<> alter(std::string_view role_name, const authentication_options& options) = 0;
|
||||
|
||||
///
|
||||
/// Delete the authentication record for a user. This will disallow the user from logging in.
|
||||
///
|
||||
virtual future<> drop(std::string_view role_name, ::service::group0_batch&) = 0;
|
||||
virtual future<> drop(std::string_view role_name) = 0;
|
||||
|
||||
///
|
||||
/// Query for custom options (those corresponding to \ref authentication_options::options).
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include <vector>
|
||||
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
|
||||
#include "auth/permission.hh"
|
||||
#include "auth/resource.hh"
|
||||
#include "service/raft/raft_group0_client.hh"
|
||||
#include "seastarx.hh"
|
||||
|
||||
namespace auth {
|
||||
@@ -81,14 +81,14 @@ public:
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if granting permissions is not supported.
|
||||
///
|
||||
virtual future<> grant(std::string_view role_name, permission_set, const resource&, ::service::group0_batch&) = 0;
|
||||
virtual future<> grant(std::string_view role_name, permission_set, const resource&) = 0;
|
||||
|
||||
///
|
||||
/// Revoke a set of permissions from a role for a particular \ref resource.
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if revoking permissions is not supported.
|
||||
///
|
||||
virtual future<> revoke(std::string_view role_name, permission_set, const resource&, ::service::group0_batch&) = 0;
|
||||
virtual future<> revoke(std::string_view role_name, permission_set, const resource&) = 0;
|
||||
|
||||
///
|
||||
/// Query for all directly granted permissions.
|
||||
@@ -102,14 +102,14 @@ public:
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if revoking permissions is not supported.
|
||||
///
|
||||
virtual future<> revoke_all(std::string_view role_name, ::service::group0_batch&) = 0;
|
||||
virtual future<> revoke_all(std::string_view role_name) = 0;
|
||||
|
||||
///
|
||||
/// Revoke all permissions granted to any role for a particular resource.
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if revoking permissions is not supported.
|
||||
///
|
||||
virtual future<> revoke_all(const resource&, ::service::group0_batch&) = 0;
|
||||
virtual future<> revoke_all(const resource&) = 0;
|
||||
|
||||
///
|
||||
/// System resources used internally as part of the implementation. These are made inaccessible to users.
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
#include "auth/certificate_authenticator.hh"
|
||||
|
||||
#include <regex>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "utils/class_registrator.hh"
|
||||
#include "utils/to_string.hh"
|
||||
#include "data_dictionary/data_dictionary.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "db/config.hh"
|
||||
@@ -76,7 +74,7 @@ auth::certificate_authenticator::certificate_authenticator(cql3::query_processor
|
||||
continue;
|
||||
} catch (std::out_of_range&) {
|
||||
// just fallthrough
|
||||
} catch (boost::regex_error&) {
|
||||
} catch (std::regex_error&) {
|
||||
std::throw_with_nested(std::invalid_argument(fmt::format("Invalid query expression: {}", map.at(cfg_query_attr))));
|
||||
}
|
||||
}
|
||||
@@ -149,7 +147,7 @@ future<std::optional<auth::authenticated_user>> auth::certificate_authenticator:
|
||||
co_return username;
|
||||
}
|
||||
}
|
||||
throw exceptions::authentication_exception(seastar::format("Subject '{}'/'{}' does not match any query expression", subject, altname));
|
||||
throw exceptions::authentication_exception(format("Subject '{}'/'{}' does not match any query expression", subject, altname));
|
||||
}
|
||||
|
||||
|
||||
@@ -157,16 +155,16 @@ future<auth::authenticated_user> auth::certificate_authenticator::authenticate(c
|
||||
throw exceptions::authentication_exception("Cannot authenticate using attribute map");
|
||||
}
|
||||
|
||||
future<> auth::certificate_authenticator::create(std::string_view role_name, const authentication_options& options, ::service::group0_batch& mc) {
|
||||
future<> auth::certificate_authenticator::create(std::string_view role_name, const authentication_options& options) {
|
||||
// TODO: should we keep track of roles/enforce existence? Role manager should deal with this...
|
||||
co_return;
|
||||
}
|
||||
|
||||
future<> auth::certificate_authenticator::alter(std::string_view role_name, const authentication_options& options, ::service::group0_batch& mc) {
|
||||
future<> auth::certificate_authenticator::alter(std::string_view role_name, const authentication_options& options) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
future<> auth::certificate_authenticator::drop(std::string_view role_name, ::service::group0_batch&) {
|
||||
future<> auth::certificate_authenticator::drop(std::string_view role_name) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include "auth/authenticator.hh"
|
||||
|
||||
namespace cql3 {
|
||||
@@ -46,9 +47,9 @@ public:
|
||||
future<authenticated_user> authenticate(const credentials_map& credentials) const override;
|
||||
future<std::optional<authenticated_user>> authenticate(session_dn_func) const override;
|
||||
|
||||
future<> create(std::string_view role_name, const authentication_options& options, ::service::group0_batch& mc) override;
|
||||
future<> alter(std::string_view role_name, const authentication_options& options, ::service::group0_batch&) override;
|
||||
future<> drop(std::string_view role_name, ::service::group0_batch&) override;
|
||||
future<> create(std::string_view role_name, const authentication_options& options) override;
|
||||
future<> alter(std::string_view role_name, const authentication_options& options) override;
|
||||
future<> drop(std::string_view role_name) override;
|
||||
|
||||
future<custom_options> query_custom_options(std::string_view role_name) const override;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "mutation/canonical_mutation.hh"
|
||||
#include "schema/schema_fwd.hh"
|
||||
#include "timestamp.hh"
|
||||
#include "utils/assert.hh"
|
||||
#include "utils/exponential_backoff_retry.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "cql3/statements/create_table_statement.hh"
|
||||
@@ -24,6 +23,8 @@
|
||||
#include "service/migration_manager.hh"
|
||||
#include "service/raft/group0_state_machine.hh"
|
||||
#include "timeout_config.hh"
|
||||
#include "db/config.hh"
|
||||
#include "db/system_auth_keyspace.hh"
|
||||
#include "utils/error_injection.hh"
|
||||
|
||||
namespace auth {
|
||||
@@ -40,14 +41,14 @@ constinit const std::string_view AUTH_PACKAGE_NAME("org.apache.cassandra.auth.")
|
||||
static logging::logger auth_log("auth");
|
||||
|
||||
bool legacy_mode(cql3::query_processor& qp) {
|
||||
return qp.auth_version < db::system_keyspace::auth_version_t::v2;
|
||||
return qp.auth_version < db::system_auth_keyspace::version_t::v2;
|
||||
}
|
||||
|
||||
std::string_view get_auth_ks_name(cql3::query_processor& qp) {
|
||||
if (legacy_mode(qp)) {
|
||||
return meta::legacy::AUTH_KS;
|
||||
}
|
||||
return db::system_keyspace::NAME;
|
||||
return db::system_auth_keyspace::NAME;
|
||||
}
|
||||
|
||||
// Func must support being invoked more than once.
|
||||
@@ -64,15 +65,15 @@ future<> do_after_system_ready(seastar::abort_source& as, seastar::noncopyable_f
|
||||
}).discard_result();
|
||||
}
|
||||
|
||||
static future<> create_legacy_metadata_table_if_missing_impl(
|
||||
static future<> create_metadata_table_if_missing_impl(
|
||||
std::string_view table_name,
|
||||
cql3::query_processor& qp,
|
||||
std::string_view cql,
|
||||
::service::migration_manager& mm) {
|
||||
SCYLLA_ASSERT(this_shard_id() == 0); // once_among_shards makes sure a function is executed on shard 0 only
|
||||
assert(this_shard_id() == 0); // once_among_shards makes sure a function is executed on shard 0 only
|
||||
|
||||
auto db = qp.db();
|
||||
auto parsed_statement = cql3::query_processor::parse_statement(cql, cql3::dialect{});
|
||||
auto parsed_statement = cql3::query_processor::parse_statement(cql);
|
||||
auto& parsed_cf_statement = static_cast<cql3::statements::raw::cf_statement&>(*parsed_statement);
|
||||
|
||||
parsed_cf_statement.prepare_keyspace(meta::legacy::AUTH_KS);
|
||||
@@ -97,12 +98,12 @@ static future<> create_legacy_metadata_table_if_missing_impl(
|
||||
}
|
||||
}
|
||||
|
||||
future<> create_legacy_metadata_table_if_missing(
|
||||
future<> create_metadata_table_if_missing(
|
||||
std::string_view table_name,
|
||||
cql3::query_processor& qp,
|
||||
std::string_view cql,
|
||||
::service::migration_manager& mm) noexcept {
|
||||
return futurize_invoke(create_legacy_metadata_table_if_missing_impl, table_name, qp, cql, mm);
|
||||
return futurize_invoke(create_metadata_table_if_missing_impl, table_name, qp, cql, mm);
|
||||
}
|
||||
|
||||
::service::query_state& internal_distributed_query_state() noexcept {
|
||||
@@ -122,7 +123,7 @@ static future<> announce_mutations_with_guard(
|
||||
::service::raft_group0_client& group0_client,
|
||||
std::vector<canonical_mutation> muts,
|
||||
::service::group0_guard group0_guard,
|
||||
seastar::abort_source& as,
|
||||
seastar::abort_source* as,
|
||||
std::optional<::service::raft_timeout> timeout) {
|
||||
auto group0_cmd = group0_client.prepare_command(
|
||||
::service::write_mutations{
|
||||
@@ -137,8 +138,8 @@ static future<> announce_mutations_with_guard(
|
||||
future<> announce_mutations_with_batching(
|
||||
::service::raft_group0_client& group0_client,
|
||||
start_operation_func_t start_operation_func,
|
||||
std::function<::service::mutations_generator(api::timestamp_type t)> gen,
|
||||
seastar::abort_source& as,
|
||||
std::function<mutations_generator(api::timestamp_type& t)> gen,
|
||||
seastar::abort_source* as,
|
||||
std::optional<::service::raft_timeout> timeout) {
|
||||
// account for command's overhead, it's better to use smaller threshold than constantly bounce off the limit
|
||||
size_t memory_threshold = group0_client.max_command_size() * 0.75;
|
||||
@@ -189,7 +190,7 @@ future<> announce_mutations(
|
||||
::service::raft_group0_client& group0_client,
|
||||
const sstring query_string,
|
||||
std::vector<data_value_or_unset> values,
|
||||
seastar::abort_source& as,
|
||||
seastar::abort_source* as,
|
||||
std::optional<::service::raft_timeout> timeout) {
|
||||
auto group0_guard = co_await group0_client.start_operation(as, timeout);
|
||||
auto timestamp = group0_guard.write_timestamp();
|
||||
@@ -202,17 +203,4 @@ future<> announce_mutations(
|
||||
co_await announce_mutations_with_guard(group0_client, std::move(cmuts), std::move(group0_guard), as, timeout);
|
||||
}
|
||||
|
||||
future<> collect_mutations(
|
||||
cql3::query_processor& qp,
|
||||
::service::group0_batch& collector,
|
||||
const sstring query_string,
|
||||
std::vector<data_value_or_unset> values) {
|
||||
auto muts = co_await qp.get_mutations_internal(
|
||||
query_string,
|
||||
internal_distributed_query_state(),
|
||||
collector.write_timestamp(),
|
||||
std::move(values));
|
||||
collector.add_mutations(std::move(muts), format("auth internal statement: {}", query_string));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user