/** * Constructor * * @param random a random number generator * @param persistence a persistence that can be used to write to the datastore * @param memcache a memcache service for quick access to shared transactional numbers * @param chanceToWrite a value between 0.0 and 1.0 (inclusive). Each time the counter gets * increased, a random throw of the dice decides whether to write to the store. A * chanceToWrite of 1.0 means that every change in the counter will be persisted; a * chanceToWrite of 0.0 means that no change will be persisted * @param key a key that is used to persist the counter shards in the datastore and memcache. must * not contain any slashes * @param numShards the number of shards that should be used to store the value. The more shards * the less the chance of collision on writes, but the longer it will take to load shards from * the store if memcache has been evicted */ public Counter( Random random, Persistence<byte[]> persistence, MemcacheService memcache, double chanceToWrite, String key, int numShards) { super(); Preconditions.checkNotNull(random); Preconditions.checkNotNull(memcache); Preconditions.checkNotNull(memcache); Preconditions.checkArgument( chanceToWrite >= 0.0 && chanceToWrite <= 1.0, "chanceToWrite must be bewteen 0.0 and 1.0"); Preconditions.checkArgument(key.indexOf('/') < 0, "key must not contain any slashes: " + key); Preconditions.checkArgument( numShards > 0 && numShards < 1000, "there must be at least one shard, but no more than 999"); this.random = random; this.persistence = new LongPersistence(persistence); this.memcache = memcache; this.chanceToWrite = chanceToWrite; this.prefix = '/' + key + '/'; this.memcacheKey = "aef/c/" + key; this.numShards = numShards; }
/** * Increments the value by a positive delta * * @param delta * @return the value that the counter was changed to */ public long increment(long delta) { Preconditions.checkArgument(delta > 0, "delta must be a positive value"); populateMemcache(); final Long result = memcache.increment(memcacheKey, delta); if (random.nextDouble() <= chanceToWrite) { String shardKey = prefix + random.nextInt(numShards); persistence.mutate( shardKey, new Function<Long, Long>() { @Override public Long apply(Long oldValue) { if (oldValue == null) { return result; } return Math.max(oldValue, result); } }); } return result; }