Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt update
sudo apt install libmagick++-dev libncurses-dev libpcre2-dev libbrotli-dev libluajit-5.1-dev luajit libjansson-dev libcjose-dev libmaxminddb-dev libgeoip-dev ninja-build cmake libpcre3-dev
sudo apt install libmagick++-dev libncurses-dev libpcre2-dev libbrotli-dev libluajit-5.1-dev luajit libjansson-dev libcjose-dev libmaxminddb-dev libgeoip-dev ninja-build cmake libpcre3-dev libzstd-dev liblz4-dev
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
Expand Down
48 changes: 28 additions & 20 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -489,33 +489,41 @@ set(TS_USE_MALLOC_ALLOCATOR ${ENABLE_MALLOC_ALLOCATOR})
set(TS_USE_ALLOCATOR_METRICS ${ENABLE_ALLOCATOR_METRICS})
find_package(ZLIB REQUIRED)

find_package(zstd CONFIG QUIET)
if(zstd_FOUND)

# Provide a compatibility target name if the upstream package does not export it
# Our code links against `zstd::zstd`; upstream zstd usually exports
# `zstd::libzstd_shared`/`zstd::libzstd_static`. Create an alias if needed.
if(NOT TARGET zstd::zstd)
if(TARGET zstd::libzstd_shared)
set(_zstd_target zstd::libzstd_shared)
elseif(TARGET zstd::libzstd_static)
set(_zstd_target zstd::libzstd_static)
elseif(TARGET zstd::libzstd)
set(_zstd_target zstd::libzstd)
endif()
if(DEFINED _zstd_target)
# 1.4.0 stabilized the advanced one-shot API (ZSTD_compress2 et al.) used by
# the RAM cache; plugins/compress already requires the same floor for
# ZSTD_compressStream2, so this is not specific to the cache.
find_package(ZSTD 1.4.0)
set(HAVE_ZSTD_H ${ZSTD_FOUND})

# cmake/FindZSTD.cmake creates the zstd::zstd target this tree links against,
# but with CMAKE_FIND_PACKAGE_PREFER_CONFIG the lookup can resolve through
# zstd's own config package instead: on a case-insensitive filesystem the
# ZSTDConfig.cmake CMake searches for matches the zstdConfig.cmake that zstd
# installs. That package exports zstd::libzstd_shared/_static, so alias
# whichever it gave us or the four targets linking zstd::zstd fail at generate
# time.
if(ZSTD_FOUND AND NOT TARGET zstd::zstd)
foreach(_zstd_target zstd::libzstd_shared zstd::libzstd_static zstd::libzstd)
if(TARGET ${_zstd_target})
add_library(zstd_zstd INTERFACE)
target_link_libraries(zstd_zstd INTERFACE ${_zstd_target})
add_library(zstd::zstd ALIAS zstd_zstd)
set(HAVE_ZSTD_H TRUE)
else()
set(HAVE_ZSTD_H FALSE)
break()
endif()
endforeach()
unset(_zstd_target)
if(NOT TARGET zstd::zstd)
message(WARNING "zstd found but it exports no target this build can use; building without zstd")
set(HAVE_ZSTD_H FALSE)
endif()
else()
set(HAVE_ZSTD_H FALSE)
endif()

# 1.7.0 (r129) introduced the current compression API, including
# LZ4_compress_default(), which the RAM cache uses; 1.7.5 is the floor for
# LZ4_versionString(), which traffic_layout reports.
find_package(LZ4 1.7.5)
set(HAVE_LZ4_H ${LZ4_FOUND})

# ncurses is used in traffic_top
find_package(Curses)
set(HAVE_CURSES_H ${CURSES_HAVE_CURSES_H})
Expand Down
2 changes: 1 addition & 1 deletion ci/docker/deb/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ RUN apt-get update; apt-get -y dist-upgrade; \
libhwloc-dev libunwind8 libunwind-dev zlib1g-dev \
tcl-dev tcl8.6-dev libjemalloc-dev libluajit-5.1-dev liblzma-dev \
libhiredis-dev libbrotli-dev libncurses-dev libgeoip-dev libmagick++-dev \
libzstd-dev; \
libzstd-dev liblz4-dev; \
# Optional: This is for the OpenSSH server, and Jenkins account + access (comment out if not needed)
apt-get -y install openssh-server openjdk-8-jre && mkdir /run/sshd; \
groupadd -g 665 jenkins && \
Expand Down
2 changes: 1 addition & 1 deletion ci/docker/yum/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ RUN yum -y update; \
# Devel packages that ATS needs
yum -y install openssl-devel expat-devel pcre-devel libcap-devel hwloc-devel libunwind-devel \
xz-devel libcurl-devel ncurses-devel jemalloc-devel GeoIP-devel luajit-devel brotli-devel \
ImageMagick-devel ImageMagick-c++-devel hiredis-devel zlib-devel zstd-devel \
ImageMagick-devel ImageMagick-c++-devel hiredis-devel zlib-devel zstd-devel lz4-devel \
perl-ExtUtils-MakeMaker perl-Digest-SHA perl-URI; \
# This is for autest stuff
yum -y install python3 httpd-tools procps-ng nmap-ncat \
Expand Down
74 changes: 74 additions & 0 deletions cmake/FindLZ4.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#######################
#
# 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.
#
#######################

