Commit 195f7b8f authored by Nils Goroll's avatar Nils Goroll

undo the 4.0 compat, but emit configure error for wrong API

4.0 code is updated in 4.0 branch
parent ec78aa2a
......@@ -12,11 +12,7 @@ doc_DATA = README.rst LICENSE
dist_man_MANS = vmod_vslp.3
MAINTAINERCLEANFILES = $(dist_man_MANS)
if HAVE_VMOD_ABI_4_1
vmod_vslp.3: README.rst
else
vmod_vslp.3: README.40.rst
endif
%.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8 %.9:
if HAVE_RST2MAN
......
===============
vmod_vslp
===============
------------------------------------------
Varnish Consistent Hashing Director Module
------------------------------------------
:Author: Julian Wiesener
:Date: 2014-07-16
:Version: 1.1
:Manual section: 3
.. _synopsis:
SYNOPSIS
========
::
import vslp;
sub vcl_init {
new vd = vslp.vslp();
vd.add_backend(be1);
vd.add_backend(be2);
vd.add_backend(be3);
vd.init_hashcircle(150); #number of replicas
}
sub vcl_recv {
set req.backend_hint = vd.backend();
}
# and/or
sub vcl_backend_fetch {
set bereq.backend = vd.backend();
}
DESCRIPTION
===========
VSLP Director - Varnish StateLess Persistence Director
The purpose of this director is to provide stateless persistence (or
persistency if you prefer) for backend requests based upon this simple
concept:
* Generate a load balancing key, which will be used to select the backend
The key values should be as uniformly distributed as possible.
For all requests which need to hit the same backend server, the same
key must be generated.
For strings, a hash function can be used to generate the key.
* Select the preferred backend server using an implementation of consistent
hashing (cf. Karger et al, references below), which ensures that the same
hosts are always chosen for every key (for instance hash of incoming URL)
in the same order (i.e. if the preferred host is down, then alternative
hosts are always chosen in a fixed order).
* Initialize a circular data structure consisting of the hash values of
"hostname%d" for each host and for a running number from 1 to n (n is
the number of "replicas").
* For the load balancing key, find the smallest hash value in the circle
that is larger than the key (searching clockwise and wrapping around
as necessary).
* If the host thus selected is down, choose alternative hosts by
continuing to search clockwise in the circle.
On consistent hashing see:
* http://www8.org/w8-papers/2a-webserver/caching/paper2.html
* http://www.audioscrobbler.net/development/ketama/
* svn://svn.audioscrobbler.net/misc/ketama
* http://en.wikipedia.org/wiki/Consistent_hashing
This technique allowes to implement persistence without keeping any state,
and, in particular, without the need to synchronize state between nodes of a
cluster of Varnish servers.
Still, all members of a cluster will, if otherwise configured equally,
implement the same persistence, in other words, requests sharing a common
persistence criterium will be balanced onto the same backend server no matter
which Varnish server handles the request.
The drawbacks are:
* the distribution of requests depends on the number of requests per key and
the uniformity of the distribution of key values. In short, this technique
will generally lead to less good load balancing compared to stateful
techniques.
* When a backend server becomes unavailable, every persistence technique has
to reselect a new backend server, but this technique will also switch back
to the preferred server once it becomes healthy again, so the persistence
is generally less stable compared to stateful techniques (which would
continue to use a selected server for as long as possible (or dictated by a
TTL)).
The name VSLP was chosen to accompany VTLA as another VETR (Varnish
Exception To the Rule) ;-) http://varnish-cache.org/wiki/VTLA
FUNCTIONS
=========
_Note_ The vmod functions differ varnish 4.0 and 4.1. This is the
documentation for the varnish 4.0 interface.
VSLP needs to be configured before it can hand out backends. The functions not
returning a backend should only be called in vcl_init, as we can safely skip
locking for most parts of the code, if we do not modify the hash ring after
initialization.
VOID .add_backend(BACKEND)
--------------------------
Adds a backend to the Director, must be called in before init_hashcircle().
VOID .set_rampup_ratio(REAL)
----------------------------
Sets the ratio of requests (0.0 to 1.0) that goes to the next alternate backend
to warm it up when the preferd backend is healthy. If the preferred backend just
recovered from being unhealthy, the ratio will be used to warm the preferred
backend. Defaults to 0.1 (10%).
VOID .set_rampup_time(DURATION)
-------------------------------
Sets the duration that will be used to determine if a backend will be counted
as warmed after recovering from an unhealthy state. During the warm-up period,
only the rampup-ratio of requests will go to the recoverd backend. Defaults
to 60 seconds.
VOID .set_hash(ENUM { CRC32, SHA256, RS })
------------------------------------------
Sets the default hash algorithm used to choose a backend by backend(). Defaults
to CRC32.
VOID .init_hashcircle(INT)
--------------------------
Initializes the hash ring. This function must be called after all backends are
added. The argument is the numbers of replicas the hash ring contains for each
backend.
INT .hash_string(STRING string, ENUM { CRC32, SHA256, RS })
---------------------------------------------------------------
Returns the hash of its first argument using the hash
algorithm defined, defaults to CRC32.
BACKEND .backend()
------------------
Returns a backend based on the default hash of the request URL.
BACKEND .backend_n(INT, BOOL, BOOL, INT)
----------------------------------------
Returns the n-th backend (first parameter) with respect of altsrv_p
(second parameter) and respect of its healthy state (third parameter)
for the given hash (last parameter).
BACKEND .backend_by_int(INT)
----------------------------
Returns a backend based on the value of its parameter. The value
should be evenly distributet between 0 and MAX_INT to get a good
distribution of requests.
Example: To select a backend from director `vd` based on a string
value hashed with `SHA256`, use::
vd.backend_by_int(vd.hash_string("some_string", SHA256))
LIMITATIONS
===========
* The number of backends per director is limited to 64.
* Adding backends after initializing the hash ring is possible, but invalid, as
such backends will not be choosen. Calling init_hashcircle() twice on the same
VSLP instance is invalid.
INSTALLATION
============
The source tree is based on autotools to configure the building, and
does also have the necessary bits in place to do functional unit tests
using the ``varnishtest`` tool.
Building requires the Varnish header files and uses pkg-config to find
the necessary paths.
Usage::
./autogen.sh
./configure
If you have installed Varnish to a non-standard directory, call
``autogen.sh`` and ``configure`` with ``PKG_CONFIG_PATH`` pointing to
the appropriate path. For example, when varnishd configure was called
with ``--prefix=$PREFIX``, use
PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
export PKG_CONFIG_PATH
Make targets:
* make - builds the vmod.
* make install - installs your vmod.
* make check - runs the unit tests in ``src/tests/*.vtc``
* make distcheck - run check and prepare a tarball of the vmod.
Installation directories
------------------------
By default, the vmod ``configure`` script installs the built vmod in
the same directory as Varnish, determined via ``pkg-config(1)``. The
vmod installation directory can be overridden by passing the
``VMOD_DIR`` variable to ``configure``.
Other files like man-pages and documentation are installed in the
locations determined by ``configure``, which inherits its default
``--prefix`` setting from Varnish.
MISSING
=======
* Documentation of interactions with restarts/retries
HISTORY
=======
Version 1.0: Initial version.
ACKNOWLEDGEMENTS
================
Development of this module was partly sponsored by Deutsche Telekom AG
– Products & Innovation
COPYRIGHT
=========
This document is licensed under the same license as the
libvmod-vslp project. See LICENSE for details.
Copyright 2013-2015 UPLEX Nils Goroll Systemoptimierung. All rights reserved.
......@@ -109,7 +109,7 @@ Exception To the Rule) ;-) http://varnish-cache.org/wiki/VTLA
FUNCTIONS
=========
_Note_ The vmod functions differ varnish 4.0 and 4.1. This is the
_Note_ The vmod functions differ for varnish 4.0 and 4.1. This is the
documentation for the varnish 4.1 interface.
VSLP needs to be configured before it can hand out backends. The functions not
......
......@@ -83,12 +83,9 @@ AC_CHECK_FILE([${VAPI_INCLUDE_DIR}/vmod_abi.h],
])
AS_CASE([${VMOD_ABI_VERSION}],
[4.0*], [AC_SUBST([VMOD_ABI_VERSION], [40])],
[4.0*], [AC_MSG_ERROR([Usupported ABI version. Do you need the 4.0 branch?])]
[AC_SUBST([VMOD_ABI_VERSION], [41])]
)
AC_DEFINE_UNQUOTED(VMOD_ABI_VERSION, ${VMOD_ABI_VERSION},
[our vmod abi version])
AM_CONDITIONAL(HAVE_VMOD_ABI_4_1, [test "x${VMOD_ABI_VERSION}" = "x41"])
# This corresponds to FreeBSD's WARNS level 6
DEVELOPER_CFLAGS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wunused-parameter -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wformat"
......
......@@ -22,25 +22,14 @@ vmod_vslp.lo: vcc_if.c vcc_if.h
vcc_if.c: vcc_if.h
if HAVE_VMOD_ABI_4_1
vcc_if.h: @VMODTOOL@ $(top_srcdir)/src/vmod_vslp.vcc
@VMODTOOL@ $(top_srcdir)/src/vmod_vslp.vcc
else
vcc_if.h: @VMODTOOL@ $(top_srcdir)/src/vmod_vslp.40.vcc
@VMODTOOL@ $(top_srcdir)/src/vmod_vslp.40.vcc
endif
VMOD_TESTS = $(top_srcdir)/src/tests/*.vtc
if HAVE_VMOD_ABI_4_1
VMOD_TESTS += $(top_srcdir)/src/tests/v41/*.vtc
else
VMOD_TESTS += $(top_srcdir)/src/tests/v40/*.vtc
endif
.PHONY: $(VMOD_TESTS)
$(top_srcdir)/src/tests/*.vtc $(top_srcdir)/src/tests/*/*.vtc: libvmod_vslp.la
$(top_srcdir)/src/tests/*.vtc: libvmod_vslp.la
@VARNISHTEST@ -Dvarnishd=@VARNISHD@ -Dvmod_topbuild=$(abs_top_builddir) $@
check: $(VMOD_TESTS)
......
varnishtest "VSLP director/int key"
server s1 {
rxreq
txresp -body "ech3Ooj"
} -start
server s2 {
rxreq
txresp -body "ieQu2qua"
} -start
server s3 {
rxreq
txresp -body "xiuFi3Pe"
} -start
varnish v1 -vcl+backend {
import vslp from "${vmod_topbuild}/src/.libs/libvmod_vslp.so" ;
sub vcl_init {
new vd = vslp.vslp();
vd.add_backend(s1);
vd.add_backend(s2);
vd.add_backend(s3);
vd.set_rampup_ratio(0);
vd.init_hashcircle(25);
}
sub vcl_recv {
if(req.url == "/1") {
set req.backend_hint = vd.backend_by_int(1);
}
if(req.url == "/2") {
set req.backend_hint = vd.backend_by_int(2147483647);
}
if(req.url == "/3") {
set req.backend_hint = vd.backend_by_int(4294967295);
}
return(pass);
}
} -start
client c1 {
txreq -url /1
rxresp
expect resp.body == "ech3Ooj"
txreq -url /2
rxresp
expect resp.body == "ieQu2qua"
txreq -url /3
rxresp
expect resp.body == "xiuFi3Pe"
} -run
varnishtest "VSLP director String"
server s1 {
rxreq
txresp -body "ech3Ooj"
rxreq
txresp -body "ech3Ooj"
rxreq
txresp -body "ech3Ooj"
} -start
server s2 {
rxreq
txresp -body "ieQu2qua"
} -start
server s3 {
rxreq
txresp -body "xiuFi3Pe"
} -start
varnish v1 -vcl+backend {
import vslp from "${vmod_topbuild}/src/.libs/libvmod_vslp.so" ;
sub vcl_init {
new vd = vslp.vslp();
vd.add_backend(s1);
vd.add_backend(s2);
vd.add_backend(s3);
vd.set_rampup_ratio(0);
vd.init_hashcircle(25);
}
sub recv_sub {
set req.backend_hint = vd.backend_by_int(vd.hash_string(req.http.X-Hash, RS));
}
sub vcl_recv {
if(req.url == "/1") {
set req.backend_hint = vd.backend_by_int(vd.hash_string("/eishoSu2", CRC32));
} else if (req.url == "/2") {
set req.backend_hint = vd.backend_by_int(vd.hash_string("/eishoSu2", SHA256));
} else if (req.url == "/3") {
set req.http.X-Hash = "/oob3dahS";
call recv_sub;
} else if (req.url == "/null_by_string") {
set req.backend_hint = vd.backend_by_int(vd.hash_string(req.http.NonExistent, CRC32));
} else if (req.url == "/null_by_string_hash") {
set req.backend_hint = vd.backend_by_int(vd.hash_string(req.http.NonExistent, SHA256));
}
return(pass);
}
} -start
client c1 {
txreq -url /1
rxresp
expect resp.body == "ech3Ooj"
txreq -url /2
rxresp
expect resp.body == "ieQu2qua"
txreq -url /3
rxresp
expect resp.body == "xiuFi3Pe"
txreq -url /null_by_string
rxresp
expect resp.body == "ech3Ooj"
txreq -url /null_by_string_hash
rxresp
expect resp.body == "ech3Ooj"
} -run
varnishtest "VSLP director Restarts"
server s1 {
rxreq
txresp -body "ech3Ooj"
rxreq
txresp -body "ech3Ooj"
rxreq
txresp -body "ech3Ooj"
} -start
server s2 {
rxreq
txresp -body "ieQu2qua"
rxreq
txresp -body "ieQu2qua"
rxreq
txresp -body "ieQu2qua"
} -start
server s3 {
rxreq
txresp -body "xiuFi3Pe"
rxreq
txresp -body "xiuFi3Pe"
rxreq
txresp -body "xiuFi3Pe"
} -start
varnish v1 -vcl+backend {
import vslp from "${vmod_topbuild}/src/.libs/libvmod_vslp.so" ;
sub vcl_init {
new vd = vslp.vslp();
vd.add_backend(s1);
vd.add_backend(s2);
vd.add_backend(s3);
vd.set_rampup_ratio(0);
vd.init_hashcircle(25);
}
sub vcl_recv {
set req.backend_hint = vd.backend_by_int(vd.hash_string("/eishoSu2", CRC32));
if(req.url == "/2" && req.restarts > 0) {
unset req.http.vrstart;
}
if(req.url == "/3" && req.restarts > 1) {
unset req.http.vrstart;
}
return(pass);
}
sub vcl_deliver {
if(req.http.vrstart) {
return(restart);
}
}
} -start
client c1 {
txreq -url /1
rxresp
expect resp.body == "ech3Ooj"
txreq -url /2 -hdr "vrstart: 1"
rxresp
expect resp.body == "ieQu2qua"
txreq -url /3 -hdr "vrstart: 1"
rxresp
expect resp.body == "xiuFi3Pe"
} -run
varnishtest "VSLP director Unhealthy"
server s1 {
rxreq
txresp -body "ech3Ooj"
} -start
server s2 {
rxreq
txresp -body "ieQu2qua"
} -start
server s3 {
rxreq
txresp -body "xiuFi3Pe"
} -start
varnish v1 -vcl+backend {
import vslp from "${vmod_topbuild}/src/.libs/libvmod_vslp.so" ;
sub vcl_init {
new vd = vslp.vslp();
vd.add_backend(s1);
vd.add_backend(s2);
vd.add_backend(s3);
vd.set_rampup_ratio(0);
vd.init_hashcircle(25);
}
sub vcl_recv {
set req.backend_hint = vd.backend_by_int(vd.hash_string("/eishoSu2", CRC32));
return(pass);
}
sub vcl_deliver {
if(req.http.vrstart) {
return(restart);
}
}
} -start
varnish v1 -cliok "backend.set_health s1 sick"
client c1 {
txreq
rxresp
expect resp.body == "ieQu2qua"
} -run
varnish v1 -cliok "backend.set_health s2 sick"
client c1 {
txreq
rxresp
expect resp.body == "xiuFi3Pe"
} -run
varnishtest "VSLP director Rampup Time"
server s1 {
rxreq
txresp -body "ech3Ooj"
} -start
server s2 {
rxreq
txresp -body "ieQu2qua"
rxreq
txresp -body "ieQu2qua"
} -start
server s3 {
rxreq
txresp -body "xiuFi3Pe"
} -start
varnish v1 -vcl+backend {
import vslp from "${vmod_topbuild}/src/.libs/libvmod_vslp.so" ;
sub vcl_init {
new vd = vslp.vslp();
vd.add_backend(s1);
vd.add_backend(s2);
vd.add_backend(s3);
vd.set_rampup_ratio(0);
vd.set_rampup_time(15s);
vd.init_hashcircle(25);
}
sub vcl_recv {
set req.backend_hint = vd.backend_by_int(vd.hash_string("/eishoSu2", CRC32));
return(pass);
}
sub vcl_deliver {
if(req.http.vrstart) {
return(restart);
}
}
} -start
varnish v1 -cliok "backend.set_health s1 sick"
client c1 {
txreq
rxresp
expect resp.body == "ieQu2qua"
} -run
varnish v1 -cliok "backend.set_health s1 healthy"
client c1 {
txreq
rxresp
expect resp.body == "ieQu2qua"
} -run
delay 16
client c1 {
txreq
rxresp
expect resp.body == "ech3Ooj"
} -run
#-
# Copyright 2009-2014 UPLEX - Nils Goroll Systemoptimierung
# All rights reserved.
#
# Author: Julian Wiesener <jw@uplex.de>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
$Module vslp 3
$Object vslp()
$Method VOID .add_backend(BACKEND)
$Method VOID .set_rampup_ratio(REAL)
$Method VOID .set_rampup_time(DURATION)
$Method VOID .set_hash(ENUM { CRC32, SHA256, RS })
$Method VOID .init_hashcircle(INT)
$Method INT .hash_string(STRING, ENUM { CRC32, SHA256, RS })
$Method BACKEND .backend()
$Method BACKEND .backend_n(INT, BOOL, BOOL, INT)
$Method BACKEND .backend_by_int(INT)
......@@ -34,11 +34,7 @@
#include <arpa/inet.h>
#include "cache/cache.h"
#if VMOD_ABI_VERSION == 40
#include "cache/cache_backend.h"
#else
#include "cache/cache_director.h"
#endif
#include "vrt.h"
#include "vbm.h"
......@@ -140,8 +136,8 @@ vmod_vslp_hash_string(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, V
return (hash);
}
static VCL_BACKEND
_vmod_vslp_backend(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, VCL_INT n, VCL_BOOL altsrv_p, VCL_BOOL healthy, VCL_INT i)
VCL_BACKEND __match_proto__(td_vslp_vslp_backend)
vmod_vslp_backend(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, VCL_INT n, VCL_BOOL altsrv_p, VCL_BOOL healthy, VCL_INT i)
{
uint32_t hash = (uint32_t) i;
VCL_BACKEND be;
......@@ -164,23 +160,3 @@ _vmod_vslp_backend(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, VCL_
return (be);
}
#if VMOD_ABI_VERSION == 40
VCL_BACKEND __match_proto__(td_vslp_vslp_backend)
vmod_vslp_backend(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd) {
return _vmod_vslp_backend(ctx, vslpd, 0, 1, 1, 0);
}
VCL_BACKEND __match_proto__(td_vslp_vslp_backend_n)
vmod_vslp_backend_n(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, VCL_INT n, VCL_BOOL altsrv_p, VCL_BOOL healthy, VCL_INT i) {
return _vmod_vslp_backend(ctx, vslpd, n, altsrv_p, healthy, i);
}
VCL_BACKEND __match_proto__(td_vslp_vslp_backend_by_int)
vmod_vslp_backend_by_int(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, VCL_INT n) {
return _vmod_vslp_backend(ctx, vslpd, 0, 1, 1, n);
}
#else
VCL_BACKEND __match_proto__(td_vslp_vslp_backend)
vmod_vslp_backend(const struct vrt_ctx *ctx, struct vmod_vslp_vslp *vslpd, VCL_INT n, VCL_BOOL altsrv_p, VCL_BOOL healthy, VCL_INT i) {
return _vmod_vslp_backend(ctx, vslpd, n, altsrv_p, healthy, i);
}
#endif
......@@ -37,14 +37,7 @@
#include <time.h>
#include "cache/cache.h"
#if VMOD_ABI_VERSION == 40
# include "cache/cache_backend.h"
# define BE_HEALTHY(dir, bo, changed) ((dir)->healthy((dir), (changed)))
#else
# include "cache/cache_director.h"
# define BE_HEALTHY(dir, bo, changed) ((dir)->healthy((dir), (bo), (changed)))
#endif
#include "cache/cache_director.h"
#include "vrt.h"
......@@ -185,7 +178,7 @@ vslp_choose_next_healthy(struct vslp_state *state, uint32_t n_retry)
be = state->vslpd->backend[chosen];
AN(be);