47 $map = array_filter( $map,
function ( $w ) {
50 if ( !
count( $map ) ) {
51 throw new UnexpectedValueException(
"Ring is empty or all weights are zero." );
53 $this->sourceMap = $map;
56 foreach ( $map
as $location => $weight ) {
57 $hashes[$location] = sha1( $location );
59 uksort( $map,
function ( $a, $b )
use (
$hashes ) {
63 $sum = array_sum( $map );
65 foreach ( $map
as $location => $weight ) {
66 $standardMap[$location] = (int)floor( $weight / $sum * self::RING_SIZE );
70 foreach ( $standardMap
as $location => $weight ) {
72 $this->ring[$location] = [ $index, $index + $weight ];
101 $primaryLocation =
null;
102 $spot = hexdec( substr( sha1( $item ), 0, 7 ) );
103 foreach ( $this->ring
as $location => $range ) {
104 if (
count( $locations ) >= $limit ) {
109 if ( ( $range[0] <= $spot && $spot < $range[1] ) || $primaryLocation !==
null ) {
110 if ( $primaryLocation ===
null ) {
111 $primaryLocation = $location;
113 $locations[] = $location;
117 reset( $this->ring );
118 while (
count( $locations ) < $limit ) {
119 list( $location, ) = each( $this->ring );
120 if ( $location === $primaryLocation ) {
123 $locations[] = $location;
146 unset( $map[$location] );
148 return count( $map ) ?
new self( $map ) :
false;
159 if ( !isset( $this->sourceMap[$location] ) ) {
160 throw new UnexpectedValueException(
"No location '$location' in the ring." );
162 $expiry = time() + $ttl;
163 $this->liveRing =
null;
164 $this->ejectionExpiries[$location] = $expiry;
165 $this->ejectionNextExpiry = min( $expiry, $this->ejectionNextExpiry );
167 return (
count( $this->ejectionExpiries ) <
count( $this->sourceMap ) );
178 if ( $this->liveRing ===
null || $this->ejectionNextExpiry <= $now ) {
179 $this->ejectionExpiries = array_filter(
180 $this->ejectionExpiries,
181 function ( $expiry )
use ( $now ) {
182 return ( $expiry > $now );
185 if (
count( $this->ejectionExpiries ) ) {
186 $map = array_diff_key( $this->sourceMap, $this->ejectionExpiries );
187 $this->liveRing =
count( $map ) ?
new self( $map ) :
false;
189 $this->ejectionNextExpiry = min( $this->ejectionExpiries );
191 $this->liveRing = clone $this;
192 $this->liveRing->ejectionExpiries = [];
193 $this->liveRing->ejectionNextExpiry = INF;
194 $this->liveRing->liveRing =
null;
196 $this->ejectionNextExpiry = INF;
199 if ( !$this->liveRing ) {
200 throw new UnexpectedValueException(
"The live ring is currently empty." );
226 return $this->
getLiveRing()->getLocations( $item, $limit );