# FindLZ4.cmake
#
# This will define the following variables
#
# LZ4_FOUND
# LZ4_LIBRARY
# LZ4_INCLUDE_DIRS
# LZ4_VERSION
#
# and the following imported target
#
# LZ4::LZ4
#

find_library(LZ4_LIBRARY NAMES lz4 liblz4)
find_path(LZ4_INCLUDE_DIR NAMES lz4.h)

mark_as_advanced(LZ4_FOUND LZ4_LIBRARY LZ4_INCLUDE_DIR)

# The version lives in three separate macros in lz4.h; a config package would
# supply it, but this module has to read them out to satisfy a version request.
if(LZ4_INCLUDE_DIR AND EXISTS "${LZ4_INCLUDE_DIR}/lz4.h")
set(_LZ4_version_parts "")
foreach(_LZ4_part MAJOR MINOR RELEASE)
file(STRINGS "${LZ4_INCLUDE_DIR}/lz4.h" _LZ4_line REGEX "^#define[ \t]+LZ4_VERSION_${_LZ4_part}[ \t]+[0-9]+")
# The value may be followed by a comment, so capture it rather than
# anchoring on the end of the line.
if(_LZ4_line MATCHES "^#define[ \t]+LZ4_VERSION_${_LZ4_part}[ \t]+([0-9]+)")
list(APPEND _LZ4_version_parts "${CMAKE_MATCH_1}")
endif()
endforeach()
list(LENGTH _LZ4_version_parts _LZ4_version_count)
if(_LZ4_version_count EQUAL 3)
list(JOIN _LZ4_version_parts "." LZ4_VERSION)
endif()
unset(_LZ4_line)
unset(_LZ4_part)
unset(_LZ4_version_parts)
unset(_LZ4_version_count)
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
LZ4
REQUIRED_VARS LZ4_LIBRARY LZ4_INCLUDE_DIR
VERSION_VAR LZ4_VERSION
)

if(LZ4_FOUND)
set(LZ4_INCLUDE_DIRS "${LZ4_INCLUDE_DIR}")
endif()

if(LZ4_FOUND AND NOT TARGET LZ4::LZ4)
add_library(LZ4::LZ4 INTERFACE IMPORTED)
target_include_directories(LZ4::LZ4 INTERFACE ${LZ4_INCLUDE_DIRS})
target_link_libraries(LZ4::LZ4 INTERFACE "${LZ4_LIBRARY}")
endif()
74 changes: 74 additions & 0 deletions cmake/FindZSTD.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#######################
#
# 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.
#
#######################

# FindZSTD.cmake
#
# This will define the following variables
#
# ZSTD_FOUND
# ZSTD_LIBRARY
# ZSTD_INCLUDE_DIRS
# ZSTD_VERSION
#
# and the following imported target
#
# zstd::zstd
#

find_library(ZSTD_LIBRARY NAMES zstd libzstd)
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)

mark_as_advanced(ZSTD_FOUND ZSTD_LIBRARY ZSTD_INCLUDE_DIR)

