Compare commits

..

2 Commits

Author SHA1 Message Date
Takuya ASADA
f32307a4a3 dist: support ./SCYLLA-VERSION-GEN on ubuntu package
Signed-off-by: Takuya ASADA <syuu@scylladb.com>
2015-11-22 17:55:54 +02:00
Pekka Enberg
215d267a0e release: prepare for 0.12 2015-11-18 11:01:14 +02:00
123 changed files with 5872 additions and 8997 deletions

1
.github/CODEOWNERS vendored
View File

@ -1 +0,0 @@
* @penberg

9
.gitignore vendored
View File

@ -1,9 +0,0 @@
/target/
/bin/
dependency-reduced-pom.xml
scylla-apiclient/target/
.classpath
.project
.settings
build/
/.idea/

View File

@ -1,25 +1,22 @@
# Scylla JMX Server
Scylla JMX server implements the Apache Cassandra JMX interface for compatibility with tooling such as `nodetool`. The JMX server uses Scylla's REST API to communicate with a Scylla server.
## Compiling
To compile JMX server, run:
```console
$ mvn --file scylla-jmx-parent/pom.xml package
# Urchin JMX Interface
This is the JMX interface for urchin.
## Compile
To compile do:
```
mvn install
```
## Running
## Run
The maven will create an uber-jar with all dependency under the target directory. You should run it with the remote jmx enable so the nodetool will be able to connect to it.
To start the JMX server, run:
```console
$ ./scripts/scylla-jmx
```
java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=7199 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar target/urchin-mbean-1.0.jar
```
To get help on supported options:
## Setting IP and Port
By default the the JMX would connect to a node on the localhost
on port 10000.
```console
$ ./scripts/scylla-jmx --help
```
The jmx API uses the system properties to set the IP address and Port.
To change the ip address use the apiaddress property (e.g. -Dapiaddress=1.1.1.1)
To change the port use the apiport (e.g. -Dapiport=10001)

View File

@ -1,49 +1,19 @@
#!/bin/sh
PRODUCT=scylla
VERSION=666.development
VERSION=0.12
if test -f version
then
SCYLLA_VERSION=$(cat version | awk -F'-' '{print $1}')
SCYLLA_RELEASE=$(cat version | awk -F'-' '{print $2}')
else
DATE=$(date --utc +%Y%m%d)
DATE=$(date +%Y%m%d)
GIT_COMMIT=$(git log --pretty=format:'%h' -n 1)
SCYLLA_VERSION=$VERSION
SCYLLA_RELEASE=$DATE.$GIT_COMMIT
fi
usage() {
echo "usage: $0"
echo " [--version product-version-release] # override p-v-r"
exit 1
}
OVERRIDE=
while [[ $# > 0 ]]; do
case "$1" in
--version)
OVERRIDE="$2"
shift 2
;;
*)
usage
;;
esac
done
if [[ -n "$OVERRIDE" ]]; then
# regular expression for p-v-r: alphabetic+dashes for product, trailing non-dashes
# for release, everything else for version
RE='^([-a-z]+)-(.+)-([^-]+)$'
PRODUCT="$(sed -E "s/$RE/\\1/" <<<"$OVERRIDE")"
SCYLLA_VERSION="$(sed -E "s/$RE/\\2/" <<<"$OVERRIDE")"
SCYLLA_RELEASE="$(sed -E "s/$RE/\\3/" <<<"$OVERRIDE")"
fi
echo "$SCYLLA_VERSION-$SCYLLA_RELEASE"
mkdir -p build
echo "$SCYLLA_VERSION" > build/SCYLLA-VERSION-FILE
echo "$SCYLLA_RELEASE" > build/SCYLLA-RELEASE-FILE
echo "$PRODUCT" > build/SCYLLA-PRODUCT-FILE

13
debian/control vendored Normal file
View File

@ -0,0 +1,13 @@
Source: scylla-jmx
Maintainer: Takuya ASADA <syuu@scylladb.com>
Homepage: http://scylladb.com
Section: database
Priority: optional
Standards-Version: 3.9.2
Build-Depends: debhelper (>= 9), maven, openjdk-7-jdk
Package: scylla-jmx
Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, openjdk-7-jre-headless, scylla-server
Description: Scylla JMX server binaries
Scylla is a highly scalable, eventually consistent, distributed, partitioned row DB.

12
debian/copyright vendored Normal file
View File

