Optimize HashRing to avoid hashing for the common single-location case
authorAaron Schulz <aschulz@wikimedia.org>
Fri, 30 Aug 2019 02:24:53 +0000 (19:24 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Fri, 30 Aug 2019 02:30:27 +0000 (19:30 -0700)
Change-Id: I3423ac8bd5c56db60630257ac04706838a832d36

includes/libs/HashRing.php
tests/phpunit/includes/libs/HashRingTest.php

index f8ab6a3..94413c2 100644 (file)
@@ -129,6 +129,12 @@ class HashRing implements Serializable {
                        throw new InvalidArgumentException( "Invalid ring source specified." );
                }
 
+               // Short-circuit for the common single-location case. Note that if there was only one
+               // location and it was ejected from the live ring, getLiveRing() would have error out.
+               if ( count( $this->weightByLocation ) == 1 ) {
+                       return ( $limit > 0 ) ? [ $ring[0][self::KEY_LOCATION] ] : [];
+               }
+
                // Locate the node index for this item's position on the hash ring
                $itemIndex = $this->findNodeIndexForPosition( $this->getItemPosition( $item ), $ring );
 
index 4afe3b5..8ddb7c9 100644 (file)
@@ -24,6 +24,32 @@ class HashRingTest extends PHPUnit\Framework\TestCase {
                }
        }
 
+       public function testHashRingSingleLocation() {
+               // SHA-1 based and weighted
+               $ring = new HashRing( [ 's1' => 1 ], 'sha1' );
+
+               $this->assertEquals(
+                       [ 's1' => 1 ],
+                       $ring->getLocationWeights(),
+                       'Normalized location weights'
+               );
+
+               for ( $i = 0; $i < 5; $i++ ) {
+                       $this->assertEquals(
+                               's1',
+                               $ring->getLocation( "hello$i" ),
+                               'Items placed at proper locations'
+                       );
+                       $this->assertEquals(
+                               [ 's1' ],
+                               $ring->getLocations( "hello$i", 2 ),
+                               'Items placed at proper locations'
+                       );
+               }
+
+               $this->assertEquals( [], $ring->getLocations( "helloX", 0 ), "Limit of 0" );
+       }
+
        public function testHashRingMapping() {
                // SHA-1 based and weighted
                $ring = new HashRing(