Mailing List Archive

[master] b90b60d0a shard director: add optional weight parameter to .add_backend()
commit b90b60d0a066f1aa7302a70125cb91cdfa605119
Author: Nils Goroll <nils.goroll@uplex.de>
Date: Tue Jun 9 19:02:00 2020 +0200

shard director: add optional weight parameter to .add_backend()

We implement weights by scaling the number of replicas of each backend.
The replicas parameter of .reconfigure() remains a minimum.

For existing vtcs, the Debug hashcircle output has been compared
before/after this change to ensure that behaviour is exactly equivalent.

For for wighted backends, it has been checked that the number of
instances per host on the hashcircle matches the expectation.

Also refactor and clean up some of the code:

- consistently make the number of ring points a uint32_t
- some constification

Ref #3276

diff --git a/bin/varnishtest/tests/d00041.vtc b/bin/varnishtest/tests/d00041.vtc
new file mode 100644
index 000000000..bd79cfff1
--- /dev/null
+++ b/bin/varnishtest/tests/d00041.vtc
@@ -0,0 +1,224 @@
+varnishtest "d00017.vtc but with weights"
+
+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 std;
+ import directors;
+ import blob;
+
+ sub vcl_init {
+ new vd = directors.shard();
+ vd.debug(3);
+ if (!vd.add_backend(s1)) {
+ std.log("add s1 failed");
+ }
+ if (!vd.add_backend(s2, weight=2)) {
+ std.log("add s2 failed");
+ }
+ if (!vd.add_backend(s3, weight=3)) {
+ std.log("add s3 failed");
+ }
+ if (!vd.reconfigure(replicas=25)) {
+ std.log("reconfigure failed");
+ }
+ }
+
+ sub vcl_recv {
+ set req.backend_hint = vd.backend(by=BLOB,
+ key_blob=blob.decode(HEX, encoded=
+ regsub(req.url, "^/", "")));
+ return(pass);
+ }
+
+} -start
+
+logexpect l1 -v v1 -g raw -d 1 {
+ expect 0 0 CLI "^Rd vcl.load"
+
+ expect 0 = Debug {^shard: hashcircle.* 0. = .point = *238d0ef, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 1. = .point = *321c598, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 2. = .point = *3b6b56a, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 3. = .point = *408ec1e, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 4. = .point = *66986a7, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 5. = .point = *7e41e30, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 6. = .point = *b749e7b, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 7. = .point = *e543430, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 8. = .point = *10136c05, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 9. = .point = *102d847f, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 10. = .point = *1112f910, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 11. = .point = *1119a7c7, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 12. = .point = *14d95c44, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 13. = .point = *150fea1f, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 14. = .point = *1643ecb6, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 15. = .point = *189ff2f2, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 16. = .point = *19cfe9f3, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 17. = .point = *1e1c78c3, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 18. = .point = *1fe0dea0, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 19. = .point = *22464ee9, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 20. = .point = *22b35675, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 21. = .point = *2363bebb, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 22. = .point = *24f827bb, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 23. = .point = *259eeccf, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 24. = .point = *26f0c3e7, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 25. = .point = *271874d4, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 26. = .point = *28340f35, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 27. = .point = *285e8475, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 28. = .point = *28ec7a6f, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 29. = .point = *299c6298, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 30. = .point = *2aedc3f7, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 31. = .point = *2b031742, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 32. = .point = *2da0e37b, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 33. = .point = *310bd2ca, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 34. = .point = *31e5f2df, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 35. = .point = *32d6b3ed, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 36. = .point = *33047373, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 37. = .point = *3392487a, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 38. = .point = *37597c4c, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 39. = .point = *3f6b2b89, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 40. = .point = *43cf6426, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 41. = .point = *46a58f28, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 42. = .point = *4b1f5b22, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 43. = .point = *523723f2, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 44. = .point = *539234db, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 45. = .point = *564ca84f, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 46. = .point = *58501380, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 47. = .point = *58704432, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 48. = .point = *5b1bcbbe, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 49. = .point = *5d2df428, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 50. = .point = *5fa294ee, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 51. = .point = *606fd878, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 52. = .point = *60dded53, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 53. = .point = *616cdb68, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 54. = .point = *6257bc27, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 55. = .point = *64014b25, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 56. = .point = *6918f467, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 57. = .point = *6a08c380, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 58. = .point = *6bfd5a2d, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 59. = .point = *6c0b607a, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 60. = .point = *6c74d296, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 61. = .point = *6e040182, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 62. = .point = *6e3819f7, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 63. = .point = *720ec1a4, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 64. = .point = *7232b381, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 65. = .point = *74c384ad, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 66. = .point = *76d47350, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 67. = .point = *791eb3a3, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 68. = .point = *7a048f20, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 69. = .point = *7f874929, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 70. = .point = *83ce71ce, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 71. = .point = *888b6447, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 72. = .point = *8997c018, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 73. = .point = *89b7d09c, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 74. = .point = *8aa6b5b4, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 75. = .point = *8ae34bde, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 76. = .point = *8b382e03, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 77. = .point = *8b47e6ac, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 78. = .point = *8bc76115, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 79. = .point = *8bc8bc11, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 80. = .point = *8e2d3849, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 81. = .point = *8e7e012c, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 82. = .point = *8f5b4c63, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 83. = .point = *94a94162, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 84. = .point = *99892987, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 85. = .point = *9a6f2f00, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 86. = .point = *9b970b49, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 87. = .point = *9e09a3a7, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 88. = .point = *9ef9125d, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 89. = .point = *9f33cd30, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 90. = .point = *9fc69b51, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 91. = .point = *a19f99eb, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 92. = .point = *a28b9595, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 93. = .point = *a3582038, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 94. = .point = *a4b6a3b9, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 95. = .point = *a66da9cb, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 96. = .point = *a8657c76, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 97. = .point = *a8afe9c4, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 98. = .point = *aa488703, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 99. = .point = *ac7b4454, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 100. = .point = *ad923ad3, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 101. = .point = *ae8946c6, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 102. = .point = *b197e339, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 103. = .point = *b3c305e6, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 104. = .point = *b4dab004, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 105. = .point = *b6bf43ea, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 106. = .point = *b9004d3d, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 107. = .point = *b96b6455, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 108. = .point = *b9a0edb9, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 109. = .point = *b9ec6465, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 110. = .point = *bb8eed4d, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 111. = .point = *bbcc0bad, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 112. = .point = *bcfea141, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 113. = .point = *be300622, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 114. = .point = *bf514d68, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 115. = .point = *c1afc7d2, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 116. = .point = *c2542a5d, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 117. = .point = *c6c43fa7, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 118. = .point = *c945958a, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 119. = .point = *c9f304a4, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 120. = .point = *cb896aa8, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 121. = .point = *cbd9198a, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 122. = .point = *ccd61dad, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 123. = .point = *d07e4431, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 124. = .point = *d21fe35f, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 125. = .point = *d4c93105, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 126. = .point = *d570b815, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 127. = .point = *d7de63b6, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 128. = .point = *d8634aef, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 129. = .point = *d92d916d, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 130. = .point = *d937a7df, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 131. = .point = *dac52229, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 132. = .point = *db7840f0, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 133. = .point = *dd5c6bef, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 134. = .point = *dded5798, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 135. = .point = *dfd5333b, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 136. = .point = *e183345a, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 137. = .point = *e2c71c27, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 138. = .point = *e49bf9d8, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 139. = .point = *e72bc224, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 140. = .point = *e8b27f41, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 141. = .point = *e991584c, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 142. = .point = *ea201c5e, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 143. = .point = *ec8891c5, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 144. = .point = *edcc8dd9, host = 1.}
+ expect 0 = Debug {^shard: hashcircle.* 145. = .point = *ef6b4ab5, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 146. = .point = *f08ad325, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 147. = .point = *f3325ba2, host = 2.}
+ expect 0 = Debug {^shard: hashcircle.* 148. = .point = *f6530dd1, host = 0.}
+ expect 0 = Debug {^shard: hashcircle.* 149. = .point = *fc28e8d2, host = 2.}
+
+ expect 0 = CLI Loaded
+
+ expect * = Debug {^shard: lookup key 564ca84f idx 45 host 0}
+ expect * = Debug {^shard: lookup key 19cfe9f3 idx 16 host 1}
+ expect * = Debug {^shard: lookup key 46a58f28 idx 41 host 2}
+} -start
+
+client c1 {
+ txreq -url /564ca84f
+ rxresp
+ expect resp.body == "ech3Ooj"
+
+ txreq -url /19cfe9f3
+ rxresp
+ expect resp.body == "ieQu2qua"
+
+ txreq -url /46a58f28
+ rxresp
+ expect resp.body == "xiuFi3Pe"
+} -run
+
+logexpect l1 -wait
diff --git a/lib/libvmod_directors/shard_cfg.c b/lib/libvmod_directors/shard_cfg.c
index 6276e1728..3f478ecf1 100644
--- a/lib/libvmod_directors/shard_cfg.c
+++ b/lib/libvmod_directors/shard_cfg.c
@@ -55,6 +55,7 @@ struct shard_change_task {
#define SHARD_CHANGE_TASK_MAGIC 0x1e1168af
enum shard_change_task_e task;
void *priv;
+ VCL_REAL weight;
VSTAILQ_ENTRY(shard_change_task) list;
};

