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 $location =
key( $this->ring );
120 if ( $location === $primaryLocation ) {
123 $locations[] = $location;
147 if ( !isset( $this->sourceMap[$location] ) ) {
148 throw new UnexpectedValueException(
"No location '$location' in the ring." );
150 $expiry = time() + $ttl;
151 $this->liveRing =
null;
152 $this->ejectionExpiries[$location] = $expiry;
153 $this->ejectionNextExpiry = min( $expiry, $this->ejectionNextExpiry );
155 return (
count( $this->ejectionExpiries ) <
count( $this->sourceMap ) );
166 if ( $this->liveRing ===
null || $this->ejectionNextExpiry <= $now ) {
167 $this->ejectionExpiries = array_filter(
168 $this->ejectionExpiries,
169 function ( $expiry )
use ( $now ) {
170 return ( $expiry > $now );
173 if (
count( $this->ejectionExpiries ) ) {
174 $map = array_diff_key( $this->sourceMap, $this->ejectionExpiries );
175 $this->liveRing =
count( $map ) ?
new self( $map ) :
false;
177 $this->ejectionNextExpiry = min( $this->ejectionExpiries );
179 $this->liveRing = clone $this;
180 $this->liveRing->ejectionExpiries = [];
181 $this->liveRing->ejectionNextExpiry = INF;
182 $this->liveRing->liveRing =
null;
184 $this->ejectionNextExpiry = INF;
187 if ( !$this->liveRing ) {
188 throw new UnexpectedValueException(
"The live ring is currently empty." );
214 return $this->
getLiveRing()->getLocations( $item, $limit );