* Implementation note: this method accepts a {@code null} entryID in order to eliminate null checks in client code.
* In particular, client code has to deal with the special case where a target entry does not have a parent because
* the target entry is a base entry within the backend.
*
* @param txn storage transaction
* @param entryID The entryID identifying to the counter, which may be
* {@code null} in which case calling this method has no effect.
* @param delta The value to add. Can be negative to decrease counter value.
*/
void updateCount(final WriteableTransaction txn, final EntryID entryID, final long delta) {
if (entryID != null)
{
addToCounter(txn, entryID, delta);
}
}
/**
* Updates the total count which should be the sum of all counters.
* @param txn storage transaction
* @param delta The value to add. Can be negative to decrease counter value.
*/
void updateTotalCount(final WriteableTransaction txn, final long delta) {
addToCounter(txn, TOTAL_COUNT_ENTRY_ID, delta);
}
private void addToCounter(WriteableTransaction txn, EntryID entryID, final long delta)
{
final ByteSequence shardedKey = getShardedKey(entryID);
txn.update(getName(), shardedKey, new UpdateFunction()
{
@Override
public ByteSequence computeNewValue(ByteSequence oldValue)
{
final long currentValue = oldValue == null ? 0 : oldValue.asReader().getLong();
final long newValue = currentValue + delta;
return newValue == 0 ? null : toValue(newValue);
}
});
}
void importPut(Importer importer, EntryID entryID, long total)
{
Reject.ifTrue(entryID.longValue() >= TOTAL_COUNT_ENTRY_ID.longValue(), "EntryID overflow.");
importPut0(importer, entryID, total);
}
void importPutTotalCount(Importer importer, long total)
{
importPut0(importer, TOTAL_COUNT_ENTRY_ID, total);
}
private void importPut0(Importer importer, EntryID entryID, final long delta)
{
Reject.ifNull(importer, "importer must not be null");
if (delta != 0)
{
final ByteSequence shardedKey = getShardedKey(entryID);
importer.put(getName(), shardedKey, toValue(delta));
}
}
private ByteSequence getShardedKey(EntryID entryID)
{
final long bucket = Thread.currentThread().getId() & (SHARD_COUNT - 1);
return getKeyFromEntryIDAndBucket(entryID, bucket);
}
ByteString toValue(final long count)
{
Reject.ifFalse(count != 0, "count must be != 0");
return ByteString.valueOf(count);
}
long fromValue(ByteString value)
{
Reject.ifNull(value, "value must not be null");
return value.toLong();
}
@Override
public String keyToString(ByteString key)
{
ByteSequenceReader keyReader = key.asReader();
long keyID = keyReader.getCompactUnsigned();
long shardBucket = keyReader.getCompactUnsigned();
return (keyID == TOTAL_COUNT_ENTRY_ID.longValue() ? "Total Children Count" : keyID) + "#" + shardBucket;
}
@Override
public String valueToString(ByteString value)
{
return String.valueOf(fromValue(value));
}
@Override
public ByteString generateKey(String data)
{
EntryID entryID = new EntryID(Long.parseLong(data));
return entryID.toByteString();
}
/**
* Get the counter value for the specified key
* @param txn storage transaction
* @param entryID The entryID identifying to the counter
* @return Value of the counter. 0 if no counter is associated yet.
*/
long getCount(ReadableTransaction txn, EntryID entryID)
{
long counterValue = 0;
try(final Cursor