]> git.ipfire.org Git - thirdparty/squid.git/blame - src/carp.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / carp.cc
CommitLineData
afd88fbe 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
0cdcddb9 7 */
afd88fbe 8
bbc27441
AJ
9/* DEBUG: section 39 Cache Array Routing Protocol */
10
582c2af2 11#include "squid.h"
a011edee 12#include "CachePeer.h"
de03b596 13#include "HttpRequest.h"
8822ebee 14#include "mgr/Registration.h"
f0ba2534 15#include "neighbors.h"
4d5904f7 16#include "SquidConfig.h"
e6ccf245 17#include "Store.h"
b1bd952a 18#include "URL.h"
afd88fbe 19
074d6a40 20#include <cmath>
582c2af2 21
b3995439 22#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
23
24static int n_carp_peers = 0;
a3c6762c 25static CachePeer **carp_peers = NULL;
8ee9b49f 26static OBJH carpCachemgr;
27
b3995439 28static int
29peerSortWeight(const void *a, const void *b)
30{
a3c6762c
FC
31 const CachePeer *const *p1 = (const CachePeer *const *)a;
32 const CachePeer *const *p2 = (const CachePeer *const *)b;
b3995439 33 return (*p1)->weight - (*p2)->weight;
34}
35
5f5e883f
FC
36static void
37carpRegisterWithCacheManager(void)
38{
8822ebee 39 Mgr::RegisterAction("carp", "CARP information", carpCachemgr, 0, 1);
5f5e883f
FC
40}
41
afd88fbe 42void
43carpInit(void)
44{
b3995439 45 int W = 0;
46 int K;
afd88fbe 47 int k;
b3995439 48 double P_last, X_last, Xn;
a3c6762c
FC
49 CachePeer *p;
50 CachePeer **P;
b3995439 51 char *t;
52 /* Clean up */
62e76326 53
d7ae3534 54 for (k = 0; k < n_carp_peers; ++k) {
62e76326 55 cbdataReferenceDone(carp_peers[k]);
b3995439 56 }
62e76326 57
b3995439 58 safe_free(carp_peers);
59 n_carp_peers = 0;
8cb183ec
FC
60
61 /* initialize cache manager before we have a chance to leave the execution path */
62 carpRegisterWithCacheManager();
63
b3995439 64 /* find out which peers we have */
62e76326 65
afd88fbe 66 for (p = Config.peers; p; p = p->next) {
62e76326 67 if (!p->options.carp)
68 continue;
69
70 assert(p->type == PEER_PARENT);
71
72 if (p->weight == 0)
73 continue;
74
d7ae3534 75 ++n_carp_peers;
62e76326 76
77 W += p->weight;
afd88fbe 78 }
62e76326 79
b3995439 80 if (n_carp_peers == 0)
62e76326 81 return;
82
a3c6762c 83 carp_peers = (CachePeer **)xcalloc(n_carp_peers, sizeof(*carp_peers));
62e76326 84
b3995439 85 /* Build a list of the found peers and calculate hashes and load factors */
86 for (P = carp_peers, p = Config.peers; p; p = p->next) {
62e76326 87 if (!p->options.carp)
88 continue;
89
90 if (p->weight == 0)
91 continue;
92
93 /* calculate this peers hash */
94 p->carp.hash = 0;
95
d7ae3534 96 for (t = p->name; *t != 0; ++t)
62e76326 97 p->carp.hash += ROTATE_LEFT(p->carp.hash, 19) + (unsigned int) *t;
98
99 p->carp.hash += p->carp.hash * 0x62531965;
100
101 p->carp.hash = ROTATE_LEFT(p->carp.hash, 21);
102
103 /* and load factor */
104 p->carp.load_factor = ((double) p->weight) / (double) W;
105
106 if (floor(p->carp.load_factor * 1000.0) == 0.0)
107 p->carp.load_factor = 0.0;
108
109 /* add it to our list of peers */
a38ec4b1
FC
110 *P = cbdataReference(p);
111 ++P;
67766c17 112 }
62e76326 113
b3995439 114 /* Sort our list on weight */
115 qsort(carp_peers, n_carp_peers, sizeof(*carp_peers), peerSortWeight);
62e76326 116
b3995439 117 /* Calculate the load factor multipliers X_k
118 *
119 * X_1 = pow ((K*p_1), (1/K))
120 * X_k = ([K-k+1] * [P_k - P_{k-1}])/(X_1 * X_2 * ... * X_{k-1})
121 * X_k += pow ((X_{k-1}, {K-k+1})
122 * X_k = pow (X_k, {1/(K-k+1)})
123 * simplified to have X_1 part of the loop
afd88fbe 124 */
b3995439 125 K = n_carp_peers;
62e76326 126
f53969cc 127 P_last = 0.0; /* Empty P_0 */
62e76326 128
f53969cc 129 Xn = 1.0; /* Empty starting point of X_1 * X_2 * ... * X_{x-1} */
62e76326 130
f53969cc 131 X_last = 0.0; /* Empty X_0, nullifies the first pow statement */
62e76326 132
d7ae3534 133 for (k = 1; k <= K; ++k) {
62e76326 134 double Kk1 = (double) (K - k + 1);
135 p = carp_peers[k - 1];
136 p->carp.load_multiplier = (Kk1 * (p->carp.load_factor - P_last)) / Xn;
137 p->carp.load_multiplier += pow(X_last, Kk1);
138 p->carp.load_multiplier = pow(p->carp.load_multiplier, 1.0 / Kk1);
139 Xn *= p->carp.load_multiplier;
140 X_last = p->carp.load_multiplier;
141 P_last = p->carp.load_factor;
afd88fbe 142 }
62ee09ca 143}
62e76326 144
a3c6762c 145CachePeer *
190154cf 146carpSelectParent(HttpRequest * request)
afd88fbe 147{
b3995439 148 int k;
a3c6762c
FC
149 CachePeer *p = NULL;
150 CachePeer *tp;
b3995439 151 unsigned int user_hash = 0;
152 unsigned int combined_hash;
153 double score;
154 double high_score = 0;
b3995439 155
156 if (n_carp_peers == 0)
62e76326 157 return NULL;
b3995439 158
b3995439 159 /* calculate hash key */
851feda6 160 debugs(39, 2, "carpSelectParent: Calculating hash for " << request->effectiveRequestUri());
62e76326 161
a3c6762c 162 /* select CachePeer */
d7ae3534 163 for (k = 0; k < n_carp_peers; ++k) {
2128aa19 164 SBuf key;
62e76326 165 tp = carp_peers[k];
de03b596 166 if (tp->options.carp_key.set) {
851feda6
AJ
167 // this code follows URI syntax pattern.
168 // corner cases should use the full effective request URI
de03b596 169 if (tp->options.carp_key.scheme) {
d31d59d8 170 key.append(request->url.getScheme().image());
2128aa19 171 if (key.length()) //if the scheme is not empty
de03b596
FC
172 key.append("://");
173 }
174 if (tp->options.carp_key.host) {
5c51bffb 175 key.append(request->url.host());
de03b596
FC
176 }
177 if (tp->options.carp_key.port) {
5c51bffb 178 key.appendf(":%u", request->url.port());
de03b596
FC
179 }
180 if (tp->options.carp_key.path) {
51b5dcf5
AJ
181 // XXX: fix when path and query are separate
182 key.append(request->url.path().substr(0,request->url.path().find('?'))); // 0..N
de03b596
FC
183 }
184 if (tp->options.carp_key.params) {
51b5dcf5
AJ
185 // XXX: fix when path and query are separate
186 SBuf::size_type pos;
187 if ((pos=request->url.path().find('?')) != SBuf::npos)
188 key.append(request->url.path().substr(pos)); // N..npos
de03b596 189 }
96f6f33b 190 }
de03b596
FC
191 // if the url-based key is empty, e.g. because the user is
192 // asking to balance on the path but the request doesn't supply any,
851feda6 193 // then fall back to the effective request URI
de03b596 194
2128aa19 195 if (key.isEmpty())
851feda6 196 key=request->effectiveRequestUri();
de03b596 197
2128aa19 198 for (const char *c = key.rawContent(), *e=key.rawContent()+key.length(); c < e; ++c)
de03b596 199 user_hash += ROTATE_LEFT(user_hash, 19) + *c;
62e76326 200 combined_hash = (user_hash ^ tp->carp.hash);
201 combined_hash += combined_hash * 0x62531965;
202 combined_hash = ROTATE_LEFT(combined_hash, 21);
203 score = combined_hash * tp->carp.load_multiplier;
de03b596
FC
204 debugs(39, 3, "carpSelectParent: key=" << key << " name=" << tp->name << " combined_hash=" << combined_hash <<
205 " score=" << std::setprecision(0) << score);
62e76326 206
207 if ((score > high_score) && peerHTTPOkay(tp, request)) {
208 p = tp;
209 high_score = score;
210 }
afd88fbe 211 }
62e76326 212
afd88fbe 213 if (p)
d4140027 214 debugs(39, 2, "carpSelectParent: selected " << p->name);
62e76326 215
afd88fbe 216 return p;
217}
8ee9b49f 218
219static void
220carpCachemgr(StoreEntry * sentry)
221{
a3c6762c 222 CachePeer *p;
8ee9b49f 223 int sumfetches = 0;
224 storeAppendPrintf(sentry, "%24s %10s %10s %10s %10s\n",
62e76326 225 "Hostname",
226 "Hash",
227 "Multiplier",
228 "Factor",
229 "Actual");
230
8ee9b49f 231 for (p = Config.peers; p; p = p->next)
62e76326 232 sumfetches += p->stats.fetches;
233
8ee9b49f 234 for (p = Config.peers; p; p = p->next) {
62e76326 235 storeAppendPrintf(sentry, "%24s %10x %10f %10f %10f\n",
d4140027 236 p->name, p->carp.hash,
62e76326 237 p->carp.load_multiplier,
238 p->carp.load_factor,
239 sumfetches ? (double) p->stats.fetches / sumfetches : -1.0);
8ee9b49f 240 }
8ee9b49f 241}
f53969cc 242