(userID string, globalLimit int)
| 309 | } |
| 310 | |
| 311 | func (l *Limiter) convertGlobalToLocalLimit(userID string, globalLimit int) int { |
| 312 | if globalLimit == 0 { |
| 313 | return 0 |
| 314 | } |
| 315 | |
| 316 | // Given we don't need a super accurate count (ie. when the ingesters |
| 317 | // topology changes) and we prefer to always be in favor of the tenant, |
| 318 | // we can use a per-ingester limit equal to: |
| 319 | // (global limit / number of ingesters) * replication factor |
| 320 | numIngesters := l.ring.HealthyInstancesCount() |
| 321 | |
| 322 | // May happen because the number of ingesters is asynchronously updated. |
| 323 | // If happens, we just temporarily ignore the global limit. |
| 324 | if numIngesters == 0 { |
| 325 | return 0 |
| 326 | } |
| 327 | |
| 328 | // If the number of available ingesters is greater than the tenant's shard |
| 329 | // size, then we should honor the shard size because series/metadata won't |
| 330 | // be written to more ingesters than it. |
| 331 | if shardSize := l.getShardSize(userID); shardSize > 0 { |
| 332 | // We use Min() to protect from the case the expected shard size is > available ingesters. |
| 333 | numIngesters = min(numIngesters, util.ShuffleShardExpectedInstances(shardSize, l.getNumZones())) |
| 334 | } |
| 335 | |
| 336 | return int((float64(globalLimit) / float64(numIngesters)) * float64(l.replicationFactor)) |
| 337 | } |
| 338 | |
| 339 | func (l *Limiter) getShardSize(userID string) int { |
| 340 | if !l.shuffleShardingEnabled { |
no test coverage detected