48 $map = array_filter( $map,
function ( $w ) {
51 if ( !
count( $map ) ) {
52 throw new UnexpectedValueException(
"Ring is empty or all weights are zero." );
54 $this->sourceMap = $map;
57 foreach ( $map
as $location => $weight ) {
58 $hashes[$location] = sha1( $location );
60 uksort( $map,
function ( $a, $b )
use (
$hashes ) {
64 $sum = array_sum( $map );
66 foreach ( $map
as $location => $weight ) {
67 $standardMap[$location] = (int)floor( $weight / $sum * self::RING_SIZE );
71 foreach ( $standardMap
as $location => $weight ) {
73 $this->ring[$location] = [ $index, $index + $weight ];
102 $primaryLocation =
null;
103 $spot = hexdec( substr( sha1( $item ), 0, 7 ) );
104 foreach ( $this->ring
as $location => $range ) {
110 if ( ( $range[0] <= $spot && $spot < $range[1] ) || $primaryLocation !==
null ) {
111 if ( $primaryLocation ===
null ) {
112 $primaryLocation = $location;
114 $locations[] = $location;
118 reset( $this->ring );
120 list( $location, ) = each( $this->ring );
121 if ( $location === $primaryLocation ) {
124 $locations[] = $location;
147 unset( $map[$location] );
149 return count( $map ) ?
new self( $map ) :
false;
160 if ( !isset( $this->sourceMap[$location] ) ) {
161 throw new UnexpectedValueException(
"No location '$location' in the ring." );
163 $expiry = time() + $ttl;
164 $this->liveRing =
null;
165 $this->ejectionExpiries[$location] = $expiry;
166 $this->ejectionNextExpiry = min( $expiry, $this->ejectionNextExpiry );
168 return (
count( $this->ejectionExpiries ) <
count( $this->sourceMap ) );
179 if ( $this->liveRing ===
null || $this->ejectionNextExpiry <= $now ) {
180 $this->ejectionExpiries = array_filter(
181 $this->ejectionExpiries,
182 function( $expiry )
use ( $now ) {
183 return ( $expiry > $now );
186 if (
count( $this->ejectionExpiries ) ) {
187 $map = array_diff_key( $this->sourceMap, $this->ejectionExpiries );
188 $this->liveRing =
count( $map ) ?
new self( $map ) :
false;
190 $this->ejectionNextExpiry = min( $this->ejectionExpiries );
192 $this->liveRing = clone $this;
193 $this->liveRing->ejectionExpiries = [];
194 $this->liveRing->ejectionNextExpiry = INF;
195 $this->liveRing->liveRing =
null;
197 $this->ejectionNextExpiry = INF;
200 if ( !$this->liveRing ) {
201 throw new UnexpectedValueException(
"The live ring is currently empty." );