Add JDK21 support

Patch by Josh McKenzie; reviewed by Mick Semb Wever and Ekaterina Dimitrova for CASSANDRA-18831

Co-authored-by: Aleksey Yeschenko
Co-authored-by: Achilles Benetopoulos
Co-authored-by: Mick Semb Wever
This commit is contained in:
Josh McKenzie 2024-11-19 13:27:03 -05:00 committed by Josh McKenzie
parent 4184e759de
commit 940739a488
87 changed files with 1182 additions and 672 deletions

View File

@ -56,7 +56,8 @@ fi
################################ ################################
if grep "^ID=" /etc/os-release | grep -q 'debian\|ubuntu' ; then if grep "^ID=" /etc/os-release | grep -q 'debian\|ubuntu' ; then
sudo update-java-alternatives --set java-1.${java_version}.0-openjdk-$(dpkg --print-architecture) sudo update-alternatives --set java /usr/lib/jvm/java-${java_version}-openjdk-$(dpkg --print-architecture)/bin/java
sudo update-alternatives --set javac /usr/lib/jvm/java-${java_version}-openjdk-$(dpkg --print-architecture)/bin/javac
else else
sudo alternatives --set java $(alternatives --display java | grep "family java-${java_version}-openjdk" | cut -d' ' -f1) sudo alternatives --set java $(alternatives --display java | grep "family java-${java_version}-openjdk" | cut -d' ' -f1)
sudo alternatives --set javac $(alternatives --display javac | grep "family java-${java_version}-openjdk" | cut -d' ' -f1) sudo alternatives --set javac $(alternatives --display javac | grep "family java-${java_version}-openjdk" | cut -d' ' -f1)

View File

@ -42,6 +42,7 @@ RUN yum -y install \
git \ git \
java-11-openjdk-devel \ java-11-openjdk-devel \
java-17-openjdk-devel \ java-17-openjdk-devel \
java-21-openjdk-devel \
make \ make \
rpm-build \ rpm-build \
sudo \ sudo \

View File

@ -18,5 +18,5 @@
# #
# Creates the tarball artifact # Creates the tarball artifact
$(dirname -- "$0")/_docker_run.sh bullseye-build.docker build-artifacts.sh $1 $(dirname -- "$0")/_docker_run.sh debian-build.docker build-artifacts.sh $1
exit $? exit $?

View File

@ -32,5 +32,5 @@ echo
# #
# Creates the debian package # Creates the debian package
$(dirname -- "$0")/_docker_run.sh bullseye-build.docker docker/_build-debian.sh $1 $(dirname -- "$0")/_docker_run.sh debian-build.docker docker/_build-debian.sh $1
exit $? exit $?

View File

@ -18,5 +18,5 @@
# #
# Build the jars # Build the jars
$(dirname -- "$0")/_docker_run.sh bullseye-build.docker build-jars.sh $1 $(dirname -- "$0")/_docker_run.sh debian-build.docker build-jars.sh $1
exit $? exit $?

View File

@ -20,4 +20,4 @@
export CASSANDRA_DOCKER_ANT_OPTS="-Ddependency-check.home.base=/tmp" export CASSANDRA_DOCKER_ANT_OPTS="-Ddependency-check.home.base=/tmp"
$(dirname -- "$0")/_docker_run.sh bullseye-build.docker check-code.sh $1 $(dirname -- "$0")/_docker_run.sh debian-build.docker check-code.sh $1

View File

@ -36,16 +36,31 @@ RUN echo 'Acquire::http::Timeout "60";' > /etc/apt/apt.conf.d/80proxy.conf
RUN echo 'Acquire::ftp::Timeout "60";' >> /etc/apt/apt.conf.d/80proxy.conf RUN echo 'Acquire::ftp::Timeout "60";' >> /etc/apt/apt.conf.d/80proxy.conf
# install deps # install deps
RUN until apt-get update \ RUN until apt-get update && apt-get -y install ant build-essential curl devscripts ed git sudo \
&& apt-get -y install ant build-essential curl devscripts ed git sudo \
python3-pip rsync procps dh-python quilt bash-completion ; \ python3-pip rsync procps dh-python quilt bash-completion ; \
do echo "apt failed… trying again in 10s… " ; sleep 10 ; done do echo "apt failed… trying again in 10s… " ; sleep 10 ; done
RUN until apt-get update \ RUN until apt-get install -y --no-install-recommends openjdk-11-jdk openjdk-17-jdk; \
&& apt-get install -y --no-install-recommends openjdk-11-jdk openjdk-17-jdk ; \
do echo "apt failed… trying again in 10s… " ; sleep 10 ; done do echo "apt failed… trying again in 10s… " ; sleep 10 ; done
RUN update-java-alternatives --set java-1.11.0-openjdk-$(dpkg --print-architecture) # Download and install OpenJDK 21.0.2 for the current architecture
RUN sh -c '\
JDK_OS=linux ;\
JDK_TAR="openjdk-21.0.2_${JDK_OS}-$(uname -m | sed "s/x86_64/x64/")_bin.tar.gz" ;\
JDK_VERSION_SHAS="08db1392a48d4eb5ea5315cf8f18b89dbaf36cda663ba882cf03c704c9257ec2 a2def047a73941e01a73739f92755f86b895811afb1f91243db214cff5bdac3f 8fd09e15dc406387a0aba70bf5d99692874e999bf9cd9208b452b5d76ac922d3 b3d588e16ec1e0ef9805d8a696591bd518a5cea62567da8f53b5ce32d11d22e4" ;\
curl -L --fail --silent --retry 2 --retry-delay 5 --max-time 3600 "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/${JDK_TAR}" -o $JDK_TAR ;\
JDK_SHA="$(sha256sum $JDK_TAR | cut -d" " -f2)" ;\
echo "$JDK_VERSION_SHAS" | sed "s/ /\n/g" | grep -q "$JDK_SHA" || { echo "SHA256 mismatch for $JDK_TAR $JDK_SHA"; exit 1; } ;\
mkdir -p /usr/lib/jvm/java-21-openjdk-$(dpkg --print-architecture) ;\
tar -C /usr/lib/jvm/java-21-openjdk-$(dpkg --print-architecture) --strip-components=1 -xzf $JDK_TAR ;\
rm $JDK_TAR'
# Add OpenJDK 21 to update-alternatives
RUN update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-$(dpkg --print-architecture)/bin/java 1
RUN update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-21-openjdk-$(dpkg --print-architecture)/bin/javac 1
RUN update-alternatives --set java /usr/lib/jvm/java-11-openjdk-$(dpkg --print-architecture)/bin/java
RUN update-alternatives --set javac /usr/lib/jvm/java-11-openjdk-$(dpkg --print-architecture)/bin/javac
# python3 is needed for the gen-doc target # python3 is needed for the gen-doc target
RUN pip install --upgrade pip RUN pip install --upgrade pip
@ -58,11 +73,8 @@ RUN sh -c '\
GO_VERSION="1.24.3" ;\ GO_VERSION="1.24.3" ;\
GO_VERSION_SHAS="3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b 64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb" ;\ GO_VERSION_SHAS="3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b 64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb" ;\
GO_OS=linux ;\ GO_OS=linux ;\
[ $(uname) = "Darwin" ] && GO_OS=darwin ;\ GO_TAR="go${GO_VERSION}.${GO_OS}-$(dpkg --print-architecture).tar.gz" ;\
GO_PLATFORM=amd64 ;\ curl -L --fail --silent --retry 2 --retry-delay 5 --max-time 3600 https://go.dev/dl/$GO_TAR -o $GO_TAR ;\
[ $(uname -m) = "aarch64" ] && GO_PLATFORM=arm64 ;\
GO_TAR="go${GO_VERSION}.${GO_OS}-${GO_PLATFORM}.tar.gz" ;\
curl -L --fail --silent --retry 2 --retry-delay 5 --max-time 30 https://go.dev/dl/$GO_TAR -o $GO_TAR ;\
GO_SHA="$(sha256sum $GO_TAR | cut -d" " -f2)" ;\ GO_SHA="$(sha256sum $GO_TAR | cut -d" " -f2)" ;\
echo "$GO_VERSION_SHAS" | sed "s/ /\n/g" | grep -q "$GO_SHA" || { echo "SHA256 mismatch for $GO_TAR $GO_SHA"; exit 1; } ;\ echo "$GO_VERSION_SHAS" | sed "s/ /\n/g" | grep -q "$GO_SHA" || { echo "SHA256 mismatch for $GO_TAR $GO_SHA"; exit 1; } ;\
tar -C /usr/local -xzf $GO_TAR ;\ tar -C /usr/local -xzf $GO_TAR ;\
@ -70,4 +82,11 @@ RUN sh -c '\
ENV GOROOT="/usr/local/go" ENV GOROOT="/usr/local/go"
ENV GOPATH="$BUILD_HOME/go" ENV GOPATH="$BUILD_HOME/go"
ENV PATH="$PATH:/usr/local/go/bin" ENV PATH="$PATH:/usr/local/go/bin"
# allow lower UIDs and GIDs
RUN sed -i 's/UID_MIN 1000/UID_MIN 100/' /etc/login.defs
RUN sed -i 's/UID_MIN 1000/UID_MIN 10/' /etc/login.defs
# suppress warnings about mismatching ownership
RUN git config --global --add safe.directory ${CASSANDRA_DIR}

View File

@ -134,7 +134,7 @@ docker --version
pushd ${cassandra_dir}/.build >/dev/null pushd ${cassandra_dir}/.build >/dev/null
# build test image # build test image
dockerfile="ubuntu2004_test.docker" dockerfile="ubuntu-test.docker"
image_tag="$(md5sum docker/${dockerfile} | cut -d' ' -f1)" image_tag="$(md5sum docker/${dockerfile} | cut -d' ' -f1)"
image_name="apache/cassandra-${dockerfile/.docker/}:${image_tag}" image_name="apache/cassandra-${dockerfile/.docker/}:${image_tag}"
docker_mounts="-v ${cassandra_dir}:/home/cassandra/cassandra -v "${build_dir}":/home/cassandra/cassandra/build -v ${m2_dir}:/home/cassandra/.m2/repository" docker_mounts="-v ${cassandra_dir}:/home/cassandra/cassandra -v "${build_dir}":/home/cassandra/cassandra/build -v ${m2_dir}:/home/cassandra/.m2/repository"

View File

@ -11,7 +11,7 @@
# limitations under the License. # limitations under the License.
FROM ubuntu:20.04 FROM ubuntu:20.04
MAINTAINER Apache Cassandra <dev@cassandra.apache.org> LABEL org.opencontainers.image.authors="Apache Cassandra <dev@cassandra.apache.org>"
# CONTEXT is expected to be cassandra/.build # CONTEXT is expected to be cassandra/.build
@ -52,7 +52,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
python3.11 python3.11-venv python3.11-dev \ python3.11 python3.11-venv python3.11-dev \
virtualenv net-tools libev4 libev-dev wget gcc libxml2 libxslt1-dev \ virtualenv net-tools libev4 libev-dev wget gcc libxml2 libxslt1-dev \
vim lsof sudo libjemalloc2 dumb-init locales rsync \ vim lsof sudo libjemalloc2 dumb-init locales rsync \
openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk ant ant-optional openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk openjdk-21-jdk ant ant-optional
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.8 2 RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.8 2
@ -71,8 +71,9 @@ RUN chmod 0644 /opt/requirements.txt
RUN pip3 install virtualenv virtualenv-clone RUN pip3 install virtualenv virtualenv-clone
RUN pip3 install --upgrade wheel RUN pip3 install --upgrade wheel
# make Java 8 the default executable (we use to run all tests against Java 8) # make Java 11 the default executable
RUN update-java-alternatives --set java-1.8.0-openjdk-$(dpkg --print-architecture) RUN update-alternatives --set java /usr/lib/jvm/java-11-openjdk-$(dpkg --print-architecture)/bin/java
RUN update-alternatives --set javac /usr/lib/jvm/java-11-openjdk-$(dpkg --print-architecture)/bin/javac
# enable legacy TLSv1 and TLSv1.1 (CASSANDRA-16848) # enable legacy TLSv1 and TLSv1.1 (CASSANDRA-16848)
RUN find /etc -type f -name java.security -exec sed -i 's/TLSv1, TLSv1.1//' {} \; RUN find /etc -type f -name java.security -exec sed -i 's/TLSv1, TLSv1.1//' {} \;

View File

@ -36,8 +36,8 @@
</license> </license>
</licenses> </licenses>
<properties> <properties>
<bytebuddy.version>1.12.13</bytebuddy.version> <bytebuddy.version>1.17.8</bytebuddy.version>
<byteman.version>4.0.20</byteman.version> <byteman.version>4.0.26</byteman.version>
<netty.version>4.1.130.Final</netty.version> <netty.version>4.1.130.Final</netty.version>
<ohc.version>0.5.1</ohc.version> <ohc.version>0.5.1</ohc.version>
<async-profiler.version>4.2</async-profiler.version> <async-profiler.version>4.2</async-profiler.version>
@ -494,13 +494,13 @@
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>4.7.0</version> <version>5.12.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId> <artifactId>mockito-inline</artifactId>
<version>4.7.0</version> <version>5.2.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -891,7 +891,8 @@
<dependency> <dependency>
<groupId>net.openhft</groupId> <groupId>net.openhft</groupId>
<artifactId>chronicle-queue</artifactId> <artifactId>chronicle-queue</artifactId>
<version>5.23.37</version> <!-- chronicle ea builds before the corresponding Final are code identical; Final builds are not made available on Maven -->
<version>5.25ea16</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>tools</artifactId> <artifactId>tools</artifactId>
@ -907,7 +908,12 @@
<dependency> <dependency>
<groupId>net.openhft</groupId> <groupId>net.openhft</groupId>
<artifactId>chronicle-core</artifactId> <artifactId>chronicle-core</artifactId>
<version>2.23.36</version> <!-- While we broadly don't want to use ea libraries in the project, the Chronicle ecosystem has moved
to an approach of only allowing paid usage for non-ea builds. The ea builds are identical to the .0 release
of the next major version, so "ea" in this context signifies "stable GA build" for this major release line.
We plan to move away from chronicle for this and other reasons; the details of the conversation on the mailing
list can be found here: https://issues.apache.org/jira/browse/CASSANDRA-18831?focusedCommentId=17902034&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-17902034 -->
<version>2.25ea14</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>chronicle-analytics</artifactId> <artifactId>chronicle-analytics</artifactId>
@ -922,7 +928,8 @@
<dependency> <dependency>
<groupId>net.openhft</groupId> <groupId>net.openhft</groupId>
<artifactId>chronicle-bytes</artifactId> <artifactId>chronicle-bytes</artifactId>
<version>2.23.33</version> <!-- See above for details about exception re: inclusion of ea lib -->
<version>2.25ea10</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
@ -933,7 +940,8 @@
<dependency> <dependency>
<groupId>net.openhft</groupId> <groupId>net.openhft</groupId>
<artifactId>chronicle-wire</artifactId> <artifactId>chronicle-wire</artifactId>
<version>2.23.39</version> <!-- See above for details about exception re: inclusion of ea lib -->
<version>2.25ea15</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>compiler</artifactId> <artifactId>compiler</artifactId>
@ -949,7 +957,8 @@
<dependency> <dependency>
<groupId>net.openhft</groupId> <groupId>net.openhft</groupId>
<artifactId>chronicle-threads</artifactId> <artifactId>chronicle-threads</artifactId>
<version>2.23.25</version> <!-- See above for details about exception re: inclusion of ea lib -->
<version>2.25ea7</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<!-- pulls in affinity-3.23ea1 which pulls in third-party-bom-3.22.4-SNAPSHOT --> <!-- pulls in affinity-3.23ea1 which pulls in third-party-bom-3.22.4-SNAPSHOT -->

View File

@ -138,7 +138,7 @@ set -e # enable immediate exit if venv setup fails
# fresh virtualenv and test logs results everytime # fresh virtualenv and test logs results everytime
[[ "/" == "${DIST_DIR}" ]] || rm -rf "${DIST_DIR}/venv" "${DIST_DIR}/test/{html,output,logs}" [[ "/" == "${DIST_DIR}" ]] || rm -rf "${DIST_DIR}/venv" "${DIST_DIR}/test/{html,output,logs}"
# re-use when possible the pre-installed virtualenv found in the cassandra-ubuntu2004_test docker image # re-use when possible the pre-installed virtualenv found in the cassandra2004_test docker image
virtualenv-clone ${BUILD_HOME}/env${python_version} ${DIST_DIR}/venv || virtualenv --python=python${python_version} ${DIST_DIR}/venv virtualenv-clone ${BUILD_HOME}/env${python_version} ${DIST_DIR}/venv || virtualenv --python=python${python_version} ${DIST_DIR}/venv
source ${DIST_DIR}/venv/bin/activate source ${DIST_DIR}/venv/bin/activate
pip3 install --exists-action w -r ${CASSANDRA_DTEST_DIR}/requirements.txt pip3 install --exists-action w -r ${CASSANDRA_DTEST_DIR}/requirements.txt

View File

