@@ -123,6 +123,84 @@ func TestConsistentLoad(t *testing.T) {
123123 })
124124}
125125
126+ // Regression test for https://github.com/buraksezer/consistent/issues/13
127+ //
128+ // distributeWithLoad increased the counter before it checked a position on the
129+ // ring. So the last position was never checked. A ring with one position always
130+ // panicked, even if the member was empty.
131+ func TestConsistentDistributeWithLoadCoversWholeRing (t * testing.T ) {
132+ newMembers := func (count int ) []Member {
133+ var members []Member
134+ for i := 0 ; i < count ; i ++ {
135+ members = append (members , testMember (fmt .Sprintf ("node%d.olric" , i )))
136+ }
137+ return members
138+ }
139+
140+ cases := []struct {
141+ name string
142+ memberCount int
143+ config Config
144+ }{
145+ {
146+ name : "single member on a single ring position" ,
147+ memberCount : 1 ,
148+ config : Config {PartitionCount : 100 , ReplicationFactor : 1 , Load : 1.25 , Hasher : hasher {}},
149+ },
150+ {
151+ name : "one partition on one member" ,
152+ memberCount : 1 ,
153+ config : Config {PartitionCount : 1 , ReplicationFactor : 1 , Load : 1 , Hasher : hasher {}},
154+ },
155+ {
156+ name : "few partitions over few members without replicas" ,
157+ memberCount : 2 ,
158+ config : Config {PartitionCount : 10 , ReplicationFactor : 1 , Load : 1.1 , Hasher : hasher {}},
159+ },
160+ {
161+ name : "default configuration" ,
162+ memberCount : 8 ,
163+ config : Config {PartitionCount : 271 , ReplicationFactor : 20 , Load : 1.25 , Hasher : hasher {}},
164+ },
165+ }
166+
167+ for _ , tc := range cases {
168+ t .Run (tc .name , func (t * testing.T ) {
169+ defer func () {
170+ if err := recover (); err != nil {
171+ t .Fatalf ("Expected no panic, Got: %v" , err )
172+ }
173+ }()
174+
175+ c := New (newMembers (tc .memberCount ), tc .config )
176+ // Every partition must have an owner.
177+ for partID := 0 ; partID < tc .config .PartitionCount ; partID ++ {
178+ if c .GetPartitionOwner (partID ) == nil {
179+ t .Fatalf ("partition %d has no owner" , partID )
180+ }
181+ }
182+ })
183+ }
184+ }
185+
186+ // The panic must still work after the fix above. If there is no room for all
187+ // partitions, distributeWithLoad has to panic, not loop forever.
188+ func TestConsistentDistributeWithLoadExhausted (t * testing.T ) {
189+ defer func () {
190+ if err := recover (); err == nil {
191+ t .Fatal ("Expected a panic, Got: nil" )
192+ }
193+ }()
194+
195+ var members []Member
196+ for i := 0 ; i < 2 ; i ++ {
197+ members = append (members , testMember (fmt .Sprintf ("node%d.olric" , i )))
198+ }
199+ // Average load is Ceil(10/2 * 0.5) = 3. Two members can take 6 partitions,
200+ // but we have 10 of them.
201+ New (members , Config {PartitionCount : 10 , ReplicationFactor : 1 , Load : 0.5 , Hasher : hasher {}})
202+ }
203+
126204func TestConsistentLocateKey (t * testing.T ) {
127205 cfg := newConfig ()
128206 c := New (nil , cfg )
0 commit comments