@ -0,0 +1,12 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Scylla DB
Upstream-Contact: http://www.scylladb.com/
Source: https://github.com/scylladb/scylla-jmx
Files: *
Copyright: Copyright (C) 2015 ScyllaDB
License: AGPL-3.0
Files: debian/*
Copyright: Copyright (C) 2015 ScyllaDB
License: AGPL-3.0

27
debian/rules vendored Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/make -f
DOC = $(CURDIR)/debian/scylla-jmx/usr/share/doc/scylla-jmx
DEST = $(CURDIR)/debian/scylla-jmx/usr/lib/scylla/jmx
override_dh_auto_build:
mvn install
override_dh_auto_clean:
rm -rf target
override_dh_auto_install:
mkdir -p $(CURDIR)/debian/scylla-jmx/etc/default/ && \
cp $(CURDIR)/dist/common/sysconfig/scylla-jmx \
$(CURDIR)/debian/scylla-jmx/etc/default/
mkdir -p $(DOC) && \
cp $(CURDIR)/*.md $(DOC)
cp $(CURDIR)/NOTICE $(DOC)
cp $(CURDIR)/LICENSE.AGPL $(DOC)
mkdir -p $(DEST)
cp $(CURDIR)/dist/common/scripts/* $(DEST)
cp $(CURDIR)/target/urchin-mbean-1.0.jar $(DEST)
%:
dh $@

18
debian/scylla-jmx.upstart vendored Normal file
View File

@ -0,0 +1,18 @@
# scylla-jmx - ScyllaDB
#
# ScyllaDB
description "ScyllaDB jmx"
start on started scylla-server
stop on stopping scylla-server
umask 022
console log
script
. /etc/default/scylla-jmx
export JMX_LOCAL_PORT
exec /usr/lib/scylla/jmx/jmx_run
end script

5
dist/common/scripts/jmx_run vendored Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh -e
args="-Djava.net.preferIPv4Stack=true -Dcom.sun.management.jmxremote.port=$JMX_LOCAL_PORT -Dcom.sun.management.jmxremote.rmi.port=$JMX_LOCAL_PORT -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
exec java $args -jar /usr/lib/scylla/jmx/urchin-mbean-1.0.jar

View File

@ -1,32 +1 @@
# scylla home dir
SCYLLA_HOME=/var/lib/scylla
# scylla config dir
SCYLLA_CONF=/etc/scylla
# The jmx port to open
# SCYLLA_JMX_PORT="-jp 7199"
# The API port to connect to
#SCYLLA_API_PORT="-p 10000"
# API address to connect to
#SCYLLA_API_ADDR="-a localhost"
# use alternate jmx address
#SCYLLA_JMX_ADDR="-ja localhost"
# A configuration file to use
#SCYLLA_JMX_FILE="-cf /etc/scylla.d/scylla-user.cfg"
# The location of the jmx proxy jar file
SCYLLA_JMX_LOCAL="-l /opt/scylladb/jmx"
# allow to run remotely
#SCYLLA_JMX_REMOTE="-r"
# allow debug
#SCYLLA_JMX_DEBUG="-d"
# specify JVM options
JAVA_TOOL_OPTIONS=""
JMX_LOCAL_PORT=7199

View File

@ -1,18 +0,0 @@
[Unit]
Description=Scylla JMX
Requires=scylla-server.service
After=scylla-server.service
[Service]
Type=simple
EnvironmentFile=/etc/sysconfig/scylla-jmx
User=scylla
Group=scylla
ExecStart=/opt/scylladb/jmx/scylla-jmx $SCYLLA_JMX_PORT $SCYLLA_API_PORT $SCYLLA_API_ADDR $SCYLLA_JMX_ADDR $SCYLLA_JMX_FILE $SCYLLA_JMX_LOCAL $SCYLLA_JMX_REMOTE $SCYLLA_JMX_DEBUG
KillMode=process
Restart=on-abnormal
Slice=scylla-helper.slice
WorkingDirectory=/var/lib/scylla
[Install]
WantedBy=multi-user.target

View File

@ -1,14 +0,0 @@
Source: %{product}-jmx
Maintainer: Takuya ASADA <syuu@scylladb.com>
Homepage: http://scylladb.com
Section: database
Priority: optional
Standards-Version: 3.9.5
Rules-Requires-Root: no
Package: %{product}-jmx
Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, openjdk-8-jre-headless | openjdk-8-jre | oracle-java8-set-default | adoptopenjdk-8-hotspot-jre | openjdk-11-jre-headless | openjdk-11-jre |oracle-java11-set-default , %{product}-server
Description: Scylla JMX server binaries
Scylla is a highly scalable, eventually consistent, distributed,
partitioned row DB.

View File

@ -1,706 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Scylla DB
Upstream-Contact: http://www.scylladb.com/
Source: https://github.com/scylladb/scylla-jmx
Files: *
Copyright: Copyright (C) 2015 ScyllaDB
License: AGPL-3.0
Files: debian/*
Copyright: Copyright (C) 2015 ScyllaDB
License: AGPL-3.0
Files: scripts/git-archive-all
Copyright: Copyright (c) 2010 Ilya Kulakov
License: MIT
License: AGPL-3.0
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
.
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
.
Preamble
.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
.
The precise terms and conditions for copying, distribution and
modification follow.
.
TERMS AND CONDITIONS
.
0. Definitions.
.
"This License" refers to version 3 of the GNU Affero General Public License.
.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
.
A "covered work" means either the unmodified Program or a work based
on the Program.
.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
.
1. Source Code.
.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
.
The Corresponding Source for a work in source code form is that
same work.
.
2. Basic Permissions.
.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
.
4. Conveying Verbatim Copies.
.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
.
5. Conveying Modified Source Versions.
.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
.
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
.
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
.
6. Conveying Non-Source Forms.
.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
.
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
.
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
.
7. Additional Terms.
.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
.
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
.
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
.
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
.
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
.
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
.
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
.
8. Termination.
.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
.
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
.
9. Acceptance Not Required for Having Copies.
.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
.
10. Automatic Licensing of Downstream Recipients.
.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
.
11. Patents.
.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
.
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
.
12. No Surrender of Others' Freedom.
.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
.
13. Remote Network Interaction; Use with the GNU General Public License.
.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
.
14. Revised Versions of this License.
.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
.
15. Disclaimer of Warranty.
.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
.
16. Limitation of Liability.
.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
.
17. Interpretation of Sections 15 and 16.
.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
.
END OF TERMS AND CONDITIONS
.
How to Apply These Terms to Your New Programs
.
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
.
Also add information on how to contact you by electronic and paper mail.
.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
License: MIT
Copyright (c) 2010 Ilya Kulakov
.
.
.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
.
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,23 +0,0 @@
#!/usr/bin/make -f
include /usr/share/dpkg/pkg-info.mk
override_dh_auto_build:
override_dh_auto_clean:
override_dh_auto_install:
dh_auto_install
cd scylla-jmx; ./install.sh --packaging --root "$(CURDIR)/debian/tmp" --sysconfdir /etc/default
override_dh_installinit:
ifeq ($(DEB_SOURCE),scylla-jmx)
dh_installinit --no-start
else
dh_installinit --no-start --name scylla-jmx
endif
override_dh_strip_nondeterminism:
%:
dh $@

View File

@ -1,4 +0,0 @@
etc/default/scylla-jmx
etc/systemd/system/scylla-jmx.service.d/sysconfdir.conf
opt/scylladb/jmx/*
usr/lib/scylla/jmx/*

View File

@ -1,7 +0,0 @@
#!/bin/sh
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
fi
#DEBHELPER#

View File

@ -1,7 +0,0 @@
#!/bin/sh
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
fi
#DEBHELPER#

View File

@ -1 +0,0 @@
../../common/systemd/scylla-jmx.service

View File

@ -1 +0,0 @@
3.0 (quilt)

View File

@ -1 +0,0 @@
extend-diff-ignore = ^build/

View File

@ -1,80 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 ScyllaDB
#
#
# This file is part of Scylla.
#
# Scylla is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Scylla is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
#
import string
import os
import shutil
import re
from pathlib import Path
class DebianFilesTemplate(string.Template):
delimiter = '%'
scriptdir = os.path.dirname(__file__)
with open(os.path.join(scriptdir, 'changelog.template')) as f:
changelog_template = f.read()
with open(os.path.join(scriptdir, 'control.template')) as f:
control_template = f.read()
with open('build/SCYLLA-PRODUCT-FILE') as f:
product = f.read().strip()
with open('build/SCYLLA-VERSION-FILE') as f:
version = f.read().strip().replace('.rc', '~rc').replace('_', '-')
with open('build/SCYLLA-RELEASE-FILE') as f:
release = f.read().strip()
if os.path.exists('build/debian/debian'):
shutil.rmtree('build/debian/debian')
shutil.copytree('dist/debian/debian', 'build/debian/debian')
if product != 'scylla':
for p in Path('build/debian/debian').glob('scylla-*'):
# pat1: scylla-server.service
# -> scylla-enterprise-server.scylla-server.service
# pat2: scylla-server.scylla-fstrim.service
# -> scylla-enterprise-server.scylla-fstrim.service
# pat3: scylla-conf.install
# -> scylla-enterprise-conf.install
if m := re.match(r'^scylla(-[^.]+)\.service$', p.name):
p.rename(p.parent / f'{product}{m.group(1)}.{p.name}')
elif m := re.match(r'^scylla(-[^.]+\.scylla-[^.]+\.[^.]+)$', p.name):
p.rename(p.parent / f'{product}{m.group(1)}')
else:
p.rename(p.parent / p.name.replace('scylla', product, 1))
s = DebianFilesTemplate(changelog_template)
changelog_applied = s.substitute(product=product, version=version, release=release, revision='1', codename='stable')
s = DebianFilesTemplate(control_template)
control_applied = s.substitute(product=product)
with open('build/debian/debian/changelog', 'w') as f:
f.write(changelog_applied)
with open('build/debian/debian/control', 'w') as f:
f.write(control_applied)

34
dist/redhat/build_rpm.sh vendored Executable file
View File

@ -0,0 +1,34 @@
#!/bin/sh -e
RPMBUILD=`pwd`/build/rpmbuild
if [ ! -e dist/redhat/build_rpm.sh ]; then
echo "run build_rpm.sh in top of scylla-jmx dir"
exit 1
fi
sudo yum install -y rpm-build git
OS=`awk '{print $1}' /etc/redhat-release`
if [ "$OS" = "Fedora" ] && [ ! -f /usr/bin/mock ]; then
sudo yum -y install mock
elif [ "$OS" = "CentOS" ] && [ ! -f /usr/bin/yum-builddep ]; then
sudo yum -y install yum-utils
fi
VERSION=$(./SCYLLA-VERSION-GEN)
SCYLLA_VERSION=$(cat build/SCYLLA-VERSION-FILE)
SCYLLA_RELEASE=$(cat build/SCYLLA-RELEASE-FILE)
mkdir -p $RPMBUILD/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
git archive --format=tar --prefix=scylla-jmx-$SCYLLA_VERSION/ HEAD -o build/rpmbuild/SOURCES/scylla-jmx-$VERSION.tar
cp dist/redhat/scylla-jmx.spec.in $RPMBUILD/SPECS/scylla-jmx.spec
sed -i -e "s/@@VERSION@@/$SCYLLA_VERSION/g" $RPMBUILD/SPECS/scylla-jmx.spec
sed -i -e "s/@@RELEASE@@/$SCYLLA_RELEASE/g" $RPMBUILD/SPECS/scylla-jmx.spec
if [ "$OS" = "Fedora" ]; then
rpmbuild -bs --define "_topdir $RPMBUILD" $RPMBUILD/SPECS/scylla-jmx.spec
/usr/bin/mock rebuild --resultdir=`pwd`/build/rpms $RPMBUILD/SRPMS/scylla-jmx-$VERSION*.src.rpm
else
sudo yum-builddep -y $RPMBUILD/SPECS/scylla-jmx.spec
rpmbuild -ba --define "_topdir $RPMBUILD" $RPMBUILD/SPECS/scylla-jmx.spec
fi

View File

@ -1,75 +0,0 @@
Name: %{product}-jmx
Version: %{version}
Release: %{release}%{?dist}
Summary: Scylla JMX
Group: Applications/Databases
License: AGPLv3
URL: http://www.scylladb.com/
Source0: %{reloc_pkg}
BuildArch: noarch
BuildRequires: systemd-units
Requires: %{product}-server jre-1.8.0-headless
AutoReqProv: no
%description
%prep
%setup -q -n scylla-jmx
%build
%install
./install.sh --packaging --root "$RPM_BUILD_ROOT"
%pre
/usr/sbin/groupadd scylla 2> /dev/null || :
/usr/sbin/useradd -g scylla -s /sbin/nologin -r -d ${_sharedstatedir}/scylla scylla 2> /dev/null || :
ping -c1 `hostname` > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo
echo "**************************************************************"
echo "* WARNING: You need to add hostname on /etc/hosts, otherwise *"
echo "* scylla-jmx will not able to start up. *"
echo "**************************************************************"
echo
fi
%post
if [ $1 -eq 1 ] ; then
/usr/bin/systemctl preset scylla-jmx.service ||:
fi
/usr/bin/systemctl daemon-reload ||:
%preun
if [ $1 -eq 0 ] ; then
/usr/bin/systemctl --no-reload disable scylla-jmx.service ||:
/usr/bin/systemctl stop scylla-jmx.service ||:
fi
%postun
/usr/bin/systemctl daemon-reload ||:
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
%config(noreplace) %{_sysconfdir}/sysconfig/scylla-jmx
%{_unitdir}/scylla-jmx.service
/opt/scylladb/jmx/scylla-jmx
/opt/scylladb/jmx/scylla-jmx-1.1.jar
/opt/scylladb/jmx/symlinks/scylla-jmx
%{_prefix}/lib/scylla/jmx/scylla-jmx
%{_prefix}/lib/scylla/jmx/scylla-jmx-1.1.jar
%{_prefix}/lib/scylla/jmx/symlinks/scylla-jmx
%changelog
* Fri Aug 7 2015 Takuya ASADA Takuya ASADA <syuu@cloudius-systems.com>
- inital version of scylla-tools.spec

74
dist/redhat/scylla-jmx.spec.in vendored Normal file
View File

@ -0,0 +1,74 @@
Name: scylla-jmx
Version: @@VERSION@@
Release: @@RELEASE@@%{?dist}
Summary: Scylla JMX
Group: Applications/Databases
License: AGPLv3
URL: http://www.scylladb.com/
Source0: %{name}-@@VERSION@@-@@RELEASE@@.tar
BuildArch: noarch
BuildRequires: maven systemd-units java-devel
Requires: scylla-server java-headless
%description
%prep
%setup -q
%build
mvn install
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/
mkdir -p $RPM_BUILD_ROOT%{_unitdir}
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/scylla/
install -m644 dist/common/sysconfig/scylla-jmx $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/
install -m644 dist/redhat/systemd/scylla-jmx.service $RPM_BUILD_ROOT%{_unitdir}/
install -d -m755 $RPM_BUILD_ROOT%{_prefix}/lib/scylla
install -d -m755 $RPM_BUILD_ROOT%{_prefix}/lib/scylla/jmx
install -m644 target/urchin-mbean-1.0.jar $RPM_BUILD_ROOT%{_prefix}/lib/scylla/jmx/
install -m755 dist/common/scripts/* $RPM_BUILD_ROOT%{_prefix}/lib/scylla/jmx
%pre
/usr/sbin/groupadd scylla 2> /dev/null || :
/usr/sbin/useradd -g scylla -s /sbin/nologin -r -d ${_sharedstatedir}/scylla scylla 2> /dev/null || :
ping -c1 `hostname` > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo
echo "**************************************************************"
echo "* WARNING: You need to add hostname on /etc/hosts, otherwise *"
echo "* scylla-jmx will not able to start up. *"
echo "**************************************************************"
echo
fi
%post
%systemd_post scylla-jmx.service
%preun
%systemd_preun scylla-jmx.service
%postun
%systemd_postun
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
%{_sysconfdir}/sysconfig/scylla-jmx
%{_unitdir}/scylla-jmx.service
%{_prefix}/lib/scylla/jmx/jmx_run
%{_prefix}/lib/scylla/jmx/urchin-mbean-1.0.jar
%changelog
* Fri Aug 7 2015 Takuya ASADA Takuya ASADA <syuu@cloudius-systems.com>
- inital version of scylla-tools.spec

16
dist/redhat/systemd/scylla-jmx.service vendored Normal file
View File

@ -0,0 +1,16 @@
[Unit]
Description=Scylla JMX
Requires=scylla-server.service
After=scylla-server.service
[Service]
Type=simple
EnvironmentFile=/etc/sysconfig/scylla-jmx
User=scylla
Group=scylla
ExecStart=/usr/lib/scylla/jmx/jmx_run
KillMode=process
Restart=always
[Install]
WantedBy=multi-user.target

20
dist/ubuntu/build_deb.sh vendored Normal file
View File

@ -0,0 +1,20 @@
#!/bin/sh -e
if [ ! -e dist/ubuntu/build_deb.sh ]; then
echo "run build_deb.sh in top of scylla dir"
exit 1
fi
sudo apt-get -y install debhelper maven openjdk-7-jdk devscripts
VERSION=$(./SCYLLA-VERSION-GEN)
SCYLLA_VERSION=$(cat build/SCYLLA-VERSION-FILE)
SCYLLA_RELEASE=$(cat build/SCYLLA-RELEASE-FILE)
if [ "$SCYLLA_VERSION" = "development" ]; then
SCYLLA_VERSION=0development
fi
cp dist/ubuntu/changelog.in debian/changelog
sed -i -e "s/@@VERSION@@/$SCYLLA_VERSION/g" debian/changelog
sed -i -e "s/@@RELEASE@@/$SCYLLA_RELEASE/g" debian/changelog
debuild -r fakeroot --no-tgz-check -us -uc

View File

@ -1,4 +1,4 @@
%{product}-jmx (%{version}-%{release}-%{revision}) %{codename}; urgency=medium
scylla-jmx (@@VERSION@@-@@RELEASE@@-ubuntu1) trusty; urgency=medium
* Initial release.

View File

@ -1,3 +0,0 @@
License: MIT
https://github.com/Kentzo/git-archive-all

View File

@ -1,26 +0,0 @@
#!/bin/bash
#
# This file is open source software, licensed to you under the terms
# of the Apache License, Version 2.0 (the "License"). See the NOTICE file
# distributed with this work for additional information regarding copyright
# ownership. 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.
#
. /etc/os-release
if [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ]; then
apt -y install maven openjdk-8-jdk-headless
elif [ "$ID" = "fedora" ] || [ "$ID" = "centos" ]; then
dnf install -y maven java-1.8.0-openjdk-devel
fi

View File

@ -1,173 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2019 ScyllaDB
#
#
# This file is part of Scylla.
#
# Scylla is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Scylla is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
#
set -e
print_usage() {
cat <<EOF
Usage: install.sh [options]
Options:
--root /path/to/root alternative install root (default /)
--prefix /prefix directory prefix (default /usr)
--nonroot shortcut of '--disttype nonroot'
--sysconfdir /etc/sysconfig specify sysconfig directory name
--packaging use install.sh for packaging
--without-systemd skip installing systemd units
--help this helpful message
EOF
exit 1
}
root=/
sysconfdir=/etc/sysconfig
nonroot=false
packaging=false
without_systemd=false
while [ $# -gt 0 ]; do
case "$1" in
"--root")
root="$2"
shift 2
;;
"--prefix")
prefix="$2"
shift 2
;;
"--nonroot")
nonroot=true
shift 1
;;
"--sysconfdir")
sysconfdir="$2"
shift 2
;;
"--packaging")
packaging=true
shift 1
;;
"--without-systemd")
without_systemd=true
shift 1
;;
"--help")
shift 1
print_usage
;;
*)
print_usage
;;
esac
done
check_usermode_support() {
user=$(systemctl --help|grep -e '--user')
[ -n "$user" ]
}
if ! $packaging; then
has_java=false
if [ -x /usr/bin/java ]; then
javaver=$(/usr/bin/java -version 2>&1|head -n1|cut -f 3 -d " ")
has_java=true
fi
if ! $has_java; then
echo "Please install openjdk-8, openjdk-11, or openjdk-17 before running install.sh."
exit 1
fi
fi
if [ -z "$prefix" ]; then
if $nonroot; then
prefix=~/scylladb
else
prefix=/opt/scylladb
fi
fi
rprefix=$(realpath -m "$root/$prefix")
if ! $nonroot; then
retc="$root/etc"
rsysconfdir="$root/$sysconfdir"
rusr="$root/usr"
rsystemd="$rusr/lib/systemd/system"
else
retc="$rprefix/etc"
rsysconfdir="$rprefix/$sysconfdir"
rsystemd="$HOME/.config/systemd/user"
fi
install -d -m755 "$rsysconfdir"
if ! $without_systemd; then
install -d -m755 "$rsystemd"
fi
install -d -m755 "$rprefix/scripts" "$rprefix/jmx" "$rprefix/jmx/symlinks"
install -m644 dist/common/sysconfig/scylla-jmx -Dt "$rsysconfdir"
if ! $without_systemd; then
install -m644 dist/common/systemd/scylla-jmx.service -Dt "$rsystemd"
fi
if ! $nonroot && ! $without_systemd; then
if [ "$sysconfdir" != "/etc/sysconfig" ]; then
install -d -m755 "$retc"/systemd/system/scylla-jmx.service.d
cat << EOS > "$retc"/systemd/system/scylla-jmx.service.d/sysconfdir.conf
[Service]
EnvironmentFile=
EnvironmentFile=$sysconfdir/scylla-jmx
EOS
fi
elif ! $without_systemd; then
install -d -m755 "$rsystemd"/scylla-jmx.service.d
cat << EOS > "$rsystemd"/scylla-jmx.service.d/nonroot.conf
[Service]
EnvironmentFile=
EnvironmentFile=$retc/sysconfig/scylla-jmx
ExecStart=
ExecStart=$rprefix/jmx/scylla-jmx \$SCYLLA_JMX_PORT \$SCYLLA_API_PORT \$SCYLLA_API_ADDR \$SCYLLA_JMX_ADDR \$SCYLLA_JMX_FILE \$SCYLLA_JMX_LOCAL \$SCYLLA_JMX_REMOTE \$SCYLLA_JMX_DEBUG
User=
Group=
WorkingDirectory=$rprefix
EOS
fi
install -m644 scylla-jmx-1.1.jar "$rprefix/jmx"
install -m755 scylla-jmx "$rprefix/jmx"
ln -sf /usr/bin/java "$rprefix/jmx/symlinks/scylla-jmx"
if ! $nonroot; then
install -m755 -d "$rusr"/lib/scylla/jmx/symlinks
ln -srf "$rprefix"/jmx/scylla-jmx-1.1.jar "$rusr"/lib/scylla/jmx/
ln -srf "$rprefix"/jmx/scylla-jmx "$rusr"/lib/scylla/jmx/
ln -sf /usr/bin/java "$rusr"/lib/scylla/jmx/symlinks/scylla-jmx
fi
if $nonroot; then
sed -i -e "s#/var/lib/scylla#$rprefix#g" "$rsysconfdir"/scylla-jmx
sed -i -e "s#/etc/scylla#$rprefix/etc/scylla#g" "$rsysconfdir"/scylla-jmx
sed -i -e "s#/opt/scylladb/jmx#$rprefix/jmx#g" "$rsysconfdir"/scylla-jmx
if ! $without_systemd && check_usermode_support; then
systemctl --user daemon-reload
fi
echo "Scylla-JMX non-root install completed."
elif ! $without_systemd && ! $packaging; then
systemctl --system daemon-reload
fi

113
pom.xml
View File

@ -2,81 +2,78 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>scylla-jmx</artifactId>
<version>1.1</version>
<groupId>com.cloudius.urchin</groupId>
<artifactId>urchin-mbean</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>it.cavallium.scylladb.jmx</groupId>
<artifactId>scylla-jmx-parent</artifactId>
<version>1.1</version>
<relativePath>./scylla-jmx-parent/pom.xml</relativePath>
</parent>
<name>Urchin MBean</name>
<name>Scylla JMX</name>
<properties>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.7</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>it.cavallium.scylladb.jmx</groupId>
<artifactId>scylla-apiclient</artifactId>
<version>1.1</version>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>com.yammer.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>11</source>
<target>11</target>
<compilerArgs>
<arg>--add-exports</arg>
<arg>java.management/com.sun.jmx.mbeanserver=scylla.jmx</arg>
<arg>--add-exports</arg>
<arg>java.management/com.sun.jmx.interceptor=scylla.jmx</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<artifactSet>
<includes>
<include>*:*</include>
</includes>
<excludes>
<exclude>com.sun.activation:jakarta.activation</exclude>
</excludes>
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>module-info.class</exclude>
<exclude>META-INF/versions/*/module-info.class</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>META-INF/*.MD</exclude>
<exclude>META-INF/*.md</exclude>
<exclude>META-INF/LICENSE</exclude>
<exclude>META-INF/LICENSE.txt</exclude>
<exclude>META-INF/NOTICE</exclude>
</excludes>
</filter>
</filters>
</configuration>
<version>2.4.1</version>
<executions>
<execution>
<phase>package</phase>
@ -87,7 +84,7 @@
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.scylladb.jmx.main.Main</Main-Class>
<Main-Class>com.cloudius.urchin.main.Main</Main-Class>
</manifestEntries>
</transformer>
</transformers>

View File

@ -1,42 +0,0 @@
#!/bin/bash -e
print_usage() {
echo "build_deb.sh --reloc-pkg build/scylla-jmx-package.tar.gz"
echo " --reloc-pkg specify relocatable package path"
echo " --builddir specify Debian package build path"
exit 1
}
RELOC_PKG=build/scylla-jmx-package.tar.gz
BUILDDIR=build/debian
while [ $# -gt 0 ]; do
case "$1" in
"--reloc-pkg")
RELOC_PKG=$2
shift 2
;;
"--builddir")
BUILDDIR="$2"
shift 2
;;
*)
print_usage
;;
esac
done
RELOC_PKG=$(readlink -f $RELOC_PKG)
rm -rf "$BUILDDIR"/scylla-package "$BUILDDIR"/scylla-package.orig "$BUILDDIR"/debian
mkdir -p "$BUILDDIR"/scylla-package
tar -C "$BUILDDIR"/scylla-package -xpf $RELOC_PKG
cd "$BUILDDIR"/scylla-package
RELOC_PKG=$(readlink -f $RELOC_PKG)
mv scylla-jmx/debian debian
PKG_NAME=$(dpkg-parsechangelog --show-field Source)
# XXX: Drop revision number from version string.
# Since it always '1', this should be okay for now.
PKG_VERSION=$(dpkg-parsechangelog --show-field Version |sed -e 's/-1$//')
ln -fv $RELOC_PKG ../"$PKG_NAME"_"$PKG_VERSION".orig.tar.gz
debuild -rfakeroot -us -uc

View File

@ -1,70 +0,0 @@
#!/bin/bash -e
. /etc/os-release
print_usage() {
echo "build_reloc.sh --clean --nodeps"
echo " --clean clean build directory"
echo " --nodeps skip installing dependencies"
echo " --version V product-version-release string (overriding SCYLLA-VERSION-GEN)"
exit 1
}
CLEAN=
NODEPS=
VERSION_OVERRIDE=
while [ $# -gt 0 ]; do
case "$1" in
"--clean")
CLEAN=yes
shift 1
;;
"--nodeps")
NODEPS=yes
shift 1
;;
"--version")
VERSION_OVERRIDE="$2"
shift 2
;;
*)
print_usage
;;
esac
done
VERSION=$(./SCYLLA-VERSION-GEN ${VERSION_OVERRIDE:+ --version "$VERSION_OVERRIDE"})
# the former command should generate build/SCYLLA-PRODUCT-FILE and some other version
# related files
PRODUCT=`cat build/SCYLLA-PRODUCT-FILE`
DEST="build/$PRODUCT-jmx-$VERSION.noarch.tar.gz"
is_redhat_variant() {
[ -f /etc/redhat-release ]
}
is_debian_variant() {
[ -f /etc/debian_version ]
}
if [ ! -e reloc/build_reloc.sh ]; then
echo "run build_reloc.sh in top of scylla dir"
exit 1
fi
if [ "$CLEAN" = "yes" ]; then
rm -rf build target
fi
if [ -f "$DEST" ]; then
rm "$DEST"
fi
if [ -z "$NODEPS" ]; then
sudo ./install-dependencies.sh
fi
mvn -B --file scylla-jmx-parent/pom.xml install
./SCYLLA-VERSION-GEN ${VERSION_OVERRIDE:+ --version "$VERSION_OVERRIDE"}
./dist/debian/debian_files_gen.py
scripts/create-relocatable-package.py "$DEST"

View File

@ -1,52 +0,0 @@
#!/bin/bash -e
print_usage() {
echo "build_rpm.sh --reloc-pkg build/scylla-jmx-package.tar.gz"
echo " --reloc-pkg specify relocatable package path"
echo " --builddir specify rpmbuild directory"
exit 1
}
RELOC_PKG=build/scylla-jmx-package.tar.gz
BUILDDIR=build/redhat
while [ $# -gt 0 ]; do
case "$1" in
"--reloc-pkg")
RELOC_PKG=$2
shift 2
;;
"--builddir")
BUILDDIR="$2"
shift 2
;;
*)
print_usage
;;
esac
done
RELOC_PKG=$(readlink -f $RELOC_PKG)
RPMBUILD=$(readlink -f $BUILDDIR)
mkdir -p "$BUILDDIR"
tar -C "$BUILDDIR" -xpf $RELOC_PKG scylla-jmx/SCYLLA-RELEASE-FILE scylla-jmx/SCYLLA-RELOCATABLE-FILE scylla-jmx/SCYLLA-VERSION-FILE scylla-jmx/SCYLLA-PRODUCT-FILE scylla-jmx/dist/redhat
cd "$BUILDDIR"/scylla-jmx
RELOC_PKG_BASENAME=$(basename "$RELOC_PKG")
SCYLLA_VERSION=$(cat SCYLLA-VERSION-FILE)
SCYLLA_RELEASE=$(cat SCYLLA-RELEASE-FILE)
VERSION=$SCYLLA_VERSION-$SCYLLA_RELEASE
PRODUCT=$(cat SCYLLA-PRODUCT-FILE)
mkdir -p $RPMBUILD/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
ln -fv $RELOC_PKG $RPMBUILD/SOURCES/
parameters=(
-D"version $SCYLLA_VERSION"
-D"release $SCYLLA_RELEASE"
-D"product $PRODUCT"
-D"reloc_pkg $RELOC_PKG_BASENAME"
)
cp dist/redhat/scylla-jmx.spec $RPMBUILD/SPECS
# this rpm can be install on both fedora / centos7, so drop distribution name from the file name
rpmbuild -ba "${parameters[@]}" --define '_binary_payload w2.xzdio' --define "_topdir $RPMBUILD" --undefine "dist" $RPMBUILD/SPECS/scylla-jmx.spec

View File

@ -1,64 +0,0 @@
#!/usr/bin/python3
#
# Copyright (C) 2018 ScyllaDB
#
#
# This file is part of Scylla.
#
# Scylla is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Scylla is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
import io
import os
import tarfile
import pathlib
RELOC_PREFIX='scylla-jmx'
def reloc_add(self, name, arcname=None, recursive=True, *, filter=None):
if arcname:
return self.add(name, arcname="{}/{}".format(RELOC_PREFIX, arcname))
else:
return self.add(name, arcname="{}/{}".format(RELOC_PREFIX, name))
tarfile.TarFile.reloc_add = reloc_add
ap = argparse.ArgumentParser(description='Create a relocatable scylla package.')
ap.add_argument('dest',
help='Destination file (tar format)')
args = ap.parse_args()
output = args.dest
ar = tarfile.open(output, mode='w|gz')
# relocatable package format version = 2.2
with open('build/.relocatable_package_version', 'w') as f:
f.write('2.2\n')
ar.add('build/.relocatable_package_version', arcname='.relocatable_package_version')
pathlib.Path('build/SCYLLA-RELOCATABLE-FILE').touch()
ar.reloc_add('build/SCYLLA-RELOCATABLE-FILE', arcname='SCYLLA-RELOCATABLE-FILE')
ar.reloc_add('build/SCYLLA-RELEASE-FILE', arcname='SCYLLA-RELEASE-FILE')
ar.reloc_add('build/SCYLLA-VERSION-FILE', arcname='SCYLLA-VERSION-FILE')
ar.reloc_add('build/SCYLLA-PRODUCT-FILE', arcname='SCYLLA-PRODUCT-FILE')
ar.reloc_add('dist')
ar.reloc_add('install.sh')
ar.reloc_add('target/scylla-jmx-1.1.jar', arcname='scylla-jmx-1.1.jar')
ar.reloc_add('scripts/scylla-jmx', arcname='scylla-jmx')
ar.reloc_add('README.md')
ar.reloc_add('NOTICE')
ar.reloc_add('build/debian/debian', arcname='debian')

View File

@ -1,494 +0,0 @@
#! /usr/bin/env python
# coding=utf-8
from __future__ import print_function
from __future__ import unicode_literals
__version__ = "1.9"
import logging
from os import extsep, path, readlink, curdir
from subprocess import CalledProcessError, Popen, PIPE
import sys
import tarfile
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED
class GitArchiver(object):
"""
GitArchiver
Scan a git repository and export all tracked files, and submodules.
Checks for .gitattributes files in each directory and uses 'export-ignore'
pattern entries for ignore files in the archive.
>>> archiver = GitArchiver(main_repo_abspath='my/repo/path')
>>> archiver.create('output.zip')
"""
LOG = logging.getLogger('GitArchiver')
def __init__(self, prefix='', exclude=True, force_sub=False, extra=None, main_repo_abspath=None):
"""
@param prefix: Prefix used to prepend all paths in the resulting archive.
Extra file paths are only prefixed if they are not relative.
E.g. if prefix is 'foo' and extra is ['bar', '/baz'] the resulting archive will look like this:
/
baz
foo/
bar
@type prefix: string
@param exclude: Determines whether archiver should follow rules specified in .gitattributes files.
@type exclude: bool
@param force_sub: Determines whether submodules are initialized and updated before archiving.
@type force_sub: bool
@param extra: List of extra paths to include in the resulting archive.
@type extra: list
@param main_repo_abspath: Absolute path to the main repository (or one of subdirectories).
If given path is path to a subdirectory (but not a submodule directory!) it will be replaced
with abspath to top-level directory of the repository.
If None, current cwd is used.
@type main_repo_abspath: string
"""
if extra is None:
extra = []
if main_repo_abspath is None:
main_repo_abspath = path.abspath('')
elif not path.isabs(main_repo_abspath):
raise ValueError("You MUST pass absolute path to the main git repository.")
try:
self.run_shell("[ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1", main_repo_abspath)
except Exception as e:
raise ValueError("Not a git repository (or any of the parent directories).")
main_repo_abspath = path.abspath(self.read_git_shell('git rev-parse --show-toplevel', main_repo_abspath).rstrip())
self.prefix = prefix
self.exclude = exclude
self.extra = extra
self.force_sub = force_sub
self.main_repo_abspath = main_repo_abspath
def create(self, output_path, dry_run=False, output_format=None):
"""
Create the archive at output_file_path.
Type of the archive is determined either by extension of output_file_path or by output_format.
Supported formats are: gz, zip, bz2, xz, tar, tgz, txz
@param output_path: Output file path.
@type output_path: string
@param dry_run: Determines whether create should do nothing but print what it would archive.
@type dry_run: bool
@param output_format: Determines format of the output archive. If None, format is determined from extension
of output_file_path.
@type output_format: string
"""
if output_format is None:
file_name, file_ext = path.splitext(output_path)
output_format = file_ext[len(extsep):].lower()
self.LOG.debug("Output format is not explicitly set, determined format is {}.".format(output_format))
if not dry_run:
if output_format == 'zip':
archive = ZipFile(path.abspath(output_path), 'w')
def add_file(file_path, arcname):
if not path.islink(file_path):
archive.write(file_path, arcname, ZIP_DEFLATED)
else:
i = ZipInfo(arcname)
i.create_system = 3
i.external_attr = 0xA1ED0000
archive.writestr(i, readlink(file_path))
elif output_format in ['tar', 'bz2', 'gz', 'xz', 'tgz', 'txz']:
if output_format == 'tar':
t_mode = 'w'
elif output_format == 'tgz':
t_mode = 'w:gz'
elif output_format == 'txz':
t_mode = 'w:xz'
else:
t_mode = 'w:{}'.format(output_format)
archive = tarfile.open(path.abspath(output_path), t_mode)
add_file = lambda file_path, arcname: archive.add(file_path, arcname)
else:
raise RuntimeError("Unknown format: {}".format(output_format))
def archiver(file_path, arcname):
self.LOG.debug("Compressing {} => {}...".format(file_path, arcname))
add_file(file_path, arcname)
else:
archive = None
archiver = lambda file_path, arcname: self.LOG.info("{} => {}".format(file_path, arcname))
self.archive_all_files(archiver)
if archive is not None:
archive.close()
def get_exclude_patterns(self, repo_abspath, repo_file_paths):
"""
Returns exclude patterns for a given repo. It looks for .gitattributes files in repo_file_paths.
Resulting dictionary will contain exclude patterns per path (relative to the repo_abspath).
E.g. {('.', 'Catalyst', 'Editions', 'Base'), ['Foo*', '*Bar']}
@type repo_abspath: string
@param repo_abspath: Absolute path to the git repository.
@type repo_file_paths: list
@param repo_file_paths: List of paths relative to the repo_abspath that are under git control.
@rtype: dict
@return: Dictionary representing exclude patterns.
Keys are tuples of strings. Values are lists of strings.
Returns None if self.exclude is not set.
"""
if not self.exclude:
return None
def read_attributes(attributes_abspath):
patterns = []
if path.isfile(attributes_abspath):
attributes = open(attributes_abspath, 'r').readlines()
patterns = []
for line in attributes:
tokens = line.strip().split()
if "export-ignore" in tokens[1:]:
patterns.append(tokens[0])
return patterns
exclude_patterns = {(): []}
# There may be no gitattributes.
try:
global_attributes_abspath = self.read_shell("git config --get core.attributesfile", repo_abspath).rstrip()
exclude_patterns[()] = read_attributes(global_attributes_abspath)
except:
# And it's valid to not have them.
pass
for attributes_abspath in [path.join(repo_abspath, f) for f in repo_file_paths if f.endswith(".gitattributes")]:
# Each .gitattributes affects only files within its directory.
key = tuple(self.get_path_components(repo_abspath, path.dirname(attributes_abspath)))
exclude_patterns[key] = read_attributes(attributes_abspath)
local_attributes_abspath = path.join(repo_abspath, ".git", "info", "attributes")
key = tuple(self.get_path_components(repo_abspath, repo_abspath))
if key in exclude_patterns:
exclude_patterns[key].extend(read_attributes(local_attributes_abspath))
else:
exclude_patterns[key] = read_attributes(local_attributes_abspath)
return exclude_patterns
def is_file_excluded(self, repo_abspath, repo_file_path, exclude_patterns):
"""
Checks whether file at a given path is excluded.
@type repo_abspath: string
@param repo_abspath: Absolute path to the git repository.
@type repo_file_path: string
@param repo_file_path: Path to a file within repo_abspath.
@type exclude_patterns: dict
@param exclude_patterns: Exclude patterns with format specified for get_exclude_patterns.
@rtype: bool
@return: True if file should be excluded. Otherwise False.
"""
if exclude_patterns is None or not len(exclude_patterns):
return False
from fnmatch import fnmatch
file_name = path.basename(repo_file_path)
components = self.get_path_components(repo_abspath, path.join(repo_abspath, path.dirname(repo_file_path)))
is_excluded = False
# We should check all patterns specified in intermediate directories to the given file.
# At the end we should also check for the global patterns (key '()' or empty tuple).
while not is_excluded:
key = tuple(components)
if key in exclude_patterns:
patterns = exclude_patterns[key]
for p in patterns:
if fnmatch(file_name, p) or fnmatch(repo_file_path, p):
self.LOG.debug("Exclude pattern matched {}: {}".format(p, repo_file_path))
is_excluded = True
if not len(components):
break
components.pop()
return is_excluded
def archive_all_files(self, archiver):
"""
Archive all files using archiver.
@param archiver: Function that accepts 2 arguments: abspath to file on the system and relative path within archive.
"""
for file_path in self.extra:
archiver(path.abspath(file_path), path.join(self.prefix, file_path))
for file_path in self.walk_git_files():
archiver(path.join(self.main_repo_abspath, file_path), path.join(self.prefix, file_path))
def walk_git_files(self, repo_path=''):
"""
An iterator method that yields a file path relative to main_repo_abspath
for each file that should be included in the archive.
Skips those that match the exclusion patterns found in
any discovered .gitattributes files along the way.
Recurs into submodules as well.
@type repo_path: string
@param repo_path: Path to the git submodule repository relative to main_repo_abspath.
@rtype: iterator
@return: Iterator to traverse files under git control relative to main_repo_abspath.
"""
repo_abspath = path.join(self.main_repo_abspath, repo_path)
repo_file_paths = self.read_git_shell("git ls-files --cached --full-name --no-empty-directory", repo_abspath).splitlines()
exclude_patterns = self.get_exclude_patterns(repo_abspath, repo_file_paths)
for repo_file_path in repo_file_paths:
# Git puts path in quotes if file path has unicode characters.
repo_file_path = repo_file_path.strip('"') # file path relative to current repo
file_name = path.basename(repo_file_path)
main_repo_file_path = path.join(repo_path, repo_file_path) # file path relative to the main repo
# Only list symlinks and files that don't start with git.
if file_name.startswith(".git") or (not path.islink(main_repo_file_path) and path.isdir(main_repo_file_path)):
continue
if self.is_file_excluded(repo_abspath, repo_file_path, exclude_patterns):
continue
yield main_repo_file_path
if self.force_sub:
self.run_shell("git submodule init", repo_abspath)
self.run_shell("git submodule update", repo_abspath)
for submodule_path in self.read_shell("git submodule --quiet foreach 'pwd -P'", repo_abspath).splitlines():
# Shell command returns absolute paths to submodules.
submodule_path = path.relpath(submodule_path, self.main_repo_abspath)
for file_path in self.walk_git_files(submodule_path):
yield file_path
@staticmethod
def get_path_components(repo_abspath, abspath):
"""
Split given abspath into components relative to repo_abspath.
These components are primarily used as unique keys of files and folders within a repository.
E.g. if repo_abspath is '/Documents/Hobby/ParaView/' and abspath is
'/Documents/Hobby/ParaView/Catalyst/Editions/Base/', function will return:
['.', 'Catalyst', 'Editions', 'Base']
First element is always '.' (concrete symbol depends on OS).
@param repo_abspath: Absolute path to the git repository. Normalized via os.path.normpath.
@type repo_abspath: string
@param abspath: Absolute path to a file within repo_abspath. Normalized via os.path.normpath.
@type abspath: string
@return: List of path components.
@rtype: list
"""
repo_abspath = path.normpath(repo_abspath)
abspath = path.normpath(abspath)
if not path.isabs(repo_abspath):
raise ValueError("repo_abspath MUST be absolute path.")
if not path.isabs(abspath):
raise ValueError("abspath MUST be absoulte path.")
if not path.commonprefix([repo_abspath, abspath]):
raise ValueError("abspath (\"{}\") MUST have common prefix with repo_abspath (\"{}\")".format(abspath, repo_abspath))
components = []
while not abspath == repo_abspath:
abspath, tail = path.split(abspath)
if tail:
components.insert(0, tail)
components.insert(0, curdir)
return components
@staticmethod
def run_shell(cmd, cwd=None):
"""
Runs shell command.
@type cmd: string
@param cmd: Command to be executed.
@type cwd: string
@param cwd: Working directory.
@rtype: int
@return: Return code of the command.
@raise CalledProcessError: Raises exception if return code of the command is non-zero.
"""
p = Popen(cmd, shell=True, cwd=cwd)
p.wait()
if p.returncode:
raise CalledProcessError(returncode=p.returncode, cmd=cmd)
return p.returncode
@staticmethod
def read_shell(cmd, cwd=None, encoding='utf-8'):
"""
Runs shell command and reads output.
@type cmd: string
@param cmd: Command to be executed.
@type cwd: string
@param cwd: Working directory.
@type encoding: string
@param encoding: Encoding used to decode bytes returned by Popen into string.
@rtype: string
@return: Output of the command.
@raise CalledProcessError: Raises exception if return code of the command is non-zero.
"""
p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd)
output, _ = p.communicate()
output = output.decode(encoding)
if p.returncode:
if sys.version_info > (2,6):
raise CalledProcessError(returncode=p.returncode, cmd=cmd, output=output)
else:
raise CalledProcessError(returncode=p.returncode, cmd=cmd)
return output
@staticmethod
def read_git_shell(cmd, cwd=None):
"""
Runs git shell command, reads output and decodes it into unicode string
@type cmd: string
@param cmd: Command to be executed.
@type cwd: string
@param cwd: Working directory.
@rtype: string
@return: Output of the command.
@raise CalledProcessError: Raises exception if return code of the command is non-zero.
"""
p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd)
output, _ = p.communicate()
output = output.decode('unicode_escape').encode('raw_unicode_escape').decode('utf-8')
if p.returncode:
if sys.version_info > (2,6):
raise CalledProcessError(returncode=p.returncode, cmd=cmd, output=output)
else:
raise CalledProcessError(returncode=p.returncode, cmd=cmd)
return output
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser(usage="usage: %prog [-v] [--prefix PREFIX] [--no-exclude] [--force-submodules] [--extra EXTRA1 [EXTRA2]] [--dry-run] OUTPUT_FILE",
version="%prog {}".format(__version__))
parser.add_option('--prefix',
type='string',
dest='prefix',
default=None,
help="prepend PREFIX to each filename in the archive. OUTPUT_FILE name is used by default to avoid tarbomb. You can set it to '' in order to explicitly request tarbomb")
parser.add_option('-v', '--verbose',
action='store_true',
dest='verbose',
help='enable verbose mode')
parser.add_option('--no-exclude',
action='store_false',
dest='exclude',
default=True,
help="don't read .gitattributes files for patterns containing export-ignore attrib")
parser.add_option('--force-submodules',
action='store_true',
dest='force_sub',
help="force a git submodule init && git submodule update at each level before iterating submodules")
parser.add_option('--extra',
action='append',
dest='extra',
default=[],
help="any additional files to include in the archive")
parser.add_option('--dry-run',
action='store_true',
dest='dry_run',
help="don't actually archive anything, just show what would be done")
options, args = parser.parse_args()
if len(args) != 1:
parser.error("You must specify exactly one output file")
output_file_path = args[0]
if path.isdir(output_file_path):
parser.error("You cannot use directory as output")
# avoid tarbomb
if options.prefix is not None:
options.prefix = path.join(options.prefix, '')
else:
import re
output_name = path.basename(output_file_path)
output_name = re.sub('(\.zip|\.tar|\.tgz|\.txz|\.gz|\.bz2|\.xz|\.tar\.gz|\.tar\.bz2|\.tar\.xz)$', '', output_name) or "Archive"
options.prefix = path.join(output_name, '')
try:
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('%(message)s'))
GitArchiver.LOG.addHandler(handler)
GitArchiver.LOG.setLevel(logging.DEBUG if options.verbose else logging.INFO)
archiver = GitArchiver(options.prefix,
options.exclude,
options.force_sub,
options.extra)
archiver.create(output_file_path, options.dry_run)
except Exception as e:
parser.exit(2, "{}\n".format(e))
sys.exit(0)

View File

@ -1,38 +1,21 @@
#!/bin/bash
#!/bin/sh
#
# Copyright (C) 2015 Cloudius Systems, Ltd.
JMX_PORT="7199"
JMX_ADDR=
API_ADDR=
API_PORT=
CONF_FILE=""
DEBUG=""
JMX_PORT=7199
API_ADDR="127.0.0.1"
API_PORT="10000"
PARAM_HELP="-h"
PARAM_JMX_PORT="-jp"
PARAM_JMX_ADDR="-ja"
PARAM_API_PORT="-p"
PARAM_ADDR="-a"
PARAM_LOCATION="-l"
LOCATION="target"
LOCATION_SCRIPTS="scripts"
PARAM_FILE="-cf"
ALLOW_REMOTE="-r"
ALLOW_DEBUG="-d"
REMOTE=0
HOSTNAME=`hostname`
PROPERTIES=
JMX_AUTH=-Dcom.sun.management.jmxremote.authenticate=false
JMX_SSL=-Dcom.sun.management.jmxremote.ssl=false
print_help() {
cat <<HLPEND
scylla-jmx [$PARAM_HELP] [$PARAM_API_PORT port] [$PARAM_ADDR address] [$PARAM_JMX_PORT port] [$PARAM_FILE file]
scylla-jmx [$PARAM_HELP] [$PARAM_PORT port] [$PARAM_ADDR address]
This script is used to run the jmx proxy
@ -44,11 +27,7 @@ This script receives the following command line arguments:
$PARAM_JMX_PORT <port> - The jmx port to open
$PARAM_API_PORT <port> - The API port to connect to
$PARAM_ADDR <address> - The API address to connect to
$PARAM_JMX_ADDR <address> - JMX bind address
$PARAM_FILE <file> - A configuration file to use
$PARAM_LOCATION <location> - The location of the jmx proxy jar file
$ALLOW_REMOTE - When set allow remote jmx connectivity
$ALLOW_DEBUG - When set open debug ports for remote debugger
HLPEND
}
@ -56,88 +35,31 @@ while test "$#" -ne 0
do
case "$1" in
"$PARAM_API_PORT")
API_PORT="-Dapiport="$2
API_PORT=$2
shift 2
;;
"$PARAM_ADDR")
API_ADDR="-Dapiaddress="$2
API_ADDR=$2
shift 2
;;
"$PARAM_PORT")
API_ADDR=$2
shift 2
;;
"$PARAM_JMX_PORT")
JMX_PORT=$2
shift 2
;;
"$PARAM_JMX_ADDR")
JMX_ADDR=-Dcom.sun.management.jmxremote.host=$2
shift 2
;;
"$PARAM_LOCATION")
LOCATION=$2
LOCATION_SCRIPTS="$2"
shift 2
;;
"$PARAM_FILE")
CONF_FILE="-Dapiconfig="$2
shift 2
;;
"$ALLOW_REMOTE")
REMOTE=1
shift 1
;;
"$PARAM_HELP")
print_help
exit 0
;;
"$ALLOW_DEBUG")
DEBUG="-agentlib:jdwp=transport=dt_socket,address=127.0.0.1:7690,server=y,suspend=n"
shift 1
;;
-Dcom.sun.management.jmxremote.host=*)
JMX_ADDR=$1
HOSTNAME=${1:36}
shift
;;
-Dcom.sun.management.jmxremote.authenticate=*)
JMX_AUTH=$1
shift 1
;;
-Dcom.sun.management.jmxremote.ssl=*)
JMX_SSL=$1
shift 1
;;
-Dcom.sun.management.jmxremote.local.only=*)
JMX_LOCAL=$1
shift 1
;;
-D*)
PROPERTIES="$PROPERTIES $1"
shift 1
;;
*)
echo "Unknown parameter: $1"
print_help
exit 1
esac
done
if [ $REMOTE -eq 0 ]; then
if [ -z $JMX_ADDR ]; then
JMX_ADDR=-Dcom.sun.management.jmxremote.host=localhost
fi
HOSTNAME=localhost
else
if [ -z $JMX_LOCAL ]; then
JMX_LOCAL=-Dcom.sun.management.jmxremote.local.only=false
fi
fi
"$LOCATION_SCRIPTS"/symlinks/scylla-jmx $DEBUG \
$API_PORT $API_ADDR $CONF_FILE -Xmx256m -XX:+UseSerialGC \
-XX:+HeapDumpOnOutOfMemoryError \
$JMX_AUTH $JMX_SSL $JMX_ADDR $JMX_LOCAL \
--add-exports java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED \
--add-exports java.management/com.sun.jmx.interceptor=ALL-UNNAMED \
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=$JMX_PORT \
-Djava.rmi.server.hostname=$HOSTNAME -Dcom.sun.management.jmxremote.rmi.port=$JMX_PORT \
-Djavax.management.builder.initial=com.scylladb.jmx.utils.APIBuilder \
$PROPERTIES -jar $LOCATION/scylla-jmx-1.1.jar
java -Dapiaddress=$API_ADDR -Dapiport=$API_PORT -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=$JMX_PORT -Dcom.sun.management.jmxremote.rmi.port=$JMX_PORT -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar $LOCATION/urchin-mbean-1.0.jar

View File

@ -1 +0,0 @@
/usr/bin/java

View File

@ -1,99 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>scylla-apiclient</artifactId>
<packaging>jar</packaging>
<version>1.1</version>
<parent>
<relativePath>../scylla-jmx-parent/pom.xml</relativePath>
<groupId>it.cavallium.scylladb.jmx</groupId>
<artifactId>scylla-jmx-parent</artifactId>
<version>1.1</version>
</parent>
<name>Scylla REST API client</name>
<properties>
<jackson.version>2.14.0</jackson.version>
<jackson.databind.version>2.14.0</jackson.databind.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jakarta.rs</groupId>
<artifactId>jackson-jakarta-rs-json-provider</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,15 +0,0 @@
module scylla.apiclient {
exports com.scylladb.jmx.api;
exports com.scylladb.jmx.api.utils;
requires org.eclipse.parsson;
requires jakarta.ws.rs;
requires com.fasterxml.jackson.jakarta.rs.json;
requires jersey.client;
requires java.logging;
requires jakarta.json;
requires java.management;
requires org.yaml.snakeyaml;
requires com.google.common;
requires jersey.common;
requires jersey.hk2;
}

View File

@ -1,29 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>it.cavallium.scylladb.jmx</groupId>
<artifactId>scylla-jmx-parent</artifactId>
<version>1.1</version>
<packaging>pom</packaging>
<modules>
<module>../</module>
<module>../scylla-apiclient</module>
</modules>
<name>Scylla JMX Parent</name>
<distributionManagement>
<repository>
<id>mchv-release-distribution</id>
<name>MCHV Release Apache Maven Packages Distribution</name>
<url>https://mvn.mchv.eu/repository/mchv</url>
</repository>
<snapshotRepository>
<id>mchv-snapshot-distribution</id>
<name>MCHV Snapshot Apache Maven Packages Distribution</name>
<url>https://mvn.mchv.eu/repository/mchv-snapshot</url>
</snapshotRepository>
</distributionManagement>
</project>

View File

@ -1,64 +1,44 @@
/*
* Copyright 2015 Cloudius Systems
*/
package com.scylladb.jmx.api;
package com.cloudius.urchin.api;
import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.json.JsonReaderFactory;
import jakarta.json.JsonString;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import java.io.StringReader;
import java.lang.System.Logger.Level;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.logging.Logger;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonReaderFactory;
import javax.json.JsonString;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import com.scylladb.jmx.api.utils.SnapshotDetailsTabularData;
import com.cloudius.urchin.utils.EstimatedHistogram;
import com.cloudius.urchin.utils.SnapshotDetailsTabularData;
import com.yammer.metrics.core.HistogramValues;
public class APIClient {
private Map<String, CacheEntry> cache = new HashMap<String, CacheEntry>();
private final APIConfig config;
private final ClientConfig clientConfig;
private final Client client;
private JsonReaderFactory factory = Json.createReaderFactory(null);
private static final Logger logger = Logger.getLogger(APIClient.class.getName());
public APIClient(APIConfig config) {
this.config = config;
this.clientConfig = new ClientConfig();
clientConfig.register(new JacksonJsonProvider());
this.client = ClientBuilder.newClient(clientConfig);
}
private String getCacheKey(String key, MultivaluedMap<String, String> param, long duration) {
Map<String, CacheEntry> cache = new HashMap<String, CacheEntry>();
String getCacheKey(String key, MultivaluedMap<String, String> param, long duration) {
if (duration <= 0) {
return null;
}
@ -73,31 +53,35 @@ public class APIClient {
return key;
}
private String getStringFromCache(String key, long duration) {
String getStringFromCache(String key, long duration) {
if (key == null) {
return null;
}
CacheEntry value = cache.get(key);
return (value != null && value.valid(duration)) ? value.stringValue() : null;
return (value!= null && value.valid(duration))? value.stringValue() : null;
}
private JsonObject getJsonObjectFromCache(String key, long duration) {
EstimatedHistogram getEstimatedHistogramFromCache(String key, long duration) {
if (key == null) {
return null;
}
CacheEntry value = cache.get(key);
return (value != null && value.valid(duration)) ? value.jsonObject() : null;
return (value!= null && value.valid(duration))? value.getEstimatedHistogram() : null;
}
JsonReaderFactory factory = Json.createReaderFactory(null);
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(APIClient.class.getName());
private String getBaseUrl() {
return config.getBaseUrl();
public static String getBaseUrl() {
return APIConfig.getBaseUrl();
}
public Invocation.Builder get(String path, MultivaluedMap<String, String> queryParams) {
Client client = ClientBuilder.newClient( new ClientConfig());
WebTarget webTarget = client.target(getBaseUrl()).path(path);
if (queryParams != null) {
for (Entry<String, List<String>> qp : queryParams.entrySet()) {
for (Entry<String, List<String>> qp : queryParams.entrySet()) {
for (String e : qp.getValue()) {
webTarget = webTarget.queryParam(qp.getKey(), e);
}
@ -111,34 +95,22 @@ public class APIClient {
}
public Response post(String path, MultivaluedMap<String, String> queryParams) {
return post(path, queryParams, null);
}
public Response post(String path, MultivaluedMap<String, String> queryParams, Object object, String type) {
try {
Response response = get(path, queryParams).post(Entity.entity(object, type));
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
throw getException("Scylla API server HTTP POST to URL '" + path + "' failed",
response.readEntity(String.class));
}
return response;
} catch (ProcessingException e) {
throw new IllegalStateException("Unable to connect to Scylla API server: " + e.getMessage());
Response response = get(path, queryParams).post(Entity.entity(null, MediaType.TEXT_PLAIN));
if (response.getStatus() != Response.Status.OK.getStatusCode() ) {
throw getException(response.readEntity(String.class));
}
}
return response;
public Response post(String path, MultivaluedMap<String, String> queryParams, Object object) {
return post(path, queryParams, object, MediaType.TEXT_PLAIN);
}
public void post(String path) {
post(path, null);
}
public IllegalStateException getException(String msg, String json) {
JsonReader reader = factory.createReader(new StringReader(json));
public RuntimeException getException(String txt) {
JsonReader reader = factory.createReader(new StringReader(txt));
JsonObject res = reader.readObject();
return new IllegalStateException(msg + ": " + res.getString("message"));
return new RuntimeException(res.getString("message"));
}
public String postGetVal(String path, MultivaluedMap<String, String> queryParams) {
@ -158,47 +130,40 @@ public class APIClient {
get(path, queryParams).delete();
return;
}
Response response = get(path).delete();
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
throw getException("Scylla API server HTTP delete to URL '" + path + "' failed",
response.readEntity(String.class));
}
get(path).delete();
}
public void delete(String path) {
delete(path, null);
}
public String getRawValue(String string, MultivaluedMap<String, String> queryParams, long duration) {
try {
if (string.equals("")) {
return "";
}
String key = getCacheKey(string, queryParams, duration);
String res = getStringFromCache(key, duration);
if (res != null) {
return res;
}
Response response = get(string, queryParams).get(Response.class);
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
// TBD
// We are currently not caching errors,
// it should be reconsider.
throw getException("Scylla API server HTTP GET to URL '" + string + "' failed",
response.readEntity(String.class));
}
res = response.readEntity(String.class);
if (duration > 0) {
cache.put(key, new CacheEntry(res));
}
return res;
} catch (ProcessingException e) {
throw new IllegalStateException("Unable to connect to Scylla API server: " + e.getMessage());
public String getRawValue(String string,
MultivaluedMap<String, String> queryParams, long duration) {
if (string.equals("")) {
return "";
}
String key = getCacheKey(string, queryParams, duration);
String res = getStringFromCache(key, duration);
if (res != null) {
return res;
}
Response response = get(string, queryParams).get(Response.class);
if (response.getStatus() != Response.Status.OK.getStatusCode() ) {
// TBD
// We are currently not caching errors,
// it should be reconsider.
throw getException(response.readEntity(String.class));
}
res = response.readEntity(String.class);
if (duration > 0) {
cache.put(key, new CacheEntry(res));
}
return res;
}
public String getRawValue(String string, MultivaluedMap<String, String> queryParams) {
public String getRawValue(String string,
MultivaluedMap<String, String> queryParams) {
return getRawValue(string, queryParams, 0);
}
@ -211,19 +176,23 @@ public class APIClient {
}
public String getStringValue(String string, MultivaluedMap<String, String> queryParams) {
return getRawValue(string, queryParams).replaceAll("^\"|\"$", "");
return getRawValue(string,
queryParams).replaceAll("^\"|\"$", "");
}
public String getStringValue(String string, MultivaluedMap<String, String> queryParams, long duration) {
return getRawValue(string, queryParams, duration).replaceAll("^\"|\"$", "");
return getRawValue(string,
queryParams, duration).replaceAll("^\"|\"$", "");
}
public String getStringValue(String string) {
return getStringValue(string, null);
}
public JsonReader getReader(String string, MultivaluedMap<String, String> queryParams) {
return factory.createReader(new StringReader(getRawValue(string, queryParams)));
public JsonReader getReader(String string,
MultivaluedMap<String, String> queryParams) {
return factory.createReader(new StringReader(getRawValue(string,
queryParams)));
}
public JsonReader getReader(String string) {
@ -235,7 +204,8 @@ public class APIClient {
return val.toArray(new String[val.size()]);
}
public int getIntValue(String string, MultivaluedMap<String, String> queryParams) {
public int getIntValue(String string,
MultivaluedMap<String, String> queryParams) {
return Integer.parseInt(getRawValue(string, queryParams));
}
@ -243,19 +213,6 @@ public class APIClient {
return getIntValue(string, null);
}
public static <T> BiFunction<APIClient, String, T> getReader(Class<T> type) {
if (type == String.class) {
return (c, s) -> type.cast(c.getRawValue(s));
} else if (type == Integer.class) {
return (c, s) -> type.cast(c.getIntValue(s));
} else if (type == Double.class) {
return (c, s) -> type.cast(c.getDoubleValue(s));
} else if (type == Long.class) {
return (c, s) -> type.cast(c.getLongValue(s));
}
throw new IllegalArgumentException(type.getName());
}
public boolean getBooleanValue(String string) {
return Boolean.parseBoolean(getRawValue(string));
}
@ -264,7 +221,8 @@ public class APIClient {
return Double.parseDouble(getRawValue(string));
}
public List<String> getListStrValue(String string, MultivaluedMap<String, String> queryParams) {
public List<String> getListStrValue(String string,
MultivaluedMap<String, String> queryParams) {
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
List<String> res = new ArrayList<String>(arr.size());
@ -319,7 +277,8 @@ public class APIClient {
return join(arr, ",");
}
public static String mapToString(Map<String, String> mp, String pairJoin, String joiner) {
public static String mapToString(Map<String, String> mp, String pairJoin,
String joiner) {
String res = "";
if (mp != null) {
for (String name : mp.keySet()) {
@ -336,15 +295,19 @@ public class APIClient {
return mapToString(mp, "=", ",");
}
public static boolean set_query_param(MultivaluedMap<String, String> queryParams, String key, String value) {
if (queryParams != null && key != null && value != null && !value.equals("")) {
public static boolean set_query_param(
MultivaluedMap<String, String> queryParams, String key, String value) {
if (queryParams != null && key != null && value != null
&& !value.equals("")) {
queryParams.add(key, value);
return true;
}
return false;
}
public static boolean set_bool_query_param(MultivaluedMap<String, String> queryParams, String key, boolean value) {
public static boolean set_bool_query_param(
MultivaluedMap<String, String> queryParams, String key,
boolean value) {
if (queryParams != null && key != null && value) {
queryParams.add(key, "true");
return true;
@ -363,7 +326,8 @@ public class APIClient {
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
if (obj.containsKey("key") && obj.containsKey("value")) {
map.put(obj.getString("key"), listStrFromJArr(obj.getJsonArray("value")));
map.put(obj.getString("key"),
listStrFromJArr(obj.getJsonArray("value")));
}
}
reader.close();
@ -385,7 +349,8 @@ public class APIClient {
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
if (obj.containsKey("key") && obj.containsKey("value")) {
map.put(listStrFromJArr(obj.getJsonArray("key")), listStrFromJArr(obj.getJsonArray("value")));
map.put(listStrFromJArr(obj.getJsonArray("key")),
listStrFromJArr(obj.getJsonArray("value")));
}
}
reader.close();
@ -396,7 +361,8 @@ public class APIClient {
return getMapListStrValue(string, null);
}
public Set<String> getSetStringValue(String string, MultivaluedMap<String, String> queryParams) {
public Set<String> getSetStringValue(String string,
MultivaluedMap<String, String> queryParams) {
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
Set<String> res = new HashSet<String>();
@ -411,13 +377,14 @@ public class APIClient {
return getSetStringValue(string, null);
}
public Map<String, String> getMapStrValue(String string, MultivaluedMap<String, String> queryParams) {
public Map<String, String> getMapStrValue(String string,
MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
Map<String, String> map = new LinkedHashMap<String, String>();
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
if (obj.containsKey("key") && obj.containsKey("value")) {
@ -432,28 +399,8 @@ public class APIClient {
return getMapStrValue(string, null);
}
public Map<String, String> getReverseMapStrValue(String string, MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
if (obj.containsKey("key") && obj.containsKey("value")) {
map.put(obj.getString("value"), obj.getString("key"));
}
}
reader.close();
return map;
}
public Map<String, String> getReverseMapStrValue(String string) {
return getReverseMapStrValue(string, null);
}
public List<InetAddress> getListInetAddressValue(String string, MultivaluedMap<String, String> queryParams) {
public List<InetAddress> getListInetAddressValue(String string,
MultivaluedMap<String, String> queryParams) {
List<String> vals = getListStrValue(string, queryParams);
List<InetAddress> res = new ArrayList<InetAddress>();
for (String val : vals) {
@ -476,21 +423,23 @@ public class APIClient {
return null;
}
private TabularDataSupport getSnapshotData(String key, JsonArray arr) {
TabularDataSupport data = new TabularDataSupport(SnapshotDetailsTabularData.TABULAR_TYPE);
private TabularDataSupport getSnapshotData(String ks, JsonArray arr) {
TabularDataSupport data = new TabularDataSupport(
SnapshotDetailsTabularData.TABULAR_TYPE);
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
if (obj.containsKey("ks") && obj.containsKey("cf")) {
SnapshotDetailsTabularData.from(key, obj.getString("ks"), obj.getString("cf"), obj.getJsonNumber("total").longValue(),
obj.getJsonNumber("live").longValue(), data);
if (obj.containsKey("key") && obj.containsKey("cf")) {
SnapshotDetailsTabularData.from(obj.getString("key"), ks,
obj.getString("cf"), obj.getInt("total"),
obj.getInt("live"), data);
}
}
return data;
}
public Map<String, TabularData> getMapStringSnapshotTabularDataValue(String string,
MultivaluedMap<String, String> queryParams) {
public Map<String, TabularData> getMapStringSnapshotTabularDataValue(
String string, MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
@ -524,7 +473,8 @@ public class APIClient {
for (int i = 0; i < arr.size(); i++) {
try {
obj = arr.getJsonObject(i);
res.put(InetAddress.getByName(obj.getString("key")), Float.parseFloat(obj.getString("value")));
res.put(InetAddress.getByName(obj.getString("key")),
Float.parseFloat(obj.getString("value")));
} catch (UnknownHostException e) {
logger.warning("Bad formatted address " + obj.getString("key"));
}
@ -536,7 +486,8 @@ public class APIClient {
return getMapInetAddressFloatValue(string, null);
}
public Map<String, Long> getMapStringLongValue(String string, MultivaluedMap<String, String> queryParams) {
public Map<String, Long> getMapStringLongValue(String string,
MultivaluedMap<String, String> queryParams) {
Map<String, Long> res = new HashMap<String, Long>();
JsonReader reader = getReader(string, queryParams);
@ -554,7 +505,8 @@ public class APIClient {
return getMapStringLongValue(string, null);
}
public long[] getLongArrValue(String string, MultivaluedMap<String, String> queryParams) {
public long[] getLongArrValue(String string,
MultivaluedMap<String, String> queryParams) {
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
long[] res = new long[arr.size()];
@ -569,7 +521,8 @@ public class APIClient {
return getLongArrValue(string, null);
}
public Map<String, Integer> getMapStringIntegerValue(String string, MultivaluedMap<String, String> queryParams) {
public Map<String, Integer> getMapStringIntegerValue(String string,
MultivaluedMap<String, String> queryParams) {
Map<String, Integer> res = new HashMap<String, Integer>();
JsonReader reader = getReader(string, queryParams);
@ -587,7 +540,8 @@ public class APIClient {
return getMapStringIntegerValue(string, null);
}
public int[] getIntArrValue(String string, MultivaluedMap<String, String> queryParams) {
public int[] getIntArrValue(String string,
MultivaluedMap<String, String> queryParams) {
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
int[] res = new int[arr.size()];
@ -602,7 +556,8 @@ public class APIClient {
return getIntArrValue(string, null);
}
public Map<String, Long> getListMapStringLongValue(String string, MultivaluedMap<String, String> queryParams) {
public Map<String, Long> getListMapStringLongValue(String string,
MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
@ -619,7 +574,7 @@ public class APIClient {
if (obj.get(k) instanceof JsonString) {
key = obj.getString(k);
} else {
val = obj.getJsonNumber(k).longValue();
val = obj.getInt(k);
}
}
if (val > 0 && !key.equals("")) {
@ -635,7 +590,8 @@ public class APIClient {
return getListMapStringLongValue(string, null);
}
public JsonArray getJsonArray(String string, MultivaluedMap<String, String> queryParams) {
public JsonArray getJsonArray(String string,
MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
@ -649,7 +605,8 @@ public class APIClient {
return getJsonArray(string, null);
}
public List<Map<String, String>> getListMapStrValue(String string, MultivaluedMap<String, String> queryParams) {
public List<Map<String, String>> getListMapStrValue(String string,
MultivaluedMap<String, String> queryParams) {
JsonArray arr = getJsonArray(string, queryParams);
List<Map<String, String>> res = new ArrayList<Map<String, String>>();
for (int i = 0; i < arr.size(); i++) {
@ -667,36 +624,61 @@ public class APIClient {
return null;
}
public JsonObject getJsonObj(String string, MultivaluedMap<String, String> queryParams, long duration) {
public JsonObject getJsonObj(String string,
MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
String key = getCacheKey(string, queryParams, duration);
JsonObject res = getJsonObjectFromCache(key, duration);
if (res != null) {
return res;
}
JsonReader reader = getReader(string, queryParams);
res = reader.readObject();
JsonObject res = reader.readObject();
reader.close();
if (duration > 0) {
cache.put(key, new CacheEntry(res));
return res;
}
public HistogramValues getHistogramValue(String url,
MultivaluedMap<String, String> queryParams) {
HistogramValues res = new HistogramValues();
JsonObject obj = getJsonObj(url, queryParams);
res.count = obj.getJsonNumber("count").longValue();
res.max = obj.getJsonNumber("max").longValue();
res.min = obj.getJsonNumber("min").longValue();
res.sum = obj.getJsonNumber("sum").longValue();
res.variance = obj.getJsonNumber("variance").doubleValue();
res.mean = obj.getJsonNumber("mean").doubleValue();
JsonArray arr = obj.getJsonArray("sample");
if (arr != null) {
res.sample = new long[arr.size()];
for (int i = 0; i < arr.size(); i++) {
res.sample[i] = arr.getJsonNumber(i).longValue();
}
}
return res;
}
public JsonObject getJsonObj(String string, MultivaluedMap<String, String> queryParams) {
return getJsonObj(string, queryParams, 0);
public HistogramValues getHistogramValue(String url) {
return getHistogramValue(url, null);
}
public long[] getEstimatedHistogramAsLongArrValue(String string, MultivaluedMap<String, String> queryParams) {
public EstimatedHistogram getEstimatedHistogram(String string,
MultivaluedMap<String, String> queryParams, long duration) {
String key = getCacheKey(string, queryParams, duration);
EstimatedHistogram res = getEstimatedHistogramFromCache(key, duration);
if (res != null) {
return res;
}
res = new EstimatedHistogram(getEstimatedHistogramAsLongArrValue(string, queryParams));
if (duration > 0) {
cache.put(key, new CacheEntry(res));
}
return res;
}
public long[] getEstimatedHistogramAsLongArrValue(String string,
MultivaluedMap<String, String> queryParams) {
JsonObject obj = getJsonObj(string, queryParams);
JsonArray arr = obj.getJsonArray("buckets");
if (arr == null) {
return new long[0];
}
long res[] = new long[arr.size()];
for (int i = 0; i < arr.size(); i++) {
for (int i = 0; i< arr.size(); i++) {
res[i] = arr.getJsonNumber(i).longValue();
}
return res;
@ -705,37 +687,4 @@ public class APIClient {
public long[] getEstimatedHistogramAsLongArrValue(String string) {
return getEstimatedHistogramAsLongArrValue(string, null);
}
public Map<String, Double> getMapStringDouble(String string, MultivaluedMap<String, String> queryParams) {
if (string.equals("")) {
return null;
}
JsonReader reader = getReader(string, queryParams);
JsonArray arr = reader.readArray();
Map<String, Double> map = new HashMap<String, Double>();
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
Iterator<String> it = obj.keySet().iterator();
String key = "";
double val = -1;
while (it.hasNext()) {
String k = it.next();
if (obj.get(k) instanceof JsonString) {
key = obj.getString(k);
} else {
val = obj.getJsonNumber(k).doubleValue();
}
}
if (!key.equals("")) {
map.put(key, val);
}
}
reader.close();
return map;
}
public Map<String, Double> getMapStringDouble(String string) {
return getMapStringDouble(string, null);
}
}

View File

@ -1,4 +1,4 @@
package com.scylladb.jmx.api;
package com.cloudius.urchin.api;
import java.io.File;
import java.io.FileInputStream;
@ -30,22 +30,23 @@ import org.yaml.snakeyaml.Yaml;
*/
public class APIConfig {
private String address = "localhost";
private String port = "10000";
static String address = "localhost";
static String port = "10000";
public String getAddress() {
public static String getAddress() {
return address;
}
public String getPort() {
public static String getPort() {
return port;
}
public String getBaseUrl() {
return "http://" + address + ":" + port;
public static String getBaseUrl() {
return "http://" + address + ":"
+ port;
}
private void readFile(String name) {
public static void readFile(String name) {
System.out.println("Using config file: " + name);
InputStream input;
try {
@ -60,7 +61,7 @@ public class APIConfig {
address = (String) map.get("api_address");
}
if (map.containsKey("api_port")) {
port = map.get("api_port").toString();
port = (String) map.get("api_port").toString();
}
} catch (FileNotFoundException e) {
System.err.println("fail reading from config file: " + name);
@ -73,7 +74,7 @@ public class APIConfig {
return varTmpDir.exists();
}
private boolean loadIfExists(String path, String name) {
public static boolean loadIfExists(String path, String name) {
if (path == null) {
return false;
}
@ -83,21 +84,24 @@ public class APIConfig {
readFile(path + name);
return true;
}
/**
* setConfig load the JMX proxy configuration The configuration hierarchy is
* as follow: Command line argument takes precedence over everything Then
* configuration file in the command line (command line argument can replace
* specific values in it. Then SCYLLA_CONF/scylla.yaml Then
* SCYLLA_HOME/conf/scylla.yaml Then conf/scylla.yaml Then the default
* values With file configuration, to make it clearer what is been used,
* only one file will be chosen with the highest precedence
* setConfig load the JMX proxy configuration
* The configuration hierarchy is as follow:
* Command line argument takes precedence over everything
* Then configuration file in the command line (command line
* argument can replace specific values in it.
* Then SCYLLA_CONF/scylla.yaml
* Then SCYLLA_HOME/conf/scylla.yaml
* Then conf/scylla.yaml
* Then the default values
* With file configuration, to make it clearer what is been used, only
* one file will be chosen with the highest precedence
*/
public APIConfig() {
if (!System.getProperty("apiconfig", "").equals("")) {
public static void setConfig() {
if (!System.getProperty("apiconfig","").equals("")) {
readFile(System.getProperty("apiconfig"));
} else if (!loadIfExists(System.getenv("SCYLLA_CONF"), "/scylla.yaml")
&& !loadIfExists(System.getenv("SCYLLA_HOME"), "/conf/scylla.yaml")) {
} else if (!loadIfExists(System.getenv("SCYLLA_CONF"), "/scylla.yaml") &&
!loadIfExists(System.getenv("SCYLLA_HOME"), "/conf/scylla.yaml")) {
loadIfExists("", "conf/scylla.yaml");
}

View File

@ -19,16 +19,15 @@
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
package com.scylladb.jmx.api;
package com.cloudius.urchin.api;
import com.cloudius.urchin.utils.EstimatedHistogram;
import jakarta.json.JsonObject;
public class CacheEntry {
long time;
Object value;
class CacheEntry {
private long time;
private Object value;
public CacheEntry(Object res) {
CacheEntry(Object res) {
time = System.currentTimeMillis();
this.value = res;
}
@ -41,7 +40,7 @@ class CacheEntry {
return (String) value;
}
public JsonObject jsonObject() {
return (JsonObject) value;
public EstimatedHistogram getEstimatedHistogram() {
return (EstimatedHistogram)value;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2015 Cloudius Systems
*/
package com.cloudius.urchin.main;
import com.cloudius.urchin.api.APIConfig;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.gms.FailureDetector;
import org.apache.cassandra.locator.EndpointSnitchInfo;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.CacheService;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
public class Main {
public static void main(String[] args) throws Exception {
APIConfig.setConfig();
System.out.println("Connecting to " + APIConfig.getBaseUrl());
System.out.println("Starting the JMX server");
StorageService.getInstance();
StorageProxy.getInstance();
MessagingService.getInstance();
CommitLog.getInstance();
Gossiper.getInstance();
EndpointSnitchInfo.getInstance();
FailureDetector.getInstance();
ColumnFamilyStore.register_mbeans();
CacheService.getInstance();
CompactionManager.getInstance();
Thread.sleep(Long.MAX_VALUE);
}
}

View File

@ -0,0 +1,416 @@
package com.cloudius.urchin.metrics;
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
import java.util.concurrent.TimeUnit;
import com.yammer.metrics.core.APIMetricsRegistry;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.APITimer;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.reporting.JmxReporter;
public class APIMetrics {
private static final APIMetricsRegistry DEFAULT_REGISTRY = new APIMetricsRegistry();
private static final Thread SHUTDOWN_HOOK = new Thread() {
public void run() {
JmxReporter.shutdownDefault();
}
};
static {
JmxReporter.startDefault(DEFAULT_REGISTRY);
Runtime.getRuntime().addShutdownHook(SHUTDOWN_HOOK);
}
private APIMetrics() { /* unused */
}
/**
* Given a new {@link com.yammer.metrics.core.Gauge}, registers it under the
* given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param metric
* the metric
* @param <T>
* the type of the value returned by the metric
* @return {@code metric}
*/
public static <T> Gauge<T> newGauge(Class<?> klass, String name,
Gauge<T> metric) {
return DEFAULT_REGISTRY.newGauge(klass, name, metric);
}
/**
* Given a new {@link com.yammer.metrics.core.Gauge}, registers it under the
* given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param metric
* the metric
* @param <T>
* the type of the value returned by the metric
* @return {@code metric}
*/
public static <T> Gauge<T> newGauge(Class<?> klass, String name,
String scope, Gauge<T> metric) {
return DEFAULT_REGISTRY.newGauge(klass, name, scope, metric);
}
/**
* Given a new {@link com.yammer.metrics.core.Gauge}, registers it under the
* given metric name.
*
* @param metricName
* the name of the metric
* @param metric
* the metric
* @param <T>
* the type of the value returned by the metric
* @return {@code metric}
*/
public static <T> Gauge<T> newGauge(MetricName metricName, Gauge<T> metric) {
return DEFAULT_REGISTRY.newGauge(metricName, metric);
}
/**
* Creates a new {@link com.yammer.metrics.core.Counter} and registers it
* under the given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @return a new {@link com.yammer.metrics.core.Counter}
*/
public static Counter newCounter(String url, Class<?> klass, String name) {
return DEFAULT_REGISTRY.newCounter(url, klass, name);
}
/**
* Creates a new {@link com.yammer.metrics.core.Counter} and registers it
* under the given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @return a new {@link com.yammer.metrics.core.Counter}
*/
public static Counter newCounter(String url, Class<?> klass, String name,
String scope) {
return DEFAULT_REGISTRY.newCounter(url, klass, name, scope);
}
/**
* Creates a new {@link com.yammer.metrics.core.Counter} and registers it
* under the given metric name.
*
* @param metricName
* the name of the metric
* @return a new {@link com.yammer.metrics.core.Counter}
*/
public static Counter newCounter(String url, MetricName metricName) {
return DEFAULT_REGISTRY.newCounter(url, metricName);
}
/**
* Creates a new {@link com.yammer.metrics.core.Histogram} and registers it
* under the given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param biased
* whether or not the histogram should be biased
* @return a new {@link com.yammer.metrics.core.Histogram}
*/
public static Histogram newHistogram(String url, Class<?> klass,
String name, boolean biased) {
return DEFAULT_REGISTRY.newHistogram(url, klass, name, biased);
}
/**
* Creates a new {@link com.yammer.metrics.core.Histogram} and registers it
* under the given class, name, and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param biased
* whether or not the histogram should be biased
* @return a new {@link com.yammer.metrics.core.Histogram}
*/
public static Histogram newHistogram(String url, Class<?> klass,
String name, String scope, boolean biased) {
return DEFAULT_REGISTRY.newHistogram(url, klass, name, scope, biased);
}
/**
* Creates a new {@link com.yammer.metrics.core.Histogram} and registers it
* under the given metric name.
*
* @param metricName
* the name of the metric
* @param biased
* whether or not the histogram should be biased
* @return a new {@link com.yammer.metrics.core.Histogram}
*/
public static Histogram newHistogram(String url, MetricName metricName,
boolean biased) {
return DEFAULT_REGISTRY.newHistogram(url, metricName, biased);
}
/**
* Creates a new non-biased {@link com.yammer.metrics.core.Histogram} and
* registers it under the given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @return a new {@link com.yammer.metrics.core.Histogram}
*/
public static Histogram newHistogram(String url, Class<?> klass, String name) {
return DEFAULT_REGISTRY.newHistogram(url, klass, name);
}
/**
* Creates a new non-biased {@link com.yammer.metrics.core.Histogram} and
* registers it under the given class, name, and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @return a new {@link com.yammer.metrics.core.Histogram}
*/
public static Histogram newHistogram(String url, Class<?> klass,
String name, String scope) {
return DEFAULT_REGISTRY.newHistogram(url, klass, name, scope);
}
/**
* Creates a new non-biased {@link com.yammer.metrics.core.Histogram} and
* registers it under the given metric name.
*
* @param metricName
* the name of the metric
* @return a new {@link com.yammer.metrics.core.Histogram}
*/
public static Histogram newHistogram(String url, MetricName metricName) {
return newHistogram(url, metricName, false);
}
/**
* Creates a new {@link com.yammer.metrics.core.Meter} and registers it
* under the given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link com.yammer.metrics.core.Meter}
*/
public static Meter newMeter(String url, Class<?> klass, String name,
String eventType, TimeUnit unit) {
return DEFAULT_REGISTRY.newMeter(url, klass, name, eventType, unit);
}
/**
* Creates a new {@link com.yammer.metrics.core.Meter} and registers it
* under the given class, name, and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link com.yammer.metrics.core.Meter}
*/
public static Meter newMeter(String url, Class<?> klass, String name,
String scope, String eventType, TimeUnit unit) {
return DEFAULT_REGISTRY.newMeter(url, klass, name, scope, eventType,
unit);
}
/**
* Creates a new {@link com.yammer.metrics.core.Meter} and registers it
* under the given metric name.
*
* @param metricName
* the name of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link com.yammer.metrics.core.Meter}
*/
public static Meter newMeter(String url, MetricName metricName,
String eventType, TimeUnit unit) {
return DEFAULT_REGISTRY.newMeter(url, metricName, eventType, unit);
}
/**
* Creates a new {@link com.yammer.metrics.core.Meter} and registers it
* under the given metric name.
*
* @param metricName
* the name of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link com.yammer.metrics.core.Meter}
*/
public static Meter newSettableMeter(MetricName metricName,
String eventType, TimeUnit unit) {
return DEFAULT_REGISTRY.newSettableMeter(metricName, eventType, unit);
}
/**
* Creates a new {@link com.yammer.metrics.core.APITimer} and registers it
* under the given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param durationUnit
* the duration scale unit of the new timer
* @param rateUnit
* the rate scale unit of the new timer
* @return a new {@link com.yammer.metrics.core.APITimer}
*/
public static Timer newTimer(String url, Class<?> klass, String name,
TimeUnit durationUnit, TimeUnit rateUnit) {
return DEFAULT_REGISTRY.newTimer(url, klass, name, durationUnit, rateUnit);
}
/**
* Creates a new {@link com.yammer.metrics.core.APITimer} and registers it
* under the given class and name, measuring elapsed time in milliseconds
* and invocations per second.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @return a new {@link com.yammer.metrics.core.APITimer}
*/
public static Timer newTimer(String url, Class<?> klass, String name) {
return DEFAULT_REGISTRY.newTimer(url, klass, name);
}
/**
* Creates a new {@link com.yammer.metrics.core.APITimer} and registers it
* under the given class, name, and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param durationUnit
* the duration scale unit of the new timer
* @param rateUnit
* the rate scale unit of the new timer
* @return a new {@link com.yammer.metrics.core.APITimer}
*/
public static Timer newTimer(String url, Class<?> klass, String name, String scope,
TimeUnit durationUnit, TimeUnit rateUnit) {
return DEFAULT_REGISTRY.newTimer(url, klass, name, scope, durationUnit,
rateUnit);
}
/**
* Creates a new {@link com.yammer.metrics.core.APITimer} and registers it
* under the given class, name, and scope, measuring elapsed time in
* milliseconds and invocations per second.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @return a new {@link com.yammer.metrics.core.APITimer}
*/
public static Timer newTimer(String url, Class<?> klass, String name, String scope) {
return DEFAULT_REGISTRY.newTimer(url, klass, name, scope);
}
/**
* Creates a new {@link com.yammer.metrics.core.APITimer} and registers it
* under the given metric name.
*
* @param metricName
* the name of the metric
* @param durationUnit
* the duration scale unit of the new timer
* @param rateUnit
* the rate scale unit of the new timer
* @return a new {@link com.yammer.metrics.core.APITimer}
*/
public static Timer newTimer(String url, MetricName metricName, TimeUnit durationUnit,
TimeUnit rateUnit) {
return DEFAULT_REGISTRY.newTimer(url, metricName, durationUnit, rateUnit);
}
/**
* Returns the (static) default registry.
*
* @return the metrics registry
*/
public static APIMetricsRegistry defaultRegistry() {
return DEFAULT_REGISTRY;
}
/**
* Shuts down all thread pools for the default registry.
*/
public static void shutdown() {
DEFAULT_REGISTRY.shutdown();
JmxReporter.shutdownDefault();
Runtime.getRuntime().removeShutdownHook(SHUTDOWN_HOOK);
}
}

View File

@ -15,10 +15,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
package com.cloudius.urchin.metrics;
import com.yammer.metrics.core.MetricName;
/**
* MetricNameFactory that generates default MetricName of metrics.
@ -38,14 +43,19 @@ public class DefaultNameFactory implements MetricNameFactory {
this.scope = scope;
}
@Override
public ObjectName createMetricName(String metricName) throws MalformedObjectNameException {
public MetricName createMetricName(String metricName) {
return createMetricName(type, metricName, scope);
}
public static ObjectName createMetricName(String type, String name, String scope)
throws MalformedObjectNameException {
StringBuilder nameBuilder = new StringBuilder();
public static MetricName createMetricName(String type, String metricName,
String scope) {
return new MetricName(GROUP_NAME, type, metricName, scope,
createDefaultMBeanName(type, metricName, scope));
}
protected static String createDefaultMBeanName(String type, String name,
String scope) {
final StringBuilder nameBuilder = new StringBuilder();
nameBuilder.append(GROUP_NAME);
nameBuilder.append(":type=");
nameBuilder.append(type);
@ -57,6 +67,6 @@ public class DefaultNameFactory implements MetricNameFactory {
nameBuilder.append(",name=");
nameBuilder.append(name);
}
return new ObjectName(nameBuilder.toString());
return nameBuilder.toString();
}
}

View File

@ -15,26 +15,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
/**
* Simplified version of {@link Metrics} naming factory paradigm, simply
* generating {@link ObjectName} and nothing more.
*
* @author calle
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
public interface MetricNameFactory {
package com.cloudius.urchin.metrics;
import com.yammer.metrics.core.MetricName;
public interface MetricNameFactory
{
/**
* Create a qualified name from given metric name.
*
* @param metricName
* part of qualified name.
* @param metricName part of qualified name.
* @return new String with given metric name.
* @throws MalformedObjectNameException
*/
ObjectName createMetricName(String metricName) throws MalformedObjectNameException;
MetricName createMetricName(String metricName);
}

View File

@ -0,0 +1,312 @@
/*
* 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.
*/
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
package com.cloudius.urchin.utils;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLongArray;
import com.google.common.base.Objects;
import org.slf4j.Logger;
public class EstimatedHistogram {
/**
* The series of values to which the counts in `buckets` correspond: 1, 2,
* 3, 4, 5, 6, 7, 8, 10, 12, 14, 17, 20, etc. Thus, a `buckets` of [0, 0, 1,
* 10] would mean we had seen one value of 3 and 10 values of 4.
*
* The series starts at 1 and grows by 1.2 each time (rounding and removing
* duplicates). It goes from 1 to around 36M by default (creating 90+1
* buckets), which will give us timing resolution from microseconds to 36
* seconds, with less precision as the numbers get larger.
*
* Each bucket represents values from (previous bucket offset, current
* offset].
*/
private final long[] bucketOffsets;
// buckets is one element longer than bucketOffsets -- the last element is
// values greater than the last offset
final AtomicLongArray buckets;
public EstimatedHistogram() {
this(90);
}
public EstimatedHistogram(int bucketCount) {
bucketOffsets = newOffsets(bucketCount);
buckets = new AtomicLongArray(bucketOffsets.length + 1);
}
public EstimatedHistogram(long[] offsets, long[] bucketData) {
assert bucketData.length == offsets.length + 1;
bucketOffsets = offsets;
buckets = new AtomicLongArray(bucketData);
}
public EstimatedHistogram(long[] bucketData) {
bucketOffsets = newOffsets(bucketData.length - 1);
buckets = new AtomicLongArray(bucketData);
}
private static long[] newOffsets(int size) {
long[] result = new long[size];
long last = 1;
result[0] = last;
for (int i = 1; i < size; i++) {
long next = Math.round(last * 1.2);
if (next == last)
next++;
result[i] = next;
last = next;
}
return result;
}
/**
* @return the histogram values corresponding to each bucket index
*/
public long[] getBucketOffsets() {
return bucketOffsets;
}
/**
* Increments the count of the bucket closest to n, rounding UP.
*
* @param n
*/
public void add(long n) {
int index = Arrays.binarySearch(bucketOffsets, n);
if (index < 0) {
// inexact match, take the first bucket higher than n
index = -index - 1;
}
// else exact match; we're good
buckets.incrementAndGet(index);
}
/**
* @return the count in the given bucket
*/
long get(int bucket) {
return buckets.get(bucket);
}
/**
* @param reset
* zero out buckets afterwards if true
* @return a long[] containing the current histogram buckets
*/
public long[] getBuckets(boolean reset) {
final int len = buckets.length();
long[] rv = new long[len];
if (reset)
for (int i = 0; i < len; i++)
rv[i] = buckets.getAndSet(i, 0L);
else
for (int i = 0; i < len; i++)
rv[i] = buckets.get(i);
return rv;
}
/**
* @return the smallest value that could have been added to this histogram
*/
public long min() {
for (int i = 0; i < buckets.length(); i++) {
if (buckets.get(i) > 0)
return i == 0 ? 0 : 1 + bucketOffsets[i - 1];
}
return 0;
}
/**
* @return the largest value that could have been added to this histogram.
* If the histogram overflowed, returns Long.MAX_VALUE.
*/
public long max() {
int lastBucket = buckets.length() - 1;
if (buckets.get(lastBucket) > 0)
return Long.MAX_VALUE;
for (int i = lastBucket - 1; i >= 0; i--) {
if (buckets.get(i) > 0)
return bucketOffsets[i];
}
return 0;
}
/**
* @param percentile
* @return estimated value at given percentile
*/
public long percentile(double percentile) {
assert percentile >= 0 && percentile <= 1.0;
int lastBucket = buckets.length() - 1;
if (buckets.get(lastBucket) > 0)
throw new IllegalStateException(
"Unable to compute when histogram overflowed");
long pcount = (long) Math.floor(count() * percentile);
if (pcount == 0)
return 0;
long elements = 0;
for (int i = 0; i < lastBucket; i++) {
elements += buckets.get(i);
if (elements >= pcount)
return bucketOffsets[i];
}
return 0;
}
/**
* @return the mean histogram value (average of bucket offsets, weighted by
* count)
* @throws IllegalStateException
* if any values were greater than the largest bucket threshold
*/
public long mean() {
int lastBucket = buckets.length() - 1;
if (buckets.get(lastBucket) > 0)
throw new IllegalStateException(
"Unable to compute ceiling for max when histogram overflowed");
long elements = 0;
long sum = 0;
for (int i = 0; i < lastBucket; i++) {
long bCount = buckets.get(i);
elements += bCount;
sum += bCount * bucketOffsets[i];
}
return (long) Math.ceil((double) sum / elements);
}
/**
* @return the total number of non-zero values
*/
public long count() {
long sum = 0L;
for (int i = 0; i < buckets.length(); i++)
sum += buckets.get(i);
return sum;
}
/**
* @return true if this histogram has overflowed -- that is, a value larger
* than our largest bucket could bound was added
*/
public boolean isOverflowed() {
return buckets.get(buckets.length() - 1) > 0;
}
/**
* log.debug() every record in the histogram
*
* @param log
*/
public void log(Logger log) {
// only print overflow if there is any
int nameCount;
if (buckets.get(buckets.length() - 1) == 0)
nameCount = buckets.length() - 1;
else
nameCount = buckets.length();
String[] names = new String[nameCount];
int maxNameLength = 0;
for (int i = 0; i < nameCount; i++) {
names[i] = nameOfRange(bucketOffsets, i);
maxNameLength = Math.max(maxNameLength, names[i].length());
}
// emit log records
String formatstr = "%" + maxNameLength + "s: %d";
for (int i = 0; i < nameCount; i++) {
long count = buckets.get(i);
// sort-of-hack to not print empty ranges at the start that are only
// used to demarcate the
// first populated range. for code clarity we don't omit this record
// from the maxNameLength
// calculation, and accept the unnecessary whitespace prefixes that
// will occasionally occur
if (i == 0 && count == 0)
continue;
log.debug(String.format(formatstr, names[i], count));
}
}
private static String nameOfRange(long[] bucketOffsets, int index) {
StringBuilder sb = new StringBuilder();
appendRange(sb, bucketOffsets, index);
return sb.toString();
}
private static void appendRange(StringBuilder sb, long[] bucketOffsets,
int index) {
sb.append("[");
if (index == 0)
if (bucketOffsets[0] > 0)
// by original definition, this histogram is for values greater
// than zero only;
// if values of 0 or less are required, an entry of lb-1 must be
// inserted at the start
sb.append("1");
else
sb.append("-Inf");
else
sb.append(bucketOffsets[index - 1] + 1);
sb.append("..");
if (index == bucketOffsets.length)
sb.append("Inf");
else
sb.append(bucketOffsets[index]);
sb.append("]");
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof EstimatedHistogram))
return false;
EstimatedHistogram that = (EstimatedHistogram) o;
return Arrays.equals(getBucketOffsets(), that.getBucketOffsets())
&& Arrays.equals(getBuckets(false), that.getBuckets(false));
}
@Override
public int hashCode() {
return Objects.hashCode(getBucketOffsets(), getBuckets(false));
}
}

View File

@ -22,59 +22,71 @@
* Modified by Cloudius Systems
*/
package com.scylladb.jmx.api.utils;
package com.cloudius.urchin.utils;
import java.io.File;
import java.io.*;
import java.text.DecimalFormat;
public class FileUtils {
public class FileUtils
{
private static final double KB = 1024d;
private static final double MB = 1024 * 1024d;
private static final double GB = 1024 * 1024 * 1024d;
private static final double TB = 1024 * 1024 * 1024 * 1024d;
private static final double MB = 1024*1024d;
private static final double GB = 1024*1024*1024d;
private static final double TB = 1024*1024*1024*1024d;
private static final DecimalFormat df = new DecimalFormat("#.##");
public static String stringifyFileSize(double value) {
public static String stringifyFileSize(double value)
{
double d;
if (value >= TB) {
if ( value >= TB )
{
d = value / TB;
String val = df.format(d);
return val + " TB";
} else if (value >= GB) {
}
else if ( value >= GB )
{
d = value / GB;
String val = df.format(d);
return val + " GB";
} else if (value >= MB) {
}
else if ( value >= MB )
{
d = value / MB;
String val = df.format(d);
return val + " MB";
} else if (value >= KB) {
}
else if ( value >= KB )
{
d = value / KB;
String val = df.format(d);
return val + " KB";
} else {
}
else
{
String val = df.format(value);
return val + " bytes";
}
}
/**
* Get the size of a directory in bytes
*
* @param directory
* The directory for which we need size.
* @param directory The directory for which we need size.
* @return The size of the directory
*/
public static long folderSize(File directory) {
public static long folderSize(File directory)
{
long length = 0;
for (File file : directory.listFiles()) {
if (file.isFile()) {
for (File file : directory.listFiles())
{
if (file.isFile())
length += file.length();
} else {
else
length += folderSize(file);
}
}
return length;
}
}
}

View File

@ -22,42 +22,47 @@
* Modified by Cloudius Systems
*/
package com.scylladb.jmx.api.utils;
package com.cloudius.urchin.utils;
import com.google.common.base.Objects;
public class Pair<T1, T2> {
public class Pair<T1, T2>
{
public final T1 left;
public final T2 right;
protected Pair(T1 left, T2 right) {
protected Pair(T1 left, T2 right)
{
this.left = left;
this.right = right;
}
@Override
public final int hashCode() {
public final int hashCode()
{
int hashCode = 31 + (left == null ? 0 : left.hashCode());
return 31 * hashCode + (right == null ? 0 : right.hashCode());
return 31*hashCode + (right == null ? 0 : right.hashCode());
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Pair)) {
public final boolean equals(Object o)
{
if(!(o instanceof Pair))
return false;
}
@SuppressWarnings("rawtypes")
Pair that = (Pair) o;
Pair that = (Pair)o;
// handles nulls properly
return Objects.equal(left, that.left) && Objects.equal(right, that.right);
}
@Override
public String toString() {
public String toString()
{
return "(" + left + "," + right + ")";
}
public static <X, Y> Pair<X, Y> create(X x, Y y) {
public static <X, Y> Pair<X, Y> create(X x, Y y)
{
return new Pair<X, Y>(x, y);
}
}

View File

@ -0,0 +1,62 @@
package com.cloudius.urchin.utils;
/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
* RecentEstimatedHistogram In the (deprecated) 'recent' functionality, each
* call to get the values cleans the value.
*
* The RecentEstimatedHistogram support recent call to EstimatedHistogram.
* It holds the latest total values and a call to getBuckets return the delta.
*
*/
public class RecentEstimatedHistogram extends EstimatedHistogram {
public RecentEstimatedHistogram() {
}
public RecentEstimatedHistogram(int bucketCount) {
super(bucketCount);
}
public RecentEstimatedHistogram(long[] offsets, long[] bucketData) {
super(offsets, bucketData);
}
/**
* Set the current buckets to new value and return the delta from the last
* getBuckets call
*
* @param bucketData
* - new bucket value
* @return a long[] containing the current histogram difference buckets
*/
public long[] getBuckets(long[] bucketData) {
final int len = buckets.length();
long[] rv = new long[len];
for (int i = 0; i < len; i++) {
rv[i] = bucketData[i];
rv[i] -= buckets.getAndSet(i, bucketData[i]);
}
return rv;
}
}

View File

@ -20,27 +20,21 @@
*
* Modified by Cloudius Systems
*/
package com.scylladb.jmx.api.utils;
package com.cloudius.urchin.utils;
import java.util.Map;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import javax.management.openmbean.*;
import com.google.common.base.Throwables;
public class SnapshotDetailsTabularData {
private static final String[] ITEM_NAMES = new String[] { "Snapshot name", "Keyspace name", "Column family name",
"True size", "Size on disk" };
private static final String[] ITEM_NAMES = new String[] { "Snapshot name",
"Keyspace name", "Column family name", "True size", "Size on disk" };
private static final String[] ITEM_DESCS = new String[] { "snapshot_name", "keyspace_name", "columnfamily_name",
"TrueDiskSpaceUsed", "TotalDiskSpaceUsed" };
private static final String[] ITEM_DESCS = new String[] { "snapshot_name",
"keyspace_name", "columnfamily_name", "TrueDiskSpaceUsed",
"TotalDiskSpaceUsed" };
private static final String TYPE_NAME = "SnapshotDetails";
@ -54,22 +48,28 @@ public class SnapshotDetailsTabularData {
static {
try {
ITEM_TYPES = new OpenType[] { SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.STRING,
SimpleType.STRING };
ITEM_TYPES = new OpenType[] { SimpleType.STRING, SimpleType.STRING,
SimpleType.STRING, SimpleType.STRING, SimpleType.STRING };
COMPOSITE_TYPE = new CompositeType(TYPE_NAME, ROW_DESC, ITEM_NAMES, ITEM_DESCS, ITEM_TYPES);
COMPOSITE_TYPE = new CompositeType(TYPE_NAME, ROW_DESC, ITEM_NAMES,
ITEM_DESCS, ITEM_TYPES);
TABULAR_TYPE = new TabularType(TYPE_NAME, ROW_DESC, COMPOSITE_TYPE, ITEM_NAMES);
TABULAR_TYPE = new TabularType(TYPE_NAME, ROW_DESC, COMPOSITE_TYPE,
ITEM_NAMES);
} catch (OpenDataException e) {
throw Throwables.propagate(e);
}
}
public static void from(final String snapshot, final String ks, final String cf,
Map.Entry<String, Pair<Long, Long>> snapshotDetail, TabularDataSupport result) {
public static void from(final String snapshot, final String ks,
final String cf,
Map.Entry<String, Pair<Long, Long>> snapshotDetail,
TabularDataSupport result) {
try {
final String totalSize = FileUtils.stringifyFileSize(snapshotDetail.getValue().left);
final String liveSize = FileUtils.stringifyFileSize(snapshotDetail.getValue().right);
final String totalSize = FileUtils.stringifyFileSize(snapshotDetail
.getValue().left);
final String liveSize = FileUtils.stringifyFileSize(snapshotDetail
.getValue().right);
result.put(new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES,
new Object[] { snapshot, ks, cf, liveSize, totalSize }));
} catch (OpenDataException e) {
@ -77,8 +77,8 @@ public class SnapshotDetailsTabularData {
}
}
public static void from(final String snapshot, final String ks, final String cf, long total, long live,
TabularDataSupport result) {
public static void from(final String snapshot, final String ks,
final String cf, long total, long live, TabularDataSupport result) {
try {
final String totalSize = FileUtils.stringifyFileSize(total);
final String liveSize = FileUtils.stringifyFileSize(live);

View File

@ -1,77 +0,0 @@
/*
* Copyright 2015 Cloudius Systems
*/
package com.scylladb.jmx.main;
import static java.lang.management.ManagementFactory.getPlatformMBeanServer;
import static java.util.Arrays.asList;
import java.lang.reflect.Constructor;
import javax.management.MBeanServer;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.gms.FailureDetector;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.locator.EndpointSnitchInfo;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.CacheService;
import org.apache.cassandra.service.GCInspector;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.StreamManager;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.api.APIConfig;
import com.scylladb.jmx.metrics.APIMBean;
public class Main {
private static APIConfig config;
private static APIClient client;
public static synchronized APIConfig getApiConfig() {
if (config == null) {
config = new APIConfig();
}
return config;
}
public static synchronized APIClient getApiClient() {
if (client == null) {
client = new APIClient(getApiConfig());
}
return client;
}
public static void main(String[] args) throws Exception {
System.out.printf("Java %s%n", System.getProperty("java.version"));
System.out.printf("Connecting to %s%n", getApiConfig().getBaseUrl());
System.out.println("Starting the JMX server");
MBeanServer server = getPlatformMBeanServer();
for (Class<? extends APIMBean> clazz : asList(StorageService.class, StorageProxy.class, MessagingService.class,
CommitLog.class, Gossiper.class, EndpointSnitchInfo.class, FailureDetector.class, CacheService.class,
CompactionManager.class, GCInspector.class, StreamManager.class)) {
Constructor<? extends APIMBean> c = clazz.getDeclaredConstructor(APIClient.class);
APIMBean m = c.newInstance(getApiClient());
server.registerMBean(m, null);
}
try {
// forces check for dynamically created mbeans
server.queryNames(null, null);
} catch (IllegalStateException e) {
// ignore this. Just means we started before scylla.
}
String jmxPort = System.getProperty("com.sun.management.jmxremote.port");
System.out.println("JMX is enabled to receive remote connections on port: " + jmxPort);
for (;;) {
Thread.sleep(Long.MAX_VALUE);
}
}
}

View File

@ -1,195 +0,0 @@
package com.scylladb.jmx.metrics;
import java.lang.reflect.Field;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.management.BadAttributeValueExpException;
import javax.management.BadBinaryOpValueExpException;
import javax.management.BadStringOperationException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidApplicationException;
import javax.management.MBeanRegistration;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import com.scylladb.jmx.api.APIClient;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
/**
* Base type for MBeans in scylla-jmx. Wraps auto naming and {@link APIClient}
* holding.
*
* @author calle
*
*/
public class APIMBean implements MBeanRegistration {
protected final APIClient client;
protected final String mbeanName;
public APIMBean(APIClient client) {
this(null, client);
}
public APIMBean(String mbeanName, APIClient client) {
this.mbeanName = mbeanName;
this.client = client;
}
/**
* Helper method to add/remove dynamically created MBeans from a server
* instance.
*
* @param server
* The {@link MBeanServer} to check
* @param all
* All {@link ObjectName}s that should be bound
* @param predicate
* {@link QueryExp} predicate to filter relevant object names.
* @param generator
* {@link Function} to create a new MBean instance for a given
* {@link ObjectName}
* @return
* @throws MalformedObjectNameException
*/
public static boolean checkRegistration(JmxMBeanServer server, Set<ObjectName> all,
EnumSet<RegistrationMode> mode, final Predicate<ObjectName> predicate,
Function<ObjectName, Object> generator) throws MalformedObjectNameException {
Set<ObjectName> registered = queryNames(server, predicate);
if (mode.contains(RegistrationMode.Remove)) {
for (ObjectName name : registered) {
if (!all.contains(name)) {
try {
server.getMBeanServerInterceptor().unregisterMBean(name);
} catch (MBeanRegistrationException | InstanceNotFoundException e) {
}
}
}
}
int added = 0;
if (mode.contains(RegistrationMode.Add)) {
for (ObjectName name : all) {
if (!registered.contains(name)) {
try {
server.getMBeanServerInterceptor().registerMBean(generator.apply(name), name);
added++;
} catch (InstanceAlreadyExistsException | MBeanRegistrationException
| NotCompliantMBeanException e) {
}
}
}
}
return added > 0;
}
/**
* Helper method to query {@link ObjectName}s from an {@link MBeanServer}
* based on {@link Predicate}
*
* @param server
* @param predicate
* @return
*/
public static Set<ObjectName> queryNames(JmxMBeanServer server, final Predicate<ObjectName> predicate) {
@SuppressWarnings("serial")
Set<ObjectName> registered = server.queryNames(null, new QueryExp() {
@Override
public void setMBeanServer(MBeanServer s) {
}
@Override
public boolean apply(ObjectName name) throws BadStringOperationException, BadBinaryOpValueExpException,
BadAttributeValueExpException, InvalidApplicationException {
return predicate.test(name);
}
});
return registered;
}
JmxMBeanServer server;
ObjectName name;
protected final ObjectName getBoundName() {
return name;
}
/**
* Figure out an {@link ObjectName} for this object based on either
* contructor parameter, static field, or just package/class name.
*
* @return
* @throws MalformedObjectNameException
*/
protected ObjectName generateName() throws MalformedObjectNameException {
String mbeanName = this.mbeanName;
if (mbeanName == null) {
Field f;
try {
f = getClass().getDeclaredField("MBEAN_NAME");
f.setAccessible(true);
mbeanName = (String) f.get(null);
} catch (Throwable t) {
}
}
if (mbeanName == null) {
for (Class<?> c : getClass().getInterfaces()) {
Field f;
try {
f = c.getDeclaredField("OBJECT_NAME");
f.setAccessible(true);
mbeanName = (String) f.get(null);
break;
} catch (Throwable t) {
}
}
}
if (mbeanName == null) {
String name = getClass().getName();
int i = name.lastIndexOf('.');
mbeanName = name.substring(0, i) + ":type=" + name.substring(i + 1);
}
return new ObjectName(mbeanName);
}
/**
* Keeps track of bound server and optionally generates an
* {@link ObjectName} for this instance.
*/
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
if (this.server != null) {
throw new IllegalStateException("Can only exist in a single MBeanServer");
}
this.server = (JmxMBeanServer) server;
if (name == null) {
name = generateName();
}
this.name = name;
return name;
}
@Override
public void postRegister(Boolean registrationDone) {
}
@Override
public void preDeregister() throws Exception {
}
@Override
public void postDeregister() {
assert server != null;
assert name != null;
this.server = null;
this.name = null;
}
}

View File

@ -1,137 +0,0 @@
package com.scylladb.jmx.metrics;
import static java.util.Arrays.asList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.cassandra.metrics.Metrics;
import org.apache.cassandra.metrics.MetricsRegistry;
import com.scylladb.jmx.api.APIClient;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
/**
* Base type for MBeans containing {@link Metrics}.
*
* @author calle
*
*/
public abstract class MetricsMBean extends APIMBean {
private static final Map<JmxMBeanServer, Map<String, Integer>> registered = new HashMap<>();
private static final Object registrationLock = new Object();
private final Collection<Metrics> metrics;
public MetricsMBean(APIClient client, Metrics... metrics) {
this(null, client, metrics);
}
public MetricsMBean(String mbeanName, APIClient client, Metrics... metrics) {
this(mbeanName, client, asList(metrics));
}
public MetricsMBean(String mbeanName, APIClient client, Collection<Metrics> metrics) {
super(mbeanName, client);
this.metrics = metrics;
}
protected Predicate<ObjectName> getTypePredicate() {
String domain = name.getDomain();
String type = name.getKeyProperty("type");
return n -> {
return domain.equals(n.getDomain()) && type.equals(n.getKeyProperty("type"));
};
}
// Has to be called with registrationLock hold
private static boolean shouldRegisterGlobals(JmxMBeanServer server, String domainAndType, boolean reversed) {
Map<String, Integer> serverMap = registered.get(server);
if (serverMap == null) {
assert !reversed;
serverMap = new HashMap<>();
serverMap.put(domainAndType, 1);
registered.put(server, serverMap);
return true;
}
Integer count = serverMap.get(domainAndType);
if (count == null) {
assert !reversed;
serverMap.put(domainAndType, 1);
return true;
}
if (reversed) {
--count;
if (count == 0) {
serverMap.remove(domainAndType);
if (serverMap.isEmpty()) {
registered.remove(server);
}
return true;
}
serverMap.put(domainAndType, count);
return false;
} else {
serverMap.put(domainAndType, count + 1);
}
return false;
}
private void register(MetricsRegistry registry, JmxMBeanServer server, boolean reversed) throws MalformedObjectNameException {
// Check if we're the first/last of our type bound/removed.
synchronized (registrationLock) {
boolean registerGlobals = shouldRegisterGlobals(server, name.getDomain() + ":" + name.getKeyProperty("type"), reversed);
if (registerGlobals) {
for (Metrics m : metrics) {
m.registerGlobals(registry);
}
}
}
for (Metrics m : metrics) {
m.register(registry);
}
}
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
// Get name etc.
name = super.preRegister(server, name);
// Register all metrics in server
register(new MetricsRegistry(client, (JmxMBeanServer) server), (JmxMBeanServer) server, false);
return name;
}
@Override
public void postDeregister() {
// We're officially unbound. Remove all metrics we added.
try {
register(new MetricsRegistry(client, server) {
// Unbind instead of bind. Yes.
@Override
public void register(Supplier<MetricMBean> s, ObjectName... objectNames) {
for (ObjectName name : objectNames) {
try {
server.getMBeanServerInterceptor().unregisterMBean(name);
} catch (MBeanRegistrationException | InstanceNotFoundException e) {
}
}
}
}, server, true);
} catch (MalformedObjectNameException e) {
// TODO : log?
}
super.postDeregister();
}
}

View File

@ -1,69 +0,0 @@
package com.scylladb.jmx.metrics;
import static com.scylladb.jmx.metrics.RegistrationMode.Remove;
import static com.scylladb.jmx.metrics.RegistrationMode.Wait;
import static java.util.EnumSet.allOf;
import static java.util.EnumSet.of;
import java.net.UnknownHostException;
import java.util.EnumSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.management.OperationsException;
import com.scylladb.jmx.api.APIClient;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
/**
* Helper type to do optional locking for registration. Allows for
* per-bind-point locks and registration, instead of per-type or per-instance
* locks which may be misguiding, since for example one instance can be bound to
* many MBeanServers etc.
*
* Also allows for polled checks, i.e. try-lock and either wait or skip. Wait,
* because we probably should not repeat things hidden by this type too often,
* and skip because for example a periodic task checking can just skip if a
* user-initiated registration check is being done.
*
* @author calle
*
*/
@SuppressWarnings("restriction")
public abstract class RegistrationChecker {
private final Lock lock = new ReentrantLock();
public static final EnumSet<RegistrationMode> REMOVE_NO_WAIT = of(Remove);
public static final EnumSet<RegistrationMode> ADD_AND_REMOVE = allOf(RegistrationMode.class);
public final void reap(APIClient client, JmxMBeanServer server) throws OperationsException, UnknownHostException {
check(client, server, REMOVE_NO_WAIT);
}
public final void check(APIClient client, JmxMBeanServer server) throws OperationsException, UnknownHostException {
check(client, server, ADD_AND_REMOVE);
}
public final void check(APIClient client, JmxMBeanServer server, EnumSet<RegistrationMode> mode)
throws OperationsException, UnknownHostException {
if (!lock.tryLock()) {
if (mode.contains(Wait)) {
// someone is doing update.
// since this is jmx, and sloppy, we'll just
// assume that once he is done, things are
// good enough.
lock.lock();
lock.unlock();
}
return;
}
try {
doCheck(client, server, mode);
} finally {
lock.unlock();
}
}
protected abstract void doCheck(APIClient client, JmxMBeanServer server, EnumSet<RegistrationMode> mode)
throws OperationsException, UnknownHostException;
}

View File

@ -1,5 +0,0 @@
package com.scylladb.jmx.metrics;
public enum RegistrationMode {
Wait, Add, Remove,
}

View File

@ -1,496 +0,0 @@
package com.scylladb.jmx.utils;
/**
* Copyright 2016 ScyllaDB
*/
import static com.scylladb.jmx.main.Main.getApiClient;
import static com.sun.jmx.mbeanserver.Util.wildmatch;
import static java.util.logging.Level.SEVERE;
import static javax.management.MBeanServerDelegate.DELEGATE_NAME;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
import javax.management.DynamicMBean;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.MBeanServerBuilder;
import javax.management.MBeanServerDelegate;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.RuntimeOperationsException;
import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
import com.sun.jmx.mbeanserver.NamedObject;
import com.sun.jmx.mbeanserver.Repository;
/**
* This class purposly knows way to much of the inner workings
* of Oracle JDK MBeanServer workings, and pervert it for
* performance sakes. It is not portable to other MBean implementations.
*
*/
@SuppressWarnings("restriction")
public class APIBuilder extends MBeanServerBuilder {
private static final Logger logger = Logger.getLogger(APIBuilder.class.getName());
private static class TableRepository extends Repository {
private static final Logger logger = Logger.getLogger(TableRepository.class.getName());
private final Repository wrapped;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Map<TableMetricParams, DynamicMBean> tableMBeans = new HashMap<>();
private static boolean isTableMetricName(ObjectName name) {
return isTableMetricDomain(name.getDomain());
}
private static boolean isTableMetricDomain(String domain) {
return TableMetricParams.TABLE_METRICS_DOMAIN.equals(domain);
}
public TableRepository(String defaultDomain, final Repository repository) {
super(defaultDomain);
wrapped = repository;
}
@Override
public String getDefaultDomain() {
return wrapped.getDefaultDomain();
}
@Override
public boolean contains(final ObjectName name) {
if (!isTableMetricName(name)) {
return wrapped.contains(name);
} else {
lock.readLock().lock();
try {
return tableMBeans.containsKey(new TableMetricParams(name));
} finally {
lock.readLock().unlock();
}
}
}
@Override
public String[] getDomains() {
final String[] domains = wrapped.getDomains();
if (tableMBeans.isEmpty()) {
return domains;
}
final String[] res = new String[domains.length + 1];
System.arraycopy(domains, 0, res, 0, domains.length);
res[domains.length] = TableMetricParams.TABLE_METRICS_DOMAIN;
return res;
}
@Override
public Integer getCount() {
lock.readLock().lock();
try {
return wrapped.getCount() + tableMBeans.size();
} finally {
lock.readLock().unlock();
}
}
@Override
public void addMBean(final DynamicMBean bean, final ObjectName name, final RegistrationContext ctx)
throws InstanceAlreadyExistsException {
if (!isTableMetricName(name)) {
wrapped.addMBean(bean, name, ctx);
} else {
final TableMetricParams key = new TableMetricParams(name);
lock.writeLock().lock();
try {
if (tableMBeans.containsKey(key)) {
throw new InstanceAlreadyExistsException(name.toString());
}
tableMBeans.put(key, bean);
if (ctx == null) return;
try {
ctx.registering();
} catch (RuntimeOperationsException x) {
throw x;
} catch (RuntimeException x) {
throw new RuntimeOperationsException(x);
}
} finally {
lock.writeLock().unlock();
}
}
}
@Override
public void remove(final ObjectName name, final RegistrationContext ctx) throws InstanceNotFoundException {
if (!isTableMetricName(name)) {
wrapped.remove(name, ctx);
} else {
final TableMetricParams key = new TableMetricParams(name);
lock.writeLock().lock();
try {
if (tableMBeans.remove(key) == null) {
throw new InstanceNotFoundException(name.toString());
}
if (ctx == null) {
return;
}
try {
ctx.unregistered();
} catch (Exception x) {
logger.log(SEVERE, "Unexpected error.", x);
}
} finally {
lock.writeLock().unlock();
}
}
}
@Override
public DynamicMBean retrieve(final ObjectName name) {
if (!isTableMetricName(name)) {
return wrapped.retrieve(name);
} else {
lock.readLock().lock();
try {
return tableMBeans.get(new TableMetricParams(name));
} finally {
lock.readLock().unlock();
}
}
}
private void addAll(final Set<NamedObject> res) {
for (Map.Entry<TableMetricParams, DynamicMBean> e : tableMBeans.entrySet()) {
try {
res.add(new NamedObject(e.getKey().toName(), e.getValue()));
} catch (MalformedObjectNameException e1) {
// This should never happen
logger.log(SEVERE, "Unexpected error.", e1);
}
}
}
private void addAllMatching(final Set<NamedObject> res,
final ObjectNamePattern pattern) {
for (Map.Entry<TableMetricParams, DynamicMBean> e : tableMBeans.entrySet()) {
try {
ObjectName name = e.getKey().toName();
if (pattern.matchKeys(name)) {
res.add(new NamedObject(name, e.getValue()));
}
} catch (MalformedObjectNameException e1) {
// This should never happen
logger.log(SEVERE, "Unexpected error.", e1);
}
}
}
@Override
public Set<NamedObject> query(final ObjectName pattern, final QueryExp query) {
Set<NamedObject> res = wrapped.query(pattern, query);
ObjectName name;
if (pattern == null ||
pattern.getCanonicalName().length() == 0 ||
pattern.equals(ObjectName.WILDCARD)) {
name = ObjectName.WILDCARD;
} else {
name = pattern;
}
lock.readLock().lock();
try {
// If pattern is not a pattern, retrieve this mbean !
if (!name.isPattern() && isTableMetricName(name)) {
final DynamicMBean bean = tableMBeans.get(new TableMetricParams(name));
if (bean != null) {
res.add(new NamedObject(name, bean));
return res;
}
}
// All names in all domains
if (name == ObjectName.WILDCARD) {
addAll(res);
return res;
}
final String canonical_key_property_list_string =
name.getCanonicalKeyPropertyListString();
final boolean allNames =
(canonical_key_property_list_string.length()==0);
final ObjectNamePattern namePattern =
(allNames?null:new ObjectNamePattern(name));
// All names in default domain
if (name.getDomain().length() == 0) {
if (isTableMetricDomain(getDefaultDomain())) {
if (allNames) {
addAll(res);
} else {
addAllMatching(res, namePattern);
}
}
return res;
}
if (!name.isDomainPattern()) {
if (isTableMetricDomain(getDefaultDomain())) {
if (allNames) {
addAll(res);
} else {
addAllMatching(res, namePattern);
}
}
return res;
}
// Pattern matching in the domain name (*, ?)
final String dom2Match = name.getDomain();
if (wildmatch(TableMetricParams.TABLE_METRICS_DOMAIN, dom2Match)) {
if (allNames) {
addAll(res);
} else {
addAllMatching(res, namePattern);
}
}
} finally {
lock.readLock().unlock();
}
return res;
}
}
private final static class ObjectNamePattern {
private final String[] keys;
private final String[] values;
private final String properties;
private final boolean isPropertyListPattern;
private final boolean isPropertyValuePattern;
/**
* The ObjectName pattern against which ObjectNames are matched.
**/
public final ObjectName pattern;
/**
* Builds a new ObjectNamePattern object from an ObjectName pattern.
* @param pattern The ObjectName pattern under examination.
**/
public ObjectNamePattern(ObjectName pattern) {
this(pattern.isPropertyListPattern(),
pattern.isPropertyValuePattern(),
pattern.getCanonicalKeyPropertyListString(),
pattern.getKeyPropertyList(),
pattern);
}
/**
* Builds a new ObjectNamePattern object from an ObjectName pattern
* constituents.
* @param propertyListPattern pattern.isPropertyListPattern().
* @param propertyValuePattern pattern.isPropertyValuePattern().
* @param canonicalProps pattern.getCanonicalKeyPropertyListString().
* @param keyPropertyList pattern.getKeyPropertyList().
* @param pattern The ObjectName pattern under examination.
**/
ObjectNamePattern(boolean propertyListPattern,
boolean propertyValuePattern,
String canonicalProps,
Map<String,String> keyPropertyList,
ObjectName pattern) {
this.isPropertyListPattern = propertyListPattern;
this.isPropertyValuePattern = propertyValuePattern;
this.properties = canonicalProps;
final int len = keyPropertyList.size();
this.keys = new String[len];
this.values = new String[len];
int i = 0;
for (Map.Entry<String,String> entry : keyPropertyList.entrySet()) {
keys[i] = entry.getKey();
values[i] = entry.getValue();
i++;
}
this.pattern = pattern;
}
/**
* Return true if the given ObjectName matches the ObjectName pattern
* for which this object has been built.
* WARNING: domain name is not considered here because it is supposed
* not to be wildcard when called. PropertyList is also
* supposed not to be zero-length.
* @param name The ObjectName we want to match against the pattern.
* @return true if <code>name</code> matches the pattern.
**/
public boolean matchKeys(ObjectName name) {
// If key property value pattern but not key property list
// pattern, then the number of key properties must be equal
//
if (isPropertyValuePattern &&
!isPropertyListPattern &&
(name.getKeyPropertyList().size() != keys.length)) {
return false;
}
// If key property value pattern or key property list pattern,
// then every property inside pattern should exist in name
//
if (isPropertyValuePattern || isPropertyListPattern) {
for (int i = keys.length - 1; i >= 0 ; i--) {
// Find value in given object name for key at current
// index in receiver
//
String v = name.getKeyProperty(keys[i]);
// Did we find a value for this key ?
//
if (v == null) {
return false;
}
// If this property is ok (same key, same value), go to next
//
if (isPropertyValuePattern &&
pattern.isPropertyValuePattern(keys[i])) {
// wildmatch key property values
// values[i] is the pattern;
// v is the string
if (wildmatch(v,values[i])) {
continue;
} else {
return false;
}
}
if (v.equals(values[i])) {
continue;
}
return false;
}
return true;
}
// If no pattern, then canonical names must be equal
//
final String p1 = name.getCanonicalKeyPropertyListString();
final String p2 = properties;
return (p1.equals(p2));
}
}
public static class TableMetricParams {
public static final String TABLE_METRICS_DOMAIN = "org.apache.cassandra.metrics";
private final ObjectName name;
public TableMetricParams(ObjectName name) {
this.name = name;
}
public ObjectName toName() throws MalformedObjectNameException {
return name;
}
private static boolean equal(Object a, Object b) {
return (a == null) ? b == null : a.equals(b);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TableMetricParams)) {
return false;
}
TableMetricParams oo = (TableMetricParams) o;
return equal(name.getKeyProperty("keyspace"), oo.name.getKeyProperty("keyspace"))
&& equal(name.getKeyProperty("scope"), oo.name.getKeyProperty("scope"))
&& equal(name.getKeyProperty("name"), oo.name.getKeyProperty("name"))
&& equal(name.getKeyProperty("type"), oo.name.getKeyProperty("type"));
}
private static int hash(Object o) {
return o == null ? 0 : o.hashCode();
}
private static int safeAdd(int ... nums) {
long res = 0;
for (int n : nums) {
res = (res + n) % Integer.MAX_VALUE;
}
return (int)res;
}
@Override
public int hashCode() {
return safeAdd(hash(name.getKeyProperty("keyspace")),
hash(name.getKeyProperty("scope")),
hash(name.getKeyProperty("name")),
hash(name.getKeyProperty("type")));
}
}
@Override
public MBeanServer newMBeanServer(String defaultDomain, MBeanServer outer, MBeanServerDelegate delegate) {
// It is important to set |interceptors| to true while creating the
// JmxMBeanSearver. It is required for calls to
// JmxMBeanServer.setMBeanServerInterceptor() to be allowed.
JmxMBeanServer nested = (JmxMBeanServer) JmxMBeanServer.newMBeanServer(defaultDomain, outer, delegate, true);
// This is not very clean, we depend on knowledge of how the Sun/Oracle
// MBean chain looks internally. But we need haxxor support, so
// lets replace the interceptor.
// Note: Removed reflection gunk to eliminate jdk9+ warnings on
// execution. Also, if we can get by without reflection, it is
// better.
final DefaultMBeanServerInterceptor interceptor = new DefaultMBeanServerInterceptor(outer != null ? outer : nested,
delegate, nested.getMBeanInstantiator(),
new TableRepository(defaultDomain, new Repository(defaultDomain)));
nested.setMBeanServerInterceptor(interceptor);
final MBeanServerDelegate d = nested.getMBeanServerDelegate();
try {
// Interceptor needs the delegate present. Normally done
// by inaccessible method in JmxMBeanServer
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
interceptor.registerMBean(d, DELEGATE_NAME);
return null;
}
});
} catch (PrivilegedActionException e) {
logger.log(SEVERE, "Unexpected error.", e);
throw new RuntimeException(e);
}
return new APIMBeanServer(getApiClient(), nested);
}
}

View File

@ -1,327 +0,0 @@
package com.scylladb.jmx.utils;
import static java.util.Arrays.asList;
import static java.util.concurrent.Executors.newScheduledThreadPool;
import static java.util.concurrent.TimeUnit.MINUTES;
import java.io.ObjectInputStream;
import java.net.UnknownHostException;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.metrics.StreamingMetrics;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.RegistrationChecker;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
@SuppressWarnings("restriction")
public class APIMBeanServer implements MBeanServer {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(APIMBeanServer.class.getName());
private static final ScheduledExecutorService executor = newScheduledThreadPool(1);
private final RegistrationChecker columnFamilyStoreChecker = ColumnFamilyStore.createRegistrationChecker();
private final RegistrationChecker streamingMetricsChecker = StreamingMetrics.createRegistrationChecker();
private final APIClient client;
private final JmxMBeanServer server;
public APIMBeanServer(APIClient client, JmxMBeanServer server) {
this.client = client;
this.server = server;
executor.scheduleWithFixedDelay(() -> {
for (RegistrationChecker c : asList(columnFamilyStoreChecker, streamingMetricsChecker)) {
try {
c.reap(client, server);
} catch (OperationsException | UnknownHostException e) {
// TODO: log?
}
}
}, 1, 5, MINUTES);
}
private static ObjectInstance prepareForRemote(final ObjectInstance i) {
return new ObjectInstance(prepareForRemote(i.getObjectName()), i.getClassName());
}
private static ObjectName prepareForRemote(final ObjectName n) {
/*
* ObjectName.getInstance has changed in JDK (micro) updates so it no longer applies
* overridable methods -> wrong name published.
* Fix by doing explicit ObjectName instansiation.
*/
try {
return new ObjectName(n.getCanonicalName());
} catch (MalformedObjectNameException e) {
throw new IllegalArgumentException(n.toString());
}
}
@Override
public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException,
InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException {
return prepareForRemote(server.createMBean(className, name));
}
@Override
public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName)
throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException {
return prepareForRemote(server.createMBean(className, name, loaderName));
}
@Override
public ObjectInstance createMBean(String className, ObjectName name, Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
NotCompliantMBeanException {
return prepareForRemote(server.createMBean(className, name, params, signature));
}
@Override
public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object[] params,
String[] signature) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException,
MBeanException, NotCompliantMBeanException, InstanceNotFoundException {
return prepareForRemote(server.createMBean(className, name, loaderName, params, signature));
}
@Override
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
return prepareForRemote(server.registerMBean(object, name));
}
@Override
public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException {
server.unregisterMBean(name);
}
@Override
public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException {
checkRegistrations(name);
return prepareForRemote(server.getObjectInstance(name));
}
@Override
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
checkRegistrations(name);
return server.queryNames(name, query).stream().map(n -> prepareForRemote(n)).collect(Collectors.toSet());
}
@Override
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
checkRegistrations(name);
return server.queryMBeans(name, query).stream().map(i -> prepareForRemote(i)).collect(Collectors.toSet());
}
@Override
public boolean isRegistered(ObjectName name) {
checkRegistrations(name);
return server.isRegistered(name);
}
@Override
public Integer getMBeanCount() {
return server.getMBeanCount();
}
@Override
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException {
checkRegistrations(name);
return server.getAttribute(name, attribute);
}
@Override
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException {
checkRegistrations(name);
return server.getAttributes(name, attributes);
}
@Override
public void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException,
AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
checkRegistrations(name);
server.setAttribute(name, attribute);
}
@Override
public AttributeList setAttributes(ObjectName name, AttributeList attributes)
throws InstanceNotFoundException, ReflectionException {
checkRegistrations(name);
return server.setAttributes(name, attributes);
}
@Override
public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature)
throws InstanceNotFoundException, MBeanException, ReflectionException {
checkRegistrations(name);
return server.invoke(name, operationName, params, signature);
}
@Override
public String getDefaultDomain() {
return server.getDefaultDomain();
}
@Override
public String[] getDomains() {
return server.getDomains();
}
@Override
public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException {
server.addNotificationListener(name, listener, filter, handback);
}
@Override
public void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException {
server.addNotificationListener(name, listener, filter, handback);
}
@Override
public void removeNotificationListener(ObjectName name, ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException {
server.removeNotificationListener(name, listener);
}
@Override
public void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException, ListenerNotFoundException {
server.removeNotificationListener(name, listener, filter, handback);
}
@Override
public void removeNotificationListener(ObjectName name, NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException {
server.removeNotificationListener(name, listener);
}
@Override
public void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException, ListenerNotFoundException {
server.removeNotificationListener(name, listener, filter, handback);
}
@Override
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException, ReflectionException {
checkRegistrations(name);
return server.getMBeanInfo(name);
}
@Override
public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException {
return server.isInstanceOf(name, className);
}
@Override
public Object instantiate(String className) throws ReflectionException, MBeanException {
return server.instantiate(className);
}
@Override
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException, InstanceNotFoundException {
return server.instantiate(className, loaderName);
}
@Override
public Object instantiate(String className, Object[] params, String[] signature)
throws ReflectionException, MBeanException {
return server.instantiate(className, params, signature);
}
@Override
public Object instantiate(String className, ObjectName loaderName, Object[] params, String[] signature)
throws ReflectionException, MBeanException, InstanceNotFoundException {
return server.instantiate(className, loaderName, params, signature);
}
@Override
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
return server.deserialize(name, data);
}
@Override
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
return server.deserialize(className, data);
}
@Override
@Deprecated
public ObjectInputStream deserialize(String className, ObjectName loaderName, byte[] data)
throws InstanceNotFoundException, OperationsException, ReflectionException {
return server.deserialize(className, loaderName, data);
}
@Override
public ClassLoader getClassLoaderFor(ObjectName mbeanName) throws InstanceNotFoundException {
return server.getClassLoaderFor(mbeanName);
}
@Override
public ClassLoader getClassLoader(ObjectName loaderName) throws InstanceNotFoundException {
return server.getClassLoader(loaderName);
}
@Override
public ClassLoaderRepository getClassLoaderRepository() {
return server.getClassLoaderRepository();
}
static final Pattern tables = Pattern.compile("^\\*?((Index)?ColumnFamil(ies|y)|(Index)?(Table(s)?)?)$");
private void checkRegistrations(ObjectName name) {
if (name != null && server.isRegistered(name)) {
return;
}
try {
String type = name != null ? name.getKeyProperty("type") : null;
if (type == null || tables.matcher(type).matches()) {
columnFamilyStoreChecker.check(client, server);
}
if (type == null || StreamingMetrics.TYPE_NAME.equals(type)) {
streamingMetricsChecker.check(client, server);
}
} catch (OperationsException | UnknownHostException e) {
// TODO: log
}
}
}

View File

@ -1,18 +0,0 @@
package com.scylladb.jmx.utils;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import java.time.Instant;
import java.util.Date;
public class DateXmlAdapter extends XmlAdapter<String, Date> {
@Override
public String marshal(Date v) throws Exception {
return Instant.ofEpochMilli(v.getTime()).toString();
}
@Override
public Date unmarshal(String v) throws Exception {
return new Date(Instant.parse(v).toEpochMilli());
}
}

View File

@ -0,0 +1,29 @@
package com.yammer.metrics.core;
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
import com.cloudius.urchin.api.APIClient;
import com.yammer.metrics.core.Counter;
public class APICounter extends Counter {
String url;
private APIClient c = new APIClient();
public APICounter(String _url) {
super();
url = _url;
}
/**
* Returns the counter's current value.
*
* @return the counter's current value
*/
public long count() {
return c.getLongValue(url);
}
}

View File

@ -0,0 +1,201 @@
package com.yammer.metrics.core;
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import com.cloudius.urchin.api.APIClient;
import com.yammer.metrics.stats.Sample;
import com.yammer.metrics.stats.Snapshot;
public class APIHistogram extends Histogram {
Field countField;
Field minField;
Field maxField;
Field sumField;
Field varianceField;
Field sampleField;
long last_update = 0;
static final long UPDATE_INTERVAL = 50;
long updateInterval;
String url;
private APIClient c = new APIClient();
private void setFields() {
try {
minField = Histogram.class.getDeclaredField("min");
minField.setAccessible(true);
maxField = Histogram.class.getDeclaredField("max");
maxField.setAccessible(true);
sumField = Histogram.class.getDeclaredField("sum");
sumField.setAccessible(true);
varianceField = Histogram.class.getDeclaredField("variance");
varianceField.setAccessible(true);
sampleField = Histogram.class.getDeclaredField("sample");
sampleField.setAccessible(true);
countField = Histogram.class.getDeclaredField("count");
countField.setAccessible(true);
try {
getCount().set(0);
} catch (IllegalArgumentException | IllegalAccessException e) {
// There's no reason to get here
// and there's nothing we can do even if we would
}
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
}
public AtomicLong getMin() throws IllegalArgumentException,
IllegalAccessException {
return (AtomicLong) minField.get(this);
}
public AtomicLong getMax() throws IllegalArgumentException,
IllegalAccessException {
return (AtomicLong) maxField.get(this);
}
public AtomicLong getSum() throws IllegalArgumentException,
IllegalAccessException {
return (AtomicLong) sumField.get(this);
}
public AtomicLong getCount() throws IllegalArgumentException,
IllegalAccessException {
return (AtomicLong) countField.get(this);
}
@SuppressWarnings("unchecked")
public AtomicReference<double[]> getVariance()
throws IllegalArgumentException, IllegalAccessException {
return (AtomicReference<double[]>) varianceField.get(this);
}
public Sample getSample() throws IllegalArgumentException,
IllegalAccessException {
return (Sample) sampleField.get(this);
}
public APIHistogram(String url, Sample sample) {
super(sample);
setFields();
this.url = url;
}
public APIHistogram(String url, SampleType type, long updateInterval) {
super(type);
setFields();
this.url = url;
this.updateInterval = updateInterval;
}
public APIHistogram(String url, SampleType type) {
this(url, type, UPDATE_INTERVAL);
}
public void update() {
long now = System.currentTimeMillis();
if (now - last_update < UPDATE_INTERVAL) {
return;
}
last_update = now;
clear();
HistogramValues vals = c.getHistogramValue(url);
try {
if (vals.sample != null) {
for (long v : vals.sample) {
getSample().update(v);
}
}
getCount().set(vals.count);
getMax().set(vals.max);
getMin().set(vals.min);
getSum().set(vals.sum);
double[] newValue = new double[2];
newValue[0] = vals.mean;
newValue[1] = vals.variance;
getVariance().getAndSet(newValue);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* Returns the number of values recorded.
*
* @return the number of values recorded
*/
public long count() {
update();
return super.count();
}
/*
* (non-Javadoc)
*
* @see com.yammer.metrics.core.Summarizable#max()
*/
@Override
public double max() {
update();
return super.max();
}
/*
* (non-Javadoc)
*
* @see com.yammer.metrics.core.Summarizable#min()
*/
@Override
public double min() {
update();
return super.min();
}
/*
* (non-Javadoc)
*
* @see com.yammer.metrics.core.Summarizable#mean()
*/
@Override
public double mean() {
update();
return super.mean();
}
/*
* (non-Javadoc)
*
* @see com.yammer.metrics.core.Summarizable#stdDev()
*/
@Override
public double stdDev() {
update();
return super.stdDev();
}
/*
* (non-Javadoc)
*
* @see com.yammer.metrics.core.Summarizable#sum()
*/
@Override
public double sum() {
update();
return super.sum();
}
@Override
public Snapshot getSnapshot() {
update();
return super.getSnapshot();
}
}

View File

@ -0,0 +1,34 @@
package com.yammer.metrics.core;
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.cloudius.urchin.api.APIClient;
public class APIMeter extends APISettableMeter {
String url;
private APIClient c = new APIClient();
public APIMeter(String _url, ScheduledExecutorService tickThread,
String eventType, TimeUnit rateUnit, Clock clock) {
super(tickThread, eventType, rateUnit, clock);
// TODO Auto-generated constructor stub
url = _url;
}
public long get_value() {
return c.getLongValue(url);
}
@Override
public void tick() {
set(get_value());
super.tick();
}
}

View File

@ -0,0 +1,384 @@
package com.yammer.metrics.core;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.yammer.metrics.core.APICounter;
import com.yammer.metrics.core.APIMeter;
import com.yammer.metrics.core.Clock;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.Metric;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.ThreadPools;
import com.yammer.metrics.core.Histogram.SampleType;
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
public class APIMetricsRegistry extends MetricsRegistry {
Field fieldMetrics;
Field fieldClock;
Field fieldThreadPool;
public APIMetricsRegistry() {
try {
fieldMetrics = MetricsRegistry.class.getDeclaredField("metrics");
fieldMetrics.setAccessible(true);
fieldClock = MetricsRegistry.class.getDeclaredField("clock");
fieldClock.setAccessible(true);
fieldThreadPool = MetricsRegistry.class
.getDeclaredField("threadPools");
fieldThreadPool.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public ThreadPools getThreadPools() {
try {
return (ThreadPools) fieldThreadPool.get(this);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public Clock getClock() {
try {
return (Clock) fieldClock.get(this);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("unchecked")
public ConcurrentMap<MetricName, Metric> getMetrics() {
try {
return (ConcurrentMap<MetricName, Metric>) fieldMetrics.get(this);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* Creates a new {@link Counter} and registers it under the given class and
* name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @return a new {@link Counter}
*/
public Counter newCounter(String url, Class<?> klass, String name) {
return newCounter(url, klass, name, null);
}
/**
* Creates a new {@link Counter} and registers it under the given class and
* name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @return a new {@link Counter}
*/
public Counter newCounter(String url, Class<?> klass, String name,
String scope) {
return newCounter(url, createName(klass, name, scope));
}
/**
* Creates a new {@link Counter} and registers it under the given metric
* name.
*
* @param metricName
* the name of the metric
* @return a new {@link Counter}
*/
public Counter newCounter(String url, MetricName metricName) {
return getOrAdd(metricName, new APICounter(url));
}
/**
* Creates a new {@link Meter} and registers it under the given class and
* name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link Meter}
*/
public Meter newMeter(String url, Class<?> klass, String name,
String eventType, TimeUnit unit) {
return newMeter(url, klass, name, null, eventType, unit);
}
/**
* Creates a new {@link Meter} and registers it under the given class, name,
* and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link Meter}
*/
public Meter newMeter(String url, Class<?> klass, String name,
String scope, String eventType, TimeUnit unit) {
return newMeter(url, createName(klass, name, scope), eventType, unit);
}
private ScheduledExecutorService newMeterTickThreadPool() {
return getThreadPools().newScheduledThreadPool(2, "meter-tick");
}
/**
* Creates a new {@link Meter} and registers it under the given metric name.
*
* @param metricName
* the name of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link Meter}
*/
public Meter newMeter(String url, MetricName metricName, String eventType,
TimeUnit unit) {
final Metric existingMetric = getMetrics().get(metricName);
if (existingMetric != null) {
return (Meter) existingMetric;
}
return getOrAdd(metricName, new APIMeter(url, newMeterTickThreadPool(),
eventType, unit, getClock()));
}
/**
* Creates a new {@link APISettableMeter} and registers it under the given metric name.
*
* @param metricName
* the name of the metric
* @param eventType
* the plural name of the type of events the meter is measuring
* (e.g., {@code "requests"})
* @param unit
* the rate unit of the new meter
* @return a new {@link Meter}
*/
public Meter newSettableMeter(MetricName metricName, String eventType,
TimeUnit unit) {
final Metric existingMetric = getMetrics().get(metricName);
if (existingMetric != null) {
return (Meter) existingMetric;
}
return getOrAdd(metricName, new APISettableMeter(newMeterTickThreadPool(),
eventType, unit, getClock()));
}
/**
* Creates a new {@link Histogram} and registers it under the given class
* and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param biased
* whether or not the histogram should be biased
* @return a new {@link Histogram}
*/
public Histogram newHistogram(String url, Class<?> klass, String name,
boolean biased) {
return newHistogram(url, klass, name, null, biased);
}
/**
* Creates a new {@link Histogram} and registers it under the given class,
* name, and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param biased
* whether or not the histogram should be biased
* @return a new {@link Histogram}
*/
public Histogram newHistogram(String url, Class<?> klass, String name,
String scope, boolean biased) {
return newHistogram(url, createName(klass, name, scope), biased);
}
/**
* Creates a new non-biased {@link Histogram} and registers it under the
* given class and name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @return a new {@link Histogram}
*/
public Histogram newHistogram(String url, Class<?> klass, String name) {
return newHistogram(url, klass, name, false);
}
/**
* Creates a new non-biased {@link Histogram} and registers it under the
* given class, name, and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @return a new {@link Histogram}
*/
public Histogram newHistogram(String url, Class<?> klass, String name,
String scope) {
return newHistogram(url, klass, name, scope, false);
}
/**
* Creates a new {@link Histogram} and registers it under the given metric
* name.
*
* @param metricName
* the name of the metric
* @param biased
* whether or not the histogram should be biased
* @return a new {@link Histogram}
*/
public Histogram newHistogram(String url, MetricName metricName,
boolean biased) {
return getOrAdd(metricName, new APIHistogram(url,
biased ? SampleType.BIASED : SampleType.UNIFORM));
}
/**
* Creates a new {@link Timer} and registers it under the given class and
* name, measuring elapsed time in milliseconds and invocations per second.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @return a new {@link Timer}
*/
public Timer newTimer(String url, Class<?> klass, String name) {
return newTimer(url, klass, name, null, TimeUnit.MILLISECONDS,
TimeUnit.SECONDS);
}
/**
* Creates a new {@link Timer} and registers it under the given class and
* name.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param durationUnit
* the duration scale unit of the new timer
* @param rateUnit
* the rate scale unit of the new timer
* @return a new {@link Timer}
*/
public Timer newTimer(String url, Class<?> klass, String name,
TimeUnit durationUnit, TimeUnit rateUnit) {
return newTimer(url, klass, name, null, durationUnit, rateUnit);
}
/**
* Creates a new {@link Timer} and registers it under the given class, name,
* and scope, measuring elapsed time in milliseconds and invocations per
* second.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @return a new {@link Timer}
*/
public Timer newTimer(String url, Class<?> klass, String name, String scope) {
return newTimer(url, klass, name, scope, TimeUnit.MILLISECONDS,
TimeUnit.SECONDS);
}
/**
* Creates a new {@link Timer} and registers it under the given class, name,
* and scope.
*
* @param klass
* the class which owns the metric
* @param name
* the name of the metric
* @param scope
* the scope of the metric
* @param durationUnit
* the duration scale unit of the new timer
* @param rateUnit
* the rate scale unit of the new timer
* @return a new {@link Timer}
*/
public Timer newTimer(String url, Class<?> klass, String name,
String scope, TimeUnit durationUnit, TimeUnit rateUnit) {
return newTimer(url, createName(klass, name, scope), durationUnit,
rateUnit);
}
/**
* Creates a new {@link Timer} and registers it under the given metric name.
*
* @param metricName
* the name of the metric
* @param durationUnit
* the duration scale unit of the new timer
* @param rateUnit
* the rate scale unit of the new timer
* @return a new {@link Timer}
*/
public Timer newTimer(String url, MetricName metricName,
TimeUnit durationUnit, TimeUnit rateUnit) {
final Metric existingMetric = getMetrics().get(metricName);
if (existingMetric != null) {
return (Timer) existingMetric;
}
return getOrAdd(metricName, new APITimer(url, newMeterTickThreadPool(),
durationUnit, rateUnit, getClock()));
}
}

View File

@ -0,0 +1,49 @@
package com.yammer.metrics.core;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/*
* Copyright 2015 ScyllaDB
*
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
public class APISettableMeter extends Meter {
public APISettableMeter(ScheduledExecutorService tickThread,
String eventType, TimeUnit rateUnit, Clock clock) {
super(tickThread, eventType, rateUnit, clock);
}
// Meter doesn't have a set value method.
// to mimic it, we clear the old value and set it to a new one.
// This is safe because the only this method would be used
// to update the values
public long set(long new_value) {
long res = super.count();
mark(-res);
mark(new_value);
return res;
}
@Override
public void tick() {
super.tick();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2015 Cloudius Systems
*
*/
package com.yammer.metrics.core;
import java.lang.reflect.Field;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.yammer.metrics.core.Histogram.SampleType;
/**
* A timer metric which aggregates timing durations and provides duration
* statistics, plus throughput statistics via {@link Meter}.
*/
public class APITimer extends Timer {
public APITimer(String url, ScheduledExecutorService tickThread,
TimeUnit durationUnit, TimeUnit rateUnit) {
super(tickThread, durationUnit, rateUnit);
setHistogram(url);
}
public APITimer(String url, ScheduledExecutorService tickThread,
TimeUnit durationUnit, TimeUnit rateUnit, Clock clock) {
super(tickThread, durationUnit, rateUnit, clock);
setHistogram(url);
}
private void setHistogram(String url) {
Field histogram;
try {
histogram = Timer.class.getDeclaredField("histogram");
histogram.setAccessible(true);
histogram.set(this, new APIHistogram(url, SampleType.BIASED));
} catch (NoSuchFieldException | SecurityException
| IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,11 @@
package com.yammer.metrics.core;
public class HistogramValues {
public long count;
public long min;
public long max;
public long sum;
public double variance;
public double mean;
public long sample[];
}

View File

@ -1,16 +0,0 @@
module scylla.jmx {
opens com.scylladb.jmx.utils;
exports com.scylladb.jmx.utils;
opens com.scylladb.jmx.main;
exports com.scylladb.jmx.main;
opens com.scylladb.jmx.metrics;
exports com.scylladb.jmx.metrics;
requires java.logging;
requires java.management;
requires scylla.apiclient;
requires jakarta.json;
requires jakarta.ws.rs;
requires com.google.common;
requires jakarta.xml.bind;
requires com.fasterxml.jackson.annotation;
}

View File

@ -23,146 +23,61 @@
*/
package org.apache.cassandra.db;
import static jakarta.json.Json.createObjectBuilder;
import static java.lang.String.valueOf;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toMap;
import java.lang.management.ManagementFactory;
import java.net.ConnectException;
import java.util.*;
import java.util.concurrent.*;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonReader;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.io.StringReader;
import java.io.OutputStream;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.management.*;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.cassandra.metrics.TableMetrics;
import org.apache.cassandra.metrics.ColumnFamilyMetrics;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.MetricsMBean;
import com.scylladb.jmx.metrics.RegistrationChecker;
import com.scylladb.jmx.metrics.RegistrationMode;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
import com.cloudius.urchin.api.APIClient;
import com.google.common.base.Throwables;
public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStoreMBean {
private static final Logger logger = Logger.getLogger(ColumnFamilyStore.class.getName());
@SuppressWarnings("unused")
private final String type;
private final String keyspace;
private final String name;
private static final String[] COUNTER_NAMES = new String[]{"raw", "count", "error", "string"};
private static final String[] COUNTER_DESCS = new String[]
{ "partition key in raw hex bytes", // Table name and comments match Cassandra, we will use the partition key
"value of this partition for given sampler",
"value is within the error bounds plus or minus of this",
"the partition key turned into a human readable format" };
private static final CompositeType COUNTER_COMPOSITE_TYPE;
private static final TabularType COUNTER_TYPE;
public class ColumnFamilyStore implements ColumnFamilyStoreMBean {
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(ColumnFamilyStore.class.getName());
private APIClient c = new APIClient();
private String type;
private String keyspace;
private String name;
private String mbeanName;
static final int INTERVAL = 1000; // update every 1second
public final ColumnFamilyMetrics metric;
private static final String[] SAMPLER_NAMES = new String[]{"cardinality", "partitions"};
private static final String[] SAMPLER_DESCS = new String[]
{ "cardinality of partitions",
"list of counter results" };
private static final String SAMPLING_RESULTS_NAME = "SAMPLING_RESULTS";
private static final CompositeType SAMPLING_RESULT;
public static final String SNAPSHOT_TRUNCATE_PREFIX = "truncated";
public static final String SNAPSHOT_DROP_PREFIX = "dropped";
private JsonObject tableSamplerResult = null;
private Future<JsonObject> futureTableSamperResult = null;
private ExecutorService service = null;
static
{
try
{
OpenType<?>[] counterTypes = new OpenType[] { SimpleType.STRING, SimpleType.LONG, SimpleType.LONG, SimpleType.STRING };
COUNTER_COMPOSITE_TYPE = new CompositeType(SAMPLING_RESULTS_NAME, SAMPLING_RESULTS_NAME, COUNTER_NAMES, COUNTER_DESCS, counterTypes);
COUNTER_TYPE = new TabularType(SAMPLING_RESULTS_NAME, SAMPLING_RESULTS_NAME, COUNTER_COMPOSITE_TYPE, COUNTER_NAMES);
OpenType<?>[] samplerTypes = new OpenType[] { SimpleType.LONG, COUNTER_TYPE };
SAMPLING_RESULT = new CompositeType(SAMPLING_RESULTS_NAME, SAMPLING_RESULTS_NAME, SAMPLER_NAMES, SAMPLER_DESCS, samplerTypes);
} catch (OpenDataException e)
{
throw Throwables.propagate(e);
}
}
protected synchronized void startTableSampling(MultivaluedMap<String, String> queryParams) {
if (futureTableSamperResult != null) {
return;
}
futureTableSamperResult = service.submit(() -> {
tableSamplerResult = client.getJsonObj("column_family/toppartitions/" + getCFName(), queryParams);
return null;
});
}
/*
* Wait until the action is completed
* It is safe to call this method multiple times
*/
public synchronized void waitUntilSamplingCompleted() {
try {
if (futureTableSamperResult != null) {
futureTableSamperResult.get();
futureTableSamperResult = null;
}
} catch (InterruptedException | ExecutionException e) {
futureTableSamperResult = null;
throw new RuntimeException("Failed getting table statistics", e);
}
}
public static final Set<String> TYPE_NAMES = new HashSet<>(asList("ColumnFamilies", "IndexTables", "Tables"));
private static Map<String, ColumnFamilyStore> cf = new HashMap<String, ColumnFamilyStore>();
private static Timer timer = new Timer("Column Family");
public void log(String str) {
logger.finest(str);
logger.info(str);
}
public ColumnFamilyStore(APIClient client, String type, String keyspace, String name) {
super(client,
new TableMetrics(keyspace, name, false /* hardcoded for now */));
public static void register_mbeans() {
TimerTask taskToExecute = new CheckRegistration();
timer.schedule(taskToExecute, 100, INTERVAL);
}
public ColumnFamilyStore(String type, String keyspace, String name) {
this.type = type;
this.keyspace = keyspace;
this.name = name;
service = Executors.newSingleThreadExecutor();
}
public ColumnFamilyStore(APIClient client, ObjectName name) {
this(client, name.getKeyProperty("type"), name.getKeyProperty("keyspace"), name.getKeyProperty("columnfamily"));
mbeanName = getName(type, keyspace, name);
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName nameObj = new ObjectName(mbeanName);
mbs.registerMBean(this, nameObj);
} catch (Exception e) {
throw new RuntimeException(e);
}
metric = new ColumnFamilyMetrics(this);
}
/** true if this CFS contains secondary index data */
@ -182,96 +97,422 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
return keyspace + ":" + name;
}
private static ObjectName getName(String type, String keyspace, String name) throws MalformedObjectNameException {
return new ObjectName(
"org.apache.cassandra.db:type=" + type + ",keyspace=" + keyspace + ",columnfamily=" + name);
private static String getName(String type, String keyspace, String name) {
return "org.apache.cassandra.db:type=" + type + ",keyspace=" + keyspace
+ ",columnfamily=" + name;
}
public static RegistrationChecker createRegistrationChecker() {
return new RegistrationChecker() {
@Override
protected void doCheck(APIClient client, JmxMBeanServer server, EnumSet<RegistrationMode> mode)
throws OperationsException {
JsonArray mbeans = client.getJsonArray("/column_family/");
Set<ObjectName> all = new HashSet<ObjectName>();
for (int i = 0; i < mbeans.size(); i++) {
JsonObject mbean = mbeans.getJsonObject(i);
all.add(getName(mbean.getString("type"), mbean.getString("ks"), mbean.getString("cf")));
}
checkRegistration(server, all, mode,
n -> TYPE_NAMES.contains(n.getKeyProperty("type")), n -> new ColumnFamilyStore(client, n));
}
};
}
private static final class CheckRegistration extends TimerTask {
private APIClient c = new APIClient();
private int missed_response = 0;
// After MAX_RETRY retry we assume the API is not available
// and the jmx will shutdown
private static final int MAX_RETRY = 30;
@Override
public void run() {
try {
JsonArray mbeans = c.getJsonArray("/column_family/");
Set<String> all_cf = new HashSet<String>();
for (int i = 0; i < mbeans.size(); i++) {
JsonObject mbean = mbeans.getJsonObject(i);
String name = getName(mbean.getString("type"),
mbean.getString("ks"), mbean.getString("cf"));
if (!cf.containsKey(name)) {
ColumnFamilyStore cfs = new ColumnFamilyStore(
mbean.getString("type"), mbean.getString("ks"),
mbean.getString("cf"));
cf.put(name, cfs);
}
all_cf.add(name);
}
// removing deleted column family
for (String n : cf.keySet()) {
if (!all_cf.contains(n)) {
cf.remove(n);
}
}
missed_response = 0;
} catch (ProcessingException e) {
if (Throwables.getRootCause(e) instanceof ConnectException) {
if (missed_response++ > MAX_RETRY) {
System.err.println("API is not available, JMX is shuting down");
System.exit(-1);
}
} else {
// ignoring exceptions, will retry on the next interval
}
} catch (Exception e) {
// ignoring exceptions, will retry on the next interval
}
}
}
/**
* @return the name of the column family
*/
@Override
public String getColumnFamilyName() {
log(" getColumnFamilyName()");
return name;
}
/**
* Returns the total amount of data stored in the memtable, including column
* related overhead.
*
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableOnHeapSize
* @return The size in bytes.
* @deprecated
*/
@Deprecated
public long getMemtableDataSize() {
log(" getMemtableDataSize()");
return c.getLongValue("/column_family/metrics/memtable_on_heap_size/" + getCFName());
}
/**
* Returns the total number of columns present in the memtable.
*
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableColumnsCount
* @return The number of columns.
*/
@Deprecated
public long getMemtableColumnsCount() {
log(" getMemtableColumnsCount()");
return metric.memtableColumnsCount.value();
}
/**
* Returns the number of times that a flush has resulted in the memtable
* being switched out.
*
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableSwitchCount
* @return the number of memtable switches
*/
@Deprecated
public int getMemtableSwitchCount() {
log(" getMemtableSwitchCount()");
return c.getIntValue("/column_family/metrics/memtable_switch_count/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentSSTablesPerRead
* @return a histogram of the number of sstable data files accessed per
* read: reading this property resets it
*/
@Deprecated
public long[] getRecentSSTablesPerReadHistogram() {
log(" getRecentSSTablesPerReadHistogram()");
return metric.getRecentSSTablesPerRead();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#sstablesPerReadHistogram
* @return a histogram of the number of sstable data files accessed per read
*/
@Deprecated
public long[] getSSTablesPerReadHistogram() {
log(" getSSTablesPerReadHistogram()");
return metric.sstablesPerRead.getBuckets(false);
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return the number of read operations on this column family
*/
@Deprecated
public long getReadCount() {
log(" getReadCount()");
return c.getIntValue("/column_family/metrics/read/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return total read latency (divide by getReadCount() for average)
*/
@Deprecated
public long getTotalReadLatencyMicros() {
log(" getTotalReadLatencyMicros()");
return c.getLongValue("/column_family/metrics/read_latency/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getLifetimeReadLatencyHistogramMicros() {
log(" getLifetimeReadLatencyHistogramMicros()");
return metric.readLatency.totalLatencyHistogram.getBuckets(false);
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getRecentReadLatencyHistogramMicros() {
log(" getRecentReadLatencyHistogramMicros()");
return metric.readLatency.getRecentLatencyHistogram();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return average latency per read operation since the last call
*/
@Deprecated
public double getRecentReadLatencyMicros() {
log(" getRecentReadLatencyMicros()");
return metric.readLatency.getRecentLatency();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return the number of write operations on this column family
*/
@Deprecated
public long getWriteCount() {
log(" getWriteCount()");
return c.getLongValue("/column_family/metrics/write/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return total write latency (divide by getReadCount() for average)
*/
@Deprecated
public long getTotalWriteLatencyMicros() {
log(" getTotalWriteLatencyMicros()");
return c.getLongValue("/column_family/metrics/write_latency/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getLifetimeWriteLatencyHistogramMicros() {
log(" getLifetimeWriteLatencyHistogramMicros()");
return metric.writeLatency.totalLatencyHistogram.getBuckets(false);
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getRecentWriteLatencyHistogramMicros() {
log(" getRecentWriteLatencyHistogramMicros()");
return metric.writeLatency.getRecentLatencyHistogram();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return average latency per write operation since the last call
*/
@Deprecated
public double getRecentWriteLatencyMicros() {
log(" getRecentWriteLatencyMicros()");
return metric.writeLatency.getRecentLatency();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#pendingFlushes
* @return the estimated number of tasks pending for this column family
*/
@Deprecated
public int getPendingTasks() {
log(" getPendingTasks()");
return c.getIntValue("/column_family/metrics/pending_flushes/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#liveSSTableCount
* @return the number of SSTables on disk for this CF
*/
@Deprecated
public int getLiveSSTableCount() {
log(" getLiveSSTableCount()");
return c.getIntValue("/column_family/metrics/live_ss_table_count/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#liveDiskSpaceUsed
* @return disk space used by SSTables belonging to this CF
*/
@Deprecated
public long getLiveDiskSpaceUsed() {
log(" getLiveDiskSpaceUsed()");
return c.getLongValue("/column_family/metrics/live_disk_space_used/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#totalDiskSpaceUsed
* @return total disk space used by SSTables belonging to this CF, including
* obsolete ones waiting to be GC'd
*/
@Deprecated
public long getTotalDiskSpaceUsed() {
log(" getTotalDiskSpaceUsed()");
return c.getLongValue("/column_family/metrics/total_disk_space_used/" + getCFName());
}
/**
* force a major compaction of this column family
*/
public void forceMajorCompaction() throws ExecutionException, InterruptedException {
public void forceMajorCompaction()
throws ExecutionException, InterruptedException {
log(" forceMajorCompaction() throws ExecutionException, InterruptedException");
client.post("column_family/major_compaction/" + getCFName());
c.post("column_family/major_compaction/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#minRowSize
* @return the size of the smallest compacted row
*/
@Deprecated
public long getMinRowSize() {
log(" getMinRowSize()");
return c.getLongValue("/column_family/metrics/min_row_size/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#maxRowSize
* @return the size of the largest compacted row
*/
@Deprecated
public long getMaxRowSize() {
log(" getMaxRowSize()");
return c.getLongValue("/column_family/metrics/max_row_size/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#meanRowSize
* @return the average row size across all the sstables
*/
@Deprecated
public long getMeanRowSize() {
log(" getMeanRowSize()");
return c.getLongValue("/column_family/metrics/mean_row_size/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterFalsePositives
*/
@Deprecated
public long getBloomFilterFalsePositives() {
log(" getBloomFilterFalsePositives()");
return c.getLongValue("/column_family/metrics/bloom_filter_false_positives/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentBloomFilterFalsePositives
*/
@Deprecated
public long getRecentBloomFilterFalsePositives() {
log(" getRecentBloomFilterFalsePositives()");
return c.getLongValue("/column_family/metrics/recent_bloom_filter_false_positives/" +getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterFalseRatio
*/
@Deprecated
public double getBloomFilterFalseRatio() {
log(" getBloomFilterFalseRatio()");
return c.getDoubleValue("/column_family/metrics/bloom_filter_false_ratio/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentBloomFilterFalseRatio
*/
@Deprecated
public double getRecentBloomFilterFalseRatio() {
log(" getRecentBloomFilterFalseRatio()");
return c.getDoubleValue("/column_family/metrics/recent_bloom_filter_false_ratio/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterDiskSpaceUsed
*/
@Deprecated
public long getBloomFilterDiskSpaceUsed() {
log(" getBloomFilterDiskSpaceUsed()");
return c.getLongValue("/column_family/metrics/bloom_filter_disk_space_used/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterOffHeapMemoryUsed
*/
@Deprecated
public long getBloomFilterOffHeapMemoryUsed() {
log(" getBloomFilterOffHeapMemoryUsed()");
return c.getLongValue("/column_family/metrics/bloom_filter_off_heap_memory_used/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#indexSummaryOffHeapMemoryUsed
*/
@Deprecated
public long getIndexSummaryOffHeapMemoryUsed() {
log(" getIndexSummaryOffHeapMemoryUsed()");
return c.getLongValue("/column_family/metrics/index_summary_off_heap_memory_used/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#compressionMetadataOffHeapMemoryUsed
*/
@Deprecated
public long getCompressionMetadataOffHeapMemoryUsed() {
log(" getCompressionMetadataOffHeapMemoryUsed()");
return c.getLongValue("/column_family/metrics/compression_metadata_off_heap_memory_used/" + getCFName());
}
/**
* Gets the minimum number of sstables in queue before compaction kicks off
*/
@Override
public int getMinimumCompactionThreshold() {
log(" getMinimumCompactionThreshold()");
return client.getIntValue("column_family/minimum_compaction/" + getCFName());
return c.getIntValue("column_family/minimum_compaction/" + getCFName());
}
/**
* Sets the minimum number of sstables in queue before compaction kicks off
*/
@Override
public void setMinimumCompactionThreshold(int threshold) {
log(" setMinimumCompactionThreshold(int threshold)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("value", Integer.toString(threshold));
client.post("column_family/minimum_compaction/" + getCFName(), queryParams);
c.post("column_family/minimum_compaction/" + getCFName(), queryParams);
}
/**
* Gets the maximum number of sstables in queue before compaction kicks off
*/
@Override
public int getMaximumCompactionThreshold() {
log(" getMaximumCompactionThreshold()");
return client.getIntValue("column_family/maximum_compaction/" + getCFName());
return c.getIntValue("column_family/maximum_compaction/" + getCFName());
}
/**
* Sets the maximum and maximum number of SSTables in queue before
* compaction kicks off
*/
@Override
public void setCompactionThresholds(int minThreshold, int maxThreshold) {
log(" setCompactionThresholds(int minThreshold, int maxThreshold)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("minimum", Integer.toString(minThreshold));
queryParams.add("maximum", Integer.toString(maxThreshold));
client.post("column_family/compaction" + getCFName(), queryParams);
c.post("column_family/compaction" + getCFName(), queryParams);
}
/**
* Sets the maximum number of sstables in queue before compaction kicks off
*/
@Override
public void setMaximumCompactionThreshold(int threshold) {
log(" setMaximumCompactionThreshold(int threshold)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("value", Integer.toString(threshold));
client.post("column_family/maximum_compaction/" + getCFName(), queryParams);
c.post("column_family/maximum_compaction/" + getCFName(), queryParams);
}
/**
@ -284,7 +525,7 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
log(" setCompactionStrategyClass(String className)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("class_name", className);
client.post("column_family/compaction_strategy/" + getCFName(), queryParams);
c.post("column_family/compaction_strategy/" + getCFName(), queryParams);
}
/**
@ -292,16 +533,17 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
*/
public String getCompactionStrategyClass() {
log(" getCompactionStrategyClass()");
return client.getStringValue("column_family/compaction_strategy/" + getCFName());
return c.getStringValue(
"column_family/compaction_strategy/" + getCFName());
}
/**
* Get the compression parameters
*/
@Override
public Map<String, String> getCompressionParameters() {
log(" getCompressionParameters()");
return client.getMapStrValue("column_family/compression_parameters/" + getCFName());
return c.getMapStrValue(
"column_family/compression_parameters/" + getCFName());
}
/**
@ -310,49 +552,73 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
* @param opts
* map of string names to values
*/
@Override
public void setCompressionParameters(Map<String, String> opts) {
log(" setCompressionParameters(Map<String,String> opts)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("opts", APIClient.mapToString(opts));
client.post("column_family/compression_parameters/" + getCFName(), queryParams);
c.post("column_family/compression_parameters/" + getCFName(),
queryParams);
}
/**
* Set new crc check chance
*/
@Override
public void setCrcCheckChance(double crcCheckChance) {
log(" setCrcCheckChance(double crcCheckChance)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("check_chance", Double.toString(crcCheckChance));
client.post("column_family/crc_check_chance/" + getCFName(), queryParams);
c.post("column_family/crc_check_chance/" + getCFName(), queryParams);
}
@Override
public boolean isAutoCompactionDisabled() {
log(" isAutoCompactionDisabled()");
return !client.getBooleanValue("column_family/autocompaction/" + getCFName());
return c.getBooleanValue("column_family/autocompaction/" + getCFName());
}
/** Number of tombstoned cells retreived during the last slicequery */
@Deprecated
public double getTombstonesPerSlice() {
log(" getTombstonesPerSlice()");
return client.getDoubleValue("");
return c.getDoubleValue("");
}
/** Number of live cells retreived during the last slicequery */
@Deprecated
public double getLiveCellsPerSlice() {
log(" getLiveCellsPerSlice()");
return client.getDoubleValue("");
return c.getDoubleValue("");
}
@Override
public long estimateKeys() {
log(" estimateKeys()");
return client.getLongValue("column_family/estimate_keys/" + getCFName());
return c.getLongValue("column_family/estimate_keys/" + getCFName());
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#estimatedRowSizeHistogram
*/
@Deprecated
public long[] getEstimatedRowSizeHistogram() {
log(" getEstimatedRowSizeHistogram()");
return metric.estimatedRowSizeHistogram.value();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#estimatedColumnCountHistogram
*/
@Deprecated
public long[] getEstimatedColumnCountHistogram() {
log(" getEstimatedColumnCountHistogram()");
return metric.estimatedColumnCountHistogram.value();
}
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#compressionRatio
*/
@Deprecated
public double getCompressionRatio() {
log(" getCompressionRatio()");
return c.getDoubleValue("/column_family/metrics/compression_ratio/" + getCFName());
}
/**
@ -360,10 +626,9 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
*
* @return list of the index names
*/
@Override
public List<String> getBuiltIndexes() {
log(" getBuiltIndexes()");
return client.getListStrValue("column_family/built_indexes/" + getCFName());
return c.getListStrValue("column_family/built_indexes/" + getCFName());
}
/**
@ -372,49 +637,30 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
* @param key
* @return list of filenames containing the key
*/
@Override
public List<String> getSSTablesForKey(String key) {
log(" getSSTablesForKey(String key)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("key", key);
return client.getListStrValue("column_family/sstables/by_key/" + getCFName(), queryParams);
return c.getListStrValue("column_family/sstables/by_key/" + getCFName(),
queryParams);
}
/**
* Returns a list of filenames that contain the given key on this node
* @param key
* @param hexFormat if key is in hex string format
* @return list of filenames containing the key
*/
@Override
public List<String> getSSTablesForKey(String key, boolean hexFormat)
{
log(" getSSTablesForKey(String key)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("key", key);
if (hexFormat) {
queryParams.add("format", "hex");
}
return client.getListStrValue("column_family/sstables/by_key/" + getCFName(), queryParams);
}
/**
* Scan through Keyspace/ColumnFamily's data directory determine which
* SSTables should be loaded and load them
*/
@Override
public void loadNewSSTables() {
log(" loadNewSSTables()");
client.post("column_family/sstable/" + getCFName());
c.post("column_family/sstable/" + getCFName());
}
/**
* @return the number of SSTables in L0. Always return 0 if Leveled
* compaction is not enabled.
*/
@Override
public int getUnleveledSSTables() {
log(" getUnleveledSSTables()");
return client.getIntValue("column_family/sstables/unleveled/" + getCFName());
return c.getIntValue("column_family/sstables/unleveled/" + getCFName());
}
/**
@ -422,16 +668,10 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
* used. array index corresponds to level(int[0] is for level 0,
* ...).
*/
@Override
public int[] getSSTableCountPerLevel() {
log(" getSSTableCountPerLevel()");
int[] res = client.getIntArrValue("column_family/sstables/per_level/" + getCFName());
if (res.length == 0) {
// no sstable count
// should return null
return null;
}
return res;
return c.getIntArrValue(
"column_family/sstables/per_level/" + getCFName());
}
/**
@ -440,20 +680,18 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
*
* @return ratio
*/
@Override
public double getDroppableTombstoneRatio() {
log(" getDroppableTombstoneRatio()");
return client.getDoubleValue("column_family/droppable_ratio/" + getCFName());
return c.getDoubleValue("column_family/droppable_ratio/" + getCFName());
}
/**
* @return the size of SSTables in "snapshots" subdirectory which aren't
* live anymore
*/
@Override
public long trueSnapshotsSize() {
log(" trueSnapshotsSize()");
return client.getLongValue("column_family/metrics/snapshots_size/" + getCFName());
return c.getLongValue("column_family/snapshots_size/" + getCFName());
}
public String getKeyspace() {
@ -461,104 +699,48 @@ public class ColumnFamilyStore extends MetricsMBean implements ColumnFamilyStore
}
@Override
public String getTableName() {
log(" getTableName()");
return name;
public long getRangeCount() {
log("getRangeCount()");
return metric.rangeLatency.latency.count();
}
@Override
public void forceMajorCompaction(boolean splitOutput) throws ExecutionException, InterruptedException {
log(" forceMajorCompaction(boolean) throws ExecutionException, InterruptedException");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.putSingle("value", valueOf(splitOutput));
client.post("column_family/major_compaction/" + getCFName(), queryParams);
public long getTotalRangeLatencyMicros() {
log("getTotalRangeLatencyMicros()");
return metric.rangeLatency.totalLatency.count();
}
@Override
public void setCompactionParametersJson(String options) {
log(" setCompactionParametersJson");
JsonReader reader = Json.createReaderFactory(null).createReader(new StringReader(options));
setCompactionParameters(
reader.readObject().entrySet().stream().collect(toMap(Map.Entry::getKey, e -> e.toString())));
public long[] getLifetimeRangeLatencyHistogramMicros() {
log("getLifetimeRangeLatencyHistogramMicros()");
return metric.rangeLatency.totalLatencyHistogram.getBuckets(false);
}
@Override
public String getCompactionParametersJson() {
log(" getCompactionParametersJson");
JsonObjectBuilder b = createObjectBuilder();
getCompactionParameters().forEach(b::add);
return b.build().toString();
public long[] getRecentRangeLatencyHistogramMicros() {
log("getRecentRangeLatencyHistogramMicros()");
return metric.rangeLatency.getRecentLatencyHistogram();
}
@Override
public void setCompactionParameters(Map<String, String> options) {
for (Map.Entry<String, String> e : options.entrySet()) {
// See below
if ("class".equals(e.getKey())) {
setCompactionStrategyClass(e.getValue());
} else {
throw new IllegalArgumentException(e.getKey());
}
}
public double getRecentRangeLatencyMicros() {
log("getRecentRangeLatencyMicros()");
return metric.rangeLatency.getRecentLatency();
}
@Override
public Map<String, String> getCompactionParameters() {
// We only currently support class. Here could have been a call that can
// be expanded only on the server side, but that raises controversy.
// Lets add some technical debt instead.
return Collections.singletonMap("class", getCompactionStrategyClass());
}
@Override
public boolean isCompactionDiskSpaceCheckEnabled() {
public void beginLocalSampling(String sampler, int capacity) {
// TODO Auto-generated method stub
log(" isCompactionDiskSpaceCheckEnabled()");
return false;
log("beginLocalSampling()");
}
@Override
public void compactionDiskSpaceCheck(boolean enable) {
public CompositeData finishLocalSampling(String sampler, int count)
throws OpenDataException {
// TODO Auto-generated method stub
log(" compactionDiskSpaceCheck()");
log("finishLocalSampling()");
return null;
}
@Override
public void beginLocalSampling(String sampler_base, int capacity) {
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("capacity", Integer.toString(capacity));
if (sampler_base.contains(":")) {
String[] parts = sampler_base.split(":");
queryParams.add("duration", parts[1]);
} else {
queryParams.add("duration", "10000");
}
startTableSampling(queryParams);
log(" beginLocalSampling()");
}
@Override
public CompositeData finishLocalSampling(String samplerType, int count) throws OpenDataException {
log(" finishLocalSampling()");
waitUntilSamplingCompleted();
TabularDataSupport result = new TabularDataSupport(COUNTER_TYPE);
JsonArray counters = tableSamplerResult.getJsonArray((samplerType.equalsIgnoreCase("reads")) ? "read" : "write");
long cardinality = tableSamplerResult.getJsonNumber((samplerType.equalsIgnoreCase("reads")) ? "read_cardinality" : "write_cardinality").longValue();
long size = 0;
if (counters != null) {
size = (count > counters.size()) ? counters.size() : count;
for (int i = 0; i < size; i++) {
JsonObject counter = counters.getJsonObject(i);
result.put(new CompositeDataSupport(COUNTER_COMPOSITE_TYPE, COUNTER_NAMES,
new Object[] { counter.getString("partition"), // raw
counter.getJsonNumber("count").longValue(), // count
counter.getJsonNumber("error").longValue(), // error
counter.getString("partition") })); // string
}
}
return new CompositeDataSupport(SAMPLING_RESULT, SAMPLER_NAMES, new Object[] { cardinality, result });
}
}

View File

@ -17,7 +17,6 @@
*/
package org.apache.cassandra.db;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@ -28,28 +27,263 @@ import javax.management.openmbean.OpenDataException;
/**
* The MBean interface for ColumnFamilyStore
*/
public interface ColumnFamilyStoreMBean {
public interface ColumnFamilyStoreMBean
{
/**
* @return the name of the column family
*/
@Deprecated
public String getColumnFamilyName();
public String getTableName();
/**
* Returns the total amount of data stored in the memtable, including
* column related overhead.
*
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableOnHeapSize
* @return The size in bytes.
* @deprecated
*/
@Deprecated
public long getMemtableDataSize();
/**
* Returns the total number of columns present in the memtable.
*
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableColumnsCount
* @return The number of columns.
*/
@Deprecated
public long getMemtableColumnsCount();
/**
* Returns the number of times that a flush has resulted in the
* memtable being switched out.
*
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableSwitchCount
* @return the number of memtable switches
*/
@Deprecated
public int getMemtableSwitchCount();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentSSTablesPerRead
* @return a histogram of the number of sstable data files accessed per read: reading this property resets it
*/
@Deprecated
public long[] getRecentSSTablesPerReadHistogram();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#sstablesPerReadHistogram
* @return a histogram of the number of sstable data files accessed per read
*/
@Deprecated
public long[] getSSTablesPerReadHistogram();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return the number of read operations on this column family
*/
@Deprecated
public long getReadCount();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return total read latency (divide by getReadCount() for average)
*/
@Deprecated
public long getTotalReadLatencyMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getLifetimeReadLatencyHistogramMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getRecentReadLatencyHistogramMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency
* @return average latency per read operation since the last call
*/
@Deprecated
public double getRecentReadLatencyMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return the number of write operations on this column family
*/
@Deprecated
public long getWriteCount();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return total write latency (divide by getReadCount() for average)
*/
@Deprecated
public long getTotalWriteLatencyMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getLifetimeWriteLatencyHistogramMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getRecentWriteLatencyHistogramMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency
* @return average latency per write operation since the last call
*/
@Deprecated
public double getRecentWriteLatencyMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#rangeLatency
* @return the number of range slice operations on this column family
*/
@Deprecated
public long getRangeCount();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#rangeLatency
* @return total range slice latency (divide by getRangeCount() for average)
*/
@Deprecated
public long getTotalRangeLatencyMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#rangeLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getLifetimeRangeLatencyHistogramMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#rangeLatency
* @return an array representing the latency histogram
*/
@Deprecated
public long[] getRecentRangeLatencyHistogramMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#rangeLatency
* @return average latency per range slice operation since the last call
*/
@Deprecated
public double getRecentRangeLatencyMicros();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#pendingFlushes
* @return the estimated number of tasks pending for this column family
*/
@Deprecated
public int getPendingTasks();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#liveSSTableCount
* @return the number of SSTables on disk for this CF
*/
@Deprecated
public int getLiveSSTableCount();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#liveDiskSpaceUsed
* @return disk space used by SSTables belonging to this CF
*/
@Deprecated
public long getLiveDiskSpaceUsed();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#totalDiskSpaceUsed
* @return total disk space used by SSTables belonging to this CF, including obsolete ones waiting to be GC'd
*/
@Deprecated
public long getTotalDiskSpaceUsed();
/**
* force a major compaction of this column family
*
* @param splitOutput
* true if the output of the major compaction should be split in
* several sstables
*/
public void forceMajorCompaction(boolean splitOutput) throws ExecutionException, InterruptedException;
public void forceMajorCompaction() throws ExecutionException, InterruptedException;
// NOT even default-throw implementing
// forceCompactionForTokenRange
// as this is clearly a misplaced method that should not be in the mbean interface
// (uses internal cassandra types)
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#minRowSize
* @return the size of the smallest compacted row
*/
@Deprecated
public long getMinRowSize();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#maxRowSize
* @return the size of the largest compacted row
*/
@Deprecated
public long getMaxRowSize();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#meanRowSize
* @return the average row size across all the sstables
*/
@Deprecated
public long getMeanRowSize();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterFalsePositives
*/
@Deprecated
public long getBloomFilterFalsePositives();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentBloomFilterFalsePositives
*/
@Deprecated
public long getRecentBloomFilterFalsePositives();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterFalseRatio
*/
@Deprecated
public double getBloomFilterFalseRatio();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentBloomFilterFalseRatio
*/
@Deprecated
public double getRecentBloomFilterFalseRatio();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterDiskSpaceUsed
*/
@Deprecated
public long getBloomFilterDiskSpaceUsed();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterOffHeapMemoryUsed
*/
@Deprecated
public long getBloomFilterOffHeapMemoryUsed();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#indexSummaryOffHeapMemoryUsed
*/
@Deprecated
public long getIndexSummaryOffHeapMemoryUsed();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#compressionMetadataOffHeapMemoryUsed
*/
@Deprecated
public long getCompressionMetadataOffHeapMemoryUsed();
/**
* Gets the minimum number of sstables in queue before compaction kicks off
@ -67,8 +301,7 @@ public interface ColumnFamilyStoreMBean {
public int getMaximumCompactionThreshold();
/**
* Sets the maximum and maximum number of SSTables in queue before
* compaction kicks off
* Sets the maximum and maximum number of SSTables in queue before compaction kicks off
*/
public void setCompactionThresholds(int minThreshold, int maxThreshold);
@ -78,44 +311,26 @@ public interface ColumnFamilyStoreMBean {
public void setMaximumCompactionThreshold(int threshold);
/**
* Sets the compaction parameters locally for this node
*
* Note that this will be set until an ALTER with compaction = {..} is
* executed or the node is restarted
*
* @param options
* compaction options with the same syntax as when doing ALTER
* ... WITH compaction = {..}
* Sets the compaction strategy by class name
* @param className the name of the compaction strategy class
*/
public void setCompactionParametersJson(String options);
public String getCompactionParametersJson();
public void setCompactionStrategyClass(String className);
/**
* Sets the compaction parameters locally for this node
*
* Note that this will be set until an ALTER with compaction = {..} is
* executed or the node is restarted
*
* @param options
* compaction options map
* Gets the compaction strategy class name
*/
public void setCompactionParameters(Map<String, String> options);
public Map<String, String> getCompactionParameters();
public String getCompactionStrategyClass();
/**
* Get the compression parameters
*/
public Map<String, String> getCompressionParameters();
public Map<String,String> getCompressionParameters();
/**
* Set the compression parameters
*
* @param opts
* map of string names to values
* @param opts map of string names to values
*/
public void setCompressionParameters(Map<String, String> opts);
public void setCompressionParameters(Map<String,String> opts);
/**
* Set new crc check chance
@ -124,92 +339,81 @@ public interface ColumnFamilyStoreMBean {
public boolean isAutoCompactionDisabled();
/** Number of tombstoned cells retreived during the last slicequery */
@Deprecated
public double getTombstonesPerSlice();
/** Number of live cells retreived during the last slicequery */
@Deprecated
public double getLiveCellsPerSlice();
public long estimateKeys();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#estimatedRowSizeHistogram
*/
@Deprecated
public long[] getEstimatedRowSizeHistogram();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#estimatedColumnCountHistogram
*/
@Deprecated
public long[] getEstimatedColumnCountHistogram();
/**
* @see org.apache.cassandra.metrics.ColumnFamilyMetrics#compressionRatio
*/
@Deprecated
public double getCompressionRatio();
/**
* Returns a list of the names of the built column indexes for current store
*
* @return list of the index names
*/
public List<String> getBuiltIndexes();
/**
* Returns a list of filenames that contain the given key on this node
*
* @param key
* @return list of filenames containing the key
*/
public List<String> getSSTablesForKey(String key);
/**
* Returns a list of filenames that contain the given key on this node
* @param key
* @param hexFormat if key is in hex string format
* @return list of filenames containing the key
*/
public List<String> getSSTablesForKey(String key, boolean hexFormat);
/**
* Scan through Keyspace/ColumnFamily's data directory determine which
* SSTables should be loaded and load them
* Scan through Keyspace/ColumnFamily's data directory
* determine which SSTables should be loaded and load them
*/
public void loadNewSSTables();
/**
* @return the number of SSTables in L0. Always return 0 if Leveled
* compaction is not enabled.
* @return the number of SSTables in L0. Always return 0 if Leveled compaction is not enabled.
*/
public int getUnleveledSSTables();
/**
* @return sstable count for each level. null unless leveled compaction is
* used. array index corresponds to level(int[0] is for level 0,
* ...).
* @return sstable count for each level. null unless leveled compaction is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public int[] getSSTableCountPerLevel();
/**
* @return sstable fanout size for level compaction strategy.
*/
default public int getLevelFanoutSize() {
// TODO: implement for real. This is sort of default.
return 10;
}
/**
* Get the ratio of droppable tombstones to real columns (and non-droppable
* tombstones)
*
* Get the ratio of droppable tombstones to real columns (and non-droppable tombstones)
* @return ratio
*/
public double getDroppableTombstoneRatio();
/**
* @return the size of SSTables in "snapshots" subdirectory which aren't
* live anymore
* @return the size of SSTables in "snapshots" subdirectory which aren't live anymore
*/
public long trueSnapshotsSize();
/**
* begin sampling for a specific sampler with a given capacity. The
* cardinality may be larger than the capacity, but depending on the use
* case it may affect its accuracy
* begin sampling for a specific sampler with a given capacity. The cardinality may
* be larger than the capacity, but depending on the use case it may affect its accuracy
*/
public void beginLocalSampling(String sampler, int capacity);
/**
* @return top <i>count</i> items for the sampler since beginLocalSampling
* was called
* @return top <i>count</i> items for the sampler since beginLocalSampling was called
*/
public CompositeData finishLocalSampling(String sampler, int count) throws OpenDataException;
/*
* Is Compaction space check enabled
*/
public boolean isCompactionDiskSpaceCheckEnabled();
/*
* Enable/Disable compaction space check
*/
public void compactionDiskSpaceCheck(boolean enable);
}

View File

@ -22,39 +22,85 @@
*/
package org.apache.cassandra.db.commitlog;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.util.*;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.cassandra.metrics.CommitLogMetrics;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.MetricsMBean;
import com.cloudius.urchin.api.APIClient;
/*
* Commit Log tracks every write operation into the system. The aim of the commit log is to be able to
* successfully recover data that was not stored to disk via the Memtable.
*/
public class CommitLog extends MetricsMBean implements CommitLogMBean {
public class CommitLog implements CommitLogMBean {
CommitLogMetrics metrics = new CommitLogMetrics();
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(CommitLog.class.getName());
private APIClient c = new APIClient();
public void log(String str) {
logger.finest(str);
logger.info(str);
}
public CommitLog(APIClient client) {
super("org.apache.cassandra.db:type=Commitlog", client, new CommitLogMetrics());
private static final CommitLog instance = new CommitLog();
public static CommitLog getInstance() {
return instance;
}
private CommitLog() {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
mbs.registerMBean(this,
new ObjectName("org.apache.cassandra.db:type=Commitlog"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Get the number of completed tasks
*
* @see org.apache.cassandra.metrics.CommitLogMetrics#completedTasks
*/
@Deprecated
public long getCompletedTasks() {
log(" getCompletedTasks()");
return c.getLongValue("");
}
/**
* Get the number of tasks waiting to be executed
*
* @see org.apache.cassandra.metrics.CommitLogMetrics#pendingTasks
*/
@Deprecated
public long getPendingTasks() {
log(" getPendingTasks()");
return c.getLongValue("");
}
/**
* Get the current size used by all the commitlog segments.
*
* @see org.apache.cassandra.metrics.CommitLogMetrics#totalCommitLogSize
*/
@Deprecated
public long getTotalCommitlogSize() {
log(" getTotalCommitlogSize()");
return c.getLongValue("");
}
/**
* Recover a single file.
*/
@Override
public void recover(String path) throws IOException {
log(" recover(String path) throws IOException");
}
@ -63,10 +109,9 @@ public class CommitLog extends MetricsMBean implements CommitLogMBean {
* @return file names (not full paths) of active commit log segments
* (segments containing unflushed data)
*/
@Override
public List<String> getActiveSegmentNames() {
log(" getActiveSegmentNames()");
List<String> lst = client.getListStrValue("/commitlog/segments/active");
List<String> lst = c.getListStrValue("/commitlog/segments/active");
Set<String> set = new HashSet<String>();
for (String l : lst) {
String name = l.substring(l.lastIndexOf("/") + 1, l.length());
@ -79,10 +124,9 @@ public class CommitLog extends MetricsMBean implements CommitLogMBean {
* @return Files which are pending for archival attempt. Does NOT include
* failed archive attempts.
*/
@Override
public List<String> getArchivingSegmentNames() {
log(" getArchivingSegmentNames()");
List<String> lst = client.getListStrValue("/commitlog/segments/archiving");
List<String> lst = c.getListStrValue("/commitlog/segments/archiving");
Set<String> set = new HashSet<String>();
for (String l : lst) {
String name = l.substring(l.lastIndexOf("/") + 1, l.length());
@ -95,54 +139,35 @@ public class CommitLog extends MetricsMBean implements CommitLogMBean {
public String getArchiveCommand() {
// TODO Auto-generated method stub
log(" getArchiveCommand()");
return client.getStringValue("");
return c.getStringValue("");
}
@Override
public String getRestoreCommand() {
// TODO Auto-generated method stub
log(" getRestoreCommand()");
return client.getStringValue("");
return c.getStringValue("");
}
@Override
public String getRestoreDirectories() {
// TODO Auto-generated method stub
log(" getRestoreDirectories()");
return client.getStringValue("");
return c.getStringValue("");
}
@Override
public long getRestorePointInTime() {
// TODO Auto-generated method stub
log(" getRestorePointInTime()");
return client.getLongValue("");
return c.getLongValue("");
}
@Override
public String getRestorePrecision() {
// TODO Auto-generated method stub
log(" getRestorePrecision()");
return client.getStringValue("");
return c.getStringValue("");
}
@Override
public long getActiveContentSize() {
// scylla does not compress commit log, so this is equivalent
return getActiveOnDiskSize();
}
@Override
public long getActiveOnDiskSize() {
return client.getLongValue("/commitlog/metrics/total_commit_log_size");
}
@Override
public Map<String, Double> getActiveSegmentCompressionRatios() {
HashMap<String, Double> res = new HashMap<>();
for (String name : getActiveSegmentNames()) {
res.put(name, 1.0);
}
return res;
}
}

View File

@ -19,9 +19,32 @@ package org.apache.cassandra.db.commitlog;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface CommitLogMBean {
/**
* Get the number of completed tasks
*
* @see org.apache.cassandra.metrics.CommitLogMetrics#completedTasks
*/
@Deprecated
public long getCompletedTasks();
/**
* Get the number of tasks waiting to be executed
*
* @see org.apache.cassandra.metrics.CommitLogMetrics#pendingTasks
*/
@Deprecated
public long getPendingTasks();
/**
* Get the current size used by all the commitlog segments.
*
* @see org.apache.cassandra.metrics.CommitLogMetrics#totalCommitLogSize
*/
@Deprecated
public long getTotalCommitlogSize();
/**
* Command to execute to archive a commitlog segment. Blank to disabled.
*/
@ -69,21 +92,4 @@ public interface CommitLogMBean {
* failed archive attempts.
*/
public List<String> getArchivingSegmentNames();
/**
* @return The size of the mutations in all active commit log segments
* (uncompressed).
*/
public long getActiveContentSize();
/**
* @return The space taken on disk by the commit log (compressed).
*/
public long getActiveOnDiskSize();
/**
* @return A map between active log segments and the compression ratio
* achieved for each.
*/
public Map<String, Double> getActiveSegmentCompressionRatios();
}

View File

@ -1,98 +0,0 @@
/*
* 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.
*/
/*
* Copyright 2015 ScyllaDB
*
* Modified by ScyllaDB
*/
package org.apache.cassandra.db.compaction;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import com.google.common.base.Throwables;
public class CompactionHistoryTabularData {
private static final String[] ITEM_NAMES = new String[] { "id", "keyspace_name", "columnfamily_name",
"compacted_at", "bytes_in", "bytes_out", "rows_merged" };
private static final String[] ITEM_DESCS = new String[] { "time uuid", "keyspace name", "column family name",
"compaction finished at", "total bytes in", "total bytes out", "total rows merged" };
private static final String TYPE_NAME = "CompactionHistory";
private static final String ROW_DESC = "CompactionHistory";
private static final OpenType<?>[] ITEM_TYPES;
private static final CompositeType COMPOSITE_TYPE;
private static final TabularType TABULAR_TYPE;
static {
try {
ITEM_TYPES = new OpenType[] { SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.LONG,
SimpleType.LONG, SimpleType.LONG, SimpleType.STRING };
COMPOSITE_TYPE = new CompositeType(TYPE_NAME, ROW_DESC, ITEM_NAMES, ITEM_DESCS, ITEM_TYPES);
TABULAR_TYPE = new TabularType(TYPE_NAME, ROW_DESC, COMPOSITE_TYPE, ITEM_NAMES);
} catch (OpenDataException e) {
throw Throwables.propagate(e);
}
}
public static TabularData from(JsonArray resultSet) throws OpenDataException {
TabularDataSupport result = new TabularDataSupport(TABULAR_TYPE);
for (int i = 0; i < resultSet.size(); i++) {
JsonObject row = resultSet.getJsonObject(i);
String id = row.getString("id");
String ksName = row.getString("ks");
String cfName = row.getString("cf");
long compactedAt = row.getJsonNumber("compacted_at").longValue();
long bytesIn = row.getJsonNumber("bytes_in").longValue();
long bytesOut = row.getJsonNumber("bytes_out").longValue();
JsonArray merged = row.getJsonArray("rows_merged");
StringBuilder sb = new StringBuilder();
if (merged != null) {
sb.append('{');
for (int m = 0; m < merged.size(); m++) {
JsonObject entry = merged.getJsonObject(m);
if (m > 0) {
sb.append(',');
}
sb.append(entry.getString("key")).append(':').append(entry.getString("value"));
}
sb.append('}');
}
result.put(new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES,
new Object[] { id, ksName, cfName, compactedAt, bytesIn, bytesOut, sb.toString() }));
}
return result;
}
}

View File

@ -17,23 +17,18 @@
*/
package org.apache.cassandra.db.compaction;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.lang.management.ManagementFactory;
import java.util.*;
import javax.management.openmbean.OpenDataException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.openmbean.TabularData;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.cassandra.metrics.CompactionMetrics;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.MetricsMBean;
import com.cloudius.urchin.api.APIClient;
/**
* A singleton which manages a private executor of ongoing compactions.
@ -45,58 +40,91 @@ import com.scylladb.jmx.metrics.MetricsMBean;
/*
* Copyright 2015 Cloudius Systems
*
*
* Modified by Cloudius Systems
*/
public class CompactionManager extends MetricsMBean implements CompactionManagerMBean {
public class CompactionManager implements CompactionManagerMBean {
public static final String MBEAN_OBJECT_NAME = "org.apache.cassandra.db:type=CompactionManager";
private static final Logger logger = Logger.getLogger(CompactionManager.class.getName());
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(CompactionManager.class.getName());
public static final CompactionManager instance;
private APIClient c = new APIClient();
CompactionMetrics metrics = new CompactionMetrics();
public void log(String str) {
logger.finest(str);
logger.info(str);
}
public CompactionManager(APIClient client) {
super(MBEAN_OBJECT_NAME, client, new CompactionMetrics());
static {
instance = new CompactionManager();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
mbs.registerMBean(instance, new ObjectName(MBEAN_OBJECT_NAME));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static CompactionManager getInstance() {
return instance;
}
/** List of running compaction objects. */
@Override
public List<Map<String, String>> getCompactions() {
log(" getCompactions()");
List<Map<String, String>> results = new ArrayList<Map<String, String>>();
JsonArray compactions = client.getJsonArray("compaction_manager/compactions");
for (int i = 0; i < compactions.size(); i++) {
JsonObject compaction = compactions.getJsonObject(i);
Map<String, String> result = new HashMap<String, String>();
result.put("total", Long.toString(compaction.getJsonNumber("total").longValue()));
result.put("completed", Long.toString(compaction.getJsonNumber("completed").longValue()));
result.put("taskType", compaction.getString("task_type"));
result.put("keyspace", compaction.getString("ks"));
result.put("columnfamily", compaction.getString("cf"));
result.put("unit", compaction.getString("unit"));
result.put("compactionId", (compaction.containsKey("id"))? compaction.getString("id") : "<none>");
results.add(result);
}
return results;
return c.getListMapStrValue("compaction_manager/compactions");
}
/** List of running compaction summary strings. */
@Override
public List<String> getCompactionSummary() {
log(" getCompactionSummary()");
return client.getListStrValue("compaction_manager/compaction_summary");
return c.getListStrValue("compaction_manager/compaction_summary");
}
/** compaction history **/
@Override
public TabularData getCompactionHistory() {
log(" getCompactionHistory()");
try {
return CompactionHistoryTabularData.from(client.getJsonArray("/compaction_manager/compaction_history"));
} catch (OpenDataException e) {
return null;
}
return c.getCQLResult("SELECT * from system.compaction_history");
}
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#pendingTasks
* @return estimated number of compactions remaining to perform
*/
@Deprecated
public int getPendingTasks() {
log(" getPendingTasks()");
return c.getIntValue("");
}
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#completedTasks
* @return number of completed compactions since server [re]start
*/
@Deprecated
public long getCompletedTasks() {
log(" getCompletedTasks()");
return c.getLongValue("");
}
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#bytesCompacted
* @return total number of bytes compacted since server [re]start
*/
@Deprecated
public long getTotalBytesCompacted() {
log(" getTotalBytesCompacted()");
return c.getLongValue("");
}
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#totalCompactionsCompleted
* @return total number of compactions since server [re]start
*/
@Deprecated
public long getTotalCompactionsCompleted() {
log(" getTotalCompactionsCompleted()");
return c.getLongValue("");
}
/**
@ -110,12 +138,12 @@ public class CompactionManager extends MetricsMBean implements CompactionManager
* contain keyspace and columnfamily name in path(for 2.1+) or
* file name itself.
*/
@Override
public void forceUserDefinedCompaction(String dataFiles) {
log(" forceUserDefinedCompaction(String dataFiles)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("dataFiles", dataFiles);
client.post("compaction_manager/force_user_defined_compaction", queryParams);
c.post("compaction_manager/compaction_manager/force_user_defined_compaction",
queryParams);
}
/**
@ -125,30 +153,28 @@ public class CompactionManager extends MetricsMBean implements CompactionManager
* the type of compaction to stop. Can be one of: - COMPACTION -
* VALIDATION - CLEANUP - SCRUB - INDEX_BUILD
*/
@Override
public void stopCompaction(String type) {
log(" stopCompaction(String type)");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("type", type);
client.post("compaction_manager/stop_compaction", queryParams);
c.post("compaction_manager/compaction_manager/stop_compaction",
queryParams);
}
/**
* Returns core size of compaction thread pool
*/
@Override
public int getCoreCompactorThreads() {
log(" getCoreCompactorThreads()");
return client.getIntValue("");
return c.getIntValue("");
}
/**
* Allows user to resize maximum size of the compaction thread pool.
*
*
* @param number
* New maximum of compaction threads
*/
@Override
public void setCoreCompactorThreads(int number) {
log(" setCoreCompactorThreads(int number)");
}
@ -156,19 +182,17 @@ public class CompactionManager extends MetricsMBean implements CompactionManager
/**
* Returns maximum size of compaction thread pool
*/
@Override
public int getMaximumCompactorThreads() {
log(" getMaximumCompactorThreads()");
return client.getIntValue("");
return c.getIntValue("");
}
/**
* Allows user to resize maximum size of the compaction thread pool.
*
*
* @param number
* New maximum of compaction threads
*/
@Override
public void setMaximumCompactorThreads(int number) {
log(" setMaximumCompactorThreads(int number)");
}
@ -176,19 +200,17 @@ public class CompactionManager extends MetricsMBean implements CompactionManager
/**
* Returns core size of validation thread pool
*/
@Override
public int getCoreValidationThreads() {
log(" getCoreValidationThreads()");
return client.getIntValue("");
return c.getIntValue("");
}
/**
* Allows user to resize maximum size of the compaction thread pool.
*
*
* @param number
* New maximum of compaction threads
*/
@Override
public void setCoreValidationThreads(int number) {
log(" setCoreValidationThreads(int number)");
}
@ -196,31 +218,19 @@ public class CompactionManager extends MetricsMBean implements CompactionManager
/**
* Returns size of validator thread pool
*/
@Override
public int getMaximumValidatorThreads() {
log(" getMaximumValidatorThreads()");
return client.getIntValue("");
return c.getIntValue("");
}
/**
* Allows user to resize maximum size of the validator thread pool.
*
*
* @param number
* New maximum of validator threads
*/
@Override
public void setMaximumValidatorThreads(int number) {
log(" setMaximumValidatorThreads(int number)");
}
@Override
public void stopCompactionById(String compactionId) {
// scylla does not have neither compaction ids nor the file described
// in:
// "Ids can be found in the transaction log files whose name starts with
// compaction_, located in the table transactions folder"
// (nodetool)
// TODO: throw?
log(" stopCompactionById");
}
}

View File

@ -19,7 +19,6 @@ package org.apache.cassandra.db.compaction;
import java.util.List;
import java.util.Map;
import javax.management.openmbean.TabularData;
public interface CompactionManagerMBean {
@ -32,6 +31,34 @@ public interface CompactionManagerMBean {
/** compaction history **/
public TabularData getCompactionHistory();
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#pendingTasks
* @return estimated number of compactions remaining to perform
*/
@Deprecated
public int getPendingTasks();
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#completedTasks
* @return number of completed compactions since server [re]start
*/
@Deprecated
public long getCompletedTasks();
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#bytesCompacted
* @return total number of bytes compacted since server [re]start
*/
@Deprecated
public long getTotalBytesCompacted();
/**
* @see org.apache.cassandra.metrics.CompactionMetrics#totalCompactionsCompleted
* @return total number of compactions since server [re]start
*/
@Deprecated
public long getTotalCompactionsCompleted();
/**
* Triggers the compaction of user specified sstables. You can specify files
* from various keyspaces and columnfamilies. If you do so, user defined
@ -43,37 +70,15 @@ public interface CompactionManagerMBean {
*/
public void forceUserDefinedCompaction(String dataFiles);
/**
* Triggers the cleanup of user specified sstables.
* You can specify files from various keyspaces and columnfamilies.
* If you do so, cleanup is performed each file individually
*
* @param dataFiles a comma separated list of sstable file to cleanup.
* must contain keyspace and columnfamily name in path(for 2.1+) or file name itself.
*/
default public void forceUserDefinedCleanup(String dataFiles) {
throw new UnsupportedOperationException();
}
/**
* Stop all running compaction-like tasks having the provided {@code type}.
*
*
* @param type
* the type of compaction to stop. Can be one of: - COMPACTION -
* VALIDATION - CLEANUP - SCRUB - INDEX_BUILD
*/
public void stopCompaction(String type);
/**
* Stop an individual running compaction using the compactionId.
*
* @param compactionId
* Compaction ID of compaction to stop. Such IDs can be found in
* the transaction log files whose name starts with compaction_,
* located in the table transactions folder.
*/
public void stopCompactionById(String compactionId);
/**
* Returns core size of compaction thread pool
*/
@ -81,7 +86,7 @@ public interface CompactionManagerMBean {
/**
* Allows user to resize maximum size of the compaction thread pool.
*
*
* @param number
* New maximum of compaction threads
*/
@ -94,7 +99,7 @@ public interface CompactionManagerMBean {
/**
* Allows user to resize maximum size of the compaction thread pool.
*
*
* @param number
* New maximum of compaction threads
*/
@ -107,7 +112,7 @@ public interface CompactionManagerMBean {
/**
* Allows user to resize maximum size of the compaction thread pool.
*
*
* @param number
* New maximum of compaction threads
*/
@ -120,7 +125,7 @@ public interface CompactionManagerMBean {
/**
* Allows user to resize maximum size of the validator thread pool.
*
*
* @param number
* New maximum of validator threads
*/

View File

@ -1,35 +0,0 @@
/*
* 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.
*/
/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* Moddified by ScyllaDB
*/
package org.apache.cassandra.gms;
public enum ApplicationState {
STATUS, LOAD, SCHEMA, DC, RACK, RELEASE_VERSION, REMOVAL_COORDINATOR, INTERNAL_IP, RPC_ADDRESS, X_11_PADDING, // padding
// specifically
// for
// 1.1
SEVERITY, NET_VERSION, HOST_ID, TOKENS,
// pad to allow adding new states to existing cluster
X1, X2, X3, X4, X5, X6, X7, X8, X9, X10,
}

View File

@ -1,109 +0,0 @@
/*
* 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.
*/
/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* Moddified by ScyllaDB
*/
package org.apache.cassandra.gms;
import java.util.HashMap;
import java.util.Map;
/**
* This abstraction represents both the HeartBeatState and the ApplicationState
* in an EndpointState instance. Any state for a given endpoint can be retrieved
* from this instance.
*/
public class EndpointState {
private volatile HeartBeatState hbState;
final Map<ApplicationState, String> applicationState = new HashMap<ApplicationState, String>();
private volatile long updateTimestamp;
private volatile boolean isAlive;
ApplicationState[] applicationValues;
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(EndpointState.class.getName());
EndpointState(HeartBeatState initialHbState) {
applicationValues = ApplicationState.values();
hbState = initialHbState;
updateTimestamp = System.nanoTime();
isAlive = true;
}
HeartBeatState getHeartBeatState() {
return hbState;
}
void setHeartBeatState(HeartBeatState newHbState) {
hbState = newHbState;
}
public String getApplicationState(ApplicationState key) {
return applicationState.get(key);
}
/**
* TODO replace this with operations that don't expose private state
*/
@Deprecated
public Map<ApplicationState, String> getApplicationStateMap() {
return applicationState;
}
void addApplicationState(ApplicationState key, String value) {
applicationState.put(key, value);
}
void addApplicationState(int key, String value) {
if (key >= applicationValues.length) {
logger.warning("Unknown application state with id:" + key);
return;
}
addApplicationState(applicationValues[key], value);
}
/* getters and setters */
/**
* @return System.nanoTime() when state was updated last time.
*/
public long getUpdateTimestamp() {
return updateTimestamp;
}
public void setUpdateTimestamp(long ts) {
updateTimestamp = ts;
}
public boolean isAlive() {
return isAlive;
}
public void setAliave(boolean alive) {
isAlive = alive;
}
@Override
public String toString() {
return "EndpointState: HeartBeatState = " + hbState + ", AppStateMap = " + applicationState;
}
}

View File

@ -24,155 +24,77 @@
package org.apache.cassandra.gms;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import java.lang.management.ManagementFactory;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import com.cloudius.urchin.api.APIClient;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.APIMBean;
public class FailureDetector extends APIMBean implements FailureDetectorMBean {
public class FailureDetector implements FailureDetectorMBean {
public static final String MBEAN_NAME = "org.apache.cassandra.net:type=FailureDetector";
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(FailureDetector.class.getName());
public FailureDetector(APIClient c) {
super(c);
}
private APIClient c = new APIClient();
public void log(String str) {
logger.finest(str);
logger.info(str);
}
private static final FailureDetector instance = new FailureDetector();
public static FailureDetector getInstance() {
return instance;
}
private FailureDetector() {
// Register this instance with JMX
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
mbs.registerMBean(this, new ObjectName(MBEAN_NAME));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void dumpInterArrivalTimes() {
log(" dumpInterArrivalTimes()");
}
@Override
public void setPhiConvictThreshold(double phi) {
log(" setPhiConvictThreshold(double phi)");
log(" setPhiConvictThreshold(double phi)");
}
@Override
public double getPhiConvictThreshold() {
log(" getPhiConvictThreshold()");
return client.getDoubleValue("/failure_detector/phi");
return c.getDoubleValue("/failure_detector/phi");
}
@Override
public String getAllEndpointStates() {
log(" getAllEndpointStates()");
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, EndpointState> entry : getEndpointStateMap().entrySet()) {
sb.append('/').append(entry.getKey()).append("\n");
appendEndpointState(sb, entry.getValue());
}
return sb.toString();
return c.getStringValue("/failure_detector/endpoints");
}
private void appendEndpointState(StringBuilder sb, EndpointState endpointState) {
sb.append(" generation:").append(endpointState.getHeartBeatState().getGeneration()).append("\n");
sb.append(" heartbeat:").append(endpointState.getHeartBeatState().getHeartBeatVersion()).append("\n");
for (Map.Entry<ApplicationState, String> state : endpointState.applicationState.entrySet()) {
if (state.getKey() == ApplicationState.TOKENS) {
continue;
}
sb.append(" ").append(state.getKey()).append(":").append(state.getValue()).append("\n");
}
}
public Map<String, EndpointState> getEndpointStateMap() {
Map<String, EndpointState> res = new HashMap<String, EndpointState>();
JsonArray arr = client.getJsonArray("/failure_detector/endpoints");
for (int i = 0; i < arr.size(); i++) {
JsonObject obj = arr.getJsonObject(i);
EndpointState ep = new EndpointState(new HeartBeatState(obj.getInt("generation"), obj.getInt("version")));
ep.setAliave(obj.getBoolean("is_alive"));
ep.setUpdateTimestamp(obj.getJsonNumber("update_time").longValue());
JsonArray states = obj.getJsonArray("application_state");
if (states != null) {
for (int j = 0; j < states.size(); j++) {
JsonObject state = states.getJsonObject(j);
ep.addApplicationState(state.getInt("application_state"), state.getString("value"));
}
}
res.put(obj.getString("addrs"), ep);
}
return res;
}
@Override
public String getEndpointState(String address) throws UnknownHostException {
log(" getEndpointState(String address) throws UnknownHostException");
return client.getStringValue("/failure_detector/endpoints/states/" + address);
return c.getStringValue("/failure_detector/endpoints/states/" + address);
}
@Override
public Map<String, String> getSimpleStates() {
log(" getSimpleStates()");
return client.getMapStrValue("/failure_detector/simple_states");
return c.getMapStrValue("/failure_detector/simple_states");
}
@Override
public int getDownEndpointCount() {
log(" getDownEndpointCount()");
return client.getIntValue("/failure_detector/count/endpoint/down");
return c.getIntValue("/failure_detector/count/endpoint/down");
}
@Override
public int getUpEndpointCount() {
log(" getUpEndpointCount()");
return client.getIntValue("/failure_detector/count/endpoint/up");
return c.getIntValue("/failure_detector/count/endpoint/up");
}
// From origin:
// this is useless except to provide backwards compatibility in
// phi_convict_threshold,
// because everyone seems pretty accustomed to the default of 8, and users
// who have
// already tuned their phi_convict_threshold for their own environments
// won't need to
// change.
private final double PHI_FACTOR = 1.0 / Math.log(10.0); // 0.434...
@Override
public TabularData getPhiValues() throws OpenDataException {
final CompositeType ct = new CompositeType("Node", "Node", new String[] { "Endpoint", "PHI" },
new String[] { "IP of the endpoint", "PHI value" },
new OpenType[] { SimpleType.STRING, SimpleType.DOUBLE });
final TabularDataSupport results = new TabularDataSupport(
new TabularType("PhiList", "PhiList", ct, new String[] { "Endpoint" }));
final JsonArray arr = client.getJsonArray("/failure_detector/endpoint_phi_values");
for (JsonValue v : arr) {
JsonObject o = (JsonObject) v;
String endpoint = o.getString("endpoint");
double phi = Double.parseDouble(o.getString("phi"));
if (phi != Double.MIN_VALUE) {
// returned values are scaled by PHI_FACTOR so that the are on
// the same scale as PhiConvictThreshold
final CompositeData data = new CompositeDataSupport(ct, new String[] { "Endpoint", "PHI" },
new Object[] { endpoint, phi * PHI_FACTOR });
results.put(data);
}
}
return results;
}
}

View File

@ -20,10 +20,8 @@ package org.apache.cassandra.gms;
import java.net.UnknownHostException;
import java.util.Map;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.TabularData;
public interface FailureDetectorMBean {
public interface FailureDetectorMBean
{
public void dumpInterArrivalTimes();
public void setPhiConvictThreshold(double phi);
@ -39,6 +37,4 @@ public interface FailureDetectorMBean {
public int getDownEndpointCount();
public int getUpEndpointCount();
public TabularData getPhiValues() throws OpenDataException;
}

View File

@ -23,13 +23,15 @@
*/
package org.apache.cassandra.gms;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.lang.management.ManagementFactory;
import java.net.UnknownHostException;
import java.util.logging.Logger;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.APIMBean;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import com.cloudius.urchin.api.APIClient;
/**
* This module is responsible for Gossiping information for the local endpoint.
@ -46,43 +48,57 @@ import com.scylladb.jmx.metrics.APIMBean;
* node as down in the Failure Detector.
*/
public class Gossiper extends APIMBean implements GossiperMBean {
public class Gossiper implements GossiperMBean {
public static final String MBEAN_NAME = "org.apache.cassandra.net:type=Gossiper";
private static final Logger logger = Logger.getLogger(Gossiper.class.getName());
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(Gossiper.class.getName());
public Gossiper(APIClient c) {
super(c);
}
private APIClient c = new APIClient();
public void log(String str) {
logger.finest(str);
logger.info(str);
}
private static final Gossiper instance = new Gossiper();
public static Gossiper getInstance() {
return instance;
}
private Gossiper() {
// Register this instance with JMX
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
mbs.registerMBean(this, new ObjectName(MBEAN_NAME));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public long getEndpointDowntime(String address) throws UnknownHostException {
log(" getEndpointDowntime(String address) throws UnknownHostException");
return client.getLongValue("gossiper/downtime/" + address);
return c.getLongValue("gossiper/downtime/" + address);
}
@Override
public int getCurrentGenerationNumber(String address) throws UnknownHostException {
public int getCurrentGenerationNumber(String address)
throws UnknownHostException {
log(" getCurrentGenerationNumber(String address) throws UnknownHostException");
return client.getIntValue("gossiper/generation_number/" + address);
return c.getIntValue("gossiper/generation_number/" + address);
}
@Override
public void unsafeAssassinateEndpoint(String address) throws UnknownHostException {
public void unsafeAssassinateEndpoint(String address)
throws UnknownHostException {
log(" unsafeAssassinateEndpoint(String address) throws UnknownHostException");
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
queryParams.add("unsafe", "True");
client.post("gossiper/assassinate/" + address, queryParams);
c.post("gossiper/assassinate/" + address, queryParams);
}
@Override
public void assassinateEndpoint(String address) throws UnknownHostException {
log(" assassinateEndpoint(String address) throws UnknownHostException");
client.post("gossiper/assassinate/" + address, null);
c.post("gossiper/assassinate/" + address, null);
}
}

View File

@ -19,13 +19,12 @@ package org.apache.cassandra.gms;
import java.net.UnknownHostException;
public interface GossiperMBean {
public interface GossiperMBean
{
public long getEndpointDowntime(String address) throws UnknownHostException;
public int getCurrentGenerationNumber(String address) throws UnknownHostException;
public void unsafeAssassinateEndpoint(String address) throws UnknownHostException;
public void assassinateEndpoint(String address) throws UnknownHostException;
}

View File

@ -1,65 +0,0 @@
/*
* 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.
*/
/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* Moddified by ScyllaDB
*/
package org.apache.cassandra.gms;
/**
* HeartBeat State associated with any given endpoint.
*/
class HeartBeatState {
private int generation;
private int version;
HeartBeatState(int gen) {
this(gen, 0);
}
HeartBeatState(int gen, int ver) {
generation = gen;
version = ver;
}
int getGeneration() {
return generation;
}
void updateHeartBeat() {
}
int getHeartBeatVersion() {
return version;
}
void forceNewerGenerationUnsafe() {
generation += 1;
}
void forceHighestPossibleVersionUnsafe() {
version = Integer.MAX_VALUE;
}
@Override
public String toString() {
return String.format("HeartBeat: generation = %d, version = %d", generation, version);
}
}

View File

@ -17,27 +17,41 @@
*/
package org.apache.cassandra.locator;
import static java.util.Collections.singletonMap;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.logging.Logger;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.APIMBean;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
public class EndpointSnitchInfo extends APIMBean implements EndpointSnitchInfoMBean {
public static final String MBEAN_NAME = "org.apache.cassandra.db:type=EndpointSnitchInfo";
private static final Logger logger = Logger.getLogger(EndpointSnitchInfo.class.getName());
import com.cloudius.urchin.api.APIClient;
public EndpointSnitchInfo(APIClient c) {
super(c);
}
public class EndpointSnitchInfo implements EndpointSnitchInfoMBean {
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(EndpointSnitchInfo.class.getName());
private APIClient c = new APIClient();
public void log(String str) {
logger.finest(str);
logger.info(str);
}
private static final EndpointSnitchInfo instance = new EndpointSnitchInfo();
public static EndpointSnitchInfo getInstance() {
return instance;
}
private EndpointSnitchInfo() {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
mbs.registerMBean(this, new ObjectName(
"org.apache.cassandra.db:type=EndpointSnitchInfo"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
@ -50,9 +64,12 @@ public class EndpointSnitchInfo extends APIMBean implements EndpointSnitchInfoMB
@Override
public String getRack(String host) throws UnknownHostException {
log("getRack(String host) throws UnknownHostException");
MultivaluedMap<String, String> queryParams = host != null ? new MultivaluedHashMap<String, String>(
singletonMap("host", InetAddress.getByName(host).getHostAddress())) : null;
return client.getStringValue("/snitch/rack", queryParams, 10000);
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
if (host == null) {
host = InetAddress.getLoopbackAddress().getHostAddress();
}
queryParams.add("host", host);
return c.getStringValue("/snitch/rack", queryParams, 10000);
}
/**
@ -65,9 +82,12 @@ public class EndpointSnitchInfo extends APIMBean implements EndpointSnitchInfoMB
@Override
public String getDatacenter(String host) throws UnknownHostException {
log(" getDatacenter(String host) throws UnknownHostException");
MultivaluedMap<String, String> queryParams = host != null ? new MultivaluedHashMap<String, String>(
singletonMap("host", InetAddress.getByName(host).getHostAddress())) : null;
return client.getStringValue("/snitch/datacenter", queryParams, 10000);
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
if (host == null) {
host = InetAddress.getLoopbackAddress().getHostAddress();
}
queryParams.add("host", host);
return c.getStringValue("/snitch/datacenter", queryParams, 10000);
}
/**
@ -78,16 +98,7 @@ public class EndpointSnitchInfo extends APIMBean implements EndpointSnitchInfoMB
@Override
public String getSnitchName() {
log(" getSnitchName()");
return client.getStringValue("/snitch/name");
return c.getStringValue("/snitch/name");
}
@Override
public String getRack() {
return client.getStringValue("/snitch/rack", null, 10000);
}
@Override
public String getDatacenter() {
return client.getStringValue("/snitch/datacenter", null, 10000);
}
}

View File

@ -22,40 +22,25 @@ import java.net.UnknownHostException;
/**
* MBean exposing standard Snitch info
*/
public interface EndpointSnitchInfoMBean {
public interface EndpointSnitchInfoMBean
{
/**
* Provides the Rack name depending on the respective snitch used, given the
* host name/ip
*
* Provides the Rack name depending on the respective snitch used, given the host name/ip
* @param host
* @throws UnknownHostException
*/
public String getRack(String host) throws UnknownHostException;
/**
* Provides the Datacenter name depending on the respective snitch used,
* given the hostname/ip
*
* Provides the Datacenter name depending on the respective snitch used, given the hostname/ip
* @param host
* @throws UnknownHostException
*/
public String getDatacenter(String host) throws UnknownHostException;
/**
* Provides the Rack name depending on the respective snitch used for this
* node
*/
public String getRack();
/**
* Provides the Datacenter name depending on the respective snitch used for
* this node
*/
public String getDatacenter();
/**
* Provides the snitch name of the cluster
*
* @return Snitch name
*/
public String getSnitchName();

View File

@ -25,20 +25,34 @@
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import com.cloudius.urchin.metrics.APIMetrics;
import com.yammer.metrics.core.*;
// TODO: In StorageProxy
public class CASClientRequestMetrics extends ClientRequestMetrics {
public CASClientRequestMetrics(String scope, String url) {
super(scope, url);
public final Histogram contention;
/* Used only for write */
public final Counter conditionNotMet;
public final Counter unfinishedCommit;
public CASClientRequestMetrics(String url, String scope) {
super(url, scope);
contention = APIMetrics.newHistogram(url + "contention",
factory.createMetricName("ContentionHistogram"), true);
conditionNotMet = APIMetrics.newCounter(url + "condition_not_met",
factory.createMetricName("ConditionNotMet"));
unfinishedCommit = APIMetrics.newCounter(url + "unfinished_commit",
factory.createMetricName("UnfinishedCommit"));
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
super.register(registry);
registry.register(() -> registry.histogram(uri + "/contention", true), names("ContentionHistogram"));
registry.register(() -> registry.counter(uri + "/condition_not_met"), names("ConditionNotMet"));
registry.register(() -> registry.counter(uri + "/unfinished_commit"), names("UnfinishedCommit"));
public void release() {
super.release();
APIMetrics.defaultRegistry().removeMetric(
factory.createMetricName("ContentionHistogram"));
APIMetrics.defaultRegistry().removeMetric(
factory.createMetricName("ConditionNotMet"));
APIMetrics.defaultRegistry().removeMetric(
factory.createMetricName("UnfinishedCommit"));
}
}

View File

@ -23,19 +23,37 @@
*/
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import com.cloudius.urchin.api.APIClient;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Meter;
/**
* Metrics for {@code ICache}.
*/
public class CacheMetrics implements Metrics {
public class CacheMetrics {
/** Cache capacity in bytes */
public final Gauge<Long> capacity;
/** Total number of cache hits */
public final Meter hits;
/** Total number of cache requests */
public final Meter requests;
/** cache hit rate */
public final Gauge<Double> hitRate;
/** Total size of cache, in bytes */
public final Gauge<Long> size;
/** Total number of cache entries */
public final Gauge<Integer> entries;
private final String type;
private final String url;
private final AtomicLong lastRequests = new AtomicLong(0);
private final AtomicLong lastHits = new AtomicLong(0);
private String compose(String value) {
return "/cache_service/metrics/" + url + "/" + value;
}
private APIClient c = new APIClient();
/**
* Create metrics for given cache.
@ -46,21 +64,59 @@ public class CacheMetrics implements Metrics {
* Cache to measure metrics
*/
public CacheMetrics(String type, final String url) {
this.type = type;
this.url = url;
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
MetricNameFactory factory = new DefaultNameFactory("Cache", type);
registry.register(() -> registry.gauge(compose("capacity")), factory.createMetricName("Capacity"));
registry.register(() -> registry.meter(compose("hits_moving_avrage")), factory.createMetricName("Hits"));
registry.register(() -> registry.meter(compose("requests_moving_avrage")),
factory.createMetricName("Requests"));
registry.register(() -> registry.gauge(Double.class, compose("hit_rate")), factory.createMetricName("HitRate"));
registry.register(() -> registry.gauge(compose("size")), factory.createMetricName("Size"));
registry.register(() -> registry.gauge(Integer.class, compose("entries")), factory.createMetricName("Entries"));
capacity = APIMetrics.newGauge(factory.createMetricName("Capacity"),
new Gauge<Long>() {
public Long value() {
return c.getLongValue("/cache_service/metrics/" + url
+ "/capacity");
}
});
hits = APIMetrics.newMeter("/cache_service/metrics/" + url
+ "/hits", factory.createMetricName("Hits"), "hits",
TimeUnit.SECONDS);
requests = APIMetrics.newMeter("/cache_service/metrics/" + url
+ "/requests", factory.createMetricName("Requests"),
"requests", TimeUnit.SECONDS);
hitRate = APIMetrics.newGauge(factory.createMetricName("HitRate"),
new Gauge<Double>() {
@Override
public Double value() {
return c.getDoubleValue("/cache_service/metrics/" + url
+ "/hit_rate");
}
});
size = APIMetrics.newGauge(factory.createMetricName("Size"),
new Gauge<Long>() {
public Long value() {
return c.getLongValue("/cache_service/metrics/" + url
+ "/size");
}
});
entries = APIMetrics.newGauge(factory.createMetricName("Entries"),
new Gauge<Integer>() {
public Integer value() {
return c.getIntValue("/cache_service/metrics/" + url
+ "/entries");
}
});
}
// for backward compatibility
@Deprecated
public double getRecentHitRate() {
long r = requests.count();
long h = hits.count();
try
{
return ((double)(h - lastHits.get())) / (r - lastRequests.get());
}
finally
{
lastRequests.set(r);
lastHits.set(h);
}
}
}

View File

@ -27,17 +27,51 @@
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import java.util.concurrent.TimeUnit;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Meter;
public class ClientRequestMetrics extends LatencyMetrics {
public ClientRequestMetrics(String scope, String url) {
super("ClientRequest", scope, url);
@Deprecated
public static final Counter readTimeouts = Metrics
.newCounter(DefaultNameFactory.createMetricName(
"ClientRequestMetrics", "ReadTimeouts", null));
@Deprecated
public static final Counter writeTimeouts = Metrics
.newCounter(DefaultNameFactory.createMetricName(
"ClientRequestMetrics", "WriteTimeouts", null));
@Deprecated
public static final Counter readUnavailables = Metrics
.newCounter(DefaultNameFactory.createMetricName(
"ClientRequestMetrics", "ReadUnavailables", null));
@Deprecated
public static final Counter writeUnavailables = Metrics
.newCounter(DefaultNameFactory.createMetricName(
"ClientRequestMetrics", "WriteUnavailables", null));
public final Meter timeouts;
public final Meter unavailables;
public ClientRequestMetrics(String url, String scope) {
super(url, "ClientRequest", scope);
timeouts = APIMetrics.newMeter(url + "/timeouts",
factory.createMetricName("Timeouts"), "timeouts",
TimeUnit.SECONDS);
unavailables = APIMetrics.newMeter(url + "/unavailables",
factory.createMetricName("Unavailables"), "unavailables",
TimeUnit.SECONDS);
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
super.register(registry);
registry.register(() -> registry.meter(uri + "/timeouts_rates"), names("Timeouts"));
registry.register(() -> registry.meter(uri + "/unavailables_rates"), names("Unavailables"));
public void release() {
super.release();
APIMetrics.defaultRegistry().removeMetric(
factory.createMetricName("Timeouts"));
APIMetrics.defaultRegistry().removeMetric(
factory.createMetricName("Unavailables"));
}
}

View File

@ -0,0 +1,577 @@
/*
* 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.
*/
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
package org.apache.cassandra.metrics;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.apache.cassandra.db.ColumnFamilyStore;
import com.cloudius.urchin.api.APIClient;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.cloudius.urchin.utils.EstimatedHistogram;
import com.cloudius.urchin.utils.RecentEstimatedHistogram;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.*;
/**
* Metrics for {@link ColumnFamilyStore}.
*/
public class ColumnFamilyMetrics {
private APIClient c = new APIClient();
/**
* Total amount of data stored in the memtable that resides on-heap,
* including column related overhead and overwritten rows.
*/
public final Gauge<Long> memtableOnHeapSize;
/**
* Total amount of data stored in the memtable that resides off-heap,
* including column related overhead and overwritten rows.
*/
public final Gauge<Long> memtableOffHeapSize;
/**
* Total amount of live data stored in the memtable, excluding any data
* structure overhead
*/
public final Gauge<Long> memtableLiveDataSize;
/**
* Total amount of data stored in the memtables (2i and pending flush
* memtables included) that resides on-heap.
*/
public final Gauge<Long> allMemtablesOnHeapSize;
/**
* Total amount of data stored in the memtables (2i and pending flush
* memtables included) that resides off-heap.
*/
public final Gauge<Long> allMemtablesOffHeapSize;
/**
* Total amount of live data stored in the memtables (2i and pending flush
* memtables included) that resides off-heap, excluding any data structure
* overhead
*/
public final Gauge<Long> allMemtablesLiveDataSize;
/** Total number of columns present in the memtable. */
public final Gauge<Long> memtableColumnsCount;
/** Number of times flush has resulted in the memtable being switched out. */
public final Counter memtableSwitchCount;
/** Current compression ratio for all SSTables */
public final Gauge<Double> compressionRatio;
/** Histogram of estimated row size (in bytes). */
public final Gauge<long[]> estimatedRowSizeHistogram;
/** Approximate number of keys in table. */
public final Gauge<Long> estimatedRowCount;
/** Histogram of estimated number of columns. */
public final Gauge<long[]> estimatedColumnCountHistogram;
/** Histogram of the number of sstable data files accessed per read */
public final ColumnFamilyHistogram sstablesPerReadHistogram;
/** (Local) read metrics */
public final LatencyMetrics readLatency;
/** (Local) range slice metrics */
public final LatencyMetrics rangeLatency;
/** (Local) write metrics */
public final LatencyMetrics writeLatency;
/** Estimated number of tasks pending for this column family */
public final Counter pendingFlushes;
/** Estimate of number of pending compactios for this CF */
public final Gauge<Integer> pendingCompactions;
/** Number of SSTables on disk for this CF */
public final Gauge<Integer> liveSSTableCount;
/** Disk space used by SSTables belonging to this CF */
public final Counter liveDiskSpaceUsed;
/**
* Total disk space used by SSTables belonging to this CF, including
* obsolete ones waiting to be GC'd
*/
public final Counter totalDiskSpaceUsed;
/** Size of the smallest compacted row */
public final Gauge<Long> minRowSize;
/** Size of the largest compacted row */
public final Gauge<Long> maxRowSize;
/** Size of the smallest compacted row */
public final Gauge<Long> meanRowSize;
/** Number of false positives in bloom filter */
public final Gauge<Long> bloomFilterFalsePositives;
/** Number of false positives in bloom filter from last read */
public final Gauge<Long> recentBloomFilterFalsePositives;
/** False positive ratio of bloom filter */
public final Gauge<Double> bloomFilterFalseRatio;
/** False positive ratio of bloom filter from last read */
public final Gauge<Double> recentBloomFilterFalseRatio;
/** Disk space used by bloom filter */
public final Gauge<Long> bloomFilterDiskSpaceUsed;
/** Off heap memory used by bloom filter */
public final Gauge<Long> bloomFilterOffHeapMemoryUsed;
/** Off heap memory used by index summary */
public final Gauge<Long> indexSummaryOffHeapMemoryUsed;
/** Off heap memory used by compression meta data */
public final Gauge<Long> compressionMetadataOffHeapMemoryUsed;
/** Key cache hit rate for this CF */
public final Gauge<Double> keyCacheHitRate;
/** Tombstones scanned in queries on this CF */
public final ColumnFamilyHistogram tombstoneScannedHistogram;
/** Live cells scanned in queries on this CF */
public final ColumnFamilyHistogram liveScannedHistogram;
/** Column update time delta on this CF */
public final ColumnFamilyHistogram colUpdateTimeDeltaHistogram;
/** Disk space used by snapshot files which */
public final Gauge<Long> trueSnapshotsSize;
/** Row cache hits, but result out of range */
public final Counter rowCacheHitOutOfRange;
/** Number of row cache hits */
public final Counter rowCacheHit;
/** Number of row cache misses */
public final Counter rowCacheMiss;
/** CAS Prepare metrics */
public final LatencyMetrics casPrepare;
/** CAS Propose metrics */
public final LatencyMetrics casPropose;
/** CAS Commit metrics */
public final LatencyMetrics casCommit;
public final Timer coordinatorReadLatency;
public final Timer coordinatorScanLatency;
/** Time spent waiting for free memtable space, either on- or off-heap */
public final Timer waitingOnFreeMemtableSpace;
private final MetricNameFactory factory;
private static final MetricNameFactory globalNameFactory = new AllColumnFamilyMetricNameFactory();
public final Counter speculativeRetries;
// for backward compatibility
@Deprecated
public final EstimatedHistogramWrapper sstablesPerRead;
// it should not be called directly
@Deprecated
protected final RecentEstimatedHistogram recentSSTablesPerRead = new RecentEstimatedHistogram(35);
private String cfName;
public final static LatencyMetrics globalReadLatency = new LatencyMetrics(
"/column_family/metrics/read_latency", globalNameFactory, "Read");
public final static LatencyMetrics globalWriteLatency = new LatencyMetrics(
"/column_family/metrics/write_latency", globalNameFactory, "Write");
public final static LatencyMetrics globalRangeLatency = new LatencyMetrics(
"/column_family/metrics/range_latency", globalNameFactory, "Range");
/**
* stores metrics that will be rolled into a single global metric
*/
public final static ConcurrentMap<String, Set<Metric>> allColumnFamilyMetrics = Maps
.newConcurrentMap();
/**
* Stores all metric names created that can be used when unregistering
*/
public final static Set<String> all = Sets.newHashSet();
/**
* Creates metrics for given {@link ColumnFamilyStore}.
*
* @param cfs
* ColumnFamilyStore to measure metrics
*/
public ColumnFamilyMetrics(final ColumnFamilyStore cfs) {
factory = new ColumnFamilyMetricNameFactory(cfs);
cfName = cfs.getCFName();
memtableColumnsCount = createColumnFamilyGauge(
"/column_family/metrics/memtable_columns_count",
"MemtableColumnsCount");
memtableOnHeapSize = createColumnFamilyGauge(
"/column_family/metrics/memtable_on_heap_size",
"MemtableOnHeapSize");
memtableOffHeapSize = createColumnFamilyGauge(
"/column_family/metrics/memtable_off_heap_size",
"MemtableOffHeapSize");
memtableLiveDataSize = createColumnFamilyGauge(
"/column_family/metrics/memtable_live_data_size",
"MemtableLiveDataSize");
allMemtablesOnHeapSize = createColumnFamilyGauge(
"/column_family/metrics/all_memtables_on_heap_size",
"AllMemtablesHeapSize");
allMemtablesOffHeapSize = createColumnFamilyGauge(
"/column_family/metrics/all_memtables_off_heap_size",
"AllMemtablesOffHeapSize");
allMemtablesLiveDataSize = createColumnFamilyGauge(
"/column_family/metrics/all_memtables_live_data_size",
"AllMemtablesLiveDataSize");
memtableSwitchCount = createColumnFamilyCounter(
"/column_family/metrics/memtable_switch_count",
"MemtableSwitchCount");
estimatedRowSizeHistogram = Metrics.newGauge(
factory.createMetricName("EstimatedRowSizeHistogram"),
new Gauge<long[]>() {
public long[] value() {
return c.getEstimatedHistogramAsLongArrValue("/column_family/metrics/estimated_row_size_histogram/"
+ cfName);
}
});
estimatedRowCount= Metrics.newGauge(
factory.createMetricName("EstimatedRowCount"),
new Gauge<Long>() {
public Long value() {
return c.getLongValue("/column_family/metrics/estimated_row_count/"
+ cfName);
}
});
estimatedColumnCountHistogram = Metrics.newGauge(
factory.createMetricName("EstimatedColumnCountHistogram"),
new Gauge<long[]>() {
public long[] value() {
return c.getEstimatedHistogramAsLongArrValue("/column_family/metrics/estimated_column_count_histogram/"
+ cfName);
}
});
sstablesPerReadHistogram = createColumnFamilyHistogram(
"/column_family/metrics/sstables_per_read_histogram",
"SSTablesPerReadHistogram");
compressionRatio = createColumnFamilyGauge("CompressionRatio",
new Gauge<Double>() {
public Double value() {
return c.getDoubleValue("/column_family/metrics/compression_ratio/"
+ cfName);
}
}, new Gauge<Double>() // global gauge
{
public Double value() {
return c.getDoubleValue("/column_family/metrics/compression_ratio/");
}
});
readLatency = new LatencyMetrics("/column_family/metrics/read_latency",
cfName, factory, "Read");
writeLatency = new LatencyMetrics(
"/column_family/metrics/write_latency", cfName, factory,
"Write");
rangeLatency = new LatencyMetrics(
"/column_family/metrics/range_latency", cfName, factory,
"Range");
pendingFlushes = createColumnFamilyCounter(
"/column_family/metrics/pending_flushes", "PendingFlushes");
pendingCompactions = createColumnFamilyGaugeInt(
"/column_family/metrics/pending_compactions",
"PendingCompactions");
liveSSTableCount = createColumnFamilyGaugeInt(
"/column_family/metrics/live_ss_table_count",
"LiveSSTableCount");
liveDiskSpaceUsed = createColumnFamilyCounter(
"/column_family/metrics/live_disk_space_used",
"LiveDiskSpaceUsed");
totalDiskSpaceUsed = createColumnFamilyCounter(
"/column_family/metrics/total_disk_space_used",
"TotalDiskSpaceUsed");
minRowSize = createColumnFamilyGauge(
"/column_family/metrics/min_row_size", "MinRowSize");
maxRowSize = createColumnFamilyGauge(
"/column_family/metrics/max_row_size", "MaxRowSize");
meanRowSize = createColumnFamilyGauge(
"/column_family/metrics/mean_row_size", "MeanRowSize");
bloomFilterFalsePositives = createColumnFamilyGauge(
"/column_family/metrics/bloom_filter_false_positives",
"BloomFilterFalsePositives");
recentBloomFilterFalsePositives = createColumnFamilyGauge(
"/column_family/metrics/recent_bloom_filter_false_positives",
"RecentBloomFilterFalsePositives");
bloomFilterFalseRatio = createColumnFamilyGaugeDouble(
"/column_family/metrics/bloom_filter_false_ratio",
"BloomFilterFalseRatio");
recentBloomFilterFalseRatio = createColumnFamilyGaugeDouble(
"/column_family/metrics/recent_bloom_filter_false_ratio",
"RecentBloomFilterFalseRatio");
bloomFilterDiskSpaceUsed = createColumnFamilyGauge(
"/column_family/metrics/bloom_filter_disk_space_used",
"BloomFilterDiskSpaceUsed");
bloomFilterOffHeapMemoryUsed = createColumnFamilyGauge(
"/column_family/metrics/bloom_filter_off_heap_memory_used",
"BloomFilterOffHeapMemoryUsed");
indexSummaryOffHeapMemoryUsed = createColumnFamilyGauge(
"/column_family/metrics/index_summary_off_heap_memory_used",
"IndexSummaryOffHeapMemoryUsed");
compressionMetadataOffHeapMemoryUsed = createColumnFamilyGauge(
"/column_family/metrics/compression_metadata_off_heap_memory_used",
"CompressionMetadataOffHeapMemoryUsed");
speculativeRetries = createColumnFamilyCounter(
"/column_family/metrics/speculative_retries",
"SpeculativeRetries");
keyCacheHitRate = Metrics.newGauge(
factory.createMetricName("KeyCacheHitRate"),
new Gauge<Double>() {
@Override
public Double value() {
return c.getDoubleValue("/column_family/metrics/key_cache_hit_rate/"
+ cfName);
}
});
tombstoneScannedHistogram = createColumnFamilyHistogram(
"/column_family/metrics/tombstone_scanned_histogram",
"TombstoneScannedHistogram");
liveScannedHistogram = createColumnFamilyHistogram(
"/column_family/metrics/live_scanned_histogram",
"LiveScannedHistogram");
colUpdateTimeDeltaHistogram = createColumnFamilyHistogram(
"/column_family/metrics/col_update_time_delta_histogram",
"ColUpdateTimeDeltaHistogram");
coordinatorReadLatency = APIMetrics.newTimer("/column_family/metrics/coordinator/read/" + cfName,
factory.createMetricName("CoordinatorReadLatency"),
TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
coordinatorScanLatency = APIMetrics.newTimer("/column_family/metrics/coordinator/scan/" + cfName,
factory.createMetricName("CoordinatorScanLatency"),
TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
waitingOnFreeMemtableSpace = APIMetrics.newTimer("/column_family/metrics/waiting_on_free_memtable/" + cfName,
factory.createMetricName("WaitingOnFreeMemtableSpace"),
TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
trueSnapshotsSize = createColumnFamilyGauge(
"/column_family/metrics/true_snapshots_size", "SnapshotsSize");
rowCacheHitOutOfRange = createColumnFamilyCounter(
"/column_family/metrics/row_cache_hit_out_of_range",
"RowCacheHitOutOfRange");
rowCacheHit = createColumnFamilyCounter(
"/column_family/metrics/row_cache_hit", "RowCacheHit");
rowCacheMiss = createColumnFamilyCounter(
"/column_family/metrics/row_cache_miss", "RowCacheMiss");
casPrepare = new LatencyMetrics("/column_family/metrics/cas_prepare/"
+ cfName, factory, "CasPrepare");
casPropose = new LatencyMetrics("/column_family/metrics/cas_propose/"
+ cfName, factory, "CasPropose");
casCommit = new LatencyMetrics("/column_family/metrics/cas_commit/"
+ cfName, factory, "CasCommit");
sstablesPerRead = new EstimatedHistogramWrapper("/column_family/metrics/sstables_per_read_histogram/" + cfName);
}
/**
* Release all associated metrics.
*/
public void release() {
for (String name : all) {
allColumnFamilyMetrics.get(name).remove(
Metrics.defaultRegistry().allMetrics()
.get(factory.createMetricName(name)));
Metrics.defaultRegistry().removeMetric(
factory.createMetricName(name));
}
readLatency.release();
writeLatency.release();
rangeLatency.release();
Metrics.defaultRegistry().removeMetric(
factory.createMetricName("EstimatedRowSizeHistogram"));
Metrics.defaultRegistry().removeMetric(
factory.createMetricName("EstimatedColumnCountHistogram"));
Metrics.defaultRegistry().removeMetric(
factory.createMetricName("KeyCacheHitRate"));
Metrics.defaultRegistry().removeMetric(
factory.createMetricName("CoordinatorReadLatency"));
Metrics.defaultRegistry().removeMetric(
factory.createMetricName("CoordinatorScanLatency"));
Metrics.defaultRegistry().removeMetric(
factory.createMetricName("WaitingOnFreeMemtableSpace"));
}
/**
* Create a gauge that will be part of a merged version of all column
* families. The global gauge will merge each CF gauge by adding their
* values
*/
protected Gauge<Double> createColumnFamilyGaugeDouble(final String url,
final String name) {
Gauge<Double> gauge = new Gauge<Double>() {
public Double value() {
return c.getDoubleValue(url + "/" + cfName);
}
};
return createColumnFamilyGauge(url, name, gauge);
}
/**
* Create a gauge that will be part of a merged version of all column
* families. The global gauge will merge each CF gauge by adding their
* values
*/
protected Gauge<Long> createColumnFamilyGauge(final String url, final String name) {
Gauge<Long> gauge = new Gauge<Long>() {
public Long value() {
return c.getLongValue(url + "/" + cfName);
}
};
return createColumnFamilyGauge(url, name, gauge);
}
/**
* Create a gauge that will be part of a merged version of all column
* families. The global gauge will merge each CF gauge by adding their
* values
*/
protected Gauge<Integer> createColumnFamilyGaugeInt(final String url,
final String name) {
Gauge<Integer> gauge = new Gauge<Integer>() {
public Integer value() {
return c.getIntValue(url + "/" + cfName);
}
};
return createColumnFamilyGauge(url, name, gauge);
}
/**
* Create a gauge that will be part of a merged version of all column
* families. The global gauge will merge each CF gauge by adding their
* values
*/
protected <T extends Number> Gauge<T> createColumnFamilyGauge(final String url,
final String name, Gauge<T> gauge) {
return createColumnFamilyGauge(name, gauge, new Gauge<Long>() {
public Long value() {
// This is an optimiztion, call once for all column families
// instead
// of iterating over all of them
return c.getLongValue(url);
}
});
}
/**
* Create a gauge that will be part of a merged version of all column
* families. The global gauge is defined as the globalGauge parameter
*/
protected <G, T> Gauge<T> createColumnFamilyGauge(String name,
Gauge<T> gauge, Gauge<G> globalGauge) {
Gauge<T> cfGauge = APIMetrics.newGauge(factory.createMetricName(name),
gauge);
if (register(name, cfGauge)) {
Metrics.newGauge(globalNameFactory.createMetricName(name),
globalGauge);
}
return cfGauge;
}
/**
* Creates a counter that will also have a global counter thats the sum of
* all counters across different column families
*/
protected Counter createColumnFamilyCounter(final String url, final String name) {
Counter cfCounter = APIMetrics.newCounter(url + "/" + cfName,
factory.createMetricName(name));
if (register(name, cfCounter)) {
Metrics.newGauge(globalNameFactory.createMetricName(name),
new Gauge<Long>() {
public Long value() {
// This is an optimiztion, call once for all column
// families instead
// of iterating over all of them
return c.getLongValue(url);
}
});
}
return cfCounter;
}
/**
* Create a histogram-like interface that will register both a CF, keyspace
* and global level histogram and forward any updates to both
*/
protected ColumnFamilyHistogram createColumnFamilyHistogram(String url,
String name) {
Histogram cfHistogram = APIMetrics.newHistogram(url + "/" + cfName,
factory.createMetricName(name), true);
register(name, cfHistogram);
// TBD add keyspace and global histograms
// keyspaceHistogram,
// Metrics.newHistogram(globalNameFactory.createMetricName(name),
// true));
return new ColumnFamilyHistogram(cfHistogram, null, null);
}
/**
* Registers a metric to be removed when unloading CF.
*
* @return true if first time metric with that name has been registered
*/
private boolean register(String name, Metric metric) {
boolean ret = allColumnFamilyMetrics.putIfAbsent(name,
new HashSet<Metric>()) == null;
allColumnFamilyMetrics.get(name).add(metric);
all.add(name);
return ret;
}
public long[] getRecentSSTablesPerRead() {
return recentSSTablesPerRead
.getBuckets(sstablesPerRead.getBuckets(false));
}
public class ColumnFamilyHistogram {
public final Histogram[] all;
public final Histogram cf;
private ColumnFamilyHistogram(Histogram cf, Histogram keyspace,
Histogram global) {
this.cf = cf;
this.all = new Histogram[] { cf, keyspace, global };
}
}
class ColumnFamilyMetricNameFactory implements MetricNameFactory {
private final String keyspaceName;
private final String columnFamilyName;
private final boolean isIndex;
ColumnFamilyMetricNameFactory(ColumnFamilyStore cfs) {
this.keyspaceName = cfs.getKeyspace();
this.columnFamilyName = cfs.getColumnFamilyName();
isIndex = cfs.isIndex();
}
public MetricName createMetricName(String metricName) {
String groupName = ColumnFamilyMetrics.class.getPackage().getName();
String type = isIndex ? "IndexColumnFamily" : "ColumnFamily";
StringBuilder mbeanName = new StringBuilder();
mbeanName.append(groupName).append(":");
mbeanName.append("type=").append(type);
mbeanName.append(",keyspace=").append(keyspaceName);
mbeanName.append(",scope=").append(columnFamilyName);
mbeanName.append(",name=").append(metricName);
return new MetricName(groupName, type, metricName, keyspaceName
+ "." + columnFamilyName, mbeanName.toString());
}
}
static class AllColumnFamilyMetricNameFactory implements MetricNameFactory {
public MetricName createMetricName(String metricName) {
String groupName = ColumnFamilyMetrics.class.getPackage().getName();
StringBuilder mbeanName = new StringBuilder();
mbeanName.append(groupName).append(":");
mbeanName.append("type=ColumnFamily");
mbeanName.append(",name=").append(metricName);
return new MetricName(groupName, "ColumnFamily", metricName, "all",
mbeanName.toString());
}
}
}

View File

@ -23,38 +23,65 @@
*/
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import com.cloudius.urchin.api.APIClient;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Timer;
import java.util.concurrent.TimeUnit;
/**
* Metrics for commit log
*/
public class CommitLogMetrics implements Metrics {
public CommitLogMetrics() {
}
public class CommitLogMetrics {
public static final MetricNameFactory factory = new DefaultNameFactory(
"CommitLog");
private APIClient c = new APIClient();
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
MetricNameFactory factory = new DefaultNameFactory("CommitLog");
/** Number of completed tasks */
registry.register(() -> registry.gauge("/commitlog/metrics/completed_tasks"),
factory.createMetricName("CompletedTasks"));
/** Number of pending tasks */
registry.register(() -> registry.gauge("/commitlog/metrics/pending_tasks"),
factory.createMetricName("PendingTasks"));
/** Current size used by all the commit log segments */
registry.register(() -> registry.gauge("/commitlog/metrics/total_commit_log_size"),
factory.createMetricName("TotalCommitLogSize"));
/**
* Time spent waiting for a CLS to be allocated - under normal
* conditions this should be zero
*/
registry.register(() -> registry.timer("/commitlog/metrics/waiting_on_segment_allocation"),
factory.createMetricName("WaitingOnSegmentAllocation"));
/**
* The time spent waiting on CL sync; for Periodic this is only occurs
* when the sync is lagging its sync interval
*/
registry.register(() -> registry.timer("/commitlog/metrics/waiting_on_commit"),
factory.createMetricName("WaitingOnCommit"));
/** Number of completed tasks */
public final Gauge<Long> completedTasks;
/** Number of pending tasks */
public final Gauge<Long> pendingTasks;
/** Current size used by all the commit log segments */
public final Gauge<Long> totalCommitLogSize;
/**
* Time spent waiting for a CLS to be allocated - under normal conditions
* this should be zero
*/
public final Timer waitingOnSegmentAllocation;
/**
* The time spent waiting on CL sync; for Periodic this is only occurs when
* the sync is lagging its sync interval
*/
public final Timer waitingOnCommit;
public CommitLogMetrics() {
completedTasks = APIMetrics.newGauge(
factory.createMetricName("CompletedTasks"), new Gauge<Long>() {
public Long value() {
return c.getLongValue("/commitlog/metrics/completed_tasks");
}
});
pendingTasks = APIMetrics.newGauge(
factory.createMetricName("PendingTasks"), new Gauge<Long>() {
public Long value() {
return c.getLongValue("/commitlog/metrics/pending_tasks");
}
});
totalCommitLogSize = APIMetrics.newGauge(
factory.createMetricName("TotalCommitLogSize"),
new Gauge<Long>() {
public Long value() {
return c.getLongValue("/commitlog/metrics/total_commit_log_size");
}
});
waitingOnSegmentAllocation = APIMetrics.newTimer("/commit_log/metrics/waiting_on_segment_allocation",
factory.createMetricName("WaitingOnSegmentAllocation"),
TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
waitingOnCommit = APIMetrics.newTimer("/commit_log/metrics/waiting_on_commit",
factory.createMetricName("WaitingOnCommit"),
TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
}
}

View File

@ -18,59 +18,57 @@
/*
* Copyright 2015 Cloudius Systems
*
*
* Modified by Cloudius Systems
*/
package org.apache.cassandra.metrics;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.management.MalformedObjectNameException;
import com.cloudius.urchin.api.APIClient;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Meter;
/**
* Metrics for compaction.
*/
public class CompactionMetrics implements Metrics {
public class CompactionMetrics {
public static final MetricNameFactory factory = new DefaultNameFactory(
"Compaction");
private APIClient c = new APIClient();
/** Estimated number of compactions remaining to perform */
public final Gauge<Integer> pendingTasks;
/** Number of completed compactions since server [re]start */
public final Gauge<Long> completedTasks;
/** Total number of compactions since server [re]start */
public final Meter totalCompactionsCompleted;
/** Total number of bytes compacted since server [re]start */
public final Counter bytesCompacted;
public CompactionMetrics() {
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
MetricNameFactory factory = new DefaultNameFactory("Compaction");
/** Estimated number of compactions remaining to perform */
registry.register(() -> registry.gauge(Integer.class, "/compaction_manager/metrics/pending_tasks"),
factory.createMetricName("PendingTasks"));
/** Number of completed compactions since server [re]start */
registry.register(() -> registry.gauge("/compaction_manager/metrics/completed_tasks"),
factory.createMetricName("CompletedTasks"));
/** Total number of compactions since server [re]start */
registry.register(() -> registry.meter("/compaction_manager/metrics/total_compactions_completed"),
factory.createMetricName("TotalCompactionsCompleted"));
/** Total number of bytes compacted since server [re]start */
registry.register(() -> registry.meter("/compaction_manager/metrics/bytes_compacted"),
pendingTasks = APIMetrics.newGauge(
factory.createMetricName("PendingTasks"), new Gauge<Integer>() {
public Integer value() {
return c.getIntValue("/compaction_manager/metrics/pending_tasks");
}
});
completedTasks = APIMetrics.newGauge(
factory.createMetricName("CompletedTasks"), new Gauge<Long>() {
public Long value() {
return c.getLongValue("/compaction_manager/metrics/completed_tasks");
}
});
totalCompactionsCompleted = APIMetrics.newMeter(
"/compaction_manager/metrics/total_compactions_completed",
factory.createMetricName("TotalCompactionsCompleted"),
"compaction completed", TimeUnit.SECONDS);
bytesCompacted = APIMetrics.newCounter(
"/compaction_manager/metrics/bytes_compacted",
factory.createMetricName("BytesCompacted"));
registry.register(() -> registry.gauge((client) -> {
Map<String, Map<String, Integer>> result = new HashMap<>();
JsonArray compactions = client.getJsonArray("compaction_manager/metrics/pending_tasks_by_table");
for (int i = 0; i < compactions.size(); i++) {
JsonObject c = compactions.getJsonObject(i);
String ks = c.getString("ks");
String cf = c.getString("cf");
if (!result.containsKey(ks)) {
result.put(ks, new HashMap<>());
}
Map<String, Integer> map = result.get(ks);
map.put(cf, (int)(c.getJsonNumber("task").longValue()));
}
return result;
}), factory.createMetricName("PendingTasksByTableName"));
}
}

View File

@ -24,27 +24,42 @@
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import java.util.concurrent.TimeUnit;
import org.apache.cassandra.net.MessagingService;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.yammer.metrics.core.APISettableMeter;
/**
* Metrics for dropped messages by verb.
*/
public class DroppedMessageMetrics implements Metrics {
private final MessagingService.Verb verb;
public class DroppedMessageMetrics {
/** Number of dropped messages */
public final APISettableMeter dropped;
private long lastDropped = 0;
public DroppedMessageMetrics(MessagingService.Verb verb) {
this.verb = verb;
MetricNameFactory factory = new DefaultNameFactory("DroppedMessage",
verb.toString());
dropped = (APISettableMeter) APIMetrics.newSettableMeter(
factory.createMetricName("Dropped"), "dropped",
TimeUnit.SECONDS);
dropped.stop();
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
MetricNameFactory factory = new DefaultNameFactory("DroppedMessage", verb.toString());
/** Number of dropped messages */
// TODO: this API url does not exist. Add meter calls for verbs.
registry.register(() -> registry.meter("/messaging_service/messages/dropped/" + verb),
factory.createMetricName("Dropped"));
@Deprecated
public int getRecentlyDropped() {
long currentDropped = dropped.count();
long recentlyDropped = currentDropped - lastDropped;
lastDropped = currentDropped;
return (int) recentlyDropped;
}
public APISettableMeter getMeter() {
return dropped;
}
}

View File

@ -0,0 +1,55 @@
package org.apache.cassandra.metrics;
/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
import javax.ws.rs.core.MultivaluedMap;
import com.cloudius.urchin.api.APIClient;
import com.cloudius.urchin.utils.EstimatedHistogram;
public class EstimatedHistogramWrapper {
private APIClient c = new APIClient();
private String url;
private MultivaluedMap<String, String> queryParams;
private static final int DURATION = 50;
private int duration;
public EstimatedHistogramWrapper(String url, MultivaluedMap<String, String> queryParams, int duration) {
this.url = url;
this.queryParams = queryParams;
this.duration = duration;
}
public EstimatedHistogramWrapper(String url) {
this(url, null, DURATION);
}
public EstimatedHistogramWrapper(String url, int duration) {
this(url, null, duration);
}
public EstimatedHistogram get() {
return c.getEstimatedHistogram(url, queryParams, duration);
}
public long[] getBuckets(boolean reset) {
return get().getBuckets(reset);
}
}

View File

@ -23,19 +23,42 @@
*/
package org.apache.cassandra.metrics;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.cloudius.urchin.utils.EstimatedHistogram;
import com.cloudius.urchin.utils.RecentEstimatedHistogram;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Timer;
/**
* Metrics about latencies
*/
public class LatencyMetrics implements Metrics {
protected final MetricNameFactory[] factories;
public class LatencyMetrics {
/** Latency */
public final Timer latency;
/** Total latency in micro sec */
public final Counter totalLatency;
/** parent metrics to replicate any updates to **/
private List<LatencyMetrics> parents = Lists.newArrayList();
protected final MetricNameFactory factory;
protected final String namePrefix;
protected final String uri;
protected final String param;
@Deprecated public EstimatedHistogramWrapper totalLatencyHistogram;
/*
* It should not be called directly, use the getRecentLatencyHistogram
*/
@Deprecated protected final RecentEstimatedHistogram recentLatencyHistogram = new RecentEstimatedHistogram();
protected long lastLatency;
protected long lastOpCount;
/**
* Create LatencyMetrics with given group, type, and scope. Name prefix for
@ -46,8 +69,8 @@ public class LatencyMetrics implements Metrics {
* @param scope
* Scope
*/
public LatencyMetrics(String type, String scope, String uri) {
this(type, "", scope, uri, null);
public LatencyMetrics(String url, String type, String scope) {
this(url, type, "", scope);
}
/**
@ -61,35 +84,88 @@ public class LatencyMetrics implements Metrics {
* @param scope
* Scope of metrics
*/
public LatencyMetrics(String type, String namePrefix, String scope, String uri, String param) {
this(namePrefix, uri, param, new DefaultNameFactory(type, scope));
public LatencyMetrics(String url, String type, String namePrefix,
String scope) {
this(url, new DefaultNameFactory(type, scope), namePrefix);
}
public LatencyMetrics(String namePrefix, String uri, MetricNameFactory... factories) {
this(namePrefix, uri, null, factories);
/**
* Create LatencyMetrics with given group, type, prefix to append to each
* metric name, and scope.
*
* @param factory
* MetricName factory to use
* @param namePrefix
* Prefix to append to each metric name
*/
public LatencyMetrics(String url, MetricNameFactory factory,
String namePrefix) {
this(url, null, factory, namePrefix);
}
public LatencyMetrics(String namePrefix, String uri, String param, MetricNameFactory... factories) {
this.factories = factories;
public LatencyMetrics(String url, String paramName,
MetricNameFactory factory, String namePrefix) {
this.factory = factory;
this.namePrefix = namePrefix;
this.uri = uri;
this.param = param;
paramName = (paramName == null)? "" : "/" + paramName;
latency = APIMetrics.newTimer(url + "/histogram" + paramName,
factory.createMetricName(namePrefix + "Latency"),
TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
totalLatency = APIMetrics.newCounter(url + paramName,
factory.createMetricName(namePrefix + "TotalLatency"));
totalLatencyHistogram = new EstimatedHistogramWrapper(url + "/estimated_histogram" + paramName);
}
protected ObjectName[] names(String suffix) throws MalformedObjectNameException {
return Arrays.stream(factories).map(f -> {
try {
return f.createMetricName(namePrefix + suffix);
} catch (MalformedObjectNameException e) {
throw new RuntimeException(e); // dung...
}
}).toArray(size -> new ObjectName[size]);
/**
* Create LatencyMetrics with given group, type, prefix to append to each
* metric name, and scope. Any updates to this will also run on parent
*
* @param factory
* MetricName factory to use
* @param namePrefix
* Prefix to append to each metric name
* @param parents
* any amount of parents to replicate updates to
*/
public LatencyMetrics(String url, MetricNameFactory factory,
String namePrefix, LatencyMetrics... parents) {
this(url, factory, namePrefix);
this.parents.addAll(ImmutableList.copyOf(parents));
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
String paramName = (param == null) ? "" : "/" + param;
registry.register(() -> registry.timer(uri + "/moving_average_histogram" + paramName), names("Latency"));
registry.register(() -> registry.counter(uri + paramName), names("TotalLatency"));
/** takes nanoseconds **/
public void addNano(long nanos) {
// convert to microseconds. 1 millionth
latency.update(nanos, TimeUnit.NANOSECONDS);
totalLatency.inc(nanos / 1000);
for (LatencyMetrics parent : parents) {
parent.addNano(nanos);
}
}
public void release() {
APIMetrics.defaultRegistry()
.removeMetric(factory.createMetricName(namePrefix + "Latency"));
APIMetrics.defaultRegistry().removeMetric(
factory.createMetricName(namePrefix + "TotalLatency"));
}
@Deprecated
public synchronized double getRecentLatency() {
long ops = latency.count();
long n = totalLatency.count();
if (ops == lastOpCount)
return 0;
try {
return ((double) n - lastLatency) / (ops - lastOpCount);
} finally {
lastLatency = n;
lastOpCount = ops;
}
}
public long[] getRecentLatencyHistogram() {
return recentLatencyHistogram.getBuckets(totalLatencyHistogram.getBuckets(false));
}
}

View File

@ -1,38 +0,0 @@
package org.apache.cassandra.metrics;
import java.util.function.Function;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
/**
* Action interface for any type that encapsulates n metrics.
*
* @author calle
*
*/
public interface Metrics {
/**
* Implementors should issue
* {@link MetricsRegistry#register(java.util.function.Supplier, javax.management.ObjectName...)}
* for every {@link Metrics} they generate. This method is called in both
* bind (create) and unbind (remove) phase, so an appropriate use of
* {@link Function} binding is advisable.
*
* @param registry
* @throws MalformedObjectNameException
*/
void register(MetricsRegistry registry) throws MalformedObjectNameException;
/**
* Same as {{@link #register(MetricsRegistry)}, but for {@link Metric}s that
* are "global" (i.e. static - not bound to an individual bean instance.
* This method is called whenever the first encapsulating MBean is
* added/removed from a {@link MBeanServer}.
*
* @param registry
* @throws MalformedObjectNameException
*/
default void registerGlobals(MetricsRegistry registry) throws MalformedObjectNameException {
}
}

View File

@ -1,813 +0,0 @@
/*
* 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.
*/
package org.apache.cassandra.metrics;
import static com.scylladb.jmx.api.APIClient.getReader;
import static java.lang.Math.floor;
import static java.util.logging.Level.SEVERE;
import jakarta.json.JsonArray;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Logger;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import com.scylladb.jmx.api.APIClient;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
/**
* Makes integrating 3.0 metrics API with 2.0.
* <p>
* The 3.0 API comes with poor JMX integration
* </p>
*/
public class MetricsRegistry {
private static final long CACHE_DURATION = 1000;
private static final long UPDATE_INTERVAL = 50;
private static final Logger logger = Logger.getLogger(MetricsRegistry.class.getName());
private final APIClient client;
private final JmxMBeanServer mBeanServer;
public MetricsRegistry(APIClient client, JmxMBeanServer mBeanServer) {
this.client = client;
this.mBeanServer = mBeanServer;
}
public MetricsRegistry(MetricsRegistry other) {
this(other.client, other.mBeanServer);
}
public MetricMBean gauge(String url) {
return gauge(Long.class, url);
}
public <T> MetricMBean gauge(Class<T> type, final String url) {
return gauge(getReader(type), url);
}
public <T> MetricMBean gauge(final BiFunction<APIClient, String, T> function, final String url) {
return gauge(c -> function.apply(c, url));
}
public <T> MetricMBean gauge(final Function<APIClient, T> function) {
return gauge(() -> function.apply(client));
}
private class JmxGauge implements JmxGaugeMBean {
private final Supplier<?> function;
public JmxGauge(Supplier<?> function) {
this.function = function;
}
@Override
public Object getValue() {
return function.get();
}
}
public <T> MetricMBean gauge(final Supplier<T> function) {
return new JmxGauge(function);
}
/**
* Default approach to register is to actually register/add to
* {@link MBeanServer} For unbind phase, override here.
*
* @param bean
* @param objectNames
*/
public void register(Supplier<MetricMBean> f, ObjectName... objectNames) {
MetricMBean bean = f.get();
for (ObjectName name : objectNames) {
try {
mBeanServer.getMBeanServerInterceptor().registerMBean(bean, name);
} catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
logger.log(SEVERE, "Could not register mbean", e);
}
}
}
private class JmxCounter implements JmxCounterMBean {
private final String url;
public JmxCounter(String url) {
super();
this.url = url;
}
@Override
public long getCount() {
return client.getLongValue(url);
}
}
public MetricMBean counter(final String url) {
if (url != null) {
return new JmxCounter(url);
}
return new JmxCounter(url) {
@Override
public long getCount() {
return 0;
}
};
}
private abstract class IntermediatelyUpdated {
private final long interval;
private final Supplier<JsonObject> supplier;
private long lastUpdate;
public IntermediatelyUpdated(String url, long interval) {
this.supplier = () -> client.getJsonObj(url, null);
this.interval = interval;
}
public IntermediatelyUpdated(Supplier<JsonObject> supplier, long interval) {
this.supplier = supplier;
this.interval = interval;
}
public abstract void update(JsonObject obj);
public final void update() {
long now = System.currentTimeMillis();
if (now - lastUpdate < interval) {
return;
}
try {
JsonObject obj = supplier.get();
update(obj);
} finally {
lastUpdate = now;
}
}
}
private static class Meter {
public final long count;
public final double oneMinuteRate;
public final double fiveMinuteRate;
public final double fifteenMinuteRate;
public final double meanRate;
public Meter(long count, double oneMinuteRate, double fiveMinuteRate, double fifteenMinuteRate,
double meanRate) {
this.count = count;
this.oneMinuteRate = oneMinuteRate;
this.fiveMinuteRate = fiveMinuteRate;
this.fifteenMinuteRate = fifteenMinuteRate;
this.meanRate = meanRate;
}
public Meter() {
this(0, 0, 0, 0, 0);
}
public Meter(JsonObject obj) {
JsonArray rates = obj.getJsonArray("rates");
oneMinuteRate = rates.getJsonNumber(0).doubleValue();
fiveMinuteRate = rates.getJsonNumber(1).doubleValue();
fifteenMinuteRate = rates.getJsonNumber(2).doubleValue();
meanRate = obj.getJsonNumber("mean_rate").doubleValue();
count = obj.getJsonNumber("count").longValue();
}
}
private static final TimeUnit RATE_UNIT = TimeUnit.SECONDS;
private static final TimeUnit DURATION_UNIT = TimeUnit.MICROSECONDS;
private static final TimeUnit API_DURATION_UNIT = TimeUnit.MICROSECONDS;
private static final double DURATION_FACTOR = 1.0 / API_DURATION_UNIT.convert(1, DURATION_UNIT);
private static double toDuration(double micro) {
return micro * DURATION_FACTOR;
}
private static String unitString(TimeUnit u) {
String s = u.toString().toLowerCase(Locale.US);
return s.substring(0, s.length() - 1);
}
private class JmxMeter extends IntermediatelyUpdated implements JmxMeterMBean {
private Meter meter = new Meter();
public JmxMeter(String url, long interval) {
super(url, interval);
}
public JmxMeter(Supplier<JsonObject> supplier, long interval) {
super(supplier, interval);
}
@Override
public void update(JsonObject obj) {
meter = new Meter(obj);
}
@Override
public long getCount() {
update();
return meter.count;
}
@Override
public double getMeanRate() {
update();
return meter.meanRate;
}
@Override
public double getOneMinuteRate() {
update();
return meter.oneMinuteRate;
}
@Override
public double getFiveMinuteRate() {
update();
return meter.fiveMinuteRate;
}
@Override
public double getFifteenMinuteRate() {
update();
return meter.fifteenMinuteRate;
}
@Override
public String getRateUnit() {
return "event/" + unitString(RATE_UNIT);
}
}
public MetricMBean meter(String url) {
return new JmxMeter(url, CACHE_DURATION);
}
private static long[] asLongArray(JsonArray a) {
return a.getValuesAs(JsonNumber.class).stream().mapToLong(n -> n.longValue()).toArray();
}
private static interface Samples {
default double getValue(double quantile) {
return 0;
}
default long[] getValues() {
return new long[0];
}
}
private static class BufferSamples implements Samples {
private final long[] samples;
public BufferSamples(long[] samples) {
this.samples = samples;
Arrays.sort(this.samples);
}
@Override
public long[] getValues() {
return samples;
}
@Override
public double getValue(double quantile) {
if (quantile < 0.0 || quantile > 1.0) {
throw new IllegalArgumentException(quantile + " is not in [0..1]");
}
if (samples.length == 0) {
return 0.0;
}
final double pos = quantile * (samples.length + 1);
if (pos < 1) {
return samples[0];
}
if (pos >= samples.length) {
return samples[samples.length - 1];
}
final double lower = samples[(int) pos - 1];
final double upper = samples[(int) pos];
return lower + (pos - floor(pos)) * (upper - lower);
}
}
private static class Histogram {
private final long count;
private final long min;
private final long max;
private final double mean;
private final double stdDev;
private final Samples samples;
public Histogram(long count, long min, long max, double mean, double stdDev, Samples samples) {
this.count = count;
this.min = min;
this.max = max;
this.mean = mean;
this.stdDev = stdDev;
this.samples = samples;
}
public Histogram() {
this(0, 0, 0, 0, 0, new Samples() {
});
}
public Histogram(JsonObject obj) {
this(obj.getJsonNumber("count").longValue(), obj.getJsonNumber("min").longValue(),
obj.getJsonNumber("max").longValue(), obj.getJsonNumber("mean").doubleValue(),
obj.getJsonNumber("variance").doubleValue(), new BufferSamples(getValues(obj)));
}
public Histogram(EstimatedHistogram h) {
this(h.count(), h.min(), h.max(), h.mean(), 0, h);
}
private static long[] getValues(JsonObject obj) {
JsonArray arr = obj.getJsonArray("sample");
if (arr != null) {
return asLongArray(arr);
}
return new long[0];
}
public long[] getValues() {
return samples.getValues();
}
// Origin (and previous iterations of scylla-jxm)
// uses biased/ExponentiallyDecaying measurements
// for the history & quantile resolution.
// However, for use that is just gobbletigook, since
// we, at occasions of being asked, and when certain time
// has passed, ask the actual scylla server for a
// "values" buffer. A buffer with no information whatsoever
// on how said values correlate to actual sampling
// time.
// So, applying time weights at this level is just
// wrong. We can just as well treat this as a uniform
// distribution.
// Obvious improvement: Send time/value tuples instead.
public double getValue(double quantile) {
return samples.getValue(quantile);
}
public long getCount() {
return count;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
public double getMean() {
return mean;
}
public double getStdDev() {
return stdDev;
}
}
private static class EstimatedHistogram implements Samples {
/**
* The series of values to which the counts in `buckets` correspond: 1,
* 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 17, 20, etc. Thus, a `buckets` of
* [0, 0, 1, 10] would mean we had seen one value of 3 and 10 values of
* 4.
*
* The series starts at 1 and grows by 1.2 each time (rounding and
* removing duplicates). It goes from 1 to around 36M by default
* (creating 90+1 buckets), which will give us timing resolution from
* microseconds to 36 seconds, with less precision as the numbers get
* larger.
*
* Each bucket represents values from (previous bucket offset, current
* offset].
*/
private final long[] bucketOffsets;
// buckets is one element longer than bucketOffsets -- the last element
// is
// values greater than the last offset
private long[] buckets;
public EstimatedHistogram(JsonObject obj) {
this(asLongArray(obj.getJsonArray("bucket_offsets")), asLongArray(obj.getJsonArray("buckets")));
}
public EstimatedHistogram(long[] offsets, long[] bucketData) {
assert bucketData.length == offsets.length + 1;
bucketOffsets = offsets;
buckets = bucketData;
}
/**
* @return the smallest value that could have been added to this
* histogram
*/
public long min() {
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] > 0) {
return i == 0 ? 0 : 1 + bucketOffsets[i - 1];
}
}
return 0;
}
/**
* @return the largest value that could have been added to this
* histogram. If the histogram overflowed, returns
* Long.MAX_VALUE.
*/
public long max() {
int lastBucket = buckets.length - 1;
if (buckets[lastBucket] > 0) {
return Long.MAX_VALUE;
}
for (int i = lastBucket - 1; i >= 0; i--) {
if (buckets[i] > 0) {
return bucketOffsets[i];
}
}
return 0;
}
@Override
public long[] getValues() {
return buckets;
}
/**
* @param percentile
* @return estimated value at given percentile
*/
@Override
public double getValue(double percentile) {
assert percentile >= 0 && percentile <= 1.0;
int lastBucket = buckets.length - 1;
if (buckets[lastBucket] > 0) {
throw new IllegalStateException("Unable to compute when histogram overflowed");
}
long pcount = (long) Math.floor(count() * percentile);
if (pcount == 0) {
return 0;
}
long elements = 0;
for (int i = 0; i < lastBucket; i++) {
elements += buckets[i];
if (elements >= pcount) {
return bucketOffsets[i];
}
}
return 0;
}
/**
* @return the mean histogram value (average of bucket offsets, weighted
* by count)
* @throws IllegalStateException
* if any values were greater than the largest bucket
* threshold
*/
public long mean() {
int lastBucket = buckets.length - 1;
if (buckets[lastBucket] > 0) {
throw new IllegalStateException("Unable to compute ceiling for max when histogram overflowed");
}
long elements = 0;
long sum = 0;
for (int i = 0; i < lastBucket; i++) {
long bCount = buckets[i];
elements += bCount;
sum += bCount * bucketOffsets[i];
}
return (long) Math.ceil((double) sum / elements);
}
/**
* @return the total number of non-zero values
*/
public long count() {
return Arrays.stream(buckets).sum();
}
/**
* @return true if this histogram has overflowed -- that is, a value
* larger than our largest bucket could bound was added
*/
@SuppressWarnings("unused")
public boolean isOverflowed() {
return buckets[buckets.length - 1] > 0;
}
}
private class JmxHistogram extends IntermediatelyUpdated implements JmxHistogramMBean {
private Histogram histogram = new Histogram();
public JmxHistogram(String url, long interval) {
super(url, interval);
}
@Override
public void update(JsonObject obj) {
if (obj.containsKey("hist")) {
obj = obj.getJsonObject("hist");
}
if (obj.containsKey("buckets")) {
histogram = new Histogram(new EstimatedHistogram(obj));
} else {
histogram = new Histogram(obj);
}
}
@Override
public long getCount() {
update();
return histogram.getCount();
}
@Override
public long getMin() {
update();
return histogram.getMin();
}
@Override
public long getMax() {
update();
return histogram.getMax();
}
@Override
public double getMean() {
update();
return histogram.getMean();
}
@Override
public double getStdDev() {
update();
return histogram.getStdDev();
}
@Override
public double get50thPercentile() {
update();
return histogram.getValue(.5);
}
@Override
public double get75thPercentile() {
update();
return histogram.getValue(.75);
}
@Override
public double get95thPercentile() {
update();
return histogram.getValue(.95);
}
@Override
public double get98thPercentile() {
update();
return histogram.getValue(.98);
}
@Override
public double get99thPercentile() {
update();
return histogram.getValue(.99);
}
@Override
public double get999thPercentile() {
update();
return histogram.getValue(.999);
}
@Override
public long[] values() {
update();
return histogram.getValues();
}
}
public MetricMBean histogram(String url, boolean considerZeroes) {
return new JmxHistogram(url, UPDATE_INTERVAL);
}
private class JmxTimer extends JmxMeter implements JmxTimerMBean {
private Histogram histogram = new Histogram();
public JmxTimer(String url, long interval) {
super(url, interval);
}
@Override
public void update(JsonObject obj) {
// TODO: this is not atomic.
super.update(obj.getJsonObject("meter"));
histogram = new Histogram(obj.getJsonObject("hist"));
}
@Override
public double getMin() {
update();
return toDuration(histogram.getMin());
}
@Override
public double getMax() {
update();
return toDuration(histogram.getMax());
}
@Override
public double getMean() {
update();
return toDuration(histogram.getMean());
}
@Override
public double getStdDev() {
update();
return toDuration(histogram.getStdDev());
}
@Override
public double get50thPercentile() {
update();
return toDuration(histogram.getValue(.5));
}
@Override
public double get75thPercentile() {
update();
return toDuration(histogram.getValue(.75));
}
@Override
public double get95thPercentile() {
update();
return toDuration(histogram.getValue(.95));
}
@Override
public double get98thPercentile() {
update();
return toDuration(histogram.getValue(.98));
}
@Override
public double get99thPercentile() {
update();
return toDuration(histogram.getValue(.99));
}
@Override
public double get999thPercentile() {
update();
return toDuration(histogram.getValue(.999));
}
@Override
public long[] values() {
update();
return histogram.getValues();
}
@Override
public String getDurationUnit() {
update();
return DURATION_UNIT.toString().toLowerCase(Locale.US);
}
}
public MetricMBean timer(String url) {
return new JmxTimer(url, UPDATE_INTERVAL);
}
public interface MetricMBean {
}
public static interface JmxGaugeMBean extends MetricMBean {
Object getValue();
}
public interface JmxHistogramMBean extends MetricMBean {
long getCount();
long getMin();
long getMax();
double getMean();
double getStdDev();
double get50thPercentile();
double get75thPercentile();
double get95thPercentile();
double get98thPercentile();
double get99thPercentile();
double get999thPercentile();
long[] values();
}
public interface JmxCounterMBean extends MetricMBean {
long getCount();
}
public interface JmxMeterMBean extends MetricMBean {
long getCount();
double getMeanRate();
double getOneMinuteRate();
double getFiveMinuteRate();
double getFifteenMinuteRate();
String getRateUnit();
}
public interface JmxTimerMBean extends JmxMeterMBean {
double getMin();
double getMax();
double getMean();
double getStdDev();
double get50thPercentile();
double get75thPercentile();
double get95thPercentile();
double get98thPercentile();
double get99thPercentile();
double get999thPercentile();
long[] values();
String getDurationUnit();
}
}

View File

@ -23,21 +23,27 @@
*/
package org.apache.cassandra.metrics;
import javax.management.MalformedObjectNameException;
import com.cloudius.urchin.metrics.APIMetrics;
import com.cloudius.urchin.metrics.DefaultNameFactory;
import com.cloudius.urchin.metrics.MetricNameFactory;
import com.yammer.metrics.core.Counter;
/**
* Metrics related to Storage.
*/
public class StorageMetrics implements Metrics {
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
MetricNameFactory factory = new DefaultNameFactory("Storage");
registry.register(() -> registry.counter("/storage_service/metrics/load"), factory.createMetricName("Load"));
registry.register(() -> registry.counter("/storage_service/metrics/exceptions"),
factory.createMetricName("Exceptions"));
registry.register(() -> registry.counter("/storage_service/metrics/hints_in_progress"),
factory.createMetricName("TotalHintsInProgress"));
registry.register(() -> registry.counter("/storage_service/metrics/total_hints"),
factory.createMetricName("TotalHints"));
}
public class StorageMetrics {
private static final MetricNameFactory factory = new DefaultNameFactory(
"Storage");
public static final Counter load = APIMetrics.newCounter(
"/storage_service/metrics/load", factory.createMetricName("Load"));
public static final Counter exceptions = APIMetrics.newCounter(
"/storage_service/metrics/exceptions",
factory.createMetricName("Exceptions"));
public static final Counter totalHintsInProgress = APIMetrics.newCounter(
"/storage_service/metrics/hints_in_progress",
factory.createMetricName("TotalHintsInProgress"));
public static final Counter totalHints = APIMetrics.newCounter(
"/storage_service/metrics/total_hints",
factory.createMetricName("TotalHints"));
}

View File

@ -1,111 +0,0 @@
/*
* 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.
*/
/*
* Copyright 2015 ScyllaDB
*
* Modified by ScyllaDB
*/
package org.apache.cassandra.metrics;
import static java.util.Arrays.asList;
import static org.apache.cassandra.metrics.DefaultNameFactory.createMetricName;
import jakarta.json.JsonArray;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.OperationsException;
import com.scylladb.jmx.api.APIClient;
import com.scylladb.jmx.metrics.APIMBean;
import com.scylladb.jmx.metrics.RegistrationChecker;
import com.scylladb.jmx.metrics.RegistrationMode;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
/**
* Metrics for streaming.
*/
public class StreamingMetrics {
public static final String TYPE_NAME = "Streaming";
private static final HashSet<ObjectName> globalNames;
static {
try {
globalNames = new HashSet<ObjectName>(asList(createMetricName(TYPE_NAME, "ActiveOutboundStreams", null),
createMetricName(TYPE_NAME, "TotalIncomingBytes", null),
createMetricName(TYPE_NAME, "TotalOutgoingBytes", null)));
} catch (MalformedObjectNameException e) {
throw new Error(e);
}
};
private StreamingMetrics() {
}
private static boolean isStreamingName(ObjectName n) {
return TYPE_NAME.equals(n.getKeyProperty("type"));
}
public static RegistrationChecker createRegistrationChecker() {
return new RegistrationChecker() {
@Override
protected void doCheck(APIClient client, JmxMBeanServer server, EnumSet<RegistrationMode> mode) throws OperationsException, UnknownHostException {
Set<ObjectName> all = new HashSet<ObjectName>(globalNames);
JsonArray streams = client.getJsonArray("/stream_manager/");
for (int i = 0; i < streams.size(); i++) {
JsonArray sessions = streams.getJsonObject(i).getJsonArray("sessions");
for (int j = 0; j < sessions.size(); j++) {
String peer = sessions.getJsonObject(j).getString("peer");
String scope = InetAddress.getByName(peer).getHostAddress().replaceAll(":", ".");
all.add(createMetricName(TYPE_NAME, "IncomingBytes", scope));
all.add(createMetricName(TYPE_NAME, "OutgoingBytes", scope));
}
}
MetricsRegistry registry = new MetricsRegistry(client, server);
APIMBean.checkRegistration(server, all, mode, StreamingMetrics::isStreamingName, n -> {
String scope = n.getKeyProperty("scope");
String name = n.getKeyProperty("name");
String url = null;
if ("ActiveOutboundStreams".equals(name)) {
url = "/stream_manager/metrics/outbound";
} else if ("IncomingBytes".equals(name) || "TotalIncomingBytes".equals(name)) {
url = "/stream_manager/metrics/incoming";
} else if ("OutgoingBytes".equals(name) || "TotalOutgoingBytes".equals(name)) {
url = "/stream_manager/metrics/outgoing";
}
if (url == null) {
throw new IllegalArgumentException();
}
if (scope != null) {
url = url + "/" + scope;
}
return registry.counter(url);
});
}
};
}
}

View File

@ -1,553 +0,0 @@
/*
* 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.
*/
package org.apache.cassandra.metrics;
import static com.scylladb.jmx.api.APIClient.getReader;
import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.util.Hashtable;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.cassandra.db.ColumnFamilyStore;
import com.scylladb.jmx.api.APIClient;
/**
* Metrics for {@link ColumnFamilyStore}.
*/
public class TableMetrics implements Metrics {
private final MetricNameFactory factory;
private final MetricNameFactory aliasFactory;
private static final MetricNameFactory globalFactory = new AllTableMetricNameFactory("Table");
private static final MetricNameFactory globalAliasFactory = new AllTableMetricNameFactory("ColumnFamily");
private static final LatencyMetrics globalLatency[] = new LatencyMetrics[] {
new LatencyMetrics("Read", compose("read_latency"), globalFactory, globalAliasFactory),
new LatencyMetrics("Write", compose("read_latency"), globalFactory, globalAliasFactory),
new LatencyMetrics("Range", compose("read_latency"), globalFactory, globalAliasFactory), };
private final String cfName;
private final LatencyMetrics latencyMetrics[];
public TableMetrics(String keyspace, String columnFamily, boolean isIndex) {
this.factory = new TableMetricNameFactory(keyspace, columnFamily, isIndex, "Table");
this.aliasFactory = new TableMetricNameFactory(keyspace, columnFamily, isIndex, "ColumnFamily");
this.cfName = keyspace + ":" + columnFamily;
latencyMetrics = new LatencyMetrics[] {
new LatencyMetrics("Read", compose("read_latency"), cfName, factory, aliasFactory),
new LatencyMetrics("Write", compose("write_latency"), cfName, factory, aliasFactory),
new LatencyMetrics("Range", compose("range_latency"), cfName, factory, aliasFactory),
new LatencyMetrics("CasPrepare", compose("cas_prepare"), cfName, factory, aliasFactory),
new LatencyMetrics("CasPropose", compose("cas_propose"), cfName, factory, aliasFactory),
new LatencyMetrics("CasCommit", compose("cas_commit"), cfName, factory, aliasFactory), };
}
@Override
public void register(MetricsRegistry registry) throws MalformedObjectNameException {
Registry r = new Registry(registry, factory, aliasFactory, cfName);
registerCommon(r);
registerLocal(r);
}
@Override
public void registerGlobals(MetricsRegistry registry) throws MalformedObjectNameException {
Registry r = new Registry(registry, globalFactory, globalAliasFactory, null);
registerCommon(r);
for (LatencyMetrics l : globalLatency) {
l.register(registry);
}
}
private static String compose(String base, String name) {
String s = "/column_family/metrics/" + base;
return name != null ? s + "/" + name : s;
}
private static String compose(String base) {
return compose(base, null);
}
/**
* Creates metrics for given {@link ColumnFamilyStore}.
*
* @param cfs
* ColumnFamilyStore to measure metrics
*/
static class Registry extends MetricsRegistry {
@SuppressWarnings("unused")
private Function<APIClient, Long> newGauge(final String url) {
return newGauge(Long.class, url);
}
public <T> Function<APIClient, T> newGauge(BiFunction<APIClient, String, T> function, String url) {
return c -> {
return function.apply(c, url);
};
}
private <T> Function<APIClient, T> newGauge(Class<T> type, final String url) {
return newGauge(getReader(type), url);
}
final MetricNameFactory factory;
final MetricNameFactory aliasFactory;
final String cfName;
final MetricsRegistry other;
public Registry(MetricsRegistry other, MetricNameFactory factory, MetricNameFactory aliasFactory,
String cfName) {
super(other);
this.other = other;
this.cfName = cfName;
this.factory = factory;
this.aliasFactory = aliasFactory;
}
@Override
public void register(Supplier<MetricMBean> f, ObjectName... objectNames) {
other.register(f, objectNames);
}
public void createTableGauge(String name, String uri) throws MalformedObjectNameException {
createTableGauge(name, name, uri);
}
public void createTableGauge(String name, String alias, String uri) throws MalformedObjectNameException {
createTableGauge(Long.class, name, alias, uri);
}
public <T> void createTableGauge(Class<T> c, String name, String uri) throws MalformedObjectNameException {
createTableGauge(c, c, name, name, uri);
}
public <T> void createTableGauge(Class<T> c, String name, String alias, String uri) throws MalformedObjectNameException {
createTableGauge(c, name, alias, uri, getReader(c));
}
public <T> void createTableGauge(Class<T> c, String name, String uri, BiFunction<APIClient, String, T> f)
throws MalformedObjectNameException {
createTableGauge(c, name, name, uri, f);
}
public <T> void createTableGauge(Class<T> c, String name, String alias, String uri,
BiFunction<APIClient, String, T> f) throws MalformedObjectNameException {
register(() -> gauge(newGauge(f, compose(uri, cfName))), factory.createMetricName(name),
aliasFactory.createMetricName(alias));
}
private static <T> BiFunction<APIClient, String, T> getDummy(Class<T> type) {
if (type == String.class) {
return (c, s) -> type.cast("");
} else if (type == Integer.class) {
return (c, s) -> type.cast(0);
} else if (type == Double.class) {
return (c, s) -> type.cast(0.0);
} else if (type == Long.class) {
return (c, s) -> type.cast(0L);
}
throw new IllegalArgumentException(type.getName());
}
public <T> void createDummyTableGauge(Class<T> c, String name) throws MalformedObjectNameException {
register(() -> gauge(newGauge(getDummy(c), null)), factory.createMetricName(name),
aliasFactory.createMetricName(name));
}
public <L, G> void createTableGauge(Class<L> c1, Class<G> c2, String name, String alias, String uri)
throws MalformedObjectNameException {
if (cfName != null) {
createTableGauge(c1, name, alias, uri, getReader(c1));
} else { // global case
createTableGauge(c2, name, alias, uri, getReader(c2));
}
}
public void createTableCounter(String name, String uri) throws MalformedObjectNameException {
createTableCounter(name, name, uri);
}
public void createTableCounter(String name, String alias, String uri) throws MalformedObjectNameException {
register(() -> counter(compose(uri, cfName)), factory.createMetricName(name),
aliasFactory.createMetricName(alias));
}
public void createDummyTableCounter(String name) throws MalformedObjectNameException {
register(() -> counter(null), factory.createMetricName(name),
aliasFactory.createMetricName(name));
}
public void createTableHistogram(String name, String uri, boolean considerZeros)
throws MalformedObjectNameException {
createTableHistogram(name, name, uri, considerZeros);
}
public void createTableHistogram(String name, String alias, String uri, boolean considerZeros)
throws MalformedObjectNameException {
register(() -> histogram(compose(uri, cfName), considerZeros), factory.createMetricName(name),
aliasFactory.createMetricName(alias));
}
public void createTimer(String name, String uri) throws MalformedObjectNameException {
register(() -> timer(compose(uri, cfName)), factory.createMetricName(name));
}
}
private void registerLocal(Registry registry) throws MalformedObjectNameException {
registry.createTableGauge(long[].class, "EstimatedPartitionSizeHistogram", "EstimatedRowSizeHistogram",
"estimated_row_size_histogram", APIClient::getEstimatedHistogramAsLongArrValue);
registry.createTableGauge("EstimatedPartitionCount", "EstimatedRowCount", "estimated_row_count");
registry.createTableGauge(long[].class, "EstimatedColumnCountHistogram", "estimated_column_count_histogram",
APIClient::getEstimatedHistogramAsLongArrValue);
registry.createTableGauge(Double.class, "KeyCacheHitRate", "key_cache_hit_rate");
registry.createTimer("CoordinatorReadLatency", "coordinator/read");
registry.createTimer("CoordinatorScanLatency", "coordinator/scan");
registry.createTimer("WaitingOnFreeMemtableSpace", "waiting_on_free_memtable");
for (LatencyMetrics l : latencyMetrics) {
l.register(registry);
}
// TODO: implement
registry.createDummyTableCounter("DroppedMutations");
}
private static void registerCommon(Registry registry) throws MalformedObjectNameException {
registry.createTableGauge("MemtableColumnsCount", "memtable_columns_count");
registry.createTableGauge("MemtableOnHeapSize", "memtable_on_heap_size");
registry.createTableGauge("MemtableOffHeapSize", "memtable_off_heap_size");
registry.createTableGauge("MemtableLiveDataSize", "memtable_live_data_size");
registry.createTableGauge("AllMemtablesHeapSize", "all_memtables_on_heap_size");
registry.createTableGauge("AllMemtablesOffHeapSize", "all_memtables_off_heap_size");
registry.createTableGauge("AllMemtablesLiveDataSize", "all_memtables_live_data_size");
registry.createTableCounter("MemtableSwitchCount", "memtable_switch_count");
registry.createTableHistogram("SSTablesPerReadHistogram", "sstables_per_read_histogram", true);
registry.createTableGauge(Double.class, "CompressionRatio", "compression_ratio");
registry.createTableCounter("PendingFlushes", "pending_flushes");
registry.createTableGauge(Integer.class, Long.class, "PendingCompactions", "PendingCompactions",
"pending_compactions");
registry.createTableGauge(Integer.class, Long.class, "LiveSSTableCount", "LiveSSTableCount",
"live_ss_table_count");
registry.createTableCounter("LiveDiskSpaceUsed", "live_disk_space_used");
registry.createTableCounter("TotalDiskSpaceUsed", "total_disk_space_used");
registry.createTableGauge("MinPartitionSize", "MinRowSize", "min_row_size");
registry.createTableGauge("MaxPartitionSize", "MaxRowSize", "max_row_size");
registry.createTableGauge("MeanPartitionSize", "MeanRowSize", "mean_row_size");
registry.createTableGauge("BloomFilterFalsePositives", "bloom_filter_false_positives");
registry.createTableGauge("RecentBloomFilterFalsePositives", "recent_bloom_filter_false_positives");
registry.createTableGauge(Double.class, "BloomFilterFalseRatio", "bloom_filter_false_ratio");
registry.createTableGauge(Double.class, "RecentBloomFilterFalseRatio", "recent_bloom_filter_false_ratio");
registry.createTableGauge("BloomFilterDiskSpaceUsed", "bloom_filter_disk_space_used");
registry.createTableGauge("BloomFilterOffHeapMemoryUsed", "bloom_filter_off_heap_memory_used");
registry.createTableGauge("IndexSummaryOffHeapMemoryUsed", "index_summary_off_heap_memory_used");
registry.createTableGauge("CompressionMetadataOffHeapMemoryUsed", "compression_metadata_off_heap_memory_used");
registry.createTableGauge("SpeculativeRetries", "speculative_retries");
registry.createTableHistogram("TombstoneScannedHistogram", "tombstone_scanned_histogram", false);
registry.createTableHistogram("LiveScannedHistogram", "live_scanned_histogram", false);
registry.createTableHistogram("ColUpdateTimeDeltaHistogram", "col_update_time_delta_histogram", false);
// We do not want to capture view mutation specific metrics for a view
// They only makes sense to capture on the base table
// TODO: views
// if (!cfs.metadata.isView())
// {
// viewLockAcquireTime = createTableTimer("ViewLockAcquireTime",
// cfs.keyspace.metric.viewLockAcquireTime);
// viewReadTime = createTableTimer("ViewReadTime",
// cfs.keyspace.metric.viewReadTime);
// }
registry.createTableGauge("SnapshotsSize", "snapshots_size");
registry.createTableCounter("RowCacheHitOutOfRange", "row_cache_hit_out_of_range");
registry.createTableCounter("RowCacheHit", "row_cache_hit");
registry.createTableCounter("RowCacheMiss", "row_cache_miss");
// TODO: implement
registry.createDummyTableGauge(Double.class, "PercentRepaired");
}
static class TableMetricObjectName extends javax.management.ObjectName {
private final TableMetricStringNameFactory factory;
private final String metricName;
public TableMetricObjectName(TableMetricStringNameFactory factory, String metricName) throws MalformedObjectNameException {
super("");
this.factory = factory;
this.metricName = metricName;
}
@Override
public boolean isPropertyValuePattern(String property) {
return false;
}
@Override
public String getCanonicalName() {
return factory.createMetricStringName(metricName);
}
@Override
public String getDomain() {
return factory.getDomain();
}
@Override
public String getKeyProperty(String property) {
if (property == "name") {
return metricName;
}
return factory.getKeyProperty(property);
}
@Override
public Hashtable<String,String> getKeyPropertyList() {
Hashtable<String, String> res = factory.getKeyPropertyList();
res.put("name", metricName);
return res;
}
@Override
public String getKeyPropertyListString() {
return factory.getKeyPropertyListString(metricName);
}
@Override
public String getCanonicalKeyPropertyListString() {
return getKeyPropertyListString();
}
@Override
public String toString() {
return getCanonicalName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
return getCanonicalName().equals(((ObjectName) o).getCanonicalName());
}
@Override
public int hashCode() {
return getCanonicalName().hashCode();
}
@Override
public boolean apply(ObjectName name) {
if (name.isDomainPattern() || name.isPropertyListPattern() || name.isPropertyValuePattern()) {
return false;
}
return getCanonicalName().equals(name.getCanonicalName());
}
@Override
public boolean isPattern() {
return false;
}
@Override
public boolean isDomainPattern() {
return false;
}
@Override
public boolean isPropertyPattern() {
return false;
}
@Override
public boolean isPropertyListPattern() {
return false;
}
@Override
public boolean isPropertyValuePattern() {
return false;
}
/**
* This type is not really serializable.
* Replace it with vanilla objectname.
*/
private Object writeReplace() throws ObjectStreamException {
try {
return new ObjectName(getDomain(), getKeyPropertyList());
} catch (MalformedObjectNameException e) {
throw new InvalidObjectException(toString());
}
}
}
static interface TableMetricStringNameFactory {
String createMetricStringName(String metricName);
String getDomain();
String getKeyProperty(String property);
Hashtable<String,String> getKeyPropertyList();
String getKeyPropertyListString(String metricName);
}
static class TableMetricNameFactory implements MetricNameFactory, TableMetricStringNameFactory {
private final String keyspaceName;
private final String tableName;
private final boolean isIndex;
private final String type;
public TableMetricNameFactory(String keyspaceName, String tableName, boolean isIndex, String type) {
this.keyspaceName = keyspaceName;
this.tableName = tableName;
this.isIndex = isIndex;
this.type = type;
}
private void appendKeyPropertyListString(final StringBuilder sb, final String metricName) {
String type = isIndex ? "Index" + this.type : this.type;
// Order matters here - keys have to be sorted
sb.append("keyspace=").append(keyspaceName);
sb.append(",name=").append(metricName);
sb.append(",scope=").append(tableName);
sb.append(",type=").append(type);
}
@Override
public String createMetricStringName(String metricName) {
String groupName = TableMetrics.class.getPackage().getName();
StringBuilder mbeanName = new StringBuilder();
mbeanName.append(groupName).append(":");
appendKeyPropertyListString(mbeanName, metricName);
return mbeanName.toString();
}
@Override
public String getDomain() {
return TableMetrics.class.getPackage().getName();
}
@Override
public String getKeyProperty(String property) {
switch (property) {
case "keyspace": return keyspaceName;
case "scope": return tableName;
case "type": return type;
default: return null;
}
}
@Override
public Hashtable<String,String> getKeyPropertyList() {
Hashtable<String, String> res = new Hashtable<>();
res.put("keyspace", keyspaceName);
res.put("scope", tableName);
res.put("type", type);
return res;
}
@Override
public String getKeyPropertyListString(String metricName) {
final StringBuilder sb = new StringBuilder();
appendKeyPropertyListString(sb, metricName);
return sb.toString();
}
@Override
public ObjectName createMetricName(String metricName) throws MalformedObjectNameException {
return new TableMetricObjectName(this, metricName);
}
}
static class AllTableMetricNameFactory implements MetricNameFactory, TableMetricStringNameFactory {
private final String type;
public AllTableMetricNameFactory(String type) {
this.type = type;
}
private void appendKeyPropertyListString(final StringBuilder sb, final String metricName) {
// Order matters here - keys have to be sorted
sb.append("name=").append(metricName);
sb.append(",type=" + type);
}
@Override
public String createMetricStringName(String metricName) {
String groupName = TableMetrics.class.getPackage().getName();
StringBuilder mbeanName = new StringBuilder();
mbeanName.append(groupName).append(":");
appendKeyPropertyListString(mbeanName, metricName);
return mbeanName.toString();
}
@Override
public String getDomain() {
return TableMetrics.class.getPackage().getName();
}
@Override
public String getKeyProperty(String property) {
switch (property) {
case "type": return type;
default: return null;
}
}
@Override
public Hashtable<String,String> getKeyPropertyList() {
Hashtable<String, String> res = new Hashtable<>();
res.put("type", type);
return res;
}
@Override
public String getKeyPropertyListString(String metricName) {
final StringBuilder sb = new StringBuilder();
appendKeyPropertyListString(sb, metricName);
return sb.toString();
}
@Override
public ObjectName createMetricName(String metricName) throws MalformedObjectNameException {
return new TableMetricObjectName(this, metricName);
}
}
public enum Sampler {
READS, WRITES
}
}

Some files were not shown because too many files have changed in this diff Show More