# The version lives in three separate macros in zstd.h; a config package would
# supply it, but this module has to read them out to satisfy a version request.
if(ZSTD_INCLUDE_DIR AND EXISTS "${ZSTD_INCLUDE_DIR}/zstd.h")
set(_ZSTD_version_parts "")
foreach(_ZSTD_part MAJOR MINOR RELEASE)
file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _ZSTD_line REGEX "^#define[ \t]+ZSTD_VERSION_${_ZSTD_part}[ \t]+[0-9]+")
# The value may be followed by a comment, so capture it rather than
# anchoring on the end of the line.
if(_ZSTD_line MATCHES "^#define[ \t]+ZSTD_VERSION_${_ZSTD_part}[ \t]+([0-9]+)")
list(APPEND _ZSTD_version_parts "${CMAKE_MATCH_1}")
endif()
endforeach()
list(LENGTH _ZSTD_version_parts _ZSTD_version_count)
if(_ZSTD_version_count EQUAL 3)
list(JOIN _ZSTD_version_parts "." ZSTD_VERSION)
endif()
unset(_ZSTD_line)
unset(_ZSTD_part)
unset(_ZSTD_version_parts)
unset(_ZSTD_version_count)
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
ZSTD
REQUIRED_VARS ZSTD_LIBRARY ZSTD_INCLUDE_DIR
VERSION_VAR ZSTD_VERSION
)

if(ZSTD_FOUND)
set(ZSTD_INCLUDE_DIRS "${ZSTD_INCLUDE_DIR}")
endif()

if(ZSTD_FOUND AND NOT TARGET zstd::zstd)
add_library(zstd::zstd INTERFACE IMPORTED)
target_include_directories(zstd::zstd INTERFACE ${ZSTD_INCLUDE_DIRS})
target_link_libraries(zstd::zstd INTERFACE "${ZSTD_LIBRARY}")
endif()
1 change: 1 addition & 0 deletions contrib/docker/ubuntu/noble/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ RUN apt update \
hwloc \
libbrotli-dev \
libzstd-dev \
liblz4-dev \
luajit \
libluajit-5.1-dev \
libcap-dev \
Expand Down
1 change: 1 addition & 0 deletions contrib/docker/ubuntu/resolute/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ RUN apt update \
hwloc \
libbrotli-dev \
libzstd-dev \
liblz4-dev \
luajit \
libluajit-5.1-dev \
libcap-dev \
Expand Down
11 changes: 9 additions & 2 deletions doc/admin-guide/files/records.yaml.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3178,11 +3178,18 @@ RAM Cache
Value Description
======== ===================================================================
``0`` No compression
``1`` Fastlz (extremely fast, relatively low compression)
``2`` Libz (moderate speed, reasonable compression)
``1`` Fastlz (extremely fast, relatively low compression) - prefer lz4
``2`` Libz (moderate speed, reasonable compression) - prefer zstd
``3`` Liblzma (very slow, high compression)
``4`` lz4 (extremely fast, relatively low compression)
``5`` zstd (fast speed, reasonable compression)
======== ===================================================================

``3``, ``4`` and ``5`` require that |TS| was built with liblzma, lz4 or
libzstd respectively; configuring one that was not compiled in is a fatal
error at startup. ``traffic_layout info`` reports which are available as
``TS_HAS_LZ4`` and ``TS_HAS_ZSTD``.

Compression runs on task threads. To use more cores for RAM cache
compression, increase :ts:cv:`proxy.config.task_threads`.

Expand Down
10 changes: 10 additions & 0 deletions doc/admin-guide/monitoring/statistics/core/cache-volume.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ a configuration with only one cache volume: :literal:`0`.

Accumulates the number of misses to the LRU RAM cache for this volume. Note that this count includes hits to the other memory caches, including the last open read and aggregation buffer caches, so it may not represent the total number of cache accesses that go to disk.

.. ts:stat:: global proxy.process.cache.volume_0.ram_cache.compress.failure integer
:type: counter

Accumulates the number of RAM cache entries the compression library could not compress, for this volume. Objects that simply did not shrink enough to be worth compressing are not counted, since that is the ordinary outcome for already-compressed content.

.. ts:stat:: global proxy.process.cache.volume_0.ram_cache.decompress.failure integer
:type: counter

Accumulates the number of RAM cache entries that failed to decompress on read, for this volume. A failed entry is dropped from the RAM cache and the read is treated as a miss. A nonzero value indicates data corruption or a compression library error, not ordinary cache churn.

.. ts:stat:: global proxy.process.cache.volume_0.last_open_read.hits integer
:type: counter

Expand Down
10 changes: 10 additions & 0 deletions doc/admin-guide/monitoring/statistics/core/cache.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ Cache

Accumulates the number of misses to the LRU RAM cache for all volumes. Note that this includes hits to the other memory caches, including the last open read and aggregation buffer caches, so it may not represent the total number of cache accesses that go to disk.

.. ts:stat:: global proxy.process.cache.ram_cache.compress.failure integer
:type: counter