@ -254,7 +254,11 @@ _run_testlist() {
for ((i=0; i < _test_iterations; i++)); do for ((i=0; i < _test_iterations; i++)); do
[ "${_test_iterations}" -eq 1 ] || printf " run ${i}\n" [ "${_test_iterations}" -eq 1 ] || printf " run ${i}\n"
set +o errexit set +o errexit
ant "$_testlist_target" -Dtest.classlistprefix="${_target_prefix}" -Dtest.classlistfile=<(echo "${testlist}") -Dtest.timeout="${_test_timeout}" ${ANT_TEST_OPTS} ant "$_testlist_target" \
-Dtest.classlistprefix="${_target_prefix}" \
-Dtest.classlistfile=<(echo "${testlist}") \
-Dtest.timeout="${_test_timeout}" \
${ANT_TEST_OPTS}
ant_status=$? ant_status=$?
set -o errexit set -o errexit
if [[ $ant_status -ne 0 ]]; then if [[ $ant_status -ne 0 ]]; then

View File

@ -352,7 +352,7 @@ def build(command, cell) {
test -f .jenkins/Jenkinsfile || { echo "Invalid git fork/branch"; exit 1; } test -f .jenkins/Jenkinsfile || { echo "Invalid git fork/branch"; exit 1; }
grep -q "Jenkins CI declaration" .jenkins/Jenkinsfile || { echo "Only Cassandra 5.0+ supported"; exit 1; } grep -q "Jenkins CI declaration" .jenkins/Jenkinsfile || { echo "Only Cassandra 5.0+ supported"; exit 1; }
""" """
fetchDockerImages("redhat" == cell.step ? ['almalinux-build'] : ['bullseye-build']) fetchDockerImages("redhat" == cell.step ? ['almalinux-build'] : ['debian-build'])
def cell_suffix = "_jdk${cell.jdk}_${cell.arch}" def cell_suffix = "_jdk${cell.jdk}_${cell.arch}"
def logfile = "stage-logs/${JOB_NAME}_${BUILD_NUMBER}_${cell.step}${cell_suffix}_attempt${attempt}.log.xz" def logfile = "stage-logs/${JOB_NAME}_${BUILD_NUMBER}_${cell.step}${cell_suffix}_attempt${attempt}.log.xz"
def script_vars = "#!/bin/bash \n set -o pipefail ; " // pipe to tee needs pipefail def script_vars = "#!/bin/bash \n set -o pipefail ; " // pipe to tee needs pipefail
@ -391,7 +391,7 @@ def test(command, cell) {
withEnv(cell.collect { k, v -> "${k}=${v}" }) { withEnv(cell.collect { k, v -> "${k}=${v}" }) {
ws("workspace/${JOB_NAME}/${BUILD_NUMBER}/${cell.step}/${cell.arch}/jdk-${cell.jdk}/python-${cell.python}") { ws("workspace/${JOB_NAME}/${BUILD_NUMBER}/${cell.step}/${cell.arch}/jdk-${cell.jdk}/python-${cell.python}") {
fetchSource(cell.step, cell.arch, cell.jdk) fetchSource(cell.step, cell.arch, cell.jdk)
fetchDockerImages(['ubuntu2004_test']) fetchDockerImages(['ubuntu-test'])
def cell_suffix = "_jdk${cell.jdk}_python_${cell.python}_${cell.cython}_${cell.arch}_${cell.split}_${splits}" def cell_suffix = "_jdk${cell.jdk}_python_${cell.python}_${cell.cython}_${cell.arch}_${cell.split}_${splits}"
def logfile = "stage-logs/${JOB_NAME}_${BUILD_NUMBER}_${cell.step}${cell_suffix}_attempt${attempt}.log.xz" def logfile = "stage-logs/${JOB_NAME}_${BUILD_NUMBER}_${cell.step}${cell_suffix}_attempt${attempt}.log.xz"
def script_vars = "#!/bin/bash \n set -o pipefail ; " // pipe to tee needs pipefail def script_vars = "#!/bin/bash \n set -o pipefail ; " // pipe to tee needs pipefail
@ -594,11 +594,11 @@ def generateTestReports() {
echo "Report for \${target} (\$(find build/test/output/\${target} -name '*.xml' | wc -l) test files)" echo "Report for \${target} (\$(find build/test/output/\${target} -name '*.xml' | wc -l) test files)"
CASSANDRA_DOCKER_ANT_OPTS="-Dbuild.test.output.dir=build/test/output/\${target} -Dbuild.test.report.dir=build/test/reports/\${target}" CASSANDRA_DOCKER_ANT_OPTS="-Dbuild.test.output.dir=build/test/output/\${target} -Dbuild.test.report.dir=build/test/reports/\${target}"
export CASSANDRA_DOCKER_ANT_OPTS export CASSANDRA_DOCKER_ANT_OPTS
.build/docker/_docker_run.sh bullseye-build.docker ci/generate-test-report.sh .build/docker/_docker_run.sh debian-build.docker ci/generate-test-report.sh
fi fi
done done
.build/docker/_docker_run.sh bullseye-build.docker ci/generate-ci-summary.sh || echo "failed generate-ci-summary.sh" .build/docker/_docker_run.sh debian-build.docker ci/generate-ci-summary.sh || echo "failed generate-ci-summary.sh"
tar -cf build/results_details.tar -C build/test/ reports tar -cf build/results_details.tar -C build/test/ reports
xz -8f build/results_details.tar ) ${teeSuffix} xz -8f build/results_details.tar ) ${teeSuffix}

View File

@ -80,6 +80,9 @@ using the provided 'sstableupgrade' tool.
New features New features
------------ ------------
- JDK21 and Generational ZGC are now officially supported and generational ZGC is now the default Garbage Collector
when using JDK21. See jvm21-server.options for more details and configuration parameters. We do not recommend using
non-generational ZGC with Apache Cassandra.
[The following is a placeholder, to be revised asap] [The following is a placeholder, to be revised asap]
- CEP-37 Auto Repair is a fully automated scheduler that provides repair orchestration within Apache Cassandra. This - CEP-37 Auto Repair is a fully automated scheduler that provides repair orchestration within Apache Cassandra. This
significantly reduces operational overhead by eliminating the need for operators to deploy external tools to submit significantly reduces operational overhead by eliminating the need for operators to deploy external tools to submit
@ -205,6 +208,11 @@ Upgrading
Deprecation Deprecation
----------- -----------
- `use_deterministic_table_id` is no longer supported and should be removed from cassandra.yaml. Table IDs may still be supplied explicitly on CREATE. - `use_deterministic_table_id` is no longer supported and should be removed from cassandra.yaml. Table IDs may still be supplied explicitly on CREATE.
- Updating dependencies for JDK21 necessitated updating Chronicle Queue, which has changed the enums used for log
rolling (cassandra.yaml -> full_query_logging_options:roll_cycle). Older legacy options will still work for the
foreseeable future but you will see warnings in logs and future dependency upgrades may break your log rolling param.
The default log rolling param has been changed from HOURLY to FAST_HOURLY, primarily different on how frequently
indexes are built (256 in FAST_HOURLY vs. 16 in HOURLY).
- IEndpointSnitch has been deprecated as ClusterMetadata is now the source of truth regarding topology information. - IEndpointSnitch has been deprecated as ClusterMetadata is now the source of truth regarding topology information.
The responsibilities of the snitch have been broken out to a handful of new classes: The responsibilities of the snitch have been broken out to a handful of new classes:
* o.a.c.locator.Locator provides datacenter and rack info for endpoints. This is not configurable as topology is * o.a.c.locator.Locator provides datacenter and rack info for endpoints. This is not configurable as topology is

View File

@ -114,56 +114,36 @@ if [ -z $JAVA ] ; then
fi fi
# Matches variable 'java.supported' in build.xml # Matches variable 'java.supported' in build.xml
java_versions_supported=11,17 java_versions_supported="11 17 21"
java_version_string=$(IFS=" "; echo "${java_versions_supported}")
# Determine the sort of JVM we'll be running on. # Determine the sort of JVM we'll be running on.
java_ver_output=`"${JAVA:-java}" -version 2>&1` JAVA_VERSION=$(java -version 2>&1 | grep '[openjdk|java] version' | cut -d '"' -f2 | cut -d '.' -f1)
jvmver=`echo "$java_ver_output" | grep '[openjdk|java] version' | awk -F'"' 'NR==1 {print $2}' | cut -d\- -f1`
JVM_VERSION=${jvmver%_*}
short=$(echo "${jvmver}" | cut -c1-2)
# Unsupported JDKs below the upper supported version are not allowed supported=0
if [ "$short" != "$(echo "$java_versions_supported" | cut -d, -f1)" ] && [ "$JVM_VERSION" \< "$(echo "$java_versions_supported" | cut -d, -f2)" ] ; then for version in ${java_versions_supported}; do
echo "Unsupported Java $JVM_VERSION. Supported are $java_versions_supported" if [ "$version" -eq "$JAVA_VERSION" ]; then
exit 1; supported=1
fi fi
# Allow execution of supported Java versions, and newer if CASSANDRA_JDK_UNSUPPORTED is set done
is_supported_version=$(echo "$java_versions_supported" | tr "," '\n' | grep -F -x "$short")
if [ -z "$is_supported_version" ] ; then
if [ -z "$CASSANDRA_JDK_UNSUPPORTED" ] ; then
echo "Unsupported Java $JVM_VERSION. Supported are $java_versions_supported"
echo "If you would like to test with newer Java versions set CASSANDRA_JDK_UNSUPPORTED to any value (for example, CASSANDRA_JDK_UNSUPPORTED=true). Unset the parameter for default behavior"
exit 1;
else
echo "######################################################################"
echo "Warning! You are using JDK$short. This Cassandra version only supports $java_versions_supported."
echo "######################################################################"
fi
fi
JAVA_VERSION=$short
jvm=`echo "$java_ver_output" | grep -A 1 '[openjdk|java] version' | awk 'NR==2 {print $1}'` if [ "$supported" -eq 0 ]; then
case "$jvm" in if [ -z "$CASSANDRA_JDK_UNSUPPORTED" ]; then
OpenJDK) echo "Unsupported Java $JAVA_VERSION. Supported are $java_version_string"
JVM_VENDOR=OpenJDK echo "If you would like to test with newer Java versions set CASSANDRA_JDK_UNSUPPORTED to any value (for example, CASSANDRA_JDK_UNSUPPORTED=true). Unset the parameter for default behavior"
# this will be "64-Bit" or "32-Bit" exit 1
JVM_ARCH=`echo "$java_ver_output" | awk 'NR==3 {print $2}'` else
;; echo "######################################################################"
"Java(TM)") echo "Warning! You are using JDK$JAVA_VERSION. This Cassandra version only supports $java_version_string"
JVM_VENDOR=Oracle echo "######################################################################"
# this will be "64-Bit" or "32-Bit" fi
JVM_ARCH=`echo "$java_ver_output" | awk 'NR==3 {print $3}'` fi
;;
*)
# Help fill in other JVM values
JVM_VENDOR=other
JVM_ARCH=unknown
;;
esac
# Read user-defined JVM options from jvm-server.options file # Read user-defined JVM options from jvm-server.options file
JVM_OPTS_FILE=$CASSANDRA_CONF/jvm${jvmoptions_variant:--clients}.options JVM_OPTS_FILE=$CASSANDRA_CONF/jvm${jvmoptions_variant:--clients}.options
if [ $JAVA_VERSION -ge 17 ] ; then if [ $JAVA_VERSION -ge 21 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm21${jvmoptions_variant:--clients}.options
elif [ $JAVA_VERSION -ge 17 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm17${jvmoptions_variant:--clients}.options JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm17${jvmoptions_variant:--clients}.options
elif [ $JAVA_VERSION -ge 11 ] ; then elif [ $JAVA_VERSION -ge 11 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm11${jvmoptions_variant:--clients}.options JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm11${jvmoptions_variant:--clients}.options

112
build.xml
View File

@ -45,7 +45,7 @@
The use of both CASSANDRA_USE_JDK11 and use-jdk11 is deprecated. The use of both CASSANDRA_USE_JDK11 and use-jdk11 is deprecated.
--> -->
<property name="java.default" value="11" /> <property name="java.default" value="11" />
<property name="java.supported" value="11,17" /> <property name="java.supported" value="11,17,21" />
<!-- directory details --> <!-- directory details -->
<property name="basedir" value="."/> <property name="basedir" value="."/>
@ -173,7 +173,7 @@
<!-- When updating ASM, please, do consider whether you might need to update also FBUtilities#ASM_BYTECODE_VERSION <!-- When updating ASM, please, do consider whether you might need to update also FBUtilities#ASM_BYTECODE_VERSION
and the simulator InterceptClasses#BYTECODE_VERSION, in particular if we are looking to provide Cassandra support and the simulator InterceptClasses#BYTECODE_VERSION, in particular if we are looking to provide Cassandra support
for newer JDKs (CASSANDRA-17873). --> for newer JDKs (CASSANDRA-17873). -->
<property name="asm.version" value="9.4"/> <property name="asm.version" value="9.5"/>
<property name="allocation-instrumenter.version" value="3.1.0"/> <property name="allocation-instrumenter.version" value="3.1.0"/>
<condition property="is.source.artifact"> <condition property="is.source.artifact">
@ -323,14 +323,82 @@
<string>--add-opens java.base/java.lang=ALL-UNNAMED</string> <string>--add-opens java.base/java.lang=ALL-UNNAMED</string>
<string>--add-opens java.base/java.util=ALL-UNNAMED</string> <string>--add-opens java.base/java.util=ALL-UNNAMED</string>
<string>--add-opens java.base/java.nio=ALL-UNNAMED</string> <string>--add-opens java.base/java.nio=ALL-UNNAMED</string>
<string>--add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED</string> <string>--add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED</string>
<!-- Needed for in-jvm dtests straddling 6.0+ -->
<string>--add-opens java.base/java.util.concurrent=ALL-UNNAMED</string>
<string>--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED</string>
</resources> </resources>
<pathconvert property="_jvm17_args_concat" refid="_jvm17_arg_items" pathsep=" "/> <pathconvert property="_jvm17_args_concat" refid="_jvm17_arg_items" pathsep=" "/>
<condition property="java-jvmargs" value="${_jvm17_args_concat}"> <condition property="java-jvmargs" value="${_jvm17_args_concat}">
<equals arg1="${ant.java.version}" arg2="17"/> <equals arg1="${ant.java.version}" arg2="17"/>
</condition> </condition>
<resources id="_jvm21_arg_items">
<string>-Djdk.attach.allowAttachSelf=true</string>
<string>-XX:+UseZGC</string>
<string>-XX:+ZGenerational</string>
<!-- Temporary workaround for jamm having incorrect default CompressedOops for JDK21 -->
<!-- Without this, memory layout and sizing calculations will be incorrect as jamm assumes compressedOops -->
<string>-XX:-UseCompressedOops</string>
<!-- <string>-XX:+UseLargePages</string> -->
<!-- Max size ZGC will _attempt_ to stay below -->
<!-- <string>-XX:+SoftMaxHeapSize=12G</string> -->
<!-- Disable returning memory to the OS -->
<!-- <string>-XX:-ZUncommit</string> -->
<!-- Time memory should have been unused before eligible for uncommit in seconds -->
<!-- <string>-XX:ZUncommitDelay=300</string> -->
<!-- Pay the tax up front to pretouch memory to smooth out latency later -->
<!-- <string>-XX:+AlwaysPreTouch</string> -->
<!-- Need to explicitly allow security manager on JDK21; deprecated for removal -->
<string>-Djava.security.manager=allow</string>
<string>--add-exports java.base/java.lang.ref=ALL-UNNAMED</string>
<string>--add-exports java.base/java.lang.reflect=ALL-UNNAMED</string>
<string>--add-exports java.base/jdk.internal.misc=ALL-UNNAMED</string>
<string>--add-exports java.base/jdk.internal.ref=ALL-UNNAMED</string>
<string>--add-exports java.base/sun.nio.ch=ALL-UNNAMED</string>
<string>--add-exports java.management.rmi/com.sun.jmx.remote.internal.rmi=ALL-UNNAMED</string>
<string>--add-exports java.rmi/sun.rmi.registry=ALL-UNNAMED</string>
<string>--add-exports java.rmi/sun.rmi.server=ALL-UNNAMED</string>
<string>--add-exports java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED</string>
<string>--add-exports java.sql/java.sql=ALL-UNNAMED</string>
<string>--add-exports jdk.unsupported/sun.misc=ALL-UNNAMED</string>
<string>--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</string>
<string>--add-exports jdk.attach/sun.tools.attach=ALL-UNNAMED</string>
<string>--add-opens java.base/java.io=ALL-UNNAMED</string>
<string>--add-opens java.base/java.lang=ALL-UNNAMED</string>
<string>--add-opens java.base/java.lang.module=ALL-UNNAMED</string>
<string>--add-opens java.base/java.lang.reflect=ALL-UNNAMED</string>
<string>--add-opens java.base/java.math=ALL-UNNAMED</string>
<string>--add-opens java.base/java.net=ALL-UNNAMED</string>
<string>--add-opens java.base/java.nio=ALL-UNNAMED</string>
<string>--add-opens java.base/java.util=ALL-UNNAMED</string>
<string>--add-opens java.base/java.util.concurrent=ALL-UNNAMED</string>
<string>--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED</string>
<string>--add-opens java.base/jdk.internal.loader=ALL-UNNAMED</string>
<string>--add-opens java.base/jdk.internal.math=ALL-UNNAMED</string>
<string>--add-opens java.base/jdk.internal.module=ALL-UNNAMED</string>
<string>--add-opens java.base/jdk.internal.ref=ALL-UNNAMED</string>
<string>--add-opens java.base/jdk.internal.reflect=ALL-UNNAMED</string>
<string>--add-opens java.base/sun.nio.ch=ALL-UNNAMED</string>
<string>--add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED</string>
<string>--add-opens jdk.management/com.sun.management=ALL-UNNAMED</string>
</resources>
<pathconvert property="_jvm21_args_concat" refid="_jvm21_arg_items" pathsep=" "/>
<condition property="java-jvmargs" value="${_jvm21_args_concat}" else="">
<equals arg1="${ant.java.version}" arg2="21"/>
</condition>
<!-- <!--
JVM arguments for tests. JVM arguments for tests.
@ -344,21 +412,48 @@
buffer construction (see CASSANDRA-16493) buffer construction (see CASSANDRA-16493)
--> -->
<resources id="_jvm11_test_arg_items"> <resources id="_jvm11_test_arg_items">
<string>-XX:+HeapDumpOnOutOfMemoryError</string>
<string>-XX:-CMSClassUnloadingEnabled</string> <string>-XX:-CMSClassUnloadingEnabled</string>
<string>-Dio.netty.tryReflectionSetAccessible=true</string> <string>-Dio.netty.tryReflectionSetAccessible=true</string>
<string>-XX:MaxMetaspaceSize=2G</string> <string>-XX:MaxMetaspaceSize=2G</string>
</resources> </resources>
<pathconvert property="_jvm11_test_arg_items_concat" refid="_jvm11_test_arg_items" pathsep=" "/> <pathconvert property="_jvm11_test_arg_items_concat" refid="_jvm11_test_arg_items" pathsep=" "/>
<resources id="_jvm17_test_arg_items"> <resources id="_jvm17_test_arg_items">
<string>-XX:+HeapDumpOnOutOfMemoryError</string>
<string>-Dio.netty.tryReflectionSetAccessible=true</string> <string>-Dio.netty.tryReflectionSetAccessible=true</string>
<!-- Open things up so we don't run into any jamm rejections that'd then go unsafe and crash out on in-jvm dtests -->
<string>--add-opens=java.base/java.lang=ALL-UNNAMED</string>
<string>--add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED</string>
</resources> </resources>
<pathconvert property="_jvm17_test_arg_items_concat" refid="_jvm17_test_arg_items" pathsep=" "/> <pathconvert property="_jvm17_test_arg_items_concat" refid="_jvm17_test_arg_items" pathsep=" "/>
<resources id="_jvm21_test_arg_items">
<string>-XX:+HeapDumpOnOutOfMemoryError</string>
<string>-Dnet.bytebuddy.experimental=true</string>
<string>-Djava.security.manager=allow</string>
<string>-Dio.netty.tryReflectionSetAccessible=true</string>
<!-- Open things up so we don't run into any jamm rejections that'd then go unsafe and crash out on in-jvm dtests-->
<string>--add-opens=java.base/java.lang=ALL-UNNAMED</string>
<string>--add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED</string>
<!-- Revert to pre-JEP-416 Core Reflection implementation (without method handles) -->
<!-- without this, FieldUtils cannot overwrite static final fields, breaking some tests -->
<!-- removed in 22 (JDK-8309635) so will need a proper fix for 25 -->
<string>-Djdk.reflect.useDirectMethodHandle=false</string>
</resources>
<pathconvert property="_jvm21_test_arg_items_concat" refid="_jvm21_test_arg_items" pathsep=" "/>
<condition property="_std-test-jvmargs" value="${_jvm11_test_arg_items_concat}"> <condition property="_std-test-jvmargs" value="${_jvm11_test_arg_items_concat}">
<equals arg1="${ant.java.version}" arg2="11"/> <equals arg1="${ant.java.version}" arg2="11"/>
</condition> </condition>
<condition property="_std-test-jvmargs" value="${_jvm17_test_arg_items_concat}"> <condition property="_std-test-jvmargs" value="${_jvm17_test_arg_items_concat}">
<equals arg1="${ant.java.version}" arg2="17"/> <equals arg1="${ant.java.version}" arg2="17"/>
</condition> </condition>
<condition property="_std-test-jvmargs" value="${_jvm21_test_arg_items_concat}">
<equals arg1="${ant.java.version}" arg2="21"/>
</condition>
<!-- needed to compile org.apache.cassandra.utils.JMXServerUtils --> <!-- needed to compile org.apache.cassandra.utils.JMXServerUtils -->
<!-- needed to compile org.apache.cassandra.distributed.impl.Instance--> <!-- needed to compile org.apache.cassandra.distributed.impl.Instance-->
@ -484,9 +579,13 @@
<target name="gen-cql3-grammar" depends="check-gen-cql3-grammar" unless="cql3current"> <target name="gen-cql3-grammar" depends="check-gen-cql3-grammar" unless="cql3current">
<echo>Building Grammar ${build.src.antlr}/Cql.g ...</echo> <echo>Building Grammar ${build.src.antlr}/Cql.g ...</echo>
<!-- antlr will crash on the first run on JDK21 and subsequently succeed if we don't fork it and execute in a separate JVM... -->
<java classname="org.antlr.Tool" <java classname="org.antlr.Tool"
classpathref="cql3-grammar.classpath" classpathref="cql3-grammar.classpath"
failonerror="true"> failonerror="true"
fork="true"
outputproperty="antlr.output"
errorproperty="antlr.error">
<arg value="-Xconversiontimeout" /> <arg value="-Xconversiontimeout" />
<arg value="10000" /> <arg value="10000" />
<arg value="${build.src.antlr}/Cql.g" /> <arg value="${build.src.antlr}/Cql.g" />
@ -1237,6 +1336,8 @@
<jvmarg value="-ea"/> <jvmarg value="-ea"/>
<jvmarg value="-Djava.io.tmpdir=${tmp.dir}"/> <jvmarg value="-Djava.io.tmpdir=${tmp.dir}"/>
<jvmarg value="-Dcassandra.debugrefcount=true"/> <jvmarg value="-Dcassandra.debugrefcount=true"/>
<!-- Pull this up for genZGC / JDK21 -->
<jvmarg value="-Xms512M"/>
<jvmarg value="${jvm_xss}"/> <jvmarg value="${jvm_xss}"/>
<!-- When we do classloader manipulation SoftReferences can cause memory leaks <!-- When we do classloader manipulation SoftReferences can cause memory leaks
that can OOM our test runs. The next two settings informs our GC that can OOM our test runs. The next two settings informs our GC
@ -1491,6 +1592,7 @@
<test if="withMethods" name="${test.name}" methods="${test.methods}" todir="${build.test.output.dir}/" outfile="TEST-${test.name}-${test.methods}"/> <test if="withMethods" name="${test.name}" methods="${test.methods}" todir="${build.test.output.dir}/" outfile="TEST-${test.name}-${test.methods}"/>
<test if="withoutMethods" name="${test.name}" todir="${build.test.output.dir}/" outfile="TEST-${test.name}"/> <test if="withoutMethods" name="${test.name}" todir="${build.test.output.dir}/" outfile="TEST-${test.name}"/>
<jvmarg value="-Dlogback.configurationFile=test/conf/logback-burntest.xml"/> <jvmarg value="-Dlogback.configurationFile=test/conf/logback-burntest.xml"/>
<jvmarg value="-Dnet.bytebuddy.experimental=true"/>
</testmacro> </testmacro>
</target> </target>

View File

@ -100,6 +100,8 @@ echo $JVM_OPTS | grep -q UseConcMarkSweepGC
USING_CMS=$? USING_CMS=$?
echo $JVM_OPTS | grep -q +UseG1GC echo $JVM_OPTS | grep -q +UseG1GC
USING_G1=$? USING_G1=$?
echo $JVM_OPTS | grep -q +UseZGC
USING_ZGC=$?
calculate_heap_sizes calculate_heap_sizes
@ -177,6 +179,11 @@ if [ $DEFINED_XMN -eq 0 ] && [ $USING_G1 -eq 0 ]; then
exit 1 exit 1
fi fi
# If a user tries to use -Xmn with ZGC we should let them know it's not going to work. Not worth killing the node over though.
if [ $DEFINED_XMN -eq 0 ] && [ $USING_ZGC -eq 0]; then
echo "-Xmn does nothing when used in conjunction with ZGC; this setting will be ignored."
fi
if [ $USING_G1 -eq 0 ] && [ $DEFINED_PARALLEL_GC_THREADS -ne 0 ] && [ $DEFINED_CONC_GC_THREADS -ne 0 ] ; then if [ $USING_G1 -eq 0 ] && [ $DEFINED_PARALLEL_GC_THREADS -ne 0 ] && [ $DEFINED_CONC_GC_THREADS -ne 0 ] ; then
# Set ParallelGCThreads and ConcGCThreads equal to number of cpu cores. # Set ParallelGCThreads and ConcGCThreads equal to number of cpu cores.
# Setting both to the same value is important to reduce STW durations. # Setting both to the same value is important to reduce STW durations.

View File

@ -2129,7 +2129,7 @@ audit_logging_options:
# excluded_categories: # excluded_categories:
# included_users: # included_users:
# excluded_users: # excluded_users:
# roll_cycle: HOURLY # roll_cycle: FAST_HOURLY
# block: true # block: true
# max_queue_weight: 268435456 # 256 MiB # max_queue_weight: 268435456 # 256 MiB
# max_log_size: 17179869184 # 16 GiB # max_log_size: 17179869184 # 16 GiB
@ -2144,7 +2144,7 @@ audit_logging_options:
# nodetool enablefullquerylog # nodetool enablefullquerylog
# full_query_logging_options: # full_query_logging_options:
# log_dir: # log_dir:
# roll_cycle: HOURLY # roll_cycle: FAST_HOURLY
# block: true # block: true
# max_queue_weight: 268435456 # 256 MiB # max_queue_weight: 268435456 # 256 MiB
# max_log_size: 17179869184 # 16 GiB # max_log_size: 17179869184 # 16 GiB

View File

@ -185,6 +185,9 @@
# For production use you may wish to adjust this for your environment. # For production use you may wish to adjust this for your environment.
# If that's the case, see MAX_HEAP_SIZE (and HEAP_NEWSIZE for CMS) in cassandra-env.sh # If that's the case, see MAX_HEAP_SIZE (and HEAP_NEWSIZE for CMS) in cassandra-env.sh
# Need experimental bytebuddy for JDK21
-Dnet.bytebuddy.experimental
##################### #####################
# OFF-HEAP SETTINGS # # OFF-HEAP SETTINGS #
##################### #####################

View File

@ -0,0 +1,53 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###########################################################################
# jvm21-clients.options #
# #
# See jvm-clients.options. This file is specific for Java 21 #
###########################################################################
###################
# JPMS SETTINGS #
###################
-Djdk.attach.allowAttachSelf=true
--add-exports java.base/java.lang.ref=ALL-UNNAMED
--add-exports java.base/jdk.internal.misc=ALL-UNNAMED
--add-exports java.management.rmi/com.sun.jmx.remote.internal.rmi=ALL-UNNAMED
--add-exports java.rmi/sun.rmi.registry=ALL-UNNAMED
--add-exports java.rmi/sun.rmi.server=ALL-UNNAMED
--add-exports java.sql/java.sql=ALL-UNNAMED
--add-exports jdk.unsupported/sun.misc=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.module=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.base/jdk.internal.math=ALL-UNNAMED
--add-opens java.base/jdk.internal.module=ALL-UNNAMED
--add-opens java.base/jdk.internal.ref=ALL-UNNAMED
--add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
# The newline in the end of file is intentional

146
conf/jvm21-server.options Normal file
View File

@ -0,0 +1,146 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###########################################################################
# jvm21-server.options #
# #
# See jvm-server.options. This file is specific for Java 21 and newer. #
###########################################################################
#################
# GC SETTINGS #
#################
### Generational ZGC settings
-XX:+UseZGC
-XX:+ZGenerational
# Temporary workaround for jamm's incorrect default CompressedOops; if you're using ZGC this needs to be set
-XX:-UseCompressedOops
# Blanket enabling of all the THP / large pages available in the env
# -XX:+UseLargePages
# Max size ZGC will _attempt_ to stay below
# -XX:+SoftMaxHeapSize=12G
# Don't let ZGC return unclaimed memory to the OS by setting the following
# -XX:-ZUncommit
# Time memory should have been unused before eligible for uncommit in seconds
# -XX:ZUncommitDelay=300
# Tell GC to touch heap and allocate and fault in memory pages backing heap at startup. Can prevent later allocation pauses
# -XX:+AlwaysPreTouch
### G1 Settings
## Use the Hotspot garbage-first collector.
#-XX:+UseG1GC
#-XX:+ParallelRefProcEnabled
#-XX:MaxTenuringThreshold=2
#-XX:G1HeapRegionSize=16m
# Floor the young generation size to 50% of the heap size
#-XX:+UnlockExperimentalVMOptions
#-XX:G1NewSizePercent=50
# Have the JVM do less remembered set work during STW, instead
# preferring concurrent GC. Reduces p99.9 latency.
#-XX:G1RSetUpdatingPauseTimePercent=5
# Main G1GC tunable: lowering the pause target will lower throughput and vise versa.
# 200ms is the JVM default and lowest viable setting
# 1000ms increases throughput. Keep it smaller than the timeouts in cassandra.yaml.
#-XX:MaxGCPauseMillis=300
## Optional G1 Settings
# Save CPU time on large (>= 16GB) heaps by delaying region scanning
# until the heap is 70% full. The default in Hotspot 8u40 is 40%.
#-XX:InitiatingHeapOccupancyPercent=70
# For systems with > 8 cores, the default ParallelGCThreads is 5/8 the number of logical cores.
# Otherwise equal to the number of cores when 8 or less.
# Machines with > 10 cores should try setting these to <= full cores.
# By default, ConcGCThreads is 1/4 of ParallelGCThreads.
# Setting both to the same value can reduce STW durations.
# When leaving both unset then cassandra-env.sh will set them both to the number of your cores.
#-XX:ParallelGCThreads=16
#-XX:ConcGCThreads=16
### JPMS
-Djdk.attach.allowAttachSelf=true
--add-exports java.base/java.lang.ref=ALL-UNNAMED
--add-exports java.base/jdk.internal.misc=ALL-UNNAMED
--add-exports java.management.rmi/com.sun.jmx.remote.internal.rmi=ALL-UNNAMED
--add-exports java.rmi/sun.rmi.registry=ALL-UNNAMED
--add-exports java.rmi/sun.rmi.server=ALL-UNNAMED
--add-exports java.sql/java.sql=ALL-UNNAMED
--add-exports jdk.unsupported/sun.misc=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.module=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.base/jdk.internal.math=ALL-UNNAMED
--add-opens java.base/jdk.internal.module=ALL-UNNAMED
--add-opens java.base/jdk.internal.ref=ALL-UNNAMED
--add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
### GC logging options -- uncomment to enable
# Java 11 (and newer) GC logging options:
# See description of https://bugs.openjdk.java.net/browse/JDK-8046148 for details about the syntax
# The following is the equivalent to -XX:+PrintGCDetails -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M
#-Xlog:gc=info,heap*=trace,age*=debug,safepoint=info,promotion*=trace:file=/var/log/cassandra/gc.log:time,uptime,pid,tid,level:filecount=10,filesize=10485760
# Notes for Java 8 migration:
#
# -XX:+PrintGCDetails maps to -Xlog:gc*:... - i.e. add a '*' after "gc"
# -XX:+PrintGCDateStamps maps to decorator 'time'
#
# -XX:+PrintHeapAtGC maps to 'heap' with level 'trace'
# -XX:+PrintTenuringDistribution maps to 'age' with level 'debug'
# -XX:+PrintGCApplicationStoppedTime maps to 'safepoint' with level 'info'
# -XX:+PrintPromotionFailure maps to 'promotion' with level 'trace'
# -XX:PrintFLSStatistics=1 maps to 'freelist' with level 'trace'
### Netty Options
# On Java >= 9 Netty requires the io.netty.tryReflectionSetAccessible system property to be set to true to enable
# creation of direct buffers using Unsafe. Without it, this falls back to ByteBuffer.allocateDirect which has
# inferior performance and risks exceeding MaxDirectMemory
-Dio.netty.tryReflectionSetAccessible=true
# Revert changes in defaults introduced in https://netty.io/news/2022/03/10/4-1-75-Final.html
-Dio.netty.allocator.useCacheForAllThreads=true
-Dio.netty.allocator.maxOrder=11
# Need to explicitly allow security manager on JDK21; deprecated for removal
-Djava.security.manager=allow
# The newline in the end of file is intentional

4
debian/control vendored
View File

@ -3,7 +3,7 @@ Section: misc
Priority: extra Priority: extra
Maintainer: Eric Evans <eevans@apache.org> Maintainer: Eric Evans <eevans@apache.org>
Uploaders: Sylvain Lebresne <slebresne@apache.org> Uploaders: Sylvain Lebresne <slebresne@apache.org>
Build-Depends: debhelper (>= 11), openjdk-17-jdk-headless | openjdk-11-jdk-headless | java17-sdk | java11-sdk, ant (>= 1.10), ant-optional (>= 1.10), dh-python, python3-dev (>= 3.6), quilt, bash-completion Build-Depends: debhelper (>= 11), openjdk-21-jdk-headless | openjdk-17-jdk-headless | openjdk-11-jdk-headless | java21-sdk | java17-sdk | java11-sdk, ant (>= 1.10), ant-optional (>= 1.10), dh-python, python3-dev (>= 3.6), quilt, bash-completion
Homepage: https://cassandra.apache.org Homepage: https://cassandra.apache.org
Vcs-Git: https://gitbox.apache.org/repos/asf/cassandra.git Vcs-Git: https://gitbox.apache.org/repos/asf/cassandra.git
Vcs-Browser: https://gitbox.apache.org/repos/asf?p=cassandra.git Vcs-Browser: https://gitbox.apache.org/repos/asf?p=cassandra.git
@ -11,7 +11,7 @@ Standards-Version: 3.8.3
Package: cassandra Package: cassandra
Architecture: all Architecture: all
Depends: openjdk-17-jre-headless | openjdk-11-jre-headless | java17-runtime | java11-runtime, adduser, python3 (>= 3.6), procps, ${misc:Depends} Depends: openjdk-21-jre-headless | openjdk-17-jre-headless | openjdk-11-jre-headless | java21-runtime | java17-runtime | java11-runtime, adduser, python3 (>= 3.6), procps, ${misc:Depends}
Recommends: ntp | time-daemon Recommends: ntp | time-daemon
Suggests: cassandra-tools Suggests: cassandra-tools
Conflicts: apache-cassandra1 Conflicts: apache-cassandra1

View File

@ -1,14 +1,13 @@
= jvm-* files = jvm-* files
Several files for JVM configuration are included in Cassandra. The Several files for JVM configuration are included in Cassandra. The
`jvm-server.options` file, and corresponding files `jvm8-server.options` `jvm-server.options` file, and corresponding JDK specific files
and `jvm11-server.options` are the main file for settings that affect `jvmN-server.options` are the main file for settings that affect
the operation of the Cassandra JVM on cluster nodes. The file includes the operation of the Cassandra JVM on cluster nodes. The file includes
startup parameters, general JVM settings such as garbage collection, and startup parameters, general JVM settings such as garbage collection, and
heap settings. The `jvm-clients.options` and corresponding heap settings. Likewise, the `jvm-clients.options` and corresponding
`jvm8-clients.options` and `jvm11-clients.options` files can be used to `jvmN-clients.options` files can be used to configure JVM settings for
configure JVM settings for clients like `nodetool` and the `sstable` clients like `nodetool` and the `sstable` tools.
tools.
See each file for examples of settings. See each file for examples of settings.
@ -18,4 +17,4 @@ The `jvm-\*` files replace the `cassandra-env.sh` file used in Cassandra
versions prior to Cassandra 3.0. The `cassandra-env.sh` bash script file versions prior to Cassandra 3.0. The `cassandra-env.sh` bash script file
is still useful if JVM settings must be dynamically calculated based on is still useful if JVM settings must be dynamically calculated based on
system settings. The `jvm-*` files only store static JVM settings. system settings. The `jvm-*` files only store static JVM settings.
==== ====

View File

@ -190,9 +190,11 @@ To use `BinAuditLogger` as a logger in AuditLogging, set the logger to `BinAudit
oldest file. Default is set to `16L * 1024L * 1024L * 1024L` oldest file. Default is set to `16L * 1024L * 1024L * 1024L`
`roll_cycle`:: `roll_cycle`::
How often to roll Audit log segments so they can potentially be How often to roll Audit log segments so they can potentially be
reclaimed. Available options are: MINUTELY, HOURLY, DAILY, reclaimed. Some available options are:
LARGE_DAILY, XLARGE_DAILY, HUGE_DAILY.For more options, refer: `FIVE_MINUTELY, FAST_HOURLY, FAST_DAILY,
net.openhft.chronicle.queue.RollCycles. Default is set to `"HOURLY"` LargeRollCycles.LARGE_DAILY, LargeRollCycles.XLARGE_DAILY, LargeRollCycles.HUGE_DAILY.`
For more options, refer: net.openhft.chronicle.queue.RollCycles.
Default is set to `"FAST_HOURLY"`
== Configuring FileAuditLogger == Configuring FileAuditLogger

View File

@ -151,6 +151,7 @@
-Dcassandra.storagedir=$PROJECT_DIR$/data -Dcassandra.storagedir=$PROJECT_DIR$/data
-Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml -Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml
-XX:HeapDumpPath=build/test -XX:HeapDumpPath=build/test
-Dnet.bytebuddy.experimental=true
-ea" /> -ea" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" /> <option name="WORKING_DIRECTORY" value="" />
@ -217,6 +218,8 @@
-XX:MaxMetaspaceSize=2G -XX:MaxMetaspaceSize=2G
-XX:ReservedCodeCacheSize=256M -XX:ReservedCodeCacheSize=256M
-XX:Tier4CompileThreshold=1000 -XX:Tier4CompileThreshold=1000
-XX:SoftRefLRUPolicyMSPerMB=0
-Dnet.bytebuddy.experimental=true
-ea" /> -ea" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<fork_mode value="class" /> <fork_mode value="class" />
@ -248,6 +251,7 @@
-Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml -Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml
-XX:HeapDumpPath=build/test -XX:HeapDumpPath=build/test
-Xmx1G -Xmx1G
-Dnet.bytebuddy.experimental=true
-ea" /> -ea" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" /> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />

View File

@ -59,7 +59,7 @@ set -e # enable immediate exit if venv setup fails
# fresh virtualenv and test logs results everytime # fresh virtualenv and test logs results everytime
rm -fr ${DIST_DIR}/venv ${DIST_DIR}/test/{html,output,logs} rm -fr ${DIST_DIR}/venv ${DIST_DIR}/test/{html,output,logs}
# re-use when possible the pre-installed virtualenv found in the cassandra-ubuntu2004_test docker image # re-use when possible the pre-installed virtualenv found in the cassandra12004_test docker image
virtualenv-clone ${BUILD_HOME}/env${python_version} ${BUILD_DIR}/venv || virtualenv --python=python3 ${BUILD_DIR}/venv virtualenv-clone ${BUILD_HOME}/env${python_version} ${BUILD_DIR}/venv || virtualenv --python=python3 ${BUILD_DIR}/venv
source ${BUILD_DIR}/venv/bin/activate source ${BUILD_DIR}/venv/bin/activate

View File

@ -121,57 +121,39 @@ if [ -z $JAVA ] ; then
exit 1; exit 1;
fi fi
# TODO: Factor this out to something we source so we don't have the duplication all over our scripts
# Matches variable 'java.supported' in build.xml # Matches variable 'java.supported' in build.xml
java_versions_supported=11,17 java_versions_supported=(11 17 21)
java_version_string=$(IFS=" "; echo "${java_versions_supported[*]}")
# Determine the sort of JVM we'll be running on. # Determine the sort of JVM we'll be running on.
java_ver_output=`"${JAVA:-java}" -version 2>&1` JAVA_VERSION=$(java -version 2>&1 | grep '[openjdk|java] version' | cut -d '"' -f2 | cut -d '.' -f1)
jvmver=`echo "$java_ver_output" | grep '[openjdk|java] version' | awk -F'"' 'NR==1 {print $2}' | cut -d\- -f1`
JVM_VERSION=${jvmver%_*}
short=$(echo "${jvmver}" | cut -c1-2)
# Unsupported JDKs below the upper supported version are not allowed supported=0
if [ "$short" != "$(echo "$java_versions_supported" | cut -d, -f1)" ] && [ "$JVM_VERSION" \< "$(echo "$java_versions_supported" | cut -d, -f2)" ] ; then for version in "${java_versions_supported[@]}"; do
echo "Unsupported Java $JVM_VERSION. Supported are $java_versions_supported" if [ "$version" -eq "$JAVA_VERSION" ]; then
exit 1; supported=1
fi fi
# Allow execution of supported Java versions, and newer if CASSANDRA_JDK_UNSUPPORTED is set done
is_supported_version=$(echo "$java_versions_supported" | tr "," '\n' | grep -F -x "$short")
if [ -z "$is_supported_version" ] ; then if [[ "$supported" -eq 0 ]]; then
if [ -z "$CASSANDRA_JDK_UNSUPPORTED" ] ; then if [ -z "$CASSANDRA_JDK_UNSUPPORTED" ]; then
echo "Unsupported Java $JVM_VERSION. Supported are $java_versions_supported" echo "Unsupported Java $JAVA_VERSION. Supported are $java_version_string"
echo "If you would like to test with newer Java versions set CASSANDRA_JDK_UNSUPPORTED to any value (for example, CASSANDRA_JDK_UNSUPPORTED=true). Unset the parameter for default behavior" echo "If you would like to test with newer Java versions set CASSANDRA_JDK_UNSUPPORTED to any value (for example, CASSANDRA_JDK_UNSUPPORTED=true). Unset the parameter for default behavior"
exit 1; exit 1
else else
echo "######################################################################" echo "######################################################################"
echo "Warning! You are using JDK$short. This Cassandra version only supports $java_versions_supported." echo "Warning! You are using JDK$JAVA_VERSION. This Cassandra version only supports $java_version_string"
echo "######################################################################" echo "######################################################################"
fi fi
fi fi
JAVA_VERSION=$short
jvm=`echo "$java_ver_output" | grep -A 1 '[openjdk|java] version' | awk 'NR==2 {print $1}'`
case "$jvm" in
OpenJDK)
JVM_VENDOR=OpenJDK
# this will be "64-Bit" or "32-Bit"
JVM_ARCH=`echo "$java_ver_output" | awk 'NR==3 {print $2}'`
;;
"Java(TM)")
JVM_VENDOR=Oracle
# this will be "64-Bit" or "32-Bit"
JVM_ARCH=`echo "$java_ver_output" | awk 'NR==3 {print $3}'`
;;
*)
# Help fill in other JVM values
JVM_VENDOR=other
JVM_ARCH=unknown
;;
esac
# Read user-defined JVM options from jvm-server.options file # Read user-defined JVM options from jvm-server.options file
JVM_OPTS_FILE=$CASSANDRA_CONF/jvm${jvmoptions_variant:--clients}.options JVM_OPTS_FILE=$CASSANDRA_CONF/jvm${jvmoptions_variant:--clients}.options
if [ $JAVA_VERSION -ge 17 ] ; then if [ $JAVA_VERSION -ge 21 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm21${jvmoptions_variant:--clients}.options
elif [ $JAVA_VERSION -ge 17 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm17${jvmoptions_variant:--clients}.options JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm17${jvmoptions_variant:--clients}.options
elif [ $JAVA_VERSION -ge 11 ] ; then elif [ $JAVA_VERSION -ge 11 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm11${jvmoptions_variant:--clients}.options JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm11${jvmoptions_variant:--clients}.options
@ -188,4 +170,4 @@ if [ -n "$USING_JDK" ] && [ "$JAVA_VERSION" -ge 17 ]; then
JVM_OPTS="$JVM_OPTS --add-exports jdk.attach/sun.tools.attach=ALL-UNNAMED" JVM_OPTS="$JVM_OPTS --add-exports jdk.attach/sun.tools.attach=ALL-UNNAMED"
JVM_OPTS="$JVM_OPTS --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" JVM_OPTS="$JVM_OPTS --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED"
JVM_OPTS="$JVM_OPTS --add-opens jdk.compiler/com.sun.tools.javac=ALL-UNNAMED" JVM_OPTS="$JVM_OPTS --add-opens jdk.compiler/com.sun.tools.javac=ALL-UNNAMED"
fi fi

View File

@ -49,7 +49,7 @@ BuildRoot: %{_tmppath}/%{relname}root-%(%{__id_u} -n)
BuildRequires: ant >= 1.9 BuildRequires: ant >= 1.9
BuildRequires: ant-junit >= 1.9 BuildRequires: ant-junit >= 1.9
Requires: (java-11-headless or java-17-headless) Requires: (java-11-headless or java-17-headless or java-21-headless)
Requires: python(abi) >= 3.6 Requires: python(abi) >= 3.6
Requires: procps-ng >= 3.3 Requires: procps-ng >= 3.3
Requires(pre): user(cassandra) Requires(pre): user(cassandra)

View File

@ -4779,27 +4779,46 @@ public class DatabaseDescriptor
public static void setGCLogThreshold(int threshold) public static void setGCLogThreshold(int threshold)
{ {
if (threshold <= 0) validateGCParams(threshold, getGCWarnThreshold());
throw new IllegalArgumentException("Threshold value for gc_log_threshold must be greater than 0");
long gcWarnThresholdInMs = getGCWarnThreshold();
if (gcWarnThresholdInMs != 0 && threshold > gcWarnThresholdInMs)
throw new IllegalArgumentException("Threshold value for gc_log_threshold (" + threshold + ") must be less than gc_warn_threshold which is currently "
+ gcWarnThresholdInMs);
conf.gc_log_threshold = new DurationSpec.IntMillisecondsBound(threshold); conf.gc_log_threshold = new DurationSpec.IntMillisecondsBound(threshold);
} }
public static void validateGCParams(long logThreshold, long warnThreshold)
{
if (logThreshold <= 0)
throw new IllegalArgumentException("Threshold value for gc_log*_threshold must be greater than 0");
if (logThreshold > Integer.MAX_VALUE)
throw new IllegalArgumentException("Threshold value for gc_log*_threshold must be less than Integer.MAX_VALUE");
if (warnThreshold <= 0)
throw new IllegalArgumentException("Threshold value for gc_warn*_threshold must be greater than 0");
if (warnThreshold > Integer.MAX_VALUE)
throw new IllegalArgumentException("Threshold value for gc_warn*_threshold must be less than Integer.MAX_VALUE");
if (warnThreshold != 0 && logThreshold > warnThreshold)
throw new IllegalArgumentException("Threshold value for gc_log*_threshold (" + logThreshold + ") must be less than gc_warn*_threshold which is currently "
+ warnThreshold);
}
public static EncryptionContext getEncryptionContext()
{
return encryptionContext;
}
public static long getGCWarnThreshold() public static long getGCWarnThreshold()
{ {
return conf.gc_warn_threshold.toMilliseconds(); return conf.gc_warn_threshold.toMilliseconds();
} }
public static void setGCWarnThreshold(int threshold) public static void setGCWarnThreshold(long threshold)
{ {
if (threshold < 0) if (threshold < 0)
throw new IllegalArgumentException("Threshold value for gc_warn_threshold must be greater than or equal to 0"); throw new IllegalArgumentException("Threshold value for gc_warn_threshold must be greater than or equal to 0");
if (threshold > Integer.MAX_VALUE)
throw new IllegalArgumentException("Threshold must be less than Integer.MAX_VALUE");
long gcLogThresholdInMs = getGCLogThreshold(); long gcLogThresholdInMs = getGCLogThreshold();
if (threshold != 0 && threshold <= gcLogThresholdInMs) if (threshold != 0 && threshold <= gcLogThresholdInMs)
throw new IllegalArgumentException("Threshold value for gc_warn_threshold (" + threshold + ") must be greater than gc_log_threshold which is currently " throw new IllegalArgumentException("Threshold value for gc_warn_threshold (" + threshold + ") must be greater than gc_log_threshold which is currently "
@ -4844,11 +4863,6 @@ public class DatabaseDescriptor
conf.gc_concurrent_phase_warn_threshold = new DurationSpec.IntMillisecondsBound(threshold); conf.gc_concurrent_phase_warn_threshold = new DurationSpec.IntMillisecondsBound(threshold);
} }
public static EncryptionContext getEncryptionContext()
{
return encryptionContext;
}
public static boolean isCDCEnabled() public static boolean isCDCEnabled()
{ {
return conf.cdc_enabled; return conf.cdc_enabled;

View File

@ -640,6 +640,7 @@ public abstract class CommitLogSegment
{ {
TableMetadata m = Schema.instance.getTableMetadata(tableId); TableMetadata m = Schema.instance.getTableMetadata(tableId);
sb.append(m == null ? "<deleted>" : m.name).append(" (").append(tableId) sb.append(m == null ? "<deleted>" : m.name).append(" (").append(tableId)
.append(", keyspace: ").append(m.keyspace)
.append(", dirty: ").append(tableDirty.get(tableId)) .append(", dirty: ").append(tableDirty.get(tableId))
.append(", clean: ").append(tableClean.get(tableId)) .append(", clean: ").append(tableClean.get(tableId))
.append("), "); .append("), ");

View File

@ -790,7 +790,7 @@ abstract class AbstractPatriciaTrie<K, V> extends AbstractTrie<K, V>
* This is implemented by going always to the left until * This is implemented by going always to the left until
* we encounter a valid uplink. That uplink is the first key. * we encounter a valid uplink. That uplink is the first key.
*/ */
TrieEntry<K, V> firstEntry() public TrieEntry<K, V> firstEntry()
{ {
// if Trie is empty, no first node. // if Trie is empty, no first node.
return isEmpty() ? null : followLeft(root); return isEmpty() ? null : followLeft(root);

View File

@ -422,7 +422,7 @@ public class PatriciaTrie<K, V> extends AbstractPatriciaTrie<K, V> implements Se
* <p>This is implemented by going always to the right until * <p>This is implemented by going always to the right until
* we encounter a valid uplink. That uplink is the last key. * we encounter a valid uplink. That uplink is the last key.
*/ */
private TrieEntry<K, V> lastEntry() public TrieEntry<K, V> lastEntry()
{ {
return followRight(root.left); return followRight(root.left);
} }

View File

@ -517,4 +517,10 @@ public class RepairSession extends AsyncFuture<RepairSessionResult> implements I
delegate.shutdown(); delegate.shutdown();
} }
} }
@VisibleForTesting
public int syncingCount()
{
return syncingTasks.size();
}
} }

View File

@ -394,4 +394,4 @@ class FullRepairState extends AutoRepairState
return getRepairRunnable(keyspace, option); return getRepairRunnable(keyspace, option);
} }
} }

View File

@ -123,15 +123,17 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean
final GarbageCollectorMXBean gcBean; final GarbageCollectorMXBean gcBean;
final boolean assumeGCIsPartiallyConcurrent; final boolean assumeGCIsPartiallyConcurrent;
final boolean assumeGCIsOldGen; final boolean assumeGCIsOldGen;
final boolean isZGC;
private String[] keys; private String[] keys;
long lastGcTotalDuration = 0; long lastGcTotalDuration = 0;
GCState(GarbageCollectorMXBean gcBean, boolean assumeGCIsPartiallyConcurrent, boolean assumeGCIsOldGen) GCState(GarbageCollectorMXBean gcBean, boolean assumeGCIsPartiallyConcurrent, boolean assumeGCIsOldGen, boolean isZGC)
{ {
this.gcBean = gcBean; this.gcBean = gcBean;
this.assumeGCIsPartiallyConcurrent = assumeGCIsPartiallyConcurrent; this.assumeGCIsPartiallyConcurrent = assumeGCIsPartiallyConcurrent;
this.assumeGCIsOldGen = assumeGCIsOldGen; this.assumeGCIsOldGen = assumeGCIsOldGen;
this.isZGC = isZGC;
} }
String[] keys(GarbageCollectionNotificationInfo info) String[] keys(GarbageCollectionNotificationInfo info)
@ -144,6 +146,11 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean
return keys; return keys;
} }
public boolean isZGC()
{
return isZGC;
}
} }
final AtomicReference<State> state = new AtomicReference<>(new State()); final AtomicReference<State> state = new AtomicReference<>(new State());
@ -158,7 +165,7 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean
for (ObjectName name : MBeanWrapper.instance.queryNames(gcName, null)) for (ObjectName name : MBeanWrapper.instance.queryNames(gcName, null))
{ {
GarbageCollectorMXBean gc = ManagementFactory.newPlatformMXBeanProxy(MBeanWrapper.instance.getMBeanServer(), name.getCanonicalName(), GarbageCollectorMXBean.class); GarbageCollectorMXBean gc = ManagementFactory.newPlatformMXBeanProxy(MBeanWrapper.instance.getMBeanServer(), name.getCanonicalName(), GarbageCollectorMXBean.class);
gcStates.put(gc.getName(), new GCState(gc, assumeGCIsPartiallyConcurrent(gc), assumeGCIsOldGen(gc))); gcStates.put(gc.getName(), new GCState(gc, assumeGCIsPartiallyConcurrent(gc), assumeGCIsOldGen(gc), isZGC(gc)));
} }
ObjectName me = new ObjectName(MBEAN_NAME); ObjectName me = new ObjectName(MBEAN_NAME);
if (!MBeanWrapper.instance.isRegistered(me)) if (!MBeanWrapper.instance.isRegistered(me))
@ -182,32 +189,45 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean
} }
} }
private static boolean isZGC(GarbageCollectorMXBean gc)
{
return gc.getName().contains("ZGC");
}
/* /*
* Assume that a GC type is at least partially concurrent and so a side channel method * Assume that a GC type is at least partially concurrent and so a side channel method
* should be used to calculate application stopped time due to the GC. * should be used to calculate application stopped time due to the GC.
* *
* If the GC isn't recognized then assume that is concurrent and we need to do our own calculation * If the GC isn't recognized then assume that is concurrent and we need to do our own calculation
* via the the side channel. * via the side channel.
*/ */
private static boolean assumeGCIsPartiallyConcurrent(GarbageCollectorMXBean gc) private static boolean assumeGCIsPartiallyConcurrent(GarbageCollectorMXBean gc)
{ {
switch (gc.getName()) switch (gc.getName())
{ {
//First two are from the serial collector // First two are from the serial collector
case "Copy": case "Copy":
case "MarkSweepCompact": case "MarkSweepCompact":
//Parallel collector // Parallel collector
case "PS MarkSweep": case "PS MarkSweep":
case "PS Scavenge": case "PS Scavenge":
case "G1 Young Generation": case "G1 Young Generation":
//CMS young generation collector // CMS young generation collector
case "ParNew": case "ParNew":
// gen zgc
case "ZGC Minor Pauses":
case "ZGC Major Pauses":
// zgc
case "ZGC Pauses":
return false; return false;
case "ConcurrentMarkSweep": case "ConcurrentMarkSweep":
case "G1 Old Generation": case "G1 Old Generation":
case "ZGC Minor Cycles":
case "ZGC Major Cycles":
case "ZGC Cycles":
return true; return true;
default: default:
//Assume possibly concurrent if unsure // Assume possibly concurrent if unsure
return true; return true;
} }
} }
@ -226,15 +246,22 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean
case "PS Scavenge": case "PS Scavenge":
case "G1 Young Generation": case "G1 Young Generation":
case "ParNew": case "ParNew":
case "ZGC Minor Cycles":
case "ZGC Minor Pauses":
return false; return false;
case "MarkSweepCompact": case "MarkSweepCompact":
case "PS MarkSweep": case "PS MarkSweep":
case "ConcurrentMarkSweep": case "ConcurrentMarkSweep":
case "G1 Old Generation": case "G1 Old Generation":
case "ZGC Major Cycles":
case "ZGC Major Pauses":
// assume non-generational zgc is old gen
case "ZGC Cycles":
case "ZGC Pauses":
return true; return true;
default: default:
//Assume not old gen otherwise, don't call // Assume not old gen otherwise, don't call
//TransactionLogs.rescheduleFailedTasks() // TransactionLogs.rescheduleFailedTasks()
return false; return false;
} }
} }
@ -425,9 +452,15 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean
} }
} }
/**
* While we _technically_ could only set the threshold based on which GC we're running, there's really no
* perceptable value-add from doing that. Responsibility for enforcing the validity of the params is entrusted
* to the {@link DatabaseDescriptor}
*/
@Override
public void setGcWarnThresholdInMs(long threshold) public void setGcWarnThresholdInMs(long threshold)
{ {
DatabaseDescriptor.setGCWarnThreshold((int)threshold); DatabaseDescriptor.setGCWarnThreshold(threshold);
} }
public long getGcWarnThresholdInMs() public long getGcWarnThresholdInMs()

View File

@ -1216,7 +1216,7 @@ public interface StorageServiceMBean extends NotificationEmitter
* Start the fully query logger. * Start the fully query logger.
* *
* @param path Path where the full query log will be stored. If null cassandra.yaml value is used. * @param path Path where the full query log will be stored. If null cassandra.yaml value is used.
* @param rollCycle How often to create a new file for query data (MINUTELY, DAILY, HOURLY) * @param rollCycle How often to create a new file for query data (FIVE_MINUTELY, FAST_DAILY, FAST_HOURLY)
* @param blocking Whether threads submitting queries to the query log should block if they can't be drained to the filesystem or alternatively drops samples and log * @param blocking Whether threads submitting queries to the query log should block if they can't be drained to the filesystem or alternatively drops samples and log
* @param maxQueueWeight How many bytes of query data to queue before blocking or dropping samples * @param maxQueueWeight How many bytes of query data to queue before blocking or dropping samples
* @param maxLogSize How many bytes of log data to store before dropping segments. Might not be respected if a log file hasn't rolled so it can be deleted. * @param maxLogSize How many bytes of log data to store before dropping segments. Might not be respected if a log file hasn't rolled so it can be deleted.

View File

@ -176,7 +176,7 @@ public class AuditLogViewer
private static class AuditLogViewerOptions private static class AuditLogViewerOptions
{ {
private final List<String> pathList; private final List<String> pathList;
private String rollCycle = "HOURLY"; private String rollCycle = "FAST_HOURLY";
private boolean follow; private boolean follow;
private boolean ignoreUnsupported; private boolean ignoreUnsupported;
@ -238,7 +238,7 @@ public class AuditLogViewer
{ {
Options options = new Options(); Options options = new Options();
options.addOption(new Option("r", ROLL_CYCLE, true, "How often to roll the log file was rolled. May be necessary for Chronicle to correctly parse file names. (MINUTELY, HOURLY, DAILY). Default HOURLY.")); options.addOption(new Option("r", ROLL_CYCLE, true, "How often to roll the log file was rolled. May be necessary for Chronicle to correctly parse file names. (FIVE_MINUTELY, FAST_HOURLY, FAST_DAILY). Default FAST_HOURLY."));
options.addOption(new Option("f", FOLLOW, false, "Upon reacahing the end of the log continue indefinitely waiting for more records")); options.addOption(new Option("f", FOLLOW, false, "Upon reacahing the end of the log continue indefinitely waiting for more records"));
options.addOption(new Option("i", IGNORE, false, "Silently ignore unsupported records")); options.addOption(new Option("i", IGNORE, false, "Silently ignore unsupported records"));
options.addOption(new Option("h", HELP_OPTION, false, "display this help message")); options.addOption(new Option("h", HELP_OPTION, false, "display this help message"));

View File

@ -49,7 +49,7 @@ public class EnableAuditLog extends AbstractCommand
@Option(paramLabel = "excluded_users", names = { "--excluded-users" }, description = "Comma separated list of users to be excluded for audit log. If not set the value from cassandra.yaml will be used") @Option(paramLabel = "excluded_users", names = { "--excluded-users" }, description = "Comma separated list of users to be excluded for audit log. If not set the value from cassandra.yaml will be used")
private String excluded_users = null; private String excluded_users = null;
@Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file (MINUTELY, HOURLY, DAILY).") @Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file (FAST_MINUTELY, FAST_HOURLY, FAST_DAILY).")
private String rollCycle = null; private String rollCycle = null;
@Option(paramLabel = "blocking", names = { "--blocking" }, description = "If the queue is full whether to block producers or drop samples [true|false].") @Option(paramLabel = "blocking", names = { "--blocking" }, description = "If the queue is full whether to block producers or drop samples [true|false].")

View File

@ -26,7 +26,7 @@ import picocli.CommandLine.Option;
@Command(name = "enablefullquerylog", description = "Enable full query logging, defaults for the options are configured in cassandra.yaml") @Command(name = "enablefullquerylog", description = "Enable full query logging, defaults for the options are configured in cassandra.yaml")
public class EnableFullQueryLog extends AbstractCommand public class EnableFullQueryLog extends AbstractCommand
{ {
@Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file (MINUTELY, HOURLY, DAILY).") @Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file (FAST_MINUTELY, FAST_HOURLY, FAST_DAILY).")
private String rollCycle = null; private String rollCycle = null;
@Option(paramLabel = "blocking", names = { "--blocking" }, description = "If the queue is full whether to block producers or drop samples [true|false].") @Option(paramLabel = "blocking", names = { "--blocking" }, description = "If the queue is full whether to block producers or drop samples [true|false].")

View File

@ -63,7 +63,7 @@ public final class JavaUtils
* @return the java version number * @return the java version number
* @throws NumberFormatException if the version cannot be retrieved * @throws NumberFormatException if the version cannot be retrieved
*/ */
private static int parseJavaVersion(String jreVersion) public static int parseJavaVersion(String jreVersion)
{ {
String version; String version;
if (jreVersion.startsWith("1.")) if (jreVersion.startsWith("1."))

View File

@ -1630,6 +1630,12 @@ public class MerkleTree
return getHelper(root, fullRange.left, fullRange.right, t); return getHelper(root, fullRange.left, fullRange.right, t);
} }
@VisibleForTesting
public boolean isReleased()
{
return root == null;
}
private TreeRange getHelper(Node node, Token pleft, Token pright, Token t) private TreeRange getHelper(Node node, Token pleft, Token pright, Token t)
{ {
int depth = 0; int depth = 0;

View File

@ -458,4 +458,19 @@ public class MerkleTrees implements Iterable<Map.Entry<Range<Token>, MerkleTree>
return rt1.compareTo(rt2); return rt1.compareTo(rt2);
} }
} }
/**
* For purposes of our checking in test, either "nothing is or was ever here" or "what was here is now gone" suffice.
*/
@VisibleForTesting
public boolean isReleased()
{
if (merkleTrees.isEmpty())
return true;
boolean result = true;
for (MerkleTree mt : merkleTrees.values())
result &= mt.isReleased();
return result;
}
} }

View File

@ -37,6 +37,11 @@ import static org.github.jamm.utils.ArrayMeasurementUtils.computeArraySize;
*/ */
public class ObjectSizes public class ObjectSizes
{ {
/**
* For debugging purposes, you can construct this with .printVisitedTree() to get a printout of all the objects
* and their calculated size on heap to debug jamm related issues. See:
* <a href="https://github.com/jbellis/jamm/blob/master/README.md#visited-object-tree">README.md on github</a>
*/
private static final MemoryMeter meter = MemoryMeter.builder().withGuessing(Guess.INSTRUMENTATION_AND_SPECIFICATION, private static final MemoryMeter meter = MemoryMeter.builder().withGuessing(Guess.INSTRUMENTATION_AND_SPECIFICATION,
Guess.UNSAFE) Guess.UNSAFE)
.build(); .build();

View File

@ -38,6 +38,7 @@ import net.openhft.chronicle.queue.ExcerptAppender;
import net.openhft.chronicle.queue.RollCycles; import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueue; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueue;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import net.openhft.chronicle.queue.rollcycles.TestRollCycles;
import net.openhft.chronicle.wire.WireOut; import net.openhft.chronicle.wire.WireOut;
import net.openhft.chronicle.wire.WriteMarshallable; import net.openhft.chronicle.wire.WriteMarshallable;
import net.openhft.posix.PosixAPI; import net.openhft.posix.PosixAPI;
@ -55,6 +56,7 @@ import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import org.apache.cassandra.utils.concurrent.WeightedQueue; import org.apache.cassandra.utils.concurrent.WeightedQueue;
import static java.lang.String.format; import static java.lang.String.format;
import static net.openhft.chronicle.queue.rollcycles.TestRollCycles.TEST_SECONDLY;
import static org.apache.cassandra.config.CassandraRelevantProperties.CHRONICLE_ANNOUNCER_DISABLE; import static org.apache.cassandra.config.CassandraRelevantProperties.CHRONICLE_ANNOUNCER_DISABLE;
import static org.apache.cassandra.utils.LocalizeString.toUpperCaseLocalized; import static org.apache.cassandra.utils.LocalizeString.toUpperCaseLocalized;
@ -140,7 +142,11 @@ public class BinLog implements Runnable
Preconditions.checkNotNull(options.roll_cycle, "roll_cycle was null"); Preconditions.checkNotNull(options.roll_cycle, "roll_cycle was null");
Preconditions.checkArgument(options.max_queue_weight > 0, "max_queue_weight must be > 0"); Preconditions.checkArgument(options.max_queue_weight > 0, "max_queue_weight must be > 0");
SingleChronicleQueueBuilder builder = SingleChronicleQueueBuilder.single(path.toFile()); // checkstyle: permit this invocation SingleChronicleQueueBuilder builder = SingleChronicleQueueBuilder.single(path.toFile()); // checkstyle: permit this invocation
builder.rollCycle(RollCycles.valueOf(options.roll_cycle));
// RollCycles.TEST_SECONDLY is deprecated; avoid logging a warning here in cases where test code may be referencing the old value.
builder.rollCycle(options.roll_cycle.equals("TEST_SECONDLY")
? TEST_SECONDLY
: RollCycles.valueOf(options.roll_cycle));
sampleQueue = new WeightedQueue<>(options.max_queue_weight); sampleQueue = new WeightedQueue<>(options.max_queue_weight);
this.archiver = archiver; this.archiver = archiver;
@ -394,7 +400,10 @@ public class BinLog implements Runnable
Preconditions.checkNotNull(rollCycle, "rollCycle was null"); Preconditions.checkNotNull(rollCycle, "rollCycle was null");
rollCycle = toUpperCaseLocalized(rollCycle); rollCycle = toUpperCaseLocalized(rollCycle);
Preconditions.checkNotNull(RollCycles.valueOf(rollCycle), "unrecognized roll cycle"); Preconditions.checkNotNull(RollCycles.valueOf(rollCycle), "unrecognized roll cycle");
this.rollCycle = rollCycle; // Avoid using deprecated RollCycles.TEST_SECONDLY in test cases where we pass the text blindly; logs a warning
this.rollCycle = rollCycle.equals("TEST_SECONDLY")
? TestRollCycles.TEST_SECONDLY.toString()
: rollCycle;
return this; return this;
} }

View File

@ -31,11 +31,12 @@ public class BinLogOptions
*/ */
public boolean allow_nodetool_archive_command = false; public boolean allow_nodetool_archive_command = false;
/** /**
* How often to roll BinLog segments so they can potentially be reclaimed. Available options are: * How often to roll BinLog segments so they can potentially be reclaimed. Some available options are:
* MINUTELY, HOURLY, DAILY, LARGE_DAILY, XLARGE_DAILY, HUGE_DAILY. * FIVE_MINUTELY, FAST_HOURLY, FAST_DAILY, LargeRollCycles.LARGE_DAILY, LargeRollCycles.XLARGE_DAILY, LargeRollCycles.HUGE_DAILY.
* For more options, refer: net.openhft.chronicle.queue.RollCycles * See FAQ: <a href="https://github.com/OpenHFT/Chronicle-Queue/blob/50af322d0022fb8d265779ed6f4c1073d32b6b54/docs/FAQ.adoc" />
* For more options, refer: {@link net.openhft.chronicle.queue.RollCycles}
*/ */
public String roll_cycle = "HOURLY"; public String roll_cycle = "FAST_HOURLY";
/** /**
* Indicates if the BinLog should block if the it falls behind or should drop bin log records. * Indicates if the BinLog should block if the it falls behind or should drop bin log records.
* Default is set to true so that BinLog records wont be lost * Default is set to true so that BinLog records wont be lost

View File

@ -328,6 +328,40 @@ public class BTreeSet<V> extends AbstractSet<V> implements NavigableSet<V>, List
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public BTreeSet<V> reversed()
{
throw new UnsupportedOperationException();
}
public V removeLast()
{
throw new UnsupportedOperationException();
}
public V removeFirst()
{
throw new UnsupportedOperationException();
}
public V getLast()
{
throw new UnsupportedOperationException();
}
public V getFirst()
{
throw new UnsupportedOperationException();
}
public void addLast(V v)
{
throw new UnsupportedOperationException();
}
public void addFirst(V v)
{
throw new UnsupportedOperationException();
}
public static class BTreeRange<V> extends BTreeSet<V> public static class BTreeRange<V> extends BTreeSet<V>
{ {

View File

@ -125,6 +125,10 @@ public final class Ref<T> implements RefCounted<T>
public static final boolean DEBUG_EVENTS_ENABLED = TEST_DEBUG_REF_EVENTS.getBoolean(); public static final boolean DEBUG_EVENTS_ENABLED = TEST_DEBUG_REF_EVENTS.getBoolean();
static OnLeak ON_LEAK; static OnLeak ON_LEAK;
/** NOT INTENDED FOR USE OUTSIDE TESTS AND DEBUGGING */
@VisibleForTesting
private static boolean TEST_TRACE_ENABLED = false;
@Shared(scope = SIMULATION) @Shared(scope = SIMULATION)
public interface OnLeak public interface OnLeak
{ {
@ -146,6 +150,14 @@ public final class Ref<T> implements RefCounted<T>
this.referent = referent; this.referent = referent;
} }
/**
* Generally don't go around mutating this willy-nilly in non-test code please.
*/
@VisibleForTesting
public static void enableTestTracing() { TEST_TRACE_ENABLED = true; }
@VisibleForTesting
public static void disableTestTracing() { TEST_TRACE_ENABLED = false; }
/** /**
* Must be called exactly once, when the logical operation for which this Ref was created has terminated. * Must be called exactly once, when the logical operation for which this Ref was created has terminated.
* Failure to abide by this contract will result in an error (eventually) being reported, assuming a * Failure to abide by this contract will result in an error (eventually) being reported, assuming a
@ -629,7 +641,7 @@ public final class Ref<T> implements RefCounted<T>
{ {
if (Thread.currentThread().isInterrupted()) if (Thread.currentThread().isInterrupted())
throw new UncheckedInterruptedException(new InterruptedException()); throw new UncheckedInterruptedException(new InterruptedException());
//If necessary fetch the next object to start tracing // If necessary fetch the next object to start tracing
if (inProgress == null) if (inProgress == null)
inProgress = path.pollLast(); inProgress = path.pollLast();
@ -646,6 +658,25 @@ public final class Ref<T> implements RefCounted<T>
field = p.right; field = p.right;
} }
if (TEST_TRACE_ENABLED)
{
logger.debug("[Ref tracing for {}, Object: {}]", rootObject, child);
if (field != null && child != null)
{
logger.debug(" - Visiting field '{}' of object {} -> value class {}",
field.getName(),
inProgress.o.getClass().getName(),
child.getClass().getName());
}
else if (field != null)
{
// Field exists but the next child is null still useful to know the path.
logger.debug(" - Visiting field '{}' of object {} -> value is null",
field.getName(),
inProgress.o.getClass().getName());
}
}
if (child != null && visited.add(child)) if (child != null && visited.add(child))
{ {
path.offer(inProgress); path.offer(inProgress);

View File

@ -83,7 +83,8 @@ OPTIONS
Path to the JMX password file Path to the JMX password file
--roll-cycle <roll_cycle> --roll-cycle <roll_cycle>
How often to roll the log file (MINUTELY, HOURLY, DAILY). How often to roll the log file (FAST_MINUTELY, FAST_HOURLY,
FAST_DAILY).
-u <username>, --username <username> -u <username>, --username <username>
Remote jmx agent username Remote jmx agent username

View File

@ -55,7 +55,8 @@ OPTIONS
Path to the JMX password file Path to the JMX password file
--roll-cycle <roll_cycle> --roll-cycle <roll_cycle>
How often to roll the log file (MINUTELY, HOURLY, DAILY). How often to roll the log file (FAST_MINUTELY, FAST_HOURLY,
FAST_DAILY).
-u <username>, --username <username> -u <username>, --username <username>
Remote jmx agent username Remote jmx agent username

View File

@ -48,7 +48,7 @@ import static org.apache.cassandra.simulator.asm.InterceptClasses.Cached.Kind.UN
// WARNING: does not implement IClassTransformer directly as must be accessible to bootstrap class loader // WARNING: does not implement IClassTransformer directly as must be accessible to bootstrap class loader
public class InterceptClasses implements BiFunction<String, byte[], byte[]> public class InterceptClasses implements BiFunction<String, byte[], byte[]>
{ {
public static final int BYTECODE_VERSION = Opcodes.ASM7; public static final int BYTECODE_VERSION = Opcodes.ASM9;
// TODO (cleanup): use annotations // TODO (cleanup): use annotations
private static final Pattern MONITORS = Pattern.compile( "org[/.]apache[/.]cassandra[/.]utils[/.]concurrent[/.].*" + private static final Pattern MONITORS = Pattern.compile( "org[/.]apache[/.]cassandra[/.]utils[/.]concurrent[/.].*" +

View File

@ -243,6 +243,12 @@ public class ShadowingTransformer extends ClassTransformer
super.visitInnerClass(name, toShadowType(outerName), innerName, access); super.visitInnerClass(name, toShadowType(outerName), innerName, access);
} }
@Override
public void visitPermittedSubclass(String permittedSubclass)
{
super.visitPermittedSubclass(toShadowType(permittedSubclass));
}
@Override @Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value)
{ {

View File

@ -21,6 +21,7 @@ package org.apache.cassandra.simulator.systems;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntSupplier; import java.util.function.IntSupplier;
import java.util.function.LongConsumer; import java.util.function.LongConsumer;
import java.util.function.ToIntFunction; import java.util.function.ToIntFunction;
@ -487,21 +488,35 @@ public interface InterceptorOfGlobalMethods extends InterceptorOfSystemMethods,
private final IntSupplier nextId; private final IntSupplier nextId;
private final WeakIdentityHashMap<Object, Integer> saved = new WeakIdentityHashMap<>(); private final WeakIdentityHashMap<Object, Integer> saved = new WeakIdentityHashMap<>();
private final ReentrantLock mapLock = new ReentrantLock();
public IdentityHashCode(IntSupplier nextId) public IdentityHashCode(IntSupplier nextId)
{ {
this.nextId = nextId; this.nextId = nextId;
} }
public synchronized int applyAsInt(Object value) /**
* As {@link #saved} is not thread safe, we want to preserve weak reference semantics, and we want to gracefully
* support virtual threads, we need to use another locking mechanism other than synchronized since that is a risk
* of deadlock in JDK21 as virtual threads block on held monitors and won't release.
*/
public int applyAsInt(Object value)
{ {
Integer id = saved.get(value); mapLock.lock();
if (id == null) try
{ {
id = nextId.getAsInt(); Integer id = saved.get(value);
saved.put(value, id); if (id == null)
{
id = nextId.getAsInt();
saved.put(value, id);
}
return id;
}
finally
{
mapLock.unlock();
} }
return id;
} }
} }

View File

@ -49,7 +49,8 @@ import static org.apache.cassandra.simulator.RandomSource.Choices.uniform;
// TODO (cleanup): when we encounter an exception and unwind the simulation, we should restore normal time to go with normal waits etc. // TODO (cleanup): when we encounter an exception and unwind the simulation, we should restore normal time to go with normal waits etc.
public class SimulatedTime public class SimulatedTime
{ {
private static final Pattern PERMITTED_TIME_THREADS = Pattern.compile("(logback|SimulationLiveness|Reconcile)[-:][0-9]+|RMI Scheduler\\(\\d+\\)"); private static final Pattern PERMITTED_TIME_THREADS = Pattern.compile("(logback|SimulationLiveness|Reconcile|Reference-Reaper)[-:][0-9]+|RMI Scheduler\\(\\d+\\)");
private static final Pattern PERMITTED_TIME_CLEANER_THREADS = Pattern.compile("(LocalPool-Cleaner[-a-z]+[-:][0-9]+|Common-Cleaner)");
@Shared(scope = Shared.Scope.SIMULATION) @Shared(scope = Shared.Scope.SIMULATION)
public interface Listener public interface Listener
@ -148,7 +149,7 @@ public class SimulatedTime
if (interceptibleThread.isIntercepting()) if (interceptibleThread.isIntercepting())
return interceptibleThread.time(); return interceptibleThread.time();
} }
if (PERMITTED_TIME_THREADS.matcher(Thread.currentThread().getName()).matches()) if (PERMITTED_TIME_THREADS.matcher(Thread.currentThread().getName()).matches() || PERMITTED_TIME_CLEANER_THREADS.matcher(Thread.currentThread().getName()).matches())
return disabled; return disabled;
throw new IllegalStateException("Using time is not allowed during simulation"); throw new IllegalStateException("Using time is not allowed during simulation");
} }

View File

@ -755,7 +755,7 @@ public class AuditLoggerTest extends CQLTester
assertEquals(1, AuthEvents.instance.listenerCount()); assertEquals(1, AuthEvents.instance.listenerCount());
Path p = Files.createTempDirectory("fql"); Path p = Files.createTempDirectory("fql");
StorageService.instance.enableFullQueryLogger(p.toString(), RollCycles.HOURLY.toString(), false, 1000, 1000, null, 0); StorageService.instance.enableFullQueryLogger(p.toString(), RollCycles.FAST_HOURLY.toString(), false, 1000, 1000, null, 0);
assertEquals(2, QueryEvents.instance.listenerCount()); assertEquals(2, QueryEvents.instance.listenerCount());
assertEquals(1, AuthEvents.instance.listenerCount()); // fql not listening to auth events assertEquals(1, AuthEvents.instance.listenerCount()); // fql not listening to auth events
StorageService.instance.resetFullQueryLogger(); StorageService.instance.resetFullQueryLogger();
@ -778,7 +778,7 @@ public class AuditLoggerTest extends CQLTester
{ {
assertEquals(1, QueryEvents.instance.listenerCount()); assertEquals(1, QueryEvents.instance.listenerCount());
assertEquals(1, AuthEvents.instance.listenerCount()); assertEquals(1, AuthEvents.instance.listenerCount());
StorageService.instance.enableFullQueryLogger(options.audit_logs_dir, RollCycles.HOURLY.toString(), false, 1000, 1000, null, 0); StorageService.instance.enableFullQueryLogger(options.audit_logs_dir, RollCycles.FAST_HOURLY.toString(), false, 1000, 1000, null, 0);
fail("Conflicting directories - should throw exception"); fail("Conflicting directories - should throw exception");
} }
catch (IllegalStateException e) catch (IllegalStateException e)
@ -796,7 +796,7 @@ public class AuditLoggerTest extends CQLTester
disableAuditLogOptions(); disableAuditLogOptions();
AuditLogOptions options = getBaseAuditLogOptions(); AuditLogOptions options = getBaseAuditLogOptions();
DatabaseDescriptor.setAuditLoggingOptions(options); DatabaseDescriptor.setAuditLoggingOptions(options);
StorageService.instance.enableFullQueryLogger(options.audit_logs_dir, RollCycles.HOURLY.toString(), false, 1000, 1000, null, 0); StorageService.instance.enableFullQueryLogger(options.audit_logs_dir, RollCycles.FAST_HOURLY.toString(), false, 1000, 1000, null, 0);
try try
{ {
assertEquals(1, QueryEvents.instance.listenerCount()); assertEquals(1, QueryEvents.instance.listenerCount());

View File

@ -25,8 +25,8 @@ import com.datastax.driver.core.Session;
import net.openhft.chronicle.queue.ChronicleQueue; import net.openhft.chronicle.queue.ChronicleQueue;
import net.openhft.chronicle.queue.ExcerptTailer; import net.openhft.chronicle.queue.ExcerptTailer;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import net.openhft.chronicle.queue.rollcycles.TestRollCycles;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -76,7 +76,7 @@ public class BinAuditLoggerTest extends CQLTester
ResultSet rs = session.execute(pstmt.bind(1)); ResultSet rs = session.execute(pstmt.bind(1));
assertEquals(1, rs.all().size()); assertEquals(1, rs.all().size());
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptTailer tailer = queue.createTailer(); ExcerptTailer tailer = queue.createTailer();
assertTrue(tailer.readDocument(wire -> { assertTrue(tailer.readDocument(wire -> {

View File

@ -62,7 +62,7 @@ public class CommitlogShutdownTest
@BMRule(name = "Make removing commitlog segments slow", @BMRule(name = "Make removing commitlog segments slow",
targetClass = "CommitLogSegment", targetClass = "CommitLogSegment",
targetMethod = "discard", targetMethod = "discard",
action = "Thread.sleep(50)") action = "Thread.sleep(250L)") // We need to add the unit to Thread.sleep calls in ByteBuddy now
public void testShutdownWithPendingTasks() throws Exception public void testShutdownWithPendingTasks() throws Exception
{ {
new Random().nextBytes(entropy); new Random().nextBytes(entropy);
@ -77,8 +77,7 @@ public class CommitlogShutdownTest
KeyspaceParams.simple(1), KeyspaceParams.simple(1),
SchemaLoader.standardCFMD(KEYSPACE1, STANDARD1, 0, AsciiType.instance, BytesType.instance)); SchemaLoader.standardCFMD(KEYSPACE1, STANDARD1, 0, AsciiType.instance, BytesType.instance));
CompactionManager.instance.disableAutoCompaction(); CompactionManager.instance.disableAutoCompaction();
ColumnFamilyStore cfs1 = Keyspace.open(KEYSPACE1).getColumnFamilyStore(STANDARD1); ColumnFamilyStore cfs1 = Keyspace.open(KEYSPACE1).getColumnFamilyStore(STANDARD1);
final Mutation m = new RowUpdateBuilder(cfs1.metadata.get(), 0, "k") final Mutation m = new RowUpdateBuilder(cfs1.metadata.get(), 0, "k")

View File

@ -61,7 +61,7 @@ public class AntiCompactionBytemanTest extends CQLTester
targetMethod = "antiCompactGroup", targetMethod = "antiCompactGroup",
condition = "not flagged(\"done\")", condition = "not flagged(\"done\")",
targetLocation = "AFTER INVOKE prepareToCommit", targetLocation = "AFTER INVOKE prepareToCommit",
action = "Thread.sleep(2000);") } ) action = "Thread.sleep(2000L);") } ) // We need to add the unit to Thread.sleep calls in ByteBuddy now
public void testRedundantTransitions() throws Throwable public void testRedundantTransitions() throws Throwable
{ {
createTable("create table %s (id int primary key, i int)"); createTable("create table %s (id int primary key, i int)");

View File

@ -121,7 +121,7 @@ public class CompactionsBytemanTest extends CQLTester
targetMethod = "submitBackground", targetMethod = "submitBackground",
targetLocation = "AT INVOKE java.util.concurrent.Future.isCancelled", targetLocation = "AT INVOKE java.util.concurrent.Future.isCancelled",
condition = "!$cfs.getKeyspaceName().contains(\"system\")", condition = "!$cfs.getKeyspaceName().contains(\"system\")",
action = "Thread.sleep(5000)") action = "Thread.sleep(5000L)") // We need to add the unit to Thread.sleep calls in ByteBuddy now
public void testCompactingCFCounting() throws Throwable public void testCompactingCFCounting() throws Throwable
{ {
createTable("CREATE TABLE %s (k INT, c INT, v INT, PRIMARY KEY (k, c))"); createTable("CREATE TABLE %s (k INT, c INT, v INT, PRIMARY KEY (k, c))");

View File

@ -45,13 +45,13 @@ public class MemtableQuickTest extends CQLTester
{ {
static final Logger logger = LoggerFactory.getLogger(MemtableQuickTest.class); static final Logger logger = LoggerFactory.getLogger(MemtableQuickTest.class);
static final int partitions = 50_000; static final int partitions = 45_000;
static final int rowsPerPartition = 4; static final int rowsPerPartition = 4;
static final int deletedPartitionsStart = 20_000; static final int deletedPartitionsStart = 15_000;
static final int deletedPartitionsEnd = deletedPartitionsStart + 10_000; static final int deletedPartitionsEnd = deletedPartitionsStart + 10_000;
static final int deletedRowsStart = 40_000; static final int deletedRowsStart = 35_000;
static final int deletedRowsEnd = deletedRowsStart + 5_000; static final int deletedRowsEnd = deletedRowsStart + 5_000;
@Parameterized.Parameter() @Parameterized.Parameter()

View File

@ -33,8 +33,8 @@ import javax.annotation.Nullable;
import net.openhft.chronicle.queue.ChronicleQueue; import net.openhft.chronicle.queue.ChronicleQueue;
import net.openhft.chronicle.queue.ExcerptTailer; import net.openhft.chronicle.queue.ExcerptTailer;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import net.openhft.chronicle.queue.rollcycles.TestRollCycles;
import net.openhft.chronicle.wire.ValueIn; import net.openhft.chronicle.wire.ValueIn;
import net.openhft.chronicle.wire.WireOut; import net.openhft.chronicle.wire.WireOut;
@ -323,7 +323,7 @@ public class FullQueryLoggerTest extends CQLTester
private boolean checkForQueries(List<String> queries) private boolean checkForQueries(List<String> queries)
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptTailer tailer = queue.createTailer(); ExcerptTailer tailer = queue.createTailer();
List<String> expectedQueries = new LinkedList<>(queries); List<String> expectedQueries = new LinkedList<>(queries);
@ -434,7 +434,7 @@ public class FullQueryLoggerTest extends CQLTester
private void assertRoundTripQuery(@Nullable String keyspace) private void assertRoundTripQuery(@Nullable String keyspace)
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptTailer tailer = queue.createTailer(); ExcerptTailer tailer = queue.createTailer();
assertTrue(tailer.readDocument(wire -> assertTrue(tailer.readDocument(wire ->
@ -473,7 +473,7 @@ public class FullQueryLoggerTest extends CQLTester
Util.spinAssertEquals(true, () -> Util.spinAssertEquals(true, () ->
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
return queue.createTailer().readingDocument().isPresent(); return queue.createTailer().readingDocument().isPresent();
} }
@ -497,7 +497,7 @@ public class FullQueryLoggerTest extends CQLTester
Util.spinAssertEquals(true, () -> Util.spinAssertEquals(true, () ->
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
return queue.createTailer().readingDocument().isPresent(); return queue.createTailer().readingDocument().isPresent();
} }
@ -509,7 +509,7 @@ public class FullQueryLoggerTest extends CQLTester
private void assertRoundTripBatch(@Nullable String keyspace) private void assertRoundTripBatch(@Nullable String keyspace)
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(tempDir.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptTailer tailer = queue.createTailer(); ExcerptTailer tailer = queue.createTailer();
assertTrue(tailer.readDocument(wire -> { assertTrue(tailer.readDocument(wire -> {

View File

@ -22,9 +22,12 @@ import java.time.Duration;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.jboss.byteman.contrib.bmunit.BMRule; import org.jboss.byteman.contrib.bmunit.BMRule;
import org.jboss.byteman.contrib.bmunit.BMUnitConfig;
import org.jboss.byteman.contrib.bmunit.BMUnitRunner; import org.jboss.byteman.contrib.bmunit.BMUnitRunner;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -91,12 +94,14 @@ public class HintServiceBytemanTest
HintsService.instance.startDispatch(); HintsService.instance.startDispatch();
} }
@Test // When this test hangs it does it _hard_. Working now but don't want to make the worker in CI hang out for full timeout.
@Test (timeout = 300000)
@BMUnitConfig(bmunitVerbose=true, verbose=true)
@BMRule(name = "Delay delivering hints", @BMRule(name = "Delay delivering hints",
targetClass = "DispatchHintsTask", targetClass = "DispatchHintsTask",
targetMethod = "run", targetMethod = "run",
action = "Thread.sleep(DatabaseDescriptor.getHintsFlushPeriodInMS() * 3L)") action = "Thread.sleep(DatabaseDescriptor.getHintsFlushPeriodInMS() * 3L)")
public void testListPendingHints() throws InterruptedException, ExecutionException public void testListPendingHints() throws InterruptedException, ExecutionException, TimeoutException
{ {
HintsService.instance.resumeDispatch(); HintsService.instance.resumeDispatch();
MockMessagingSpy spy = sendHintsAndResponses(metadata, 20000, -1); MockMessagingSpy spy = sendHintsAndResponses(metadata, 20000, -1);
@ -111,7 +116,9 @@ public class HintServiceBytemanTest
assertEquals(1, info.totalFiles); assertEquals(1, info.totalFiles);
assertEquals(info.oldestTimestamp, info.newestTimestamp); // there is 1 descriptor with only 1 timestamp assertEquals(info.oldestTimestamp, info.newestTimestamp); // there is 1 descriptor with only 1 timestamp
spy.interceptMessageOut(20000).get(); // JDK21 genZGC uncovered some flakiness / hanging here waiting on Condition
spy.interceptMessageOut(20000).get(60, TimeUnit.SECONDS);
spy.printMessageCounts();
assertEquals(Collections.emptyList(), HintsService.instance.getPendingHints()); assertEquals(Collections.emptyList(), HintsService.instance.getPendingHints());
} }
} }

View File

@ -32,7 +32,9 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.Timeout;
import accord.primitives.Keys; import accord.primitives.Keys;
import accord.primitives.TxnId; import accord.primitives.TxnId;
@ -64,6 +66,7 @@ import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Dispatcher; import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.MockFailureDetector; import org.apache.cassandra.utils.MockFailureDetector;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.apache.cassandra.Util.spinAssertEquals; import static org.apache.cassandra.Util.spinAssertEquals;
import static org.apache.cassandra.config.CassandraRelevantProperties.HINT_DISPATCH_INTERVAL_MS; import static org.apache.cassandra.config.CassandraRelevantProperties.HINT_DISPATCH_INTERVAL_MS;
import static org.apache.cassandra.hints.HintsTestUtil.sendHintsAndResponses; import static org.apache.cassandra.hints.HintsTestUtil.sendHintsAndResponses;
@ -83,6 +86,14 @@ public class HintsServiceTest
private final MockFailureDetector failureDetector = new MockFailureDetector(); private final MockFailureDetector failureDetector = new MockFailureDetector();
private static TableMetadata metadata; private static TableMetadata metadata;
// Had some trouble with this test OOM'ing and misbehaving; trying to tighten things up a bit. It's good now but leaving
// this here to defend against future long CI hangs making workers burn cycles.
@Rule
public final Timeout perTestTimeout = Timeout.builder()
.withTimeout(8, MINUTES) // match test.timeout in build.xml
.withLookingForStuckThread(true) // dumps stack of a likely stuck thread
.build();
@BeforeClass @BeforeClass
public static void defineSchema() public static void defineSchema()
{ {

View File

@ -33,7 +33,6 @@ import org.apache.cassandra.locator.InetAddressAndPort;
*/ */
public class MockMessagingService public class MockMessagingService
{ {
private MockMessagingService() private MockMessagingService()
{ {
} }

View File

@ -36,12 +36,37 @@ import org.slf4j.LoggerFactory;
import static org.apache.cassandra.utils.concurrent.BlockingQueues.newBlockingQueue; import static org.apache.cassandra.utils.concurrent.BlockingQueues.newBlockingQueue;
/** /**
* Allows inspecting the behavior of mocked messaging by observing {@link MatcherResponse}. * Test utility that spies on mocked messaging interactions.
*
* <p>This class records inbound mocked messages and outbound messages that would have been
* sent, allowing tests to capture, assert or wait for specific communication patterns.</p>
*
* <h2>Debug logging</h2>
* <p>The internal {@code debugLog} method writes detailed information about the spys
* activity. By default, the logger uses TRACE level; to make the output visible at INFO
* level, set the environment variable {@code MOCK_MESSAGING_SPY_DEBUG=true} before starting
* the JVM.
*
* @see MatcherResponse
* @see MockMessagingService
*/ */
public class MockMessagingSpy public class MockMessagingSpy
{ {
private static final Logger logger = LoggerFactory.getLogger(MockMessagingSpy.class); private static final Logger logger = LoggerFactory.getLogger(MockMessagingSpy.class);
// checkstyle: suppress below 'blockSystemPropertyUsage'
private static boolean DEBUG_ENABLED = Boolean.parseBoolean(System.getenv("MOCK_MESSAGING_SPY_DEBUG"));
private void debugLog(String format, Object... args)
{
if (DEBUG_ENABLED)
logger.info(format, args);
else
logger.trace(format, args);
}
public static void enableDebug() {DEBUG_ENABLED = true; }
public static void disableDebug() {DEBUG_ENABLED = false; }
private final AtomicInteger messagesIntercepted = new AtomicInteger(); private final AtomicInteger messagesIntercepted = new AtomicInteger();
private final AtomicInteger mockedMessageResponses = new AtomicInteger(); private final AtomicInteger mockedMessageResponses = new AtomicInteger();
@ -142,17 +167,22 @@ public class MockMessagingSpy
return mockedMessageResponses.get(); return mockedMessageResponses.get();
} }
public void printMessageCounts()
{
logger.info("Messages intercepted: {}. Mocked Message responses: {}", messagesIntercepted(), mockedMessageResponses());
}
void matchingMessage(Message<?> message) void matchingMessage(Message<?> message)
{ {
messagesIntercepted.incrementAndGet(); int count = messagesIntercepted.incrementAndGet();
logger.trace("Received matching message: {}", message); debugLog("messagesInterceptedCount: {}. Received matching message: {}", count, message);
interceptedMessages.add(message); interceptedMessages.add(message);
} }
void matchingResponse(Message<?> response) void matchingResponse(Message<?> response)
{ {
mockedMessageResponses.incrementAndGet(); int count = mockedMessageResponses.incrementAndGet();
logger.trace("Responding to intercepted message: {}", response); debugLog("mockedMessageResponseCount: {}. Responding to intercepted message: {}", count, response);
deliveredResponses.add(response); deliveredResponses.add(response);
} }

View File

@ -266,6 +266,10 @@ public class RepairJobTest
long singleTreeSize = ObjectSizes.measureDeep(mockTrees.get(addr1)); long singleTreeSize = ObjectSizes.measureDeep(mockTrees.get(addr1));
assertEquals(0, session.syncingCount());
for (var mt : mockTrees.values())
assertThat(mt.isReleased()).isFalse();
// Use addr4 instead of one of the provided trees to force everything to be remote sync tasks as // Use addr4 instead of one of the provided trees to force everything to be remote sync tasks as
// LocalSyncTasks try to reach over the network. // LocalSyncTasks try to reach over the network.
List<SyncTask> syncTasks = RepairJob.createStandardSyncTasks(SharedContext.Global.instance, sessionJobDesc, mockTreeResponses, List<SyncTask> syncTasks = RepairJob.createStandardSyncTasks(SharedContext.Global.instance, sessionJobDesc, mockTreeResponses,
@ -275,30 +279,41 @@ public class RepairJobTest
session.pullRepair, session.pullRepair,
session.previewKind); session.previewKind);
// All trees in our mockTrees should be released after SyncTask creation
for (var mt : mockTrees.values())
assertThat(mt.isReleased()).isTrue();
assertThat(session.syncingCount()).isEqualTo(0);
// SyncTasks themselves should not contain significant memory // SyncTasks themselves should not contain significant memory
SyncTaskListAssert.assertThat(syncTasks).hasSizeLessThan(0.2 * singleTreeSize); SyncTaskListAssert.assertThat(syncTasks).hasSizeLessThan(0.2 * singleTreeSize);
// Remember the size of the session before we've executed any tasks // We can't directly check the memory size in the session post JDK21; jamm has trouble walking the internal
long sizeBeforeExecution = ObjectSizes.measureDeep(session); // object graph of a ConcurrentHashMap's implementation post JDK21 and has bugs with the @Contended case (which
// indicates we should consider moving away from it... jamm, that is. Which we've discussed as a project).
// Instead, we treat the presence or removal of SyncTasks as indicative of the memory usage since the reference
// to the MerkleTrees lives on there after creation.
// block syncComplete execution until test has verified session still retains the trees // block syncComplete execution until test has verified session still retains the trees
CompletableFuture<?> future = new CompletableFuture<>(); CompletableFuture<?> future = new CompletableFuture<>();
session.registerSyncCompleteCallback(future::get); session.registerSyncCompleteCallback(future::get);
ListenableFuture<List<SyncStat>> syncResults = job.executeTasks(syncTasks);
// Immediately following execution the internal execution queue should still retain the trees ListenableFuture<List<SyncStat>> syncResults = job.executeTasks(syncTasks);
long sizeDuringExecution = ObjectSizes.measureDeep(session); // Immediately following execution the SyncTasks should still be live and thus taking memory
assertThat(sizeDuringExecution).isGreaterThan(sizeBeforeExecution + (syncTasks.size() * singleTreeSize)); assertThat(session.syncingCount()).isNotEqualTo(0);
// unblock syncComplete callback, session should remove trees // unblock syncComplete callback, session should remove trees
future.complete(null); future.complete(null);
// The session retains memory in the contained executor until the threads expire, so we wait for the threads // The session retains memory in the contained executor until the threads expire, so we wait for the threads
// that ran the Tree -> SyncTask conversions to die and release the memory // that ran the Tree -> SyncTask conversions to die and release the memory
long millisUntilFreed; long millisUntilFreed;
// Confirm that it's the thread timeout mechanism that's causing the SyncTasks to retire
assertThat(session.syncingCount()).isNotEqualTo(0);
for (millisUntilFreed = 0; millisUntilFreed < TEST_TIMEOUT_S * 1000; millisUntilFreed += THREAD_TIMEOUT_MILLIS) for (millisUntilFreed = 0; millisUntilFreed < TEST_TIMEOUT_S * 1000; millisUntilFreed += THREAD_TIMEOUT_MILLIS)
{ {
TimeUnit.MILLISECONDS.sleep(THREAD_TIMEOUT_MILLIS); TimeUnit.MILLISECONDS.sleep(THREAD_TIMEOUT_MILLIS);
if (ObjectSizes.measureDeep(session) < (sizeDuringExecution - (syncTasks.size() * singleTreeSize))) if (session.syncingCount() == 0)
break; break;
} }

View File

@ -17,6 +17,7 @@
*/ */
package org.apache.cassandra.service; package org.apache.cassandra.service;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -30,7 +31,6 @@ import static org.junit.Assert.assertTrue;
public class GCInspectorTest public class GCInspectorTest
{ {
GCInspector gcInspector; GCInspector gcInspector;
@BeforeClass @BeforeClass
@ -44,12 +44,12 @@ public class GCInspectorTest
{ {
gcInspector = new GCInspector(); gcInspector = new GCInspector();
} }
@Test @Test
public void ensureStaticFieldsHydrateFromConfig() public void ensureStaticFieldsHydrateFromConfig()
{ {
assertEquals(DatabaseDescriptor.getGCLogThreshold(), gcInspector.getGcLogThresholdInMs()); Assert.assertEquals(DatabaseDescriptor.getGCLogThreshold(), gcInspector.getGcLogThresholdInMs());
assertEquals(DatabaseDescriptor.getGCWarnThreshold(), gcInspector.getGcWarnThresholdInMs()); Assert.assertEquals(DatabaseDescriptor.getGCWarnThreshold(), gcInspector.getGcWarnThresholdInMs());
assertEquals(DatabaseDescriptor.getGCConcurrentPhaseLogThreshold(), gcInspector.getGcConcurrentPhaseLogThresholdInMs()); assertEquals(DatabaseDescriptor.getGCConcurrentPhaseLogThreshold(), gcInspector.getGcConcurrentPhaseLogThresholdInMs());
assertEquals(DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold(), gcInspector.getGcConcurrentPhaseWarnThresholdInMs()); assertEquals(DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold(), gcInspector.getGcConcurrentPhaseWarnThresholdInMs());
} }
@ -64,12 +64,12 @@ public class GCInspectorTest
public void ensureWarnGreaterThanLog() public void ensureWarnGreaterThanLog()
{ {
assertThatThrownBy(() -> gcInspector.setGcWarnThresholdInMs(gcInspector.getGcLogThresholdInMs())) assertThatThrownBy(() -> gcInspector.setGcWarnThresholdInMs(gcInspector.getGcLogThresholdInMs()))
.isInstanceOf(IllegalArgumentException.class) .isInstanceOf(IllegalArgumentException.class)
.hasMessage("Threshold value for gc_warn_threshold (200) must be greater than gc_log_threshold which is currently 200"); .hasMessageContaining("must be greater than");
assertThatThrownBy(() -> gcInspector.setGcConcurrentPhaseWarnThresholdInMs(gcInspector.getGcConcurrentPhaseLogThresholdInMs())) assertThatThrownBy(() -> gcInspector.setGcConcurrentPhaseWarnThresholdInMs(gcInspector.getGcConcurrentPhaseLogThresholdInMs()))
.isInstanceOf(IllegalArgumentException.class) .isInstanceOf(IllegalArgumentException.class)
.hasMessage("Threshold value for gc_concurrent_phase_warn_threshold (1000) must be greater than gc_concurrent_phase_log_threshold which is currently 1000"); .hasMessageContaining("must be greater than");
} }
@Test @Test
@ -84,25 +84,34 @@ public class GCInspectorTest
assertEquals(0, DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold()); assertEquals(0, DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold());
assertEquals(1000, DatabaseDescriptor.getGCConcurrentPhaseLogThreshold()); assertEquals(1000, DatabaseDescriptor.getGCConcurrentPhaseLogThreshold());
} }
/**
* We keep the concurrent log values the same for JDK21 since we have to infer
* See {@code GCInspector#assumeGCIsPartiallyConcurrent()}
*/
@Test @Test
public void ensureLogLessThanWarn() public void ensureLogLessThanWarn()
{ {
assertEquals(200, gcInspector.getGcLogThresholdInMs()); int gcLog = 200;
gcInspector.setGcWarnThresholdInMs(1000); int gcWarn = 1000;
assertEquals(1000, gcInspector.getGcWarnThresholdInMs()); int gcConcurrentLog = 1000;
int gcConcurrentWarn = 2000;
Assert.assertEquals(gcLog, gcInspector.getGcLogThresholdInMs());
gcInspector.setGcWarnThresholdInMs(gcWarn);
assertEquals(gcWarn, gcInspector.getGcWarnThresholdInMs());
assertThatThrownBy(() -> gcInspector.setGcLogThresholdInMs(gcInspector.getGcWarnThresholdInMs() + 1)) assertThatThrownBy(() -> gcInspector.setGcLogThresholdInMs(gcInspector.getGcWarnThresholdInMs() + 1))
.isInstanceOf(IllegalArgumentException.class) .isInstanceOf(IllegalArgumentException.class)
.hasMessage("Threshold value for gc_log_threshold (1001) must be less than gc_warn_threshold which is currently 1000"); .hasMessageContaining("must be less than");
assertEquals(1000, gcInspector.getGcConcurrentPhaseLogThresholdInMs()); assertEquals(gcConcurrentLog, gcInspector.getGcConcurrentPhaseLogThresholdInMs());
gcInspector.setGcConcurrentPhaseWarnThresholdInMs(2000); gcInspector.setGcConcurrentPhaseWarnThresholdInMs(gcConcurrentWarn);
assertEquals(2000, gcInspector.getGcConcurrentPhaseWarnThresholdInMs()); assertEquals(gcConcurrentWarn, gcInspector.getGcConcurrentPhaseWarnThresholdInMs());
assertThatThrownBy(() -> gcInspector.setGcConcurrentPhaseLogThresholdInMs(gcInspector.getGcConcurrentPhaseWarnThresholdInMs() + 1)) assertThatThrownBy(() -> gcInspector.setGcConcurrentPhaseLogThresholdInMs(gcInspector.getGcConcurrentPhaseWarnThresholdInMs() + 1))
.isInstanceOf(IllegalArgumentException.class) .isInstanceOf(IllegalArgumentException.class)
.hasMessage("Threshold value for gc_concurrent_phase_log_threshold (2001) must be less than gc_concurrent_phase_warn_threshold which is currently 2000"); .hasMessageContaining("must be less than");
} }
@Test @Test

View File

@ -31,8 +31,8 @@ import com.google.common.collect.Lists;
import net.openhft.chronicle.core.io.IORuntimeException; import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.queue.ChronicleQueue; import net.openhft.chronicle.queue.ChronicleQueue;
import net.openhft.chronicle.queue.ExcerptAppender; import net.openhft.chronicle.queue.ExcerptAppender;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import net.openhft.chronicle.queue.rollcycles.TestRollCycles;
import net.openhft.chronicle.wire.WireOut; import net.openhft.chronicle.wire.WireOut;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -98,7 +98,8 @@ public class AuditLogViewerTest
" -i,--ignore Silently ignore unsupported records\n" + " -i,--ignore Silently ignore unsupported records\n" +
" -r,--roll_cycle <arg> How often to roll the log file was rolled. May be\n" + " -r,--roll_cycle <arg> How often to roll the log file was rolled. May be\n" +
" necessary for Chronicle to correctly parse file\n" + " necessary for Chronicle to correctly parse file\n" +
" names. (MINUTELY, HOURLY, DAILY). Default HOURLY.\n"; " names. (FIVE_MINUTELY, FAST_HOURLY, FAST_DAILY).\n" +
" Default FAST_HOURLY.\n";
Assertions.assertThat(tool.getStdout()).isEqualTo(help); Assertions.assertThat(tool.getStdout()).isEqualTo(help);
} }
@ -162,7 +163,7 @@ public class AuditLogViewerTest
records.add("Test foo bar 1"); records.add("Test foo bar 1");
records.add("Test foo bar 2"); records.add("Test foo bar 2");
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptAppender appender = queue.acquireAppender(); ExcerptAppender appender = queue.acquireAppender();
@ -171,7 +172,7 @@ public class AuditLogViewerTest
//Read those written records //Read those written records
List<String> actualRecords = new ArrayList<>(); List<String> actualRecords = new ArrayList<>();
AuditLogViewer.dump(ImmutableList.of(path.toString()), RollCycles.TEST_SECONDLY.toString(), false, false, actualRecords::add); AuditLogViewer.dump(ImmutableList.of(path.toString()), TestRollCycles.TEST_SECONDLY.toString(), false, false, actualRecords::add);
assertRecordsMatch(records, actualRecords); assertRecordsMatch(records, actualRecords);
} }
@ -180,12 +181,12 @@ public class AuditLogViewerTest
@Test (expected = IORuntimeException.class) @Test (expected = IORuntimeException.class)
public void testRejectFutureVersionRecord() public void testRejectFutureVersionRecord()
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptAppender appender = queue.acquireAppender(); ExcerptAppender appender = queue.acquireAppender();
appender.writeDocument(createFutureRecord()); appender.writeDocument(createFutureRecord());
AuditLogViewer.dump(ImmutableList.of(path.toString()), RollCycles.TEST_SECONDLY.toString(), false, false, dummy -> {}); AuditLogViewer.dump(ImmutableList.of(path.toString()), TestRollCycles.TEST_SECONDLY.toString(), false, false, dummy -> {});
} }
catch (Exception e) catch (Exception e)
{ {
@ -201,7 +202,7 @@ public class AuditLogViewerTest
records.add("Test foo bar 1"); records.add("Test foo bar 1");
records.add("Test foo bar 2"); records.add("Test foo bar 2");
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptAppender appender = queue.acquireAppender(); ExcerptAppender appender = queue.acquireAppender();
@ -213,7 +214,7 @@ public class AuditLogViewerTest
//Read those written records //Read those written records
List<String> actualRecords = new ArrayList<>(); List<String> actualRecords = new ArrayList<>();
AuditLogViewer.dump(ImmutableList.of(path.toString()), RollCycles.TEST_SECONDLY.toString(), false, true, actualRecords::add); AuditLogViewer.dump(ImmutableList.of(path.toString()), TestRollCycles.TEST_SECONDLY.toString(), false, true, actualRecords::add);
// Assert all current records are present // Assert all current records are present
assertRecordsMatch(records, actualRecords); assertRecordsMatch(records, actualRecords);
@ -223,12 +224,12 @@ public class AuditLogViewerTest
@Test (expected = IORuntimeException.class) @Test (expected = IORuntimeException.class)
public void testRejectUnknownTypeRecord() public void testRejectUnknownTypeRecord()
{ {
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptAppender appender = queue.acquireAppender(); ExcerptAppender appender = queue.acquireAppender();
appender.writeDocument(createUnknownTypeRecord()); appender.writeDocument(createUnknownTypeRecord());
AuditLogViewer.dump(ImmutableList.of(path.toString()), RollCycles.TEST_SECONDLY.toString(), false, false, dummy -> {}); AuditLogViewer.dump(ImmutableList.of(path.toString()), TestRollCycles.TEST_SECONDLY.toString(), false, false, dummy -> {});
} }
catch (Exception e) catch (Exception e)
{ {
@ -244,7 +245,7 @@ public class AuditLogViewerTest
records.add("Test foo bar 1"); records.add("Test foo bar 1");
records.add("Test foo bar 2"); records.add("Test foo bar 2");
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptAppender appender = queue.acquireAppender(); ExcerptAppender appender = queue.acquireAppender();
@ -256,7 +257,7 @@ public class AuditLogViewerTest
//Read those written records //Read those written records
List<String> actualRecords = new ArrayList<>(); List<String> actualRecords = new ArrayList<>();
AuditLogViewer.dump(ImmutableList.of(path.toString()), RollCycles.TEST_SECONDLY.toString(), false, true, actualRecords::add); AuditLogViewer.dump(ImmutableList.of(path.toString()), TestRollCycles.TEST_SECONDLY.toString(), false, true, actualRecords::add);
// Assert all supported records are present // Assert all supported records are present
assertRecordsMatch(records, actualRecords); assertRecordsMatch(records, actualRecords);

View File

@ -18,6 +18,10 @@
package org.apache.cassandra.tools; package org.apache.cassandra.tools;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import com.datastax.driver.core.exceptions.NoHostAvailableException; import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.hamcrest.CoreMatchers; import org.hamcrest.CoreMatchers;
@ -31,17 +35,31 @@ import static org.junit.Assert.assertThat;
public class BulkLoaderTest extends OfflineToolUtils public class BulkLoaderTest extends OfflineToolUtils
{ {
private static final String[] ALLOWED_BULK_LOADER_THREADS = new String[]{
"ObjectCleanerThread",
"globalEventExecutor-[1-9]-[1-9]",
"globalEventExecutor-[1-9]-[1-9]",
"Shutdown-checker",
"cluster[0-9]-connection-reaper-[0-9]",
"Attach Listener",
"process reaper",
"JNA Cleaner",
};
// the driver isn't expected to terminate threads on close synchronously (CASSANDRA-19000)
private static final String SYNC_DRIVER_THREAD = "cluster[0-9]-nio-worker-[0-9]";
@Test @Test
public void testBulkLoader_NoArgs() public void testBulkLoader_NoArgs()
{ {
ToolResult tool = ToolRunner.invokeClass(BulkLoader.class); ToolResult tool = ToolRunner.invokeClass(BulkLoader.class);
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertThat(tool.getCleanedStderr(), CoreMatchers.containsString("Missing sstable directory argument")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsString("Missing sstable directory argument"));
assertNoUnexpectedThreadsStarted(new String[] { "ObjectCleanerThread", assertNoUnexpectedThreadsStarted(false, "ObjectCleanerThread",
"Shutdown-checker", "Shutdown-checker",
"cluster[0-9]-connection-reaper-[0-9]" }, "cluster[0-9]-connection-reaper-[0-9]");
false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -63,15 +81,8 @@ public class BulkLoaderTest extends OfflineToolUtils
if (!(tool.getException().getCause().getCause().getCause() instanceof NoHostAvailableException)) if (!(tool.getException().getCause().getCause().getCause() instanceof NoHostAvailableException))
throw tool.getException(); throw tool.getException();
assertNoUnexpectedThreadsStarted(new String[] { "ObjectCleanerThread",
"globalEventExecutor-[1-9]-[1-9]", assertNoUnexpectedThreadsStarted(false, ALLOWED_BULK_LOADER_THREADS);
"globalEventExecutor-[1-9]-[1-9]",
"Shutdown-checker",
"cluster[0-9]-connection-reaper-[0-9]",
"Attach Listener",
"process reaper",
"JNA Cleaner"},
false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -95,21 +106,11 @@ public class BulkLoaderTest extends OfflineToolUtils
if (!(tool.getException().getCause().getCause().getCause() instanceof NoHostAvailableException)) if (!(tool.getException().getCause().getCause().getCause() instanceof NoHostAvailableException))
throw tool.getException(); throw tool.getException();
assertNoUnexpectedThreadsStarted(new String[] { "ObjectCleanerThread", assertNoUnexpectedThreadsStarted(false, appendToAllowedThreads(SYNC_DRIVER_THREAD));
"globalEventExecutor-[1-9]-[1-9]", assertSchemaNotLoaded();
"globalEventExecutor-[1-9]-[1-9]", assertCLSMNotLoaded();
"Shutdown-checker", assertSystemKSNotLoaded();
"cluster[0-9]-connection-reaper-[0-9]", assertKeyspaceNotLoaded();
"Attach Listener",
"process reaper",
"JNA Cleaner",
// the driver isn't expected to terminate threads on close synchronously (CASSANDRA-19000)
"cluster[0-9]-nio-worker-[0-9]" },
false);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
assertSystemKSNotLoaded();
assertKeyspaceNotLoaded();
assertServerNotLoaded(); assertServerNotLoaded();
} }
@ -129,17 +130,7 @@ public class BulkLoaderTest extends OfflineToolUtils
if (!(tool.getException().getCause().getCause().getCause() instanceof NoHostAvailableException)) if (!(tool.getException().getCause().getCause().getCause() instanceof NoHostAvailableException))
throw tool.getException(); throw tool.getException();
assertNoUnexpectedThreadsStarted(new String[] { "ObjectCleanerThread", assertNoUnexpectedThreadsStarted(false, appendToAllowedThreads(SYNC_DRIVER_THREAD));
"globalEventExecutor-[1-9]-[1-9]",
"globalEventExecutor-[1-9]-[1-9]",
"Shutdown-checker",
"cluster[0-9]-connection-reaper-[0-9]",
"Attach Listener",
"process reaper",
"JNA Cleaner",
// the driver isn't expected to terminate threads on close synchronously (CASSANDRA-19000)
"cluster[0-9]-nio-worker-[0-9]" },
false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -216,4 +207,11 @@ public class BulkLoaderTest extends OfflineToolUtils
assertEquals(6, DatabaseDescriptor.getEntireSSTableInterDCStreamThroughputOutboundMebibytesPerSec(), 0.0); assertEquals(6, DatabaseDescriptor.getEntireSSTableInterDCStreamThroughputOutboundMebibytesPerSec(), 0.0);
throw tool.getException().getCause().getCause().getCause(); throw tool.getException().getCause().getCause().getCause();
} }
private String[] appendToAllowedThreads(String... optionalThreads)
{
var result = new ArrayList<>(Arrays.asList(ALLOWED_BULK_LOADER_THREADS));
Collections.addAll(result, optionalThreads);
return result.toArray(String[]::new);
}
} }

View File

@ -29,7 +29,7 @@ public class GetVersionTest extends OfflineToolUtils
{ {
ToolResult tool = ToolRunner.invokeClass(GetVersion.class); ToolResult tool = ToolRunner.invokeClass(GetVersion.class);
tool.assertOnCleanExit(); tool.assertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -27,15 +27,13 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.google.common.collect.Iterables;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.CassandraRelevantProperties; import org.apache.cassandra.config.CassandraRelevantProperties;
@ -44,7 +42,6 @@ import org.apache.cassandra.io.util.File;
import static org.apache.cassandra.utils.FBUtilities.preventIllegalAccessWarnings; import static org.apache.cassandra.utils.FBUtilities.preventIllegalAccessWarnings;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
/** /**
@ -52,6 +49,8 @@ import static org.junit.Assert.fail;
*/ */
public abstract class OfflineToolUtils public abstract class OfflineToolUtils
{ {
private static final Logger logger = LoggerFactory.getLogger(OfflineToolUtils.class);
static static
{ {
preventIllegalAccessWarnings(); preventIllegalAccessWarnings();
@ -60,68 +59,77 @@ public abstract class OfflineToolUtils
private static List<ThreadInfo> initialThreads; private static List<ThreadInfo> initialThreads;
static final String[] OPTIONAL_THREADS_WITH_SCHEMA = { static final String[] OPTIONAL_THREADS_WITH_SCHEMA = {
"ScheduledTasks:[1-9]", "ScheduledTasks:[1-9]",
"ScheduledFastTasks:[1-9]", "ScheduledFastTasks:[1-9]",
"OptionalTasks:[1-9]", "OptionalTasks:[1-9]",
"Reference-Reaper", "Reference-Reaper",
"LocalPool-Cleaner(-networking|-chunk-cache)", "LocalPool-Cleaner(-networking|-chunk-cache)",
"CacheCleanupExecutor:[1-9]", "CacheCleanupExecutor:[1-9]",
"CompactionExecutor:[1-9]", "CompactionExecutor:[1-9]",
"ValidationExecutor:[1-9]", "ValidationExecutor:[1-9]",
"NonPeriodicTasks:[1-9]", "NonPeriodicTasks:[1-9]",
"Sampler:[1-9]", "Sampler:[1-9]",
"SecondaryIndexManagement:[1-9]", "SecondaryIndexManagement:[1-9]",
"Strong-Reference-Leak-Detector:[1-9]", "Strong-Reference-Leak-Detector:[1-9]",
"Background_Reporter:[1-9]", "Background_Reporter:[1-9]",
"EXPIRING-MAP-REAPER:[1-9]", "EXPIRING-MAP-REAPER:[1-9]",
"ObjectCleanerThread", "ObjectCleanerThread",
"process reaper", // spawned by the jvm when executing external processes "process reaper", // spawned by the jvm when executing external processes and may still be active when we check
// and may still be active when we check // and may still be active when we check
"Attach Listener", // spawned in intellij IDEA "Attach Listener", // spawned in intellij IDEA
"JNA Cleaner", // spawned by JNA "JNA Cleaner", // spawned by JNA
"ThreadLocalMetrics-Cleaner", // spawned by org.apache.cassandra.metrics.ThreadLocalMetrics "ThreadLocalMetrics-Cleaner", // spawned by org.apache.cassandra.metrics.ThreadLocalMetrics
"Native reference cleanup thread", "Native reference cleanup thread",
"^ForkJoinPool\\.commonPool-worker-\\d+$" "^ForkJoinPool\\.commonPool-worker-\\d+$"
}; };
static final String[] NON_DEFAULT_MEMTABLE_THREADS = static final String[] NON_DEFAULT_MEMTABLE_THREADS =
{ {
"((Native|Slab|Heap)Pool|Logged)Cleaner" "((Native|Slab|Heap)Pool|Logged)Cleaner"
}; };
public void assertNoUnexpectedThreadsStarted(String[] optionalThreadNames, boolean allowNonDefaultMemtableThreads) /** We have some threads that show up in the presence of other JDK's; account for those here */
private static final String[] EXTRA_JDK_THREADS = new String[]{
"Native reference cleanup thread", // Corretto janitor thread
};
/**
* When unexpected threads or core singleton objects are inadvertantly initialized during tool runs it can lead to
* the tool hanging on shutdown. We have a whitelisted number of threads above along with whatever initial threads
* were there on first tool creation we can diff against; if we find _anything_ new, it's an error condition - this
* needs to either be added to the list above or debugged and fixed.
*/
public void assertNoUnexpectedThreadsStarted(boolean allowNonDefaultMemtableThreads, String... optionalThreadNames)
{ {
ThreadMXBean threads = ManagementFactory.getThreadMXBean(); var allowedThreadNames = initialThreads.stream()
.map(ThreadInfo::getThreadName)
.collect(Collectors.toSet());
Set<String> initial = initialThreads Collections.addAll(allowedThreadNames, EXTRA_JDK_THREADS);
.stream() Collections.addAll(allowedThreadNames, optionalThreadNames);
.map(ThreadInfo::getThreadName)
.collect(Collectors.toSet());
Set<String> current = Arrays.stream(threads.getThreadInfo(threads.getAllThreadIds()))
.filter(Objects::nonNull)
.map(ThreadInfo::getThreadName)
.collect(Collectors.toSet());
Iterable<String> optionalNames = optionalThreadNames != null
? Arrays.asList(optionalThreadNames)
: Collections.emptyList();
if (allowNonDefaultMemtableThreads && DatabaseDescriptor.getMemtableConfigurations().containsKey("default")) if (allowNonDefaultMemtableThreads && DatabaseDescriptor.getMemtableConfigurations().containsKey("default"))
optionalNames = Iterables.concat(optionalNames, Arrays.asList(NON_DEFAULT_MEMTABLE_THREADS)); Collections.addAll(allowedThreadNames, NON_DEFAULT_MEMTABLE_THREADS);
List<Pattern> optional = StreamSupport.stream(optionalNames.spliterator(), false) var allowedRegexes = allowedThreadNames.stream()
.map(Pattern::compile) .map(Pattern::compile)
.collect(Collectors.toList()); .collect(Collectors.toList());
current.removeAll(initial); var threads = ManagementFactory.getThreadMXBean();
var badThreads = Arrays.stream(threads.getThreadInfo(threads.getAllThreadIds()))
.filter(Objects::nonNull)
.filter(threadInfo -> allowedRegexes.stream().noneMatch(pattern -> pattern.matcher(threadInfo.getThreadName()).matches()))
.collect(Collectors.toSet());
Set<String> remain = current.stream() if (!badThreads.isEmpty())
.filter(threadName -> optional.stream().noneMatch(pattern -> pattern.matcher(threadName).matches())) {
.collect(Collectors.toSet()); logger.error("Found unexpected disallowed threads during check. Printing thread details.");
badThreads.forEach(info -> logger.error(info.toString()));
if (!remain.isEmpty()) var errorMessage = new StringBuilder("Bad threads found:");
System.err.println("Unexpected thread names: " + remain); badThreads.forEach(ti -> errorMessage.append("\n ").append(ti.getThreadName()));
errorMessage.append("\n-Tools should not start threads that initialize singletons or other system resources");
assertTrue("Wrong thread status, active threads unaccounted for: " + remain, remain.isEmpty()); Assert.fail(errorMessage.toString());
}
} }
public void assertSchemaNotLoaded() public void assertSchemaNotLoaded()
@ -239,7 +247,7 @@ public abstract class OfflineToolUtils
protected void assertCorrectEnvPostTest() protected void assertCorrectEnvPostTest()
{ {
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, true); assertNoUnexpectedThreadsStarted(true, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded(); assertSchemaLoaded();
assertServerNotLoaded(); assertServerNotLoaded();
} }

View File

@ -37,7 +37,7 @@ public class SSTableExpiredBlockersTest extends OfflineToolUtils
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -184,7 +184,7 @@ public class SSTableExportSchemaLoadingTest extends OfflineToolUtils
*/ */
private void assertPostTestEnv() private void assertPostTestEnv()
{ {
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, true); assertNoUnexpectedThreadsStarted(true, OPTIONAL_THREADS_WITH_SCHEMA);
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
assertKeyspaceNotLoaded(); assertKeyspaceNotLoaded();

View File

@ -108,7 +108,7 @@ public class SSTableExportTest extends OfflineToolUtils
*/ */
private void assertPostTestEnv() private void assertPostTestEnv()
{ {
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, false); assertNoUnexpectedThreadsStarted(false, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -36,7 +36,7 @@ public class SSTableLevelResetterTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty(); Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -54,7 +54,7 @@ public class SSTableMetadataViewerTest extends OfflineToolUtils
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Options:")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Options:"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
} }
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -218,7 +218,7 @@ public class SSTableMetadataViewerTest extends OfflineToolUtils
private void assertGoodEnvPostTest() private void assertGoodEnvPostTest()
{ {
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, false); assertNoUnexpectedThreadsStarted(false, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -36,7 +36,7 @@ public class SSTableOfflineRelevelTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty(); Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -101,7 +101,7 @@ public class SSTablePartitionsTest extends OfflineToolUtils
@After @After
public void assertPostTestEnv() public void assertPostTestEnv()
{ {
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, false); assertNoUnexpectedThreadsStarted(false, OPTIONAL_THREADS_WITH_SCHEMA);
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
assertKeyspaceNotLoaded(); assertKeyspaceNotLoaded();

View File

@ -41,7 +41,7 @@ public class SSTableRepairedAtSetterTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty(); Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -79,7 +79,7 @@ public class SSTableRepairedAtSetterTest extends OfflineToolUtils
"--is-repaired", "--is-repaired",
findOneSSTable("legacy_sstables", "legacy_ma_simple")); findOneSSTable("legacy_sstables", "legacy_ma_simple"));
tool.assertOnCleanExit(); tool.assertOnCleanExit();
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, false); assertNoUnexpectedThreadsStarted(false, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -95,7 +95,7 @@ public class SSTableRepairedAtSetterTest extends OfflineToolUtils
"--is-unrepaired", "--is-unrepaired",
findOneSSTable("legacy_sstables", "legacy_ma_simple")); findOneSSTable("legacy_sstables", "legacy_ma_simple"));
tool.assertOnCleanExit(); tool.assertOnCleanExit();
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, false); assertNoUnexpectedThreadsStarted(false, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -113,7 +113,7 @@ public class SSTableRepairedAtSetterTest extends OfflineToolUtils
String file = tmpFile.absolutePath(); String file = tmpFile.absolutePath();
ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class, "--really-set", "--is-repaired", "-f", file); ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class, "--really-set", "--is-repaired", "-f", file);
tool.assertOnCleanExit(); tool.assertOnCleanExit();
assertNoUnexpectedThreadsStarted(OPTIONAL_THREADS_WITH_SCHEMA, false); assertNoUnexpectedThreadsStarted(false, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -66,6 +66,7 @@ public class ToolRunner
protected static final Logger logger = LoggerFactory.getLogger(ToolRunner.class); protected static final Logger logger = LoggerFactory.getLogger(ToolRunner.class);
public static final ImmutableList<String> DEFAULT_CLEANERS = ImmutableList.of("(?im)^picked up.*\\R", public static final ImmutableList<String> DEFAULT_CLEANERS = ImmutableList.of("(?im)^picked up.*\\R",
"(?im)^.*package jdk.internal.util.jar not in java.base*\\R",
"(?im)^.*Not generating a deterministic id for table.*\\R", "(?im)^.*Not generating a deterministic id for table.*\\R",
"(?im)^.*`USE <keyspace>` with prepared statements is.*\\R"); "(?im)^.*`USE <keyspace>` with prepared statements is.*\\R");

View File

@ -33,7 +33,7 @@ public class ToolsSchemaLoadingTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -48,7 +48,7 @@ public class ToolsSchemaLoadingTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -63,7 +63,7 @@ public class ToolsSchemaLoadingTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No sstables to split")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No sstables to split"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -78,7 +78,7 @@ public class ToolsSchemaLoadingTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();
@ -93,7 +93,7 @@ public class ToolsSchemaLoadingTest extends OfflineToolUtils
assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:")); assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments")); assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
assertEquals(1, tool.getExitCode()); assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, false); assertNoUnexpectedThreadsStarted(false);
assertSchemaNotLoaded(); assertSchemaNotLoaded();
assertCLSMNotLoaded(); assertCLSMNotLoaded();
assertSystemKSNotLoaded(); assertSystemKSNotLoaded();

View File

@ -112,7 +112,7 @@ public class GetAuditLogTest extends CQLTester
final String output = getAuditLogOutput.replaceAll("( )+", " ").trim(); final String output = getAuditLogOutput.replaceAll("( )+", " ").trim();
assertThat(output).startsWith("enabled true"); assertThat(output).startsWith("enabled true");
assertThat(output).contains("logger BinAuditLogger"); assertThat(output).contains("logger BinAuditLogger");
assertThat(output).contains("roll_cycle HOURLY"); assertThat(output).contains("roll_cycle FAST_HOURLY");
assertThat(output).contains("block true"); assertThat(output).contains("block true");
assertThat(output).contains("max_log_size 17179869184"); assertThat(output).contains("max_log_size 17179869184");
assertThat(output).contains("max_queue_weight 268435456"); assertThat(output).contains("max_queue_weight 268435456");
@ -131,7 +131,7 @@ public class GetAuditLogTest extends CQLTester
final String output = getAuditLogOutput.replaceAll("( )+", " ").trim(); final String output = getAuditLogOutput.replaceAll("( )+", " ").trim();
assertThat(output).startsWith("enabled true"); assertThat(output).startsWith("enabled true");
assertThat(output).contains("logger BinAuditLogger"); assertThat(output).contains("logger BinAuditLogger");
assertThat(output).contains("roll_cycle HOURLY"); assertThat(output).contains("roll_cycle FAST_HOURLY");
assertThat(output).contains("block true"); assertThat(output).contains("block true");
assertThat(output).contains("max_log_size 17179869184"); assertThat(output).contains("max_log_size 17179869184");
assertThat(output).contains("max_queue_weight 268435456"); assertThat(output).contains("max_queue_weight 268435456");
@ -150,7 +150,7 @@ public class GetAuditLogTest extends CQLTester
final String output = getAuditLogOutput.replaceAll("( )+", " ").trim(); final String output = getAuditLogOutput.replaceAll("( )+", " ").trim();
assertThat(output).startsWith("enabled false"); assertThat(output).startsWith("enabled false");
assertThat(output).contains("logger BinAuditLogger"); assertThat(output).contains("logger BinAuditLogger");
assertThat(output).contains("roll_cycle HOURLY"); assertThat(output).contains("roll_cycle FAST_HOURLY");
assertThat(output).contains("block true"); assertThat(output).contains("block true");
assertThat(output).contains("max_log_size 17179869184"); assertThat(output).contains("max_log_size 17179869184");
assertThat(output).contains("max_queue_weight 268435456"); assertThat(output).contains("max_queue_weight 268435456");

View File

@ -17,14 +17,11 @@
*/ */
package org.apache.cassandra.transport; package org.apache.cassandra.transport;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -48,8 +45,8 @@ import org.apache.cassandra.transport.messages.QueryMessage;
import org.apache.cassandra.transport.messages.ResultMessage; import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteArrayUtil; import org.apache.cassandra.utils.ByteArrayUtil;
import org.apache.cassandra.utils.MD5Digest; import org.apache.cassandra.utils.MD5Digest;
import org.apache.cassandra.utils.ReflectionUtils;
import static org.apache.cassandra.config.CassandraRelevantProperties.CUSTOM_QUERY_HANDLER_CLASS;
import static org.apache.cassandra.utils.ByteBufferUtil.bytes; import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public class MessagePayloadTest extends CQLTester public class MessagePayloadTest extends CQLTester
@ -57,47 +54,11 @@ public class MessagePayloadTest extends CQLTester
public static Map<String, ByteBuffer> requestPayload; public static Map<String, ByteBuffer> requestPayload;
public static Map<String, ByteBuffer> responsePayload; public static Map<String, ByteBuffer> responsePayload;
private static Field cqlQueryHandlerField;
private static boolean modifiersAccessible;
@BeforeClass @BeforeClass
public static void makeCqlQueryHandlerAccessible() public static void setUpClass()
{ {
try CUSTOM_QUERY_HANDLER_CLASS.setString("org.apache.cassandra.transport.MessagePayloadTest$TestQueryHandler");
{ CQLTester.setUpClass();
cqlQueryHandlerField = ClientState.class.getDeclaredField("cqlQueryHandler");
cqlQueryHandlerField.setAccessible(true);
Field modifiersField = ReflectionUtils.getModifiersField();
modifiersAccessible = modifiersField.isAccessible();
modifiersField.setAccessible(true);
modifiersField.setInt(cqlQueryHandlerField, cqlQueryHandlerField.getModifiers() & ~Modifier.FINAL);
}
catch (IllegalAccessException | NoSuchFieldException e)
{
throw new RuntimeException(e);
}
}
@AfterClass
public static void resetCqlQueryHandlerField()
{
if (cqlQueryHandlerField == null)
return;
try
{
Field modifiersField = ReflectionUtils.getModifiersField();
modifiersField.setAccessible(true);
modifiersField.setInt(cqlQueryHandlerField, cqlQueryHandlerField.getModifiers() | Modifier.FINAL);
cqlQueryHandlerField.setAccessible(false);
modifiersField.setAccessible(modifiersAccessible);
}
catch (IllegalAccessException | NoSuchFieldException e)
{
throw new RuntimeException(e);
}
} }
@After @After
@ -116,13 +77,9 @@ public class MessagePayloadTest extends CQLTester
@Test @Test
public void testMessagePayloadBeta() throws Throwable public void testMessagePayloadBeta() throws Throwable
{ {
QueryHandler queryHandler = (QueryHandler) cqlQueryHandlerField.get(null); requireNetwork();
cqlQueryHandlerField.set(null, new TestQueryHandler());
try
{
requireNetwork();
Assert.assertSame(TestQueryHandler.class, ClientState.getCQLQueryHandler().getClass()); Assert.assertSame(TestQueryHandler.class, ClientState.getCQLQueryHandler().getClass());
SimpleClient client = new SimpleClient(nativeAddr.getHostAddress(), SimpleClient client = new SimpleClient(nativeAddr.getHostAddress(),
nativePort, nativePort,
@ -133,239 +90,216 @@ public class MessagePayloadTest extends CQLTester
{ {
client.connect(false); client.connect(false);
Map<String, ByteBuffer> reqMap; Map<String, ByteBuffer> reqMap;
Map<String, ByteBuffer> respMap; Map<String, ByteBuffer> respMap;
QueryOptions queryOptions = QueryOptions.create( QueryOptions queryOptions = QueryOptions.create(
QueryOptions.DEFAULT.getConsistency(), QueryOptions.DEFAULT.getConsistency(),
QueryOptions.DEFAULT.getValues(), QueryOptions.DEFAULT.getValues(),
QueryOptions.DEFAULT.skipMetadata(), QueryOptions.DEFAULT.skipMetadata(),
QueryOptions.DEFAULT.getPageSize(), QueryOptions.DEFAULT.getPageSize(),
QueryOptions.DEFAULT.getPagingState(), QueryOptions.DEFAULT.getPagingState(),
QueryOptions.DEFAULT.getSerialConsistency(), QueryOptions.DEFAULT.getSerialConsistency(),
ProtocolVersion.V5, ProtocolVersion.V5,
KEYSPACE); KEYSPACE);
QueryMessage queryMessage = new QueryMessage("CREATE TABLE atable (pk int PRIMARY KEY, v text)", QueryMessage queryMessage = new QueryMessage("CREATE TABLE atable (pk int PRIMARY KEY, v text)",
queryOptions); queryOptions);
PrepareMessage prepareMessage = new PrepareMessage("SELECT * FROM atable", KEYSPACE); PrepareMessage prepareMessage = new PrepareMessage("SELECT * FROM atable", KEYSPACE);
reqMap = Collections.singletonMap("foo", bytes(42)); reqMap = Collections.singletonMap("foo", bytes(42));
responsePayload = respMap = Collections.singletonMap("bar", bytes(42)); responsePayload = respMap = Collections.singletonMap("bar", bytes(42));
queryMessage.setCustomPayload(reqMap); queryMessage.setCustomPayload(reqMap);
Message.Response queryResponse = client.execute(queryMessage); Message.Response queryResponse = client.execute(queryMessage);
payloadEquals(reqMap, requestPayload); payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, queryResponse.getCustomPayload()); payloadEquals(respMap, queryResponse.getCustomPayload());
reqMap = Collections.singletonMap("foo", bytes(43)); reqMap = Collections.singletonMap("foo", bytes(43));
responsePayload = respMap = Collections.singletonMap("bar", bytes(43)); responsePayload = respMap = Collections.singletonMap("bar", bytes(43));
prepareMessage.setCustomPayload(reqMap); prepareMessage.setCustomPayload(reqMap);
ResultMessage.Prepared prepareResponse = (ResultMessage.Prepared) client.execute(prepareMessage); ResultMessage.Prepared prepareResponse = (ResultMessage.Prepared) client.execute(prepareMessage);
payloadEquals(reqMap, requestPayload); payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, prepareResponse.getCustomPayload()); payloadEquals(respMap, prepareResponse.getCustomPayload());
ExecuteMessage executeMessage = new ExecuteMessage(prepareResponse.statementId, prepareResponse.resultMetadataId, QueryOptions.DEFAULT); ExecuteMessage executeMessage = new ExecuteMessage(prepareResponse.statementId, prepareResponse.resultMetadataId, QueryOptions.DEFAULT);
reqMap = Collections.singletonMap("foo", bytes(44)); reqMap = Collections.singletonMap("foo", bytes(44));
responsePayload = respMap = Collections.singletonMap("bar", bytes(44)); responsePayload = respMap = Collections.singletonMap("bar", bytes(44));
executeMessage.setCustomPayload(reqMap); executeMessage.setCustomPayload(reqMap);
Message.Response executeResponse = client.execute(executeMessage); Message.Response executeResponse = client.execute(executeMessage);
payloadEquals(reqMap, requestPayload); payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, executeResponse.getCustomPayload()); payloadEquals(respMap, executeResponse.getCustomPayload());
BatchMessage batchMessage = new BatchMessage(BatchStatement.Type.UNLOGGED, BatchMessage batchMessage = new BatchMessage(BatchStatement.Type.UNLOGGED,
Collections.<Object>singletonList("INSERT INTO " + KEYSPACE + ".atable (pk,v) VALUES (1, 'foo')"), Collections.<Object>singletonList("INSERT INTO " + KEYSPACE + ".atable (pk,v) VALUES (1, 'foo')"),
Collections.singletonList(ByteArrayUtil.EMPTY_ARRAY_OF_BYTE_ARRAYS), Collections.singletonList(ByteArrayUtil.EMPTY_ARRAY_OF_BYTE_ARRAYS),
queryOptions); queryOptions);
reqMap = Collections.singletonMap("foo", bytes(45)); reqMap = Collections.singletonMap("foo", bytes(45));
responsePayload = respMap = Collections.singletonMap("bar", bytes(45)); responsePayload = respMap = Collections.singletonMap("bar", bytes(45));
batchMessage.setCustomPayload(reqMap); batchMessage.setCustomPayload(reqMap);
Message.Response batchResponse = client.execute(batchMessage); Message.Response batchResponse = client.execute(batchMessage);
payloadEquals(reqMap, requestPayload); payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, batchResponse.getCustomPayload()); payloadEquals(respMap, batchResponse.getCustomPayload());
}
finally
{
client.close();
}
} }
finally finally
{ {
cqlQueryHandlerField.set(null, queryHandler); client.close();
} }
} }
@Test @Test
public void testMessagePayload() throws Throwable public void testMessagePayload() throws Throwable
{ {
QueryHandler queryHandler = (QueryHandler) cqlQueryHandlerField.get(null); requireNetwork();
cqlQueryHandlerField.set(null, new TestQueryHandler());
Assert.assertSame(TestQueryHandler.class, ClientState.getCQLQueryHandler().getClass());
SimpleClient client = new SimpleClient(nativeAddr.getHostAddress(), nativePort);
try try
{ {
requireNetwork(); client.connect(false);
Assert.assertSame(TestQueryHandler.class, ClientState.getCQLQueryHandler().getClass()); Map<String, ByteBuffer> reqMap;
Map<String, ByteBuffer> respMap;
SimpleClient client = new SimpleClient(nativeAddr.getHostAddress(), nativePort); QueryMessage queryMessage = new QueryMessage(
try "CREATE TABLE " + KEYSPACE + ".atable (pk int PRIMARY KEY, v text)",
{ QueryOptions.DEFAULT
client.connect(false); );
PrepareMessage prepareMessage = new PrepareMessage("SELECT * FROM " + KEYSPACE + ".atable", null);
Map<String, ByteBuffer> reqMap; reqMap = Collections.singletonMap("foo", bytes(42));
Map<String, ByteBuffer> respMap; responsePayload = respMap = Collections.singletonMap("bar", bytes(42));
queryMessage.setCustomPayload(reqMap);
Message.Response queryResponse = client.execute(queryMessage);
payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, queryResponse.getCustomPayload());
QueryMessage queryMessage = new QueryMessage( reqMap = Collections.singletonMap("foo", bytes(43));
"CREATE TABLE " + KEYSPACE + ".atable (pk int PRIMARY KEY, v text)", responsePayload = respMap = Collections.singletonMap("bar", bytes(43));
QueryOptions.DEFAULT prepareMessage.setCustomPayload(reqMap);
); ResultMessage.Prepared prepareResponse = (ResultMessage.Prepared) client.execute(prepareMessage);
PrepareMessage prepareMessage = new PrepareMessage("SELECT * FROM " + KEYSPACE + ".atable", null); payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, prepareResponse.getCustomPayload());
reqMap = Collections.singletonMap("foo", bytes(42)); ExecuteMessage executeMessage = new ExecuteMessage(prepareResponse.statementId, prepareResponse.resultMetadataId, QueryOptions.DEFAULT);
responsePayload = respMap = Collections.singletonMap("bar", bytes(42)); reqMap = Collections.singletonMap("foo", bytes(44));
queryMessage.setCustomPayload(reqMap); responsePayload = respMap = Collections.singletonMap("bar", bytes(44));
Message.Response queryResponse = client.execute(queryMessage); executeMessage.setCustomPayload(reqMap);
payloadEquals(reqMap, requestPayload); Message.Response executeResponse = client.execute(executeMessage);
payloadEquals(respMap, queryResponse.getCustomPayload()); payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, executeResponse.getCustomPayload());
reqMap = Collections.singletonMap("foo", bytes(43)); BatchMessage batchMessage = new BatchMessage(BatchStatement.Type.UNLOGGED,
responsePayload = respMap = Collections.singletonMap("bar", bytes(43)); Collections.<Object>singletonList("INSERT INTO " + KEYSPACE + ".atable (pk,v) VALUES (1, 'foo')"),
prepareMessage.setCustomPayload(reqMap); Collections.singletonList(ByteArrayUtil.EMPTY_ARRAY_OF_BYTE_ARRAYS),
ResultMessage.Prepared prepareResponse = (ResultMessage.Prepared) client.execute(prepareMessage); QueryOptions.DEFAULT);
payloadEquals(reqMap, requestPayload); reqMap = Collections.singletonMap("foo", bytes(45));
payloadEquals(respMap, prepareResponse.getCustomPayload()); responsePayload = respMap = Collections.singletonMap("bar", bytes(45));
batchMessage.setCustomPayload(reqMap);
ExecuteMessage executeMessage = new ExecuteMessage(prepareResponse.statementId, prepareResponse.resultMetadataId, QueryOptions.DEFAULT); Message.Response batchResponse = client.execute(batchMessage);
reqMap = Collections.singletonMap("foo", bytes(44)); payloadEquals(reqMap, requestPayload);
responsePayload = respMap = Collections.singletonMap("bar", bytes(44)); payloadEquals(respMap, batchResponse.getCustomPayload());
executeMessage.setCustomPayload(reqMap);
Message.Response executeResponse = client.execute(executeMessage);
payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, executeResponse.getCustomPayload());
BatchMessage batchMessage = new BatchMessage(BatchStatement.Type.UNLOGGED,
Collections.<Object>singletonList("INSERT INTO " + KEYSPACE + ".atable (pk,v) VALUES (1, 'foo')"),
Collections.singletonList(ByteArrayUtil.EMPTY_ARRAY_OF_BYTE_ARRAYS),
QueryOptions.DEFAULT);
reqMap = Collections.singletonMap("foo", bytes(45));
responsePayload = respMap = Collections.singletonMap("bar", bytes(45));
batchMessage.setCustomPayload(reqMap);
Message.Response batchResponse = client.execute(batchMessage);
payloadEquals(reqMap, requestPayload);
payloadEquals(respMap, batchResponse.getCustomPayload());
}
finally
{
client.close();
}
} }
finally finally
{ {
cqlQueryHandlerField.set(null, queryHandler); client.close();
} }
} }
@Test @Test
public void testMessagePayloadVersion3() throws Throwable public void testMessagePayloadVersion3() throws Throwable
{ {
QueryHandler queryHandler = (QueryHandler) cqlQueryHandlerField.get(null); requireNetwork();
cqlQueryHandlerField.set(null, new TestQueryHandler());
Assert.assertSame(TestQueryHandler.class, ClientState.getCQLQueryHandler().getClass());
SimpleClient client = new SimpleClient(nativeAddr.getHostAddress(), nativePort, ProtocolVersion.V3);
try try
{ {
requireNetwork(); client.connect(false);
Assert.assertSame(TestQueryHandler.class, ClientState.getCQLQueryHandler().getClass()); Map<String, ByteBuffer> reqMap;
SimpleClient client = new SimpleClient(nativeAddr.getHostAddress(), nativePort, ProtocolVersion.V3); QueryMessage queryMessage = new QueryMessage(
"CREATE TABLE " + KEYSPACE + ".atable (pk int PRIMARY KEY, v text)",
QueryOptions.DEFAULT
);
PrepareMessage prepareMessage = new PrepareMessage("SELECT * FROM " + KEYSPACE + ".atable", null);
reqMap = Collections.singletonMap("foo", bytes(42));
responsePayload = Collections.singletonMap("bar", bytes(42));
queryMessage.setCustomPayload(reqMap);
try try
{ {
client.connect(false);
Map<String, ByteBuffer> reqMap;
QueryMessage queryMessage = new QueryMessage(
"CREATE TABLE " + KEYSPACE + ".atable (pk int PRIMARY KEY, v text)",
QueryOptions.DEFAULT
);
PrepareMessage prepareMessage = new PrepareMessage("SELECT * FROM " + KEYSPACE + ".atable", null);
reqMap = Collections.singletonMap("foo", bytes(42));
responsePayload = Collections.singletonMap("bar", bytes(42));
queryMessage.setCustomPayload(reqMap);
try
{
client.execute(queryMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
// when a protocol exception is thrown by the server the connection is closed, so need to re-connect
client.close();
client.connect(false);
queryMessage.setCustomPayload(null);
client.execute(queryMessage); client.execute(queryMessage);
Assert.fail();
reqMap = Collections.singletonMap("foo", bytes(43));
responsePayload = Collections.singletonMap("bar", bytes(43));
prepareMessage.setCustomPayload(reqMap);
try
{
client.execute(prepareMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
// when a protocol exception is thrown by the server the connection is closed, so need to re-connect
client.close();
client.connect(false);
prepareMessage.setCustomPayload(null);
ResultMessage.Prepared prepareResponse = (ResultMessage.Prepared) client.execute(prepareMessage);
ExecuteMessage executeMessage = new ExecuteMessage(prepareResponse.statementId, prepareResponse.resultMetadataId, QueryOptions.DEFAULT);
reqMap = Collections.singletonMap("foo", bytes(44));
responsePayload = Collections.singletonMap("bar", bytes(44));
executeMessage.setCustomPayload(reqMap);
try
{
client.execute(executeMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
// when a protocol exception is thrown by the server the connection is closed, so need to re-connect
client.close();
client.connect(false);
BatchMessage batchMessage = new BatchMessage(BatchStatement.Type.UNLOGGED,
Collections.<Object>singletonList("INSERT INTO " + KEYSPACE + ".atable (pk,v) VALUES (1, 'foo')"),
Collections.singletonList(ByteArrayUtil.EMPTY_ARRAY_OF_BYTE_ARRAYS),
QueryOptions.DEFAULT);
reqMap = Collections.singletonMap("foo", bytes(45));
responsePayload = Collections.singletonMap("bar", bytes(45));
batchMessage.setCustomPayload(reqMap);
try
{
client.execute(batchMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
} }
finally catch (RuntimeException e)
{ {
client.close(); Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
// when a protocol exception is thrown by the server the connection is closed, so need to re-connect
client.close();
client.connect(false);
queryMessage.setCustomPayload(null);
client.execute(queryMessage);
reqMap = Collections.singletonMap("foo", bytes(43));
responsePayload = Collections.singletonMap("bar", bytes(43));
prepareMessage.setCustomPayload(reqMap);
try
{
client.execute(prepareMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
// when a protocol exception is thrown by the server the connection is closed, so need to re-connect
client.close();
client.connect(false);
prepareMessage.setCustomPayload(null);
ResultMessage.Prepared prepareResponse = (ResultMessage.Prepared) client.execute(prepareMessage);
ExecuteMessage executeMessage = new ExecuteMessage(prepareResponse.statementId, prepareResponse.resultMetadataId, QueryOptions.DEFAULT);
reqMap = Collections.singletonMap("foo", bytes(44));
responsePayload = Collections.singletonMap("bar", bytes(44));
executeMessage.setCustomPayload(reqMap);
try
{
client.execute(executeMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
}
// when a protocol exception is thrown by the server the connection is closed, so need to re-connect
client.close();
client.connect(false);
BatchMessage batchMessage = new BatchMessage(BatchStatement.Type.UNLOGGED,
Collections.<Object>singletonList("INSERT INTO " + KEYSPACE + ".atable (pk,v) VALUES (1, 'foo')"),
Collections.singletonList(ByteArrayUtil.EMPTY_ARRAY_OF_BYTE_ARRAYS),
QueryOptions.DEFAULT);
reqMap = Collections.singletonMap("foo", bytes(45));
responsePayload = Collections.singletonMap("bar", bytes(45));
batchMessage.setCustomPayload(reqMap);
try
{
client.execute(batchMessage);
Assert.fail();
}
catch (RuntimeException e)
{
Assert.assertTrue(e.getCause() instanceof ProtocolException);
} }
} }
finally finally
{ {
cqlQueryHandlerField.set(null, queryHandler); client.close();
} }
} }

View File

@ -50,6 +50,8 @@ import org.apache.cassandra.utils.MerkleTree.TreeRange;
import org.apache.cassandra.utils.MerkleTree.TreeRangeIterator; import org.apache.cassandra.utils.MerkleTree.TreeRangeIterator;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_VERSION;
import static org.apache.cassandra.utils.JavaUtils.parseJavaVersion;
import static org.apache.cassandra.utils.MerkleTree.RECOMMENDED_DEPTH; import static org.apache.cassandra.utils.MerkleTree.RECOMMENDED_DEPTH;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -619,8 +621,12 @@ public class MerkleTreeTest
Assert.assertEquals(12, MerkleTree.estimatedMaxDepthForBytes(Murmur3Partitioner.instance, Assert.assertEquals(12, MerkleTree.estimatedMaxDepthForBytes(Murmur3Partitioner.instance,
1048576, 32)); 1048576, 32));
// With 100 mebibytes we should get a limit of 19 // With 100 mebibytes we should get a limit of 19 on JDK's < 21, 18 on JDK21
Assert.assertEquals(19, MerkleTree.estimatedMaxDepthForBytes(Murmur3Partitioner.instance, // Object size impl details can be expected to change on different JDK's so this impl will be brittle
// in the face of future JDK changes.
int version = parseJavaVersion(JAVA_VERSION.getString());
int expectedLimit = version < 21 ? 19 : 18;
Assert.assertEquals(expectedLimit, MerkleTree.estimatedMaxDepthForBytes(Murmur3Partitioner.instance,
100 * 1048576, 32)); 100 * 1048576, 32));
// With 300 mebibytes we should get the old limit of 20 // With 300 mebibytes we should get the old limit of 20

View File

@ -30,8 +30,8 @@ import java.util.function.Supplier;
import net.openhft.chronicle.queue.ChronicleQueue; import net.openhft.chronicle.queue.ChronicleQueue;
import net.openhft.chronicle.queue.ExcerptTailer; import net.openhft.chronicle.queue.ExcerptTailer;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder; import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import net.openhft.chronicle.queue.rollcycles.TestRollCycles;
import net.openhft.chronicle.wire.WireOut; import net.openhft.chronicle.wire.WireOut;
import org.junit.After; import org.junit.After;
@ -75,7 +75,7 @@ public class BinLogTest
{ {
path = tempDir(); path = tempDir();
binLog = new BinLog.Builder().path(path) binLog = new BinLog.Builder().path(path)
.rollCycle(RollCycles.TEST_SECONDLY.toString()) .rollCycle(TestRollCycles.TEST_SECONDLY.toString())
.maxQueueWeight(10) .maxQueueWeight(10)
.maxLogSize(1024 * 1024 * 128) .maxLogSize(1024 * 1024 * 128)
.blocking(false) .blocking(false)
@ -110,13 +110,13 @@ public class BinLogTest
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testConstructorZeroWeight() throws Exception public void testConstructorZeroWeight() throws Exception
{ {
new BinLog.Builder().path(tempDir()).rollCycle(RollCycles.TEST_SECONDLY.toString()).maxQueueWeight(0).build(false); new BinLog.Builder().path(tempDir()).rollCycle(TestRollCycles.TEST_SECONDLY.toString()).maxQueueWeight(0).build(false);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testConstructorLogSize() throws Exception public void testConstructorLogSize() throws Exception
{ {
new BinLog.Builder().path(tempDir()).rollCycle(RollCycles.TEST_SECONDLY.toString()).maxLogSize(0).build(false); new BinLog.Builder().path(tempDir()).rollCycle(TestRollCycles.TEST_SECONDLY.toString()).maxLogSize(0).build(false);
} }
/** /**
@ -383,7 +383,7 @@ public class BinLogTest
public void testCleanupOnOversize() throws Exception public void testCleanupOnOversize() throws Exception
{ {
tearDown(); tearDown();
binLog = new BinLog.Builder().path(path).rollCycle(RollCycles.TEST_SECONDLY.toString()).maxQueueWeight(1).maxLogSize(10000).blocking(false).build(false); binLog = new BinLog.Builder().path(path).rollCycle(TestRollCycles.TEST_SECONDLY.toString()).maxQueueWeight(1).maxLogSize(10000).blocking(false).build(false);
for (int ii = 0; ii < 5; ii++) for (int ii = 0; ii < 5; ii++)
{ {
binLog.put(record(String.valueOf(ii))); binLog.put(record(String.valueOf(ii)));
@ -496,7 +496,7 @@ public class BinLogTest
List<String> readBinLogRecords(Path path) List<String> readBinLogRecords(Path path)
{ {
List<String> records = new ArrayList<String>(); List<String> records = new ArrayList<String>();
try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(RollCycles.TEST_SECONDLY).build()) try (ChronicleQueue queue = SingleChronicleQueueBuilder.single(path.toFile()).rollCycle(TestRollCycles.TEST_SECONDLY).build())
{ {
ExcerptTailer tailer = queue.createTailer(); ExcerptTailer tailer = queue.createTailer();
while (true) while (true)

View File

@ -52,6 +52,8 @@ import org.apache.cassandra.utils.ObjectSizes;
import org.apache.cassandra.utils.Pair; import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.concurrent.Ref.Visitor; import org.apache.cassandra.utils.concurrent.Ref.Visitor;
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_VERSION;
import static org.apache.cassandra.utils.JavaUtils.parseJavaVersion;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings({"unused", "unchecked", "rawtypes"}) @SuppressWarnings({"unused", "unchecked", "rawtypes"})
@ -468,10 +470,10 @@ public class RefCountedTest
private Set<Ref.GlobalState> testCycles(Function<LambdaTestClass, Runnable> runOnCloseSupplier) private Set<Ref.GlobalState> testCycles(Function<LambdaTestClass, Runnable> runOnCloseSupplier)
{ {
LambdaTestClass test = new LambdaTestClass(); LambdaTestClass test = new LambdaTestClass();
Runnable weakRef = runOnCloseSupplier.apply(test); Runnable supplierRef = runOnCloseSupplier.apply(test);
RefCounted.Tidy tidier = new RefCounted.Tidy() RefCounted.Tidy tidier = new RefCounted.Tidy()
{ {
Runnable ref = weakRef; Runnable ref = supplierRef;
public void tidy() public void tidy()
{ {
@ -505,7 +507,17 @@ public class RefCountedTest
public void testCycles() public void testCycles()
{ {
assertThat(testCycles(LambdaTestClass::getRunOnCloseLambdaWithThis)).isNotEmpty(); // sanity test assertThat(testCycles(LambdaTestClass::getRunOnCloseLambdaWithThis)).isNotEmpty(); // sanity test
assertThat(testCycles(LambdaTestClass::getRunOnCloseInner)).isNotEmpty(); // sanity test
// Behavior changed in JDK21; this was a sanity check to explore the way things worked but doesn't impact
// the correctness of the final actual check of the test.
int version = parseJavaVersion(JAVA_VERSION.getString());
boolean runOnCloseInnerState = version >= 21;
var result = testCycles(LambdaTestClass::getRunOnCloseInner);
if (runOnCloseInnerState)
assertThat(result).isEmpty();
else
assertThat(result).isNotEmpty();
assertThat(testCycles(LambdaTestClass::getRunOnCloseLambda)).isEmpty(); assertThat(testCycles(LambdaTestClass::getRunOnCloseLambda)).isEmpty();
} }

View File

@ -85,57 +85,37 @@ if [ -z $JAVA ] ; then
fi fi
# Matches variable 'java.supported' in build.xml # Matches variable 'java.supported' in build.xml
java_versions_supported=11,17 java_versions_supported="11 17 21"
java_version_string=$(IFS=" "; echo "${java_versions_supported}")
# Determine the sort of JVM we'll be running on. # Determine the sort of JVM we'll be running on.
java_ver_output=`"${JAVA:-java}" -version 2>&1` JAVA_VERSION=$(java -version 2>&1 | grep '[openjdk|java] version' | cut -d '"' -f2 | cut -d '.' -f1)
jvmver=`echo "$java_ver_output" | grep '[openjdk|java] version' | awk -F'"' 'NR==1 {print $2}' | cut -d\- -f1`
JVM_VERSION=${jvmver%_*}
short=$(echo "${jvmver}" | cut -c1-2)
# Unsupported JDKs below the upper supported version are not allowed supported=0
if [ "$short" != "$(echo "$java_versions_supported" | cut -d, -f1)" ] && [ "$JVM_VERSION" \< "$(echo "$java_versions_supported" | cut -d, -f2)" ] ; then for version in ${java_versions_supported}; do
echo "Unsupported Java $JVM_VERSION. Supported are $java_versions_supported" if [ "$version" -eq "$JAVA_VERSION" ]; then
exit 1; supported=1
fi
done
if [ "$supported" -eq 0 ]; then
if [ -z "$CASSANDRA_JDK_UNSUPPORTED" ]; then
echo "Unsupported Java $JAVA_VERSION. Supported are $java_version_string"
echo "If you would like to test with newer Java versions set CASSANDRA_JDK_UNSUPPORTED to any value (for example, CASSANDRA_JDK_UNSUPPORTED=true). Unset the parameter for default behavior"
exit 1
else
echo "######################################################################"
echo "Warning! You are using JDK$JAVA_VERSION. This Cassandra version only supports $java_version_string"
echo "######################################################################"
fi
fi fi
# Allow execution of supported Java versions, and newer if CASSANDRA_JDK_UNSUPPORTED is set
is_supported_version=$(echo "$java_versions_supported" | tr "," '\n' | grep -F -x "$short")
if [ -z "$is_supported_version" ] ; then
if [ -z "$CASSANDRA_JDK_UNSUPPORTED" ] ; then
echo "Unsupported Java $JVM_VERSION. Supported are $java_versions_supported"
echo "If you would like to test with newer Java versions set CASSANDRA_JDK_UNSUPPORTED to any value (for example, CASSANDRA_JDK_UNSUPPORTED=true). Unset the parameter for default behavior"
exit 1;
else
echo "######################################################################"
echo "Warning! You are using JDK$short. This Cassandra version only supports $java_versions_supported."
echo "######################################################################"
fi
fi
JAVA_VERSION=$short
jvm=`echo "$java_ver_output" | grep -A 1 '[openjdk|java] version' | awk 'NR==2 {print $1}'`
case "$jvm" in
OpenJDK)
JVM_VENDOR=OpenJDK
# this will be "64-Bit" or "32-Bit"
JVM_ARCH=`echo "$java_ver_output" | awk 'NR==3 {print $2}'`
;;
"Java(TM)")
JVM_VENDOR=Oracle
# this will be "64-Bit" or "32-Bit"
JVM_ARCH=`echo "$java_ver_output" | awk 'NR==3 {print $3}'`
;;
*)
# Help fill in other JVM values
JVM_VENDOR=other
JVM_ARCH=unknown
;;
esac
# Read user-defined JVM options from jvm-server.options file # Read user-defined JVM options from jvm-server.options file
JVM_OPTS_FILE=$CASSANDRA_CONF/jvm${jvmoptions_variant:--clients}.options JVM_OPTS_FILE=$CASSANDRA_CONF/jvm${jvmoptions_variant:--clients}.options
if [ $JAVA_VERSION -ge 17 ] ; then if [ $JAVA_VERSION -ge 21 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm21${jvmoptions_variant:--clients}.options
elif [ $JAVA_VERSION -ge 17 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm17${jvmoptions_variant:--clients}.options JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm17${jvmoptions_variant:--clients}.options
elif [ $JAVA_VERSION -ge 11 ] ; then elif [ $JAVA_VERSION -ge 11 ] ; then
JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm11${jvmoptions_variant:--clients}.options JVM_DEP_OPTS_FILE=$CASSANDRA_CONF/jvm11${jvmoptions_variant:--clients}.options

View File

@ -60,8 +60,8 @@ public class Dump implements Runnable
@Parameters(paramLabel = "path", description = "Path containing the full query logs to dump.", arity = "1..*") @Parameters(paramLabel = "path", description = "Path containing the full query logs to dump.", arity = "1..*")
private List<String> arguments = new ArrayList<>(); private List<String> arguments = new ArrayList<>();
@Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file was rolled. May be necessary for Chronicle to correctly parse file names. (MINUTELY, HOURLY, DAILY). Default HOURLY.") @Option(paramLabel = "roll_cycle", names = { "--roll-cycle" }, description = "How often to roll the log file was rolled. May be necessary for Chronicle to correctly parse file names. (FAST_MINUTELY, FAST_HOURLY, FAST_DAILY). Default FAST_HOURLY.")
private String rollCycle = "HOURLY"; private String rollCycle = "FAST_HOURLY";
@Option(paramLabel = "follow", names = { "--follow" }, description = "Upon reacahing the end of the log continue indefinitely waiting for more records") @Option(paramLabel = "follow", names = { "--follow" }, description = "Upon reacahing the end of the log continue indefinitely waiting for more records")
private boolean follow = false; private boolean follow = false;