private void updateAccountWaitTime ( long clientCustomerId , long waitForMillis ) { final long minWaitTime = millisFromNow ( waitForMillis ) ; // Here we are assuming that the AtomicLong reference isn't changed once inserted. Therefore, // the content of this map grows with the number of account rate limits encountered. Future work // may address the problem of flushing this to reduce overall memory pressure. // // Overhead of AtomicLong is ~ size(long) 64 bytes, // if managing 100k customers this implies a total memory pressure of ~ 100000 * 64 = 6MB. // // Overhead of managing the map is 2 * 100k * (size(key) + size(reference)) = 24MB, // multiplying by factor of 2 to compensate for a map with 50% max load. // // An additional 36MB of RAM is a reasonable trade-off to simplify this implementation. AtomicLong recordedWaitTime = accountWaitUntil . computeIfAbsent ( clientCustomerId , k -> new AtomicLong ( minWaitTime ) ) ; // This update algorithm will eventually terminate, but possibly not on the first iteration due // to concurrent updates. A better solution would be to use // AtomicLongMap.accumulateAndGet(K, long, LongBinaryOperator) from Guava 21.0, however this // would require bumping the Guava version for all Google Ads Java libraries and their // dependents. long snapshotTime = recordedWaitTime . get ( ) ; while ( snapshotTime < minWaitTime ) { if ( recordedWaitTime . compareAndSet ( snapshotTime , minWaitTime ) ) { break ; } else { snapshotTime = recordedWaitTime . get ( ) ; } } }
Update the wait time for ACCOUNT scope .