@@ -127,7 +128,7 @@ shard_change_finish(struct shard_change *change)
VSTAILQ_INIT(&change->tasks);
}

-static void
+static struct shard_change_task *
shard_change_task_add(VRT_CTX, struct shard_change *change,
enum shard_change_task_e task_e, void *priv)
{
@@ -139,15 +140,17 @@ shard_change_task_add(VRT_CTX, struct shard_change *change,
if (task == NULL) {
shard_err0(ctx, change->shardd,
"could not get workspace for task");
- return;
+ return (NULL);
}
INIT_OBJ(task, SHARD_CHANGE_TASK_MAGIC);
task->task = task_e;
task->priv = priv;
VSTAILQ_INSERT_TAIL(&change->tasks, task, list);
+
+ return (task);
}

-static inline VCL_BOOL
+static inline struct shard_change_task *
shard_change_task_backend(VRT_CTX,
struct vmod_priv *priv, const struct sharddir *shardd,
enum shard_change_task_e task_e, VCL_BACKEND be, VCL_STRING ident,
@@ -161,22 +164,20 @@ shard_change_task_backend(VRT_CTX,

change = shard_change_get(ctx, priv, shardd);
if (change == NULL)
- return (0);
+ return (NULL);

b = WS_Alloc(ctx->ws, sizeof(*b));
if (b == NULL) {
shard_err(ctx, shardd, ".%s_backend() WS_Alloc() failed",
task_e == ADD_BE ? "add" : "remove");
- return (0);
+ return (NULL);
}

b->backend = be;
b->ident = ident != NULL && *ident != '\0' ? ident : NULL;
b->rampup = rampup;

- shard_change_task_add(ctx, change, task_e, b);
-
- return (1);
+ return (shard_change_task_add(ctx, change, task_e, b));
}

/*
@@ -186,11 +187,21 @@ shard_change_task_backend(VRT_CTX,
VCL_BOOL
shardcfg_add_backend(VRT_CTX, struct vmod_priv *priv,
const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident,
- VCL_DURATION rampup)
+ VCL_DURATION rampup, VCL_REAL weight)
{
+ struct shard_change_task *task;
+
+ assert (weight >= 1);
AN(be);
- return (shard_change_task_backend(ctx, priv, shardd, ADD_BE,
- be, ident, rampup));
+
+ task = shard_change_task_backend(ctx, priv, shardd, ADD_BE,
+ be, ident, rampup);
+
+ if (task == NULL)
+ return (0);
+
+ task->weight = weight;
+ return (1);
}

VCL_BOOL
@@ -198,7 +209,7 @@ shardcfg_remove_backend(VRT_CTX, struct vmod_priv *priv,
const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident)
{
return (shard_change_task_backend(ctx, priv, shardd, REMOVE_BE,
- be, ident, 0));
+ be, ident, 0) != NULL);
}

VCL_BOOL
@@ -212,9 +223,7 @@ shardcfg_clear(VRT_CTX, struct vmod_priv *priv, const struct sharddir *shardd)
if (change == NULL)
return (0);

- shard_change_task_add(ctx, change, CLEAR, NULL);
-
- return (1);
+ return (shard_change_task_add(ctx, change, CLEAR, NULL) != NULL);
}

/*
@@ -232,9 +241,11 @@ circlepoint_compare(const struct shard_circlepoint *a,
}

static void
-shardcfg_hashcircle(struct sharddir *shardd, VCL_INT replicas)
+shardcfg_hashcircle(struct sharddir *shardd)
{
- int i, j;
+ const struct shard_backend *backends, *b;
+ int j, h;
+ uint32_t i, n_points, r, rmax;
const char *ident;
const int len = 12; // log10(UINT32_MAX) + 2;
char s[len];
@@ -245,49 +256,60 @@ shardcfg_hashcircle(struct sharddir *shardd, VCL_INT replicas)
AZ(shardd->hashcircle);

assert(shardd->n_backend > 0);
- AN(shardd->backend);
-
- shardd->hashcircle = calloc(shardd->n_backend * replicas,
- sizeof(struct shard_circlepoint));
- AN(shardd->hashcircle);
+ backends=shardd->backend;
+ AN(backends);
+
+ n_points = 0;
+ rmax = (UINT32_MAX - 1) / shardd->n_backend;
+ for (b = backends; b < backends + shardd->n_backend; b++) {
+ CHECK_OBJ_NOTNULL(b->backend, DIRECTOR_MAGIC);
+ r = b->replicas;
+ if (r > rmax)
+ r = rmax;
+ n_points += r;
+ }

- shardd->replicas = replicas;
+ assert(n_points < UINT32_MAX);

- for (i = 0; i < shardd->n_backend; i++) {
- CHECK_OBJ_NOTNULL(shardd->backend[i].backend, DIRECTOR_MAGIC);
+ shardd->n_points = n_points;
+ shardd->hashcircle = calloc(n_points, sizeof(struct shard_circlepoint));
+ AN(shardd->hashcircle);

- ident = shardd->backend[i].ident
- ? shardd->backend[i].ident
- : VRT_BACKEND_string(shardd->backend[i].backend);
+ i = 0;
+ for (h = 0, b = backends; h < shardd->n_backend; h++, b++) {
+ ident = b->ident ? b->ident : VRT_BACKEND_string(b->backend);

AN(ident);
assert(ident[0] != '\0');

- for (j = 0; j < replicas; j++) {
+ r = b->replicas;
+ if (r > rmax)
+ r = rmax;
+
+ for (j = 0; j < r; j++) {
assert(snprintf(s, len, "%d", j) < len);
ss->n = 2;
ssp[0] = ident;
ssp[1] = s;
ss->p = ssp;
- shardd->hashcircle[i * replicas + j].point =
- VRT_HashStrands32(ss);
- shardd->hashcircle[i * replicas + j].host = i;
+ assert (i < n_points);
+ shardd->hashcircle[i].point = VRT_HashStrands32(ss);
+ shardd->hashcircle[i].host = h;
+ i++;
}
}
- qsort( (void *) shardd->hashcircle, shardd->n_backend * replicas,
+ assert (i == n_points);
+ qsort( (void *) shardd->hashcircle, n_points,
sizeof (struct shard_circlepoint), (compar) circlepoint_compare);

if ((shardd->debug_flags & SHDBG_CIRCLE) == 0)
return;

- for (i = 0; i < shardd->n_backend; i++)
- for (j = 0; j < replicas; j++)
- SHDBG(SHDBG_CIRCLE, shardd,
- "hashcircle[%5jd] = "
- "{point = %8x, host = %2u}\n",
- (intmax_t)(i * replicas + j),
- shardd->hashcircle[i * replicas + j].point,
- shardd->hashcircle[i * replicas + j].host);
+ for (i = 0; i < n_points; i++)
+ SHDBG(SHDBG_CIRCLE, shardd,
+ "hashcircle[%5jd] = {point = %8x, host = %2u}\n",
+ (intmax_t)i, shardd->hashcircle[i].point,
+ shardd->hashcircle[i].host);
}

/*
@@ -394,7 +416,7 @@ shardcfg_backend_expand(const struct backend_reconfig *re)

static void
shardcfg_backend_add(struct backend_reconfig *re,
- const struct shard_backend *b)
+ const struct shard_backend *b, uint32_t replicas)
{
unsigned i;
struct shard_backend *bb = re->shardd->backend;
@@ -419,6 +441,7 @@ shardcfg_backend_add(struct backend_reconfig *re,

re->shardd->n_backend++;
shardcfg_backend_copyin(&bb[i], b);
+ bb[i].replicas = replicas;
}

static void
@@ -499,10 +522,11 @@ shardcfg_backend_finalize(struct backend_reconfig *re)

static void
shardcfg_apply_change(VRT_CTX, struct sharddir *shardd,
- const struct shard_change *change)
+ const struct shard_change *change, VCL_INT replicas)
{
struct shard_change_task *task, *clear;
const struct shard_backend *b;
+ uint32_t b_replicas;

struct backend_reconfig re = {
.shardd = shardd,
@@ -550,7 +574,14 @@ shardcfg_apply_change(VRT_CTX, struct sharddir *shardd,
b = shardcfg_backend_lookup(&re, task->priv);

if (b == NULL) {
- shardcfg_backend_add(&re, task->priv);
+ assert (task->weight >= 1);
+ if (replicas * task->weight > UINT32_MAX)
+ b_replicas = UINT32_MAX;
+ else
+ b_replicas = replicas * task->weight;
+
+ shardcfg_backend_add(&re, task->priv,
+ b_replicas);
break;
}

@@ -599,7 +630,7 @@ shardcfg_reconfigure(VRT_CTX, struct vmod_priv *priv,

sharddir_wrlock(shardd);

- shardcfg_apply_change(ctx, shardd, change);
+ shardcfg_apply_change(ctx, shardd, change, replicas);
shard_change_finish(change);

if (shardd->hashcircle)
@@ -612,7 +643,7 @@ shardcfg_reconfigure(VRT_CTX, struct vmod_priv *priv,
return (0);
}

- shardcfg_hashcircle(shardd, replicas);
+ shardcfg_hashcircle(shardd);
sharddir_unlock(shardd);
return (1);
}
diff --git a/lib/libvmod_directors/shard_cfg.h b/lib/libvmod_directors/shard_cfg.h
index 5c6f1e6b6..0b8b8612c 100644
--- a/lib/libvmod_directors/shard_cfg.h
+++ b/lib/libvmod_directors/shard_cfg.h
@@ -30,7 +30,7 @@

VCL_BOOL shardcfg_add_backend(VRT_CTX, struct vmod_priv *priv,
const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident,
- VCL_DURATION rampup);
+ VCL_DURATION rampup, VCL_REAL weight);
VCL_BOOL shardcfg_remove_backend(VRT_CTX, struct vmod_priv *priv,
const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident);
VCL_BOOL shardcfg_clear(VRT_CTX, struct vmod_priv *priv,
diff --git a/lib/libvmod_directors/shard_dir.c b/lib/libvmod_directors/shard_dir.c
index 850aeebcf..001286011 100644
--- a/lib/libvmod_directors/shard_dir.c
+++ b/lib/libvmod_directors/shard_dir.c
@@ -60,7 +60,7 @@ struct shard_be_info {
struct shard_state {
const struct vrt_ctx *ctx;
struct sharddir *shardd;
- int idx;
+ uint32_t idx;

struct vbitmap *picklist;
int pickcount;
@@ -94,8 +94,10 @@ shard_lookup(const struct sharddir *shardd, const uint32_t key)
{
CHECK_OBJ_NOTNULL(shardd, SHARDDIR_MAGIC);

- const int n = shardd->n_backend * shardd->replicas;
- int idx = -1, high = n, low = 0, i;
+ const uint32_t n = shardd->n_points;
+ uint32_t i, idx = UINT32_MAX, high = n, low = 0;
+
+ assert (n < idx);

do {
i = (high + low) / 2 ;
@@ -113,7 +115,7 @@ shard_lookup(const struct sharddir *shardd, const uint32_t key)
high = i;
else
low = i;
- } while (idx == -1);
+ } while (idx == UINT32_MAX);

return (idx);
}
@@ -122,7 +124,6 @@ static int
shard_next(struct shard_state *state, VCL_INT skip, VCL_BOOL healthy)
{
int c, chosen = -1;
- uint32_t ringsz;
VCL_BACKEND be;
vtim_real changed;
struct shard_be_info *sbe;
@@ -134,8 +135,6 @@ shard_next(struct shard_state *state, VCL_INT skip, VCL_BOOL healthy)
if (state->pickcount >= state->shardd->n_backend)
return (-1);

- ringsz = state->shardd->n_backend * state->shardd->replicas;
-
while (state->pickcount < state->shardd->n_backend && skip >= 0) {

c = state->shardd->hashcircle[state->idx].host;
@@ -174,7 +173,7 @@ shard_next(struct shard_state *state, VCL_INT skip, VCL_BOOL healthy)
break;
}

- if (++(state->idx) == ringsz)
+ if (++(state->idx) == state->shardd->n_points)
state->idx = 0;
}
return (chosen);
diff --git a/lib/libvmod_directors/shard_dir.h b/lib/libvmod_directors/shard_dir.h
index 03df1c312..3ad305180 100644
--- a/lib/libvmod_directors/shard_dir.h
+++ b/lib/libvmod_directors/shard_dir.h
@@ -43,6 +43,7 @@ struct shard_backend {
void *freeptr;
};
VCL_DURATION rampup;
+ uint32_t replicas;
};

struct vmod_directors_shard_param;
@@ -68,7 +69,8 @@ struct sharddir {

VCL_DURATION rampup_duration;
VCL_REAL warmup;
- VCL_INT replicas;
+
+ uint32_t n_points;
};

static inline VCL_BACKEND
diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc
index 054b6c34e..8ab9f12fa 100644
--- a/lib/libvmod_directors/vmod.vcc
+++ b/lib/libvmod_directors/vmod.vcc
@@ -373,7 +373,7 @@ The association can be changed per backend request using the *param*
argument of `xshard.backend()`_.

$Method BOOL .add_backend(PRIV_TASK, BACKEND backend,
- [STRING ident], [DURATION rampup])
+ [STRING ident], [DURATION rampup], [REAL weight])

Add a backend *backend* to the director.

@@ -388,6 +388,12 @@ defaults to the backend name.
backend. Otherwise, the per-director rampup time is used (see
`xshard.set_rampup()`_).

+*weight*: Optionally specify a weight to scale the
+`xshard.reconfigure()`_ *replicas* parameter. *weight* is limited to
+at least 1. Values above 10 probably do not make much sense. The
+effect of *weight* is also capped such that the total number of
+replicas does not exceed `UINT32_MAX`.
+
NOTE: Backend changes need to be finalized with
`xshard.reconfigure()`_ and are only supported on one
shard director at a time.
diff --git a/lib/libvmod_directors/vmod_shard.c b/lib/libvmod_directors/vmod_shard.c
index c3ec5d837..8a8bda9a2 100644
--- a/lib/libvmod_directors/vmod_shard.c
+++ b/lib/libvmod_directors/vmod_shard.c
@@ -305,6 +305,8 @@ VCL_BOOL v_matchproto_(td_directors_shard_add_backend)
vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
struct VARGS(shard_add_backend) *args)
{
+ VCL_REAL weight = 1;
+
CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);

if (args->backend == NULL) {
@@ -313,10 +315,14 @@ vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
return (0);
}

+ if (args->valid_weight && args->weight > 1)
+ weight = args->weight;
+
return shardcfg_add_backend(ctx, args->arg1,
vshard->shardd, args->backend,
args->valid_ident ? args->ident : NULL,
- args->valid_rampup ? args->rampup : nan(""));
+ args->valid_rampup ? args->rampup : nan(""),
+ weight);
}

VCL_BOOL v_matchproto_(td_directors_shard_remove_backend)
_______________________________________________
varnish-commit mailing list
varnish-commit@varnish-cache.org
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-commit
Re: [master] b90b60d0a shard director: add optional weight parameter to .add_backend() [ In reply to ]
On Tue, Jun 9, 2020 at 5:03 PM Nils Goroll <nils.goroll@uplex.de> wrote:
>
>
> commit b90b60d0a066f1aa7302a70125cb91cdfa605119
> Author: Nils Goroll <nils.goroll@uplex.de>
> Date: Tue Jun 9 19:02:00 2020 +0200
>
> shard director: add optional weight parameter to .add_backend()
>
> We implement weights by scaling the number of replicas of each backend.
> The replicas parameter of .reconfigure() remains a minimum.
>
> For existing vtcs, the Debug hashcircle output has been compared
> before/after this change to ensure that behaviour is exactly equivalent.
>
> For for wighted backends, it has been checked that the number of
> instances per host on the hashcircle matches the expectation.
>
> Also refactor and clean up some of the code:
>
> - consistently make the number of ring points a uint32_t
> - some constification
>
> Ref #3276
>
> diff --git a/bin/varnishtest/tests/d00041.vtc b/bin/varnishtest/tests/d00041.vtc
> new file mode 100644
> index 000000000..bd79cfff1
> --- /dev/null
> +++ b/bin/varnishtest/tests/d00041.vtc
> @@ -0,0 +1,224 @@
> +varnishtest "d00017.vtc but with weights"
> +
> +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 std;
> + import directors;
> + import blob;
> +
> + sub vcl_init {
> + new vd = directors.shard();
> + vd.debug(3);
> + if (!vd.add_backend(s1)) {
> + std.log("add s1 failed");
> + }
> + if (!vd.add_backend(s2, weight=2)) {
> + std.log("add s2 failed");
> + }
> + if (!vd.add_backend(s3, weight=3)) {
> + std.log("add s3 failed");
> + }
> + if (!vd.reconfigure(replicas=25)) {
> + std.log("reconfigure failed");

Any reason not to return(fail("reason")) instead of std.log() calls?

> + }
> + }
> +
> + sub vcl_recv {
> + set req.backend_hint = vd.backend(by=BLOB,
> + key_blob=blob.decode(HEX, encoded=
> + regsub(req.url, "^/", "")));
> + return(pass);
> + }
> +
> +} -start
> +
> +logexpect l1 -v v1 -g raw -d 1 {
> + expect 0 0 CLI "^Rd vcl.load"
> +
> + expect 0 = Debug {^shard: hashcircle.* 0. = .point = *238d0ef, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 1. = .point = *321c598, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 2. = .point = *3b6b56a, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 3. = .point = *408ec1e, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 4. = .point = *66986a7, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 5. = .point = *7e41e30, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 6. = .point = *b749e7b, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 7. = .point = *e543430, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 8. = .point = *10136c05, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 9. = .point = *102d847f, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 10. = .point = *1112f910, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 11. = .point = *1119a7c7, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 12. = .point = *14d95c44, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 13. = .point = *150fea1f, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 14. = .point = *1643ecb6, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 15. = .point = *189ff2f2, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 16. = .point = *19cfe9f3, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 17. = .point = *1e1c78c3, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 18. = .point = *1fe0dea0, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 19. = .point = *22464ee9, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 20. = .point = *22b35675, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 21. = .point = *2363bebb, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 22. = .point = *24f827bb, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 23. = .point = *259eeccf, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 24. = .point = *26f0c3e7, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 25. = .point = *271874d4, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 26. = .point = *28340f35, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 27. = .point = *285e8475, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 28. = .point = *28ec7a6f, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 29. = .point = *299c6298, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 30. = .point = *2aedc3f7, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 31. = .point = *2b031742, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 32. = .point = *2da0e37b, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 33. = .point = *310bd2ca, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 34. = .point = *31e5f2df, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 35. = .point = *32d6b3ed, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 36. = .point = *33047373, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 37. = .point = *3392487a, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 38. = .point = *37597c4c, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 39. = .point = *3f6b2b89, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 40. = .point = *43cf6426, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 41. = .point = *46a58f28, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 42. = .point = *4b1f5b22, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 43. = .point = *523723f2, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 44. = .point = *539234db, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 45. = .point = *564ca84f, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 46. = .point = *58501380, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 47. = .point = *58704432, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 48. = .point = *5b1bcbbe, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 49. = .point = *5d2df428, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 50. = .point = *5fa294ee, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 51. = .point = *606fd878, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 52. = .point = *60dded53, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 53. = .point = *616cdb68, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 54. = .point = *6257bc27, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 55. = .point = *64014b25, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 56. = .point = *6918f467, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 57. = .point = *6a08c380, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 58. = .point = *6bfd5a2d, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 59. = .point = *6c0b607a, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 60. = .point = *6c74d296, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 61. = .point = *6e040182, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 62. = .point = *6e3819f7, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 63. = .point = *720ec1a4, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 64. = .point = *7232b381, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 65. = .point = *74c384ad, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 66. = .point = *76d47350, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 67. = .point = *791eb3a3, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 68. = .point = *7a048f20, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 69. = .point = *7f874929, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 70. = .point = *83ce71ce, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 71. = .point = *888b6447, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 72. = .point = *8997c018, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 73. = .point = *89b7d09c, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 74. = .point = *8aa6b5b4, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 75. = .point = *8ae34bde, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 76. = .point = *8b382e03, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 77. = .point = *8b47e6ac, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 78. = .point = *8bc76115, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 79. = .point = *8bc8bc11, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 80. = .point = *8e2d3849, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 81. = .point = *8e7e012c, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 82. = .point = *8f5b4c63, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 83. = .point = *94a94162, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 84. = .point = *99892987, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 85. = .point = *9a6f2f00, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 86. = .point = *9b970b49, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 87. = .point = *9e09a3a7, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 88. = .point = *9ef9125d, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 89. = .point = *9f33cd30, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 90. = .point = *9fc69b51, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 91. = .point = *a19f99eb, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 92. = .point = *a28b9595, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 93. = .point = *a3582038, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 94. = .point = *a4b6a3b9, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 95. = .point = *a66da9cb, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 96. = .point = *a8657c76, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 97. = .point = *a8afe9c4, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 98. = .point = *aa488703, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 99. = .point = *ac7b4454, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 100. = .point = *ad923ad3, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 101. = .point = *ae8946c6, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 102. = .point = *b197e339, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 103. = .point = *b3c305e6, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 104. = .point = *b4dab004, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 105. = .point = *b6bf43ea, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 106. = .point = *b9004d3d, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 107. = .point = *b96b6455, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 108. = .point = *b9a0edb9, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 109. = .point = *b9ec6465, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 110. = .point = *bb8eed4d, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 111. = .point = *bbcc0bad, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 112. = .point = *bcfea141, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 113. = .point = *be300622, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 114. = .point = *bf514d68, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 115. = .point = *c1afc7d2, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 116. = .point = *c2542a5d, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 117. = .point = *c6c43fa7, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 118. = .point = *c945958a, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 119. = .point = *c9f304a4, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 120. = .point = *cb896aa8, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 121. = .point = *cbd9198a, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 122. = .point = *ccd61dad, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 123. = .point = *d07e4431, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 124. = .point = *d21fe35f, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 125. = .point = *d4c93105, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 126. = .point = *d570b815, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 127. = .point = *d7de63b6, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 128. = .point = *d8634aef, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 129. = .point = *d92d916d, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 130. = .point = *d937a7df, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 131. = .point = *dac52229, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 132. = .point = *db7840f0, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 133. = .point = *dd5c6bef, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 134. = .point = *dded5798, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 135. = .point = *dfd5333b, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 136. = .point = *e183345a, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 137. = .point = *e2c71c27, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 138. = .point = *e49bf9d8, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 139. = .point = *e72bc224, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 140. = .point = *e8b27f41, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 141. = .point = *e991584c, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 142. = .point = *ea201c5e, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 143. = .point = *ec8891c5, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 144. = .point = *edcc8dd9, host = 1.}
> + expect 0 = Debug {^shard: hashcircle.* 145. = .point = *ef6b4ab5, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 146. = .point = *f08ad325, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 147. = .point = *f3325ba2, host = 2.}
> + expect 0 = Debug {^shard: hashcircle.* 148. = .point = *f6530dd1, host = 0.}
> + expect 0 = Debug {^shard: hashcircle.* 149. = .point = *fc28e8d2, host = 2.}
> +
> + expect 0 = CLI Loaded
> +
> + expect * = Debug {^shard: lookup key 564ca84f idx 45 host 0}
> + expect * = Debug {^shard: lookup key 19cfe9f3 idx 16 host 1}
> + expect * = Debug {^shard: lookup key 46a58f28 idx 41 host 2}
> +} -start
> +
> +client c1 {
> + txreq -url /564ca84f
> + rxresp
> + expect resp.body == "ech3Ooj"
> +
> + txreq -url /19cfe9f3
> + rxresp
> + expect resp.body == "ieQu2qua"
> +
> + txreq -url /46a58f28
> + rxresp
> + expect resp.body == "xiuFi3Pe"
> +} -run
> +
> +logexpect l1 -wait
> diff --git a/lib/libvmod_directors/shard_cfg.c b/lib/libvmod_directors/shard_cfg.c
> index 6276e1728..3f478ecf1 100644
> --- a/lib/libvmod_directors/shard_cfg.c
> +++ b/lib/libvmod_directors/shard_cfg.c
> @@ -55,6 +55,7 @@ struct shard_change_task {
> #define SHARD_CHANGE_TASK_MAGIC 0x1e1168af
> enum shard_change_task_e task;
> void *priv;
> + VCL_REAL weight;
> VSTAILQ_ENTRY(shard_change_task) list;
> };
>
> @@ -127,7 +128,7 @@ shard_change_finish(struct shard_change *change)
> VSTAILQ_INIT(&change->tasks);
> }
>
> -static void
> +static struct shard_change_task *
> shard_change_task_add(VRT_CTX, struct shard_change *change,
> enum shard_change_task_e task_e, void *priv)
> {
> @@ -139,15 +140,17 @@ shard_change_task_add(VRT_CTX, struct shard_change *change,
> if (task == NULL) {
> shard_err0(ctx, change->shardd,
> "could not get workspace for task");
> - return;
> + return (NULL);
> }
> INIT_OBJ(task, SHARD_CHANGE_TASK_MAGIC);
> task->task = task_e;
> task->priv = priv;
> VSTAILQ_INSERT_TAIL(&change->tasks, task, list);
> +
> + return (task);
> }
>
> -static inline VCL_BOOL
> +static inline struct shard_change_task *
> shard_change_task_backend(VRT_CTX,
> struct vmod_priv *priv, const struct sharddir *shardd,
> enum shard_change_task_e task_e, VCL_BACKEND be, VCL_STRING ident,
> @@ -161,22 +164,20 @@ shard_change_task_backend(VRT_CTX,
>
> change = shard_change_get(ctx, priv, shardd);
> if (change == NULL)
> - return (0);
> + return (NULL);
>
> b = WS_Alloc(ctx->ws, sizeof(*b));
> if (b == NULL) {
> shard_err(ctx, shardd, ".%s_backend() WS_Alloc() failed",
> task_e == ADD_BE ? "add" : "remove");
> - return (0);
> + return (NULL);
> }
>
> b->backend = be;
> b->ident = ident != NULL && *ident != '\0' ? ident : NULL;
> b->rampup = rampup;
>
> - shard_change_task_add(ctx, change, task_e, b);
> -
> - return (1);
> + return (shard_change_task_add(ctx, change, task_e, b));
> }
>
> /*
> @@ -186,11 +187,21 @@ shard_change_task_backend(VRT_CTX,
> VCL_BOOL
> shardcfg_add_backend(VRT_CTX, struct vmod_priv *priv,
> const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident,
> - VCL_DURATION rampup)
> + VCL_DURATION rampup, VCL_REAL weight)
> {
> + struct shard_change_task *task;
> +
> + assert (weight >= 1);
> AN(be);
> - return (shard_change_task_backend(ctx, priv, shardd, ADD_BE,
> - be, ident, rampup));
> +
> + task = shard_change_task_backend(ctx, priv, shardd, ADD_BE,
> + be, ident, rampup);
> +
> + if (task == NULL)
> + return (0);
> +
> + task->weight = weight;
> + return (1);
> }
>
> VCL_BOOL
> @@ -198,7 +209,7 @@ shardcfg_remove_backend(VRT_CTX, struct vmod_priv *priv,
> const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident)
> {
> return (shard_change_task_backend(ctx, priv, shardd, REMOVE_BE,
> - be, ident, 0));
> + be, ident, 0) != NULL);
> }
>
> VCL_BOOL
> @@ -212,9 +223,7 @@ shardcfg_clear(VRT_CTX, struct vmod_priv *priv, const struct sharddir *shardd)
> if (change == NULL)
> return (0);
>
> - shard_change_task_add(ctx, change, CLEAR, NULL);
> -
> - return (1);
> + return (shard_change_task_add(ctx, change, CLEAR, NULL) != NULL);
> }
>
> /*
> @@ -232,9 +241,11 @@ circlepoint_compare(const struct shard_circlepoint *a,
> }
>
> static void
> -shardcfg_hashcircle(struct sharddir *shardd, VCL_INT replicas)
> +shardcfg_hashcircle(struct sharddir *shardd)
> {
> - int i, j;
> + const struct shard_backend *backends, *b;
> + int j, h;
> + uint32_t i, n_points, r, rmax;
> const char *ident;
> const int len = 12; // log10(UINT32_MAX) + 2;
> char s[len];
> @@ -245,49 +256,60 @@ shardcfg_hashcircle(struct sharddir *shardd, VCL_INT replicas)
> AZ(shardd->hashcircle);
>
> assert(shardd->n_backend > 0);
> - AN(shardd->backend);
> -
> - shardd->hashcircle = calloc(shardd->n_backend * replicas,
> - sizeof(struct shard_circlepoint));
> - AN(shardd->hashcircle);
> + backends=shardd->backend;
> + AN(backends);
> +
> + n_points = 0;
> + rmax = (UINT32_MAX - 1) / shardd->n_backend;
> + for (b = backends; b < backends + shardd->n_backend; b++) {
> + CHECK_OBJ_NOTNULL(b->backend, DIRECTOR_MAGIC);
> + r = b->replicas;
> + if (r > rmax)
> + r = rmax;
> + n_points += r;
> + }
>
> - shardd->replicas = replicas;
> + assert(n_points < UINT32_MAX);
>
> - for (i = 0; i < shardd->n_backend; i++) {
> - CHECK_OBJ_NOTNULL(shardd->backend[i].backend, DIRECTOR_MAGIC);
> + shardd->n_points = n_points;
> + shardd->hashcircle = calloc(n_points, sizeof(struct shard_circlepoint));
> + AN(shardd->hashcircle);
>
> - ident = shardd->backend[i].ident
> - ? shardd->backend[i].ident
> - : VRT_BACKEND_string(shardd->backend[i].backend);
> + i = 0;
> + for (h = 0, b = backends; h < shardd->n_backend; h++, b++) {
> + ident = b->ident ? b->ident : VRT_BACKEND_string(b->backend);
>
> AN(ident);
> assert(ident[0] != '\0');
>
> - for (j = 0; j < replicas; j++) {
> + r = b->replicas;
> + if (r > rmax)
> + r = rmax;
> +
> + for (j = 0; j < r; j++) {
> assert(snprintf(s, len, "%d", j) < len);
> ss->n = 2;
> ssp[0] = ident;
> ssp[1] = s;
> ss->p = ssp;
> - shardd->hashcircle[i * replicas + j].point =
> - VRT_HashStrands32(ss);
> - shardd->hashcircle[i * replicas + j].host = i;
> + assert (i < n_points);
> + shardd->hashcircle[i].point = VRT_HashStrands32(ss);
> + shardd->hashcircle[i].host = h;
> + i++;
> }
> }
> - qsort( (void *) shardd->hashcircle, shardd->n_backend * replicas,
> + assert (i == n_points);
> + qsort( (void *) shardd->hashcircle, n_points,
> sizeof (struct shard_circlepoint), (compar) circlepoint_compare);
>
> if ((shardd->debug_flags & SHDBG_CIRCLE) == 0)
> return;
>
> - for (i = 0; i < shardd->n_backend; i++)
> - for (j = 0; j < replicas; j++)
> - SHDBG(SHDBG_CIRCLE, shardd,
> - "hashcircle[%5jd] = "
> - "{point = %8x, host = %2u}\n",
> - (intmax_t)(i * replicas + j),
> - shardd->hashcircle[i * replicas + j].point,
> - shardd->hashcircle[i * replicas + j].host);
> + for (i = 0; i < n_points; i++)
> + SHDBG(SHDBG_CIRCLE, shardd,
> + "hashcircle[%5jd] = {point = %8x, host = %2u}\n",
> + (intmax_t)i, shardd->hashcircle[i].point,
> + shardd->hashcircle[i].host);
> }
>
> /*
> @@ -394,7 +416,7 @@ shardcfg_backend_expand(const struct backend_reconfig *re)
>
> static void
> shardcfg_backend_add(struct backend_reconfig *re,
> - const struct shard_backend *b)
> + const struct shard_backend *b, uint32_t replicas)
> {
> unsigned i;
> struct shard_backend *bb = re->shardd->backend;
> @@ -419,6 +441,7 @@ shardcfg_backend_add(struct backend_reconfig *re,
>
> re->shardd->n_backend++;
> shardcfg_backend_copyin(&bb[i], b);
> + bb[i].replicas = replicas;
> }
>
> static void
> @@ -499,10 +522,11 @@ shardcfg_backend_finalize(struct backend_reconfig *re)
>
> static void
> shardcfg_apply_change(VRT_CTX, struct sharddir *shardd,
> - const struct shard_change *change)
> + const struct shard_change *change, VCL_INT replicas)
> {
> struct shard_change_task *task, *clear;
> const struct shard_backend *b;
> + uint32_t b_replicas;
>
> struct backend_reconfig re = {
> .shardd = shardd,
> @@ -550,7 +574,14 @@ shardcfg_apply_change(VRT_CTX, struct sharddir *shardd,
> b = shardcfg_backend_lookup(&re, task->priv);
>
> if (b == NULL) {
> - shardcfg_backend_add(&re, task->priv);
> + assert (task->weight >= 1);
> + if (replicas * task->weight > UINT32_MAX)
> + b_replicas = UINT32_MAX;
> + else
> + b_replicas = replicas * task->weight;
> +
> + shardcfg_backend_add(&re, task->priv,
> + b_replicas);
> break;
> }
>
> @@ -599,7 +630,7 @@ shardcfg_reconfigure(VRT_CTX, struct vmod_priv *priv,
>
> sharddir_wrlock(shardd);
>
> - shardcfg_apply_change(ctx, shardd, change);
> + shardcfg_apply_change(ctx, shardd, change, replicas);
> shard_change_finish(change);
>
> if (shardd->hashcircle)
> @@ -612,7 +643,7 @@ shardcfg_reconfigure(VRT_CTX, struct vmod_priv *priv,
> return (0);
> }
>
> - shardcfg_hashcircle(shardd, replicas);
> + shardcfg_hashcircle(shardd);
> sharddir_unlock(shardd);
> return (1);
> }
> diff --git a/lib/libvmod_directors/shard_cfg.h b/lib/libvmod_directors/shard_cfg.h
> index 5c6f1e6b6..0b8b8612c 100644
> --- a/lib/libvmod_directors/shard_cfg.h
> +++ b/lib/libvmod_directors/shard_cfg.h
> @@ -30,7 +30,7 @@
>
> VCL_BOOL shardcfg_add_backend(VRT_CTX, struct vmod_priv *priv,
> const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident,
> - VCL_DURATION rampup);
> + VCL_DURATION rampup, VCL_REAL weight);
> VCL_BOOL shardcfg_remove_backend(VRT_CTX, struct vmod_priv *priv,
> const struct sharddir *shardd, VCL_BACKEND be, VCL_STRING ident);
> VCL_BOOL shardcfg_clear(VRT_CTX, struct vmod_priv *priv,
> diff --git a/lib/libvmod_directors/shard_dir.c b/lib/libvmod_directors/shard_dir.c
> index 850aeebcf..001286011 100644
> --- a/lib/libvmod_directors/shard_dir.c
> +++ b/lib/libvmod_directors/shard_dir.c
> @@ -60,7 +60,7 @@ struct shard_be_info {
> struct shard_state {
> const struct vrt_ctx *ctx;
> struct sharddir *shardd;
> - int idx;
> + uint32_t idx;
>
> struct vbitmap *picklist;
> int pickcount;
> @@ -94,8 +94,10 @@ shard_lookup(const struct sharddir *shardd, const uint32_t key)
> {
> CHECK_OBJ_NOTNULL(shardd, SHARDDIR_MAGIC);
>
> - const int n = shardd->n_backend * shardd->replicas;
> - int idx = -1, high = n, low = 0, i;
> + const uint32_t n = shardd->n_points;
> + uint32_t i, idx = UINT32_MAX, high = n, low = 0;
> +
> + assert (n < idx);
>
> do {
> i = (high + low) / 2 ;
> @@ -113,7 +115,7 @@ shard_lookup(const struct sharddir *shardd, const uint32_t key)
> high = i;
> else
> low = i;
> - } while (idx == -1);
> + } while (idx == UINT32_MAX);
>
> return (idx);
> }
> @@ -122,7 +124,6 @@ static int
> shard_next(struct shard_state *state, VCL_INT skip, VCL_BOOL healthy)
> {
> int c, chosen = -1;
> - uint32_t ringsz;
> VCL_BACKEND be;
> vtim_real changed;
> struct shard_be_info *sbe;
> @@ -134,8 +135,6 @@ shard_next(struct shard_state *state, VCL_INT skip, VCL_BOOL healthy)
> if (state->pickcount >= state->shardd->n_backend)
> return (-1);
>
> - ringsz = state->shardd->n_backend * state->shardd->replicas;
> -
> while (state->pickcount < state->shardd->n_backend && skip >= 0) {
>
> c = state->shardd->hashcircle[state->idx].host;
> @@ -174,7 +173,7 @@ shard_next(struct shard_state *state, VCL_INT skip, VCL_BOOL healthy)
> break;
> }
>
> - if (++(state->idx) == ringsz)
> + if (++(state->idx) == state->shardd->n_points)
> state->idx = 0;
> }
> return (chosen);
> diff --git a/lib/libvmod_directors/shard_dir.h b/lib/libvmod_directors/shard_dir.h
> index 03df1c312..3ad305180 100644
> --- a/lib/libvmod_directors/shard_dir.h
> +++ b/lib/libvmod_directors/shard_dir.h
> @@ -43,6 +43,7 @@ struct shard_backend {
> void *freeptr;
> };
> VCL_DURATION rampup;
> + uint32_t replicas;
> };
>
> struct vmod_directors_shard_param;
> @@ -68,7 +69,8 @@ struct sharddir {
>
> VCL_DURATION rampup_duration;
> VCL_REAL warmup;
> - VCL_INT replicas;
> +
> + uint32_t n_points;
> };
>
> static inline VCL_BACKEND
> diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc
> index 054b6c34e..8ab9f12fa 100644
> --- a/lib/libvmod_directors/vmod.vcc
> +++ b/lib/libvmod_directors/vmod.vcc
> @@ -373,7 +373,7 @@ The association can be changed per backend request using the *param*
> argument of `xshard.backend()`_.
>
> $Method BOOL .add_backend(PRIV_TASK, BACKEND backend,
> - [STRING ident], [DURATION rampup])
> + [STRING ident], [DURATION rampup], [REAL weight])
>
> Add a backend *backend* to the director.
>
> @@ -388,6 +388,12 @@ defaults to the backend name.
> backend. Otherwise, the per-director rampup time is used (see
> `xshard.set_rampup()`_).
>
> +*weight*: Optionally specify a weight to scale the
> +`xshard.reconfigure()`_ *replicas* parameter. *weight* is limited to
> +at least 1. Values above 10 probably do not make much sense. The
> +effect of *weight* is also capped such that the total number of
> +replicas does not exceed `UINT32_MAX`.
> +
> NOTE: Backend changes need to be finalized with
> `xshard.reconfigure()`_ and are only supported on one
> shard director at a time.
> diff --git a/lib/libvmod_directors/vmod_shard.c b/lib/libvmod_directors/vmod_shard.c
> index c3ec5d837..8a8bda9a2 100644
> --- a/lib/libvmod_directors/vmod_shard.c
> +++ b/lib/libvmod_directors/vmod_shard.c
> @@ -305,6 +305,8 @@ VCL_BOOL v_matchproto_(td_directors_shard_add_backend)
> vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
> struct VARGS(shard_add_backend) *args)
> {
> + VCL_REAL weight = 1;
> +
> CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
>
> if (args->backend == NULL) {
> @@ -313,10 +315,14 @@ vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
> return (0);
> }
>
> + if (args->valid_weight && args->weight > 1)
> + weight = args->weight;
> +

Shouldn't we VRT_fail() if the supplied weight is not valid?


> return shardcfg_add_backend(ctx, args->arg1,
> vshard->shardd, args->backend,
> args->valid_ident ? args->ident : NULL,
> - args->valid_rampup ? args->rampup : nan(""));
> + args->valid_rampup ? args->rampup : nan(""),
> + weight);
> }
>
> VCL_BOOL v_matchproto_(td_directors_shard_remove_backend)
> _______________________________________________
> varnish-commit mailing list
> varnish-commit@varnish-cache.org
> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-commit
_______________________________________________
varnish-commit mailing list
varnish-commit@varnish-cache.org
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-commit
Re: [master] b90b60d0a shard director: add optional weight parameter to .add_backend() [ In reply to ]
Hi Dridi,

thank you for the review :)

> Any reason not to return(fail("reason")) instead of std.log() calls?

The reason is legacy - this was copy-pasta of a long existing test. I'll polish...

>> + if (args->valid_weight && args->weight > 1)
>> + weight = args->weight;
>> +
>
> Shouldn't we VRT_fail() if the supplied weight is not valid?

Yes we should, good point!

Nils

--

** * * UPLEX - Nils Goroll Systemoptimierung

Scheffelstraße 32
22301 Hamburg

tel +49 40 28805731
mob +49 170 2723133
fax +49 40 42949753

xmpp://slink@jabber.int.uplex.de/

http://uplex.de/