Accumulates the number of RAM cache entries the compression library could not compress, for all volumes. Objects that simply did not shrink enough to be worth compressing are not counted, since that is the ordinary outcome for already-compressed content.

.. ts:stat:: global proxy.process.cache.ram_cache.decompress.failure integer
:type: counter

Accumulates the number of RAM cache entries that failed to decompress on read, for all volumes. A failed entry is dropped from the RAM cache and the read is treated as a miss. A nonzero value indicates data corruption or a compression library error, not ordinary cache churn.

.. ts:stat:: global proxy.process.cache.last_open_read.hits integer
:type: counter

Expand Down
14 changes: 2 additions & 12 deletions doc/admin-guide/storage/index.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,8 @@ images). This should not be confused with ``Content-Encoding: gzip``, this
feature is only present to save space internally in the RAM cache itself. As
such, it is completely transparent to the User-Agent. The RAM cache
compression is enabled with the option
:ts:cv:`proxy.config.cache.ram_cache.compress`.

Possible values are:

======= =============================
Value Meaning
======= =============================
0 No compression (*default*)
1 *fastlz* compression
2 *libz* compression
3 *liblzma* compression
======= =============================
:ts:cv:`proxy.config.cache.ram_cache.compress`, which documents the available
codecs and which of them a given build supports.

.. _changing-the-size-of-the-ram-cache:

Expand Down
42 changes: 26 additions & 16 deletions doc/developer-guide/cache-architecture/ram-cache.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ following features:
* Is Scan Resistant and extracts robust hit rates even when the working set does
not fit in the RAM Cache.

* Supports compression at 3 levels: fastlz, gzip (libz), and xz (liblzma).
* Supports compression at 5 levels: fastlz, gzip (libz), xz (liblzma), lz4 and zstd.
Compression can be moved to another thread.

* Has very low CPU overhead, only slightly more than a basic LRU. Rather than
Expand Down Expand Up @@ -72,9 +72,9 @@ len Length of the object, which differs from *size* because of
compression and padding).
compressed_len Compressed length of the object.
compressed Compression type, or ``none`` if no compression. Possible types
are: *fastlz*, *libz*, and *liblzma*.
uncompressible Flag indicating that content cannot be compressed (true), or that
it mat be compressed (false).
are: *fastlz*, *libz*, *liblzma*, *lz4* and *zstd*.
incompressible Flag indicating that content cannot be compressed (true), or that
it may be compressed (false).
copy Whether or not this object should be copied in and copied out
(e.g. HTTP HDR).
LRU link
Expand Down Expand Up @@ -147,18 +147,28 @@ since we need to make a copy anyway. Those not tagged ``copy`` are inserted
uncompressed in the hope that they can be reused in uncompressed form. This is
a compile time option and may be something we want to change.

There are 3 algorithms and levels of compression (speed on an Intel i7 920
series processor using one thread):

======= ================ ================== ====================================
Method Compression Rate Decompression Rate Notes
======= ================ ================== ====================================
fastlz 173 MB/sec 442 MB/sec Basically free since disk or network
will limit first; ~53% final size.
libz 55 MB/sec 234 MB/sec Almost free, particularly
decompression; ~37% final size.
liblzma 3 MB/sec 50 MB/sec Expensive; ~27% final size.
======= ================ ================== ====================================
There are 5 algorithms and levels of compression (speed on an Intel Xeon Gold
6338 processor using lzbench and the silesia XML corpus):

======= ===== ================= ================== ====================================
Method Level Compression Rate Decompression Rate Notes
======= ===== ================= ================== ====================================
fastlz 1/2 452 MB/sec 913 MB/sec Effectively obsolete; prefer lz4.
fastlz_compress() selects
level 2 at 64 KiB and above,
so most objects use it; the
figures here are level 1.
Basically free since disk or network
will limit first; ~26% final size.
libz 6 54 MB/sec 536 MB/sec Effectively obsolete; prefer zstd.
Almost free, particularly
decompression; ~13% final size.
liblzma 6 5 MB/sec 291 MB/sec Expensive; ~8% final size.
lz4 1 727 MB/sec 3458 MB/sec Basically free since disk or network
will limit first; ~23% final size.
zstd 3 508 MB/sec 1690 MB/sec Basically free since disk or network
will limit first; ~12% final size.
======= ===== ================= ================== ====================================

These are ballpark numbers, and your millage will vary enormously. JPEG, for
example, will not compress with any of these (or at least will only do so at
Expand Down
Loading