/** * Get the next key for the current time in UTC. * * @return a long that is determined by the current time in UTC and a unique counter value for the current time. */ public long nextKey() { // Note that per Oracle the currentTimeMillis is the current number of seconds past the epoch // in UTC (not in local time). Therefore, processes with exactly synchronized clocks will // always get the same value regardless of their timezone ... final long timestamp = System.currentTimeMillis(); final int increment = counterFor(timestamp); if (increment <= maximumCounterValue) { return (timestamp << counterBits) + increment; } // The counter is surprisingly too high, so try again (repeatedly) until we get to the next millisecond ... return this.nextKey(); }
/** * Create a new generator that uses 16 bits for the counter portion of the keys. * * @return the generator instance; never null */ public static TimeBasedKeys create() { return new TimeBasedKeys(DEFAULT_BITS_IN_COUNTER); }
@Test public void shouldCorrectlyCalculateFirstAndLastCounterFor6Bit() { TimeBasedKeys counter = TimeBasedKeys.create(6); assertEquals(0L, counter.getCounterStartingAt(0L)); assertEquals(63L, counter.getCounterEndingAt(0L)); }
@Override public void run() { for (int i = 0; i != 100000; ++i) { counter.nextKey(); } } }).run();
searchBound = TIME_BASED_KEYS.getCounterStartingAt(changeSetMillisUTC - searchTimeDelta);
protected void removeRecordsOlderThan( long millisInUtc ) { if (millisInUtc <= 0 || stopped) { return; } long searchBound = TIME_BASED_KEYS.getCounterEndingAt(millisInUtc); LOGGER.debug("Removing records older than " + searchBound); NavigableMap<Long, JournalRecord> toRemove = this.records.headMap(searchBound); toRemove.clear(); journalDB.commit(); journalDB.compact(); }
@Test public void shouldCorrectlyCalculateFirstAndLastCounterFor8Bit() { TimeBasedKeys counter = TimeBasedKeys.create(8); assertEquals(0L, counter.getCounterStartingAt(0L)); assertEquals(255L, counter.getCounterEndingAt(0L)); }
searchBound = TIME_BASED_KEYS.getCounterStartingAt(changeSetMillisUTC - searchTimeDelta);
protected void removeRecordsOlderThan( long millisInUtc ) { if (millisInUtc <= 0 || stopped) { return; } long searchBound = TIME_BASED_KEYS.getCounterEndingAt(millisInUtc); LOGGER.debug("Removing records older than " + searchBound); NavigableMap<Long, JournalRecord> toRemove = this.records.headMap(searchBound); toRemove.clear(); journalDB.commit(); journalDB.compact(); }
@Test public void shouldCorrectlyCalculateFirstAndLastCounterFor1BitCounter() { TimeBasedKeys counter = TimeBasedKeys.create(1); assertEquals(0L, counter.getCounterStartingAt(0L)); assertEquals(1L, counter.getCounterEndingAt(0L)); }
/** * Get the next key for the current time in UTC. * * @return a long that is determined by the current time in UTC and a unique counter value for the current time. */ public long nextKey() { // Note that per Oracle the currentTimeMillis is the current number of seconds past the epoch // in UTC (not in local time). Therefore, processes with exactly synchronized clocks will // always get the same value regardless of their timezone ... final long timestamp = System.currentTimeMillis(); final int increment = counterFor(timestamp); if (increment <= maximumCounterValue) { return (timestamp << counterBits) + increment; } // The counter is surprisingly too high, so try again (repeatedly) until we get to the next millisecond ... return this.nextKey(); }
@Test public void shouldObtain10MillionCountersFromThreadSafeUsingMultipleThreads() { print(counter.nextKey()); for (int j = 0; j != 100; ++j) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i != 100000; ++i) { counter.nextKey(); } } }).run(); } print(counter.nextKey()); }
@Override public Iterator<NodeKey> changedNodesSince( final long timestamp ) { long searchBound = TIME_BASED_KEYS.getCounterStartingAt(timestamp - searchTimeDelta); Collection<JournalRecord> journalRecords = records.tailMap(searchBound, true).values(); if (journalRecords.isEmpty()) {
/** * Create a new generator that uses 16 bits for the counter portion of the keys. * * @return the generator instance; never null */ public static TimeBasedKeys create() { return new TimeBasedKeys(DEFAULT_BITS_IN_COUNTER); }
@Test public void shouldCorrectlyCalculateFirstAndLastCounterFor5Bit() { TimeBasedKeys counter = TimeBasedKeys.create(5); assertEquals(0L, counter.getCounterStartingAt(0L)); assertEquals(31L, counter.getCounterEndingAt(0L)); }
/** * Get the next key for the current time in UTC. * * @return a long that is determined by the current time in UTC and a unique counter value for the current time. */ public long nextKey() { // Note that per Oracle the currentTimeMillis is the current number of seconds past the epoch // in UTC (not in local time). Therefore, processes with exactly synchronized clocks will // always get the same value regardless of their timezone ... final long timestamp = System.currentTimeMillis(); final int increment = counterFor(timestamp); if (increment <= maximumCounterValue) { return (timestamp << counterBits) + increment; } // The counter is surprisingly too high, so try again (repeatedly) until we get to the next millisecond ... return this.nextKey(); }
@Override public synchronized void addRecords( JournalRecord... records ) { if (stopped) { return; } LOGGER.debug("Adding {0} records", records.length); for (JournalRecord record : records) { if (record.getTimeBasedKey() < 0) { //generate a unique timestamp only if there isn't one. In some scenarios (i.e. running in a cluster) we //always want to keep the original TS because otherwise it would be impossible to have a correct order //and therefore search long createTimeMillisUTC = TIME_BASED_KEYS.nextKey(); record.withTimeBasedKey(createTimeMillisUTC); } this.records.put(record.getTimeBasedKey(), record); } this.journalDB.commit(); }
@Override public Iterator<NodeKey> changedNodesSince( final long timestamp ) { long searchBound = TIME_BASED_KEYS.getCounterStartingAt(timestamp - searchTimeDelta); Collection<JournalRecord> journalRecords = records.tailMap(searchBound, true).values(); if (journalRecords.isEmpty()) {
/** * Create a new generator that uses 16 bits for the counter portion of the keys. * * @return the generator instance; never null */ public static TimeBasedKeys create() { return new TimeBasedKeys(DEFAULT_BITS_IN_COUNTER); }