ShardedCounter { long getCounter(String counterId) throws IOException { return getCounterSnapshot(counterId).getTotalUsage(); } ShardedCounter(Stats stats, CounterSnapshotFactory counterSnapshotFactory); boolean counterHasSpareCapacity(String resourceId); void updateCounter(StorageTransaction transaction, String counterId, long delta); static final int NUM_SHARDS; static final String KIND_COUNTER_LIMIT; static final String PROPERTY_LIMIT; static final String KIND_COUNTER_SHARD; static final String PROPERTY_SHARD_VALUE; static final String PROPERTY_SHARD_INDEX; static final String PROPERTY_COUNTER_ID; }
@Test public void shouldIncrementCounter() throws IOException { assertEquals(0, shardedCounter.getCounter(COUNTER_ID1)); updateCounterInTransaction(COUNTER_ID1, 1L); QueryResults<Entity> results = datastore.run(EntityQuery.newEntityQueryBuilder() .setKind(KIND_COUNTER_SHARD) .setFilter(CompositeFilter.and(PropertyFilter.eq(PROPERTY_COUNTER_ID, COUNTER_ID1), PropertyFilter.eq(PROPERTY_SHARD_VALUE,1))) .build()); assertEquals(1L, results.next().getLong(PROPERTY_SHARD_VALUE)); assertFalse(results.hasNext()); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(1L, shardedCounter.getCounter(COUNTER_ID1)); }
@Test public void shouldDecrementCounter() throws IOException { assertEquals(0, shardedCounter.getCounter(COUNTER_ID1)); updateShard(COUNTER_ID1,3, 1); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(1L, shardedCounter.getCounter(COUNTER_ID1)); updateCounterInTransaction(COUNTER_ID1, -1L); assertEquals(0L, datastore.get(getKey(COUNTER_ID1, 3)).getLong(PROPERTY_SHARD_VALUE)); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(0L, shardedCounter.getCounter(COUNTER_ID1)); }
@Test public void shouldDecrementShardWithExcessUsage() throws IOException { assertEquals(0, shardedCounter.getCounter(COUNTER_ID1)); updateShard(COUNTER_ID1, 0, 1); updateShard(COUNTER_ID1, 1, 1); updateLimitInStorage(COUNTER_ID1, 1); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(2L, shardedCounter.getCounter(COUNTER_ID1)); updateCounterInTransaction(COUNTER_ID1, -1L); assertEquals(1L, datastore.get(getKey(COUNTER_ID1, 0)) .getLong(PROPERTY_SHARD_VALUE)); assertEquals(0L, datastore.get(getKey(COUNTER_ID1, 1)) .getLong(PROPERTY_SHARD_VALUE)); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(1L, shardedCounter.getCounter(COUNTER_ID1)); }
@Test public void shouldDecrementShardWithALotOfExcessUsage() throws IOException { assertEquals(0, shardedCounter.getCounter(COUNTER_ID1)); updateShard(COUNTER_ID1, 0, 10); updateShard(COUNTER_ID1, 1, 0); updateLimitInStorage(COUNTER_ID1, 1); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(10L, shardedCounter.getCounter(COUNTER_ID1)); updateCounterInTransaction(COUNTER_ID1, -1L); assertEquals(9L, datastore.get(getKey(COUNTER_ID1, 0)) .getLong(PROPERTY_SHARD_VALUE)); assertEquals(0L, datastore.get(getKey(COUNTER_ID1, 1)) .getLong(PROPERTY_SHARD_VALUE)); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(9L, shardedCounter.getCounter(COUNTER_ID1)); }
@Test public void shouldDecrementShardWithNoExcessUsage() throws IOException { assertEquals(0, shardedCounter.getCounter(COUNTER_ID1)); updateShard(COUNTER_ID1, 0, 1); updateShard(COUNTER_ID1, 1, 1); updateLimitInStorage(COUNTER_ID1, 2); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(2L, shardedCounter.getCounter(COUNTER_ID1)); updateCounterInTransaction(COUNTER_ID1, -1L); assertThat(1L, either(is(datastore.get(getKey(COUNTER_ID1, 0)).getLong(PROPERTY_SHARD_VALUE))) .or(is(datastore.get(getKey(COUNTER_ID1, 1)).getLong(PROPERTY_SHARD_VALUE)))); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(1L, shardedCounter.getCounter(COUNTER_ID1)); }
@Test(expected = CounterCapacityException.class) public void shouldNotIncrementIfUsageIsAboveLimitAndShardsHaveExcessUsage() throws IOException { assertEquals(0, shardedCounter.getCounter(COUNTER_ID1)); updateLimitInStorage(COUNTER_ID1, 1); updateShard(COUNTER_ID1, 1, 1); shardedCounter.inMemSnapshot.invalidate(COUNTER_ID1); assertEquals(1L, shardedCounter.getCounter(COUNTER_ID1)); updateCounterInTransaction(COUNTER_ID1, 1L); }