[master] b90b60d0a shard director: add optional weight parameter to .add_backend()

Nils Goroll nils.goroll at uplex.de
Tue Jun 9 17:03:07 UTC 2020


commit b90b60d0a066f1aa7302a70125cb91cdfa605119
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

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)


More information about the varnish-commit mailing list