[6.0] 8b2980143 shard director: add optional weight parameter to .add_backend()

Reza Naghibi reza at naghibi.com
Wed Aug 19 13:17:07 UTC 2020


commit 8b29801432fe98dbdd8a518a8c0ced6684563008
Author: Nils Goroll <nils.goroll at 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
    
     Conflicts:
            lib/libvmod_directors/shard_cfg.c
            lib/libvmod_directors/shard_dir.h
            lib/libvmod_directors/vmod.vcc

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 a198f0c85..b3496a15b 100644
--- a/lib/libvmod_directors/shard_cfg.c
+++ b/lib/libvmod_directors/shard_cfg.c
@@ -54,6 +54,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;
 };
 
@@ -126,7 +127,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)
 {
@@ -138,15 +139,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,
@@ -160,22 +163,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));
 }
 
 /*
@@ -185,11 +186,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
@@ -197,7 +208,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
@@ -211,9 +222,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);
 }
 
 /*
@@ -231,9 +240,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];
@@ -242,47 +253,59 @@ 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
-		    : shardd->backend[i].backend->vcl_name;
+	i = 0;
+	for (h = 0, b = backends; h < shardd->n_backend; h++, b++) {
+		ident = b->ident ? b->ident : b->backend->vcl_name;
 
 		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);
-			shardd->hashcircle[i * replicas + j].point =
+			assert (i < n_points);
+			shardd->hashcircle[i].point =
 				sharddir_sha256(ident, s, vrt_magic_string_end);
-			shardd->hashcircle[i * replicas + j].host = i;
+			shardd->hashcircle[i].host = h;
+			i++;
 		}
 		/* not used in current interface */
-		shardd->backend[i].canon_point =
-		    shardd->hashcircle[i * replicas].point;
+		shardd->backend[h].canon_point =
+		    shardd->hashcircle[i].point;
 	}
-	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);
 }
 
 /*
@@ -388,7 +411,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;
@@ -413,6 +436,7 @@ shardcfg_backend_add(struct backend_reconfig *re,
 
 	re->shardd->n_backend++;
 	shardcfg_backend_copyin(&bb[i], b);
+	bb[i].replicas = replicas;
 }
 
 static void
@@ -493,10 +517,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,
@@ -544,7 +569,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;
 			}
 
@@ -593,7 +625,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)
@@ -606,7 +638,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 2ce1817b8..f1eb92412 100644
--- a/lib/libvmod_directors/shard_cfg.h
+++ b/lib/libvmod_directors/shard_cfg.h
@@ -28,7 +28,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 fffcad572..47f7b4927 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;
@@ -135,8 +135,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 ;
@@ -154,7 +156,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);
 }
@@ -163,7 +165,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;
@@ -175,8 +176,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;
@@ -215,7 +214,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 384b32cb9..8d1b86b52 100644
--- a/lib/libvmod_directors/shard_dir.h
+++ b/lib/libvmod_directors/shard_dir.h
@@ -61,6 +61,7 @@ struct shard_backend {
 	};
 	VCL_DURATION		rampup;
 	uint32_t		canon_point;
+	uint32_t		replicas;
 };
 
 #define	SHDBG_LOOKUP	 1
@@ -84,7 +85,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 54b111fba..de467ee27 100644
--- a/lib/libvmod_directors/vmod.vcc
+++ b/lib/libvmod_directors/vmod.vcc
@@ -358,7 +358,7 @@ The association can be changed per backend request using the `param`
 argument of `func_shard.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.
 
@@ -373,6 +373,12 @@ backend name.
 backend. Otherwise, the per-director rampup time is used (see
 :ref:`func_shard.set_rampup`).
 
+*weight*: Optionally specify a weight to scale the
+`shard.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 `shard.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 815b28432..03b6ace5d 100644
--- a/lib/libvmod_directors/vmod_shard.c
+++ b/lib/libvmod_directors/vmod_shard.c
@@ -340,6 +340,8 @@ VCL_BOOL v_matchproto_(td_directors_shard_add_backend)
 vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
     struct vmod_shard_add_backend_arg *args)
 {
+	VCL_REAL weight = 1;
+
 	CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
 
 	if (args->backend == NULL) {
@@ -348,10 +350,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)


More information about the varnish-commit mailing list