- Type Parameters:
T- type of SimpleDataStructure objects stored in buckets
Refactored from net.i2p.router.networkdb.kademlia.
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static classFor Collections.binarySearch.private static classKBucketSet.DummyBucket<T extends SimpleDataStructure>For Collections.binarySearch.private static classKBucketSet.Range<T extends SimpleDataStructure>Calculator of the bucket range for a key.Field Summary
FieldsModifier and TypeFieldDescriptionThe bucket list is locked by _bucketsLock, however the individual buckets are not locked.private final ReentrantReadWriteLockLocked for reading only when traversing all the buckets.private final I2PAppContextThe app context.private final LogLogger for the routing table.private final KBucketSet.Range<T> Calculator of the bucket range for a key.private final KBucketTrimmer<T> Trimmer deciding which entries to drop.private final TThe local node's key.private final intFactor scaling the bucket ranges.private final intB value from the kademlia paper.private final intEntries one bucket holds.private final intNumber of bits in a key.private final intNumber of buckets in the set.Constructor Summary
ConstructorsConstructorDescriptionKBucketSet(I2PAppContext context, T us, int max, int b) Use the default trim strategy, which removes a random entry.KBucketSet(I2PAppContext context, T us, int max, int b, KBucketTrimmer<T> trimmer) Use the supplied trim strategy.Method Summary
Modifier and TypeMethodDescriptionbooleanAdd a peer to the appropriate bucket.voidclear()Clear all buckets.createBucket(int start, int end) Builds one bucket covering a range.Builds the initial bucket list.(package private) TgenerateRandomKey(KBucket<T> bucket) Generate a random key to go within this bucket Package private for testing only.getAll()Return all entries in all buckets.All entries in all buckets, excluding the specified entries.voidgetAll(SelectionCollector<T> collector) Add all entries in all buckets to the provided collector.The bucket that the xor of the key goes in Caller must hold read lock.intThe B value: the number of subranges the key space is split into at the top level.intThe number of entries a single bucket holds before splitting.intNumber of buckets in this set.getBucketEntries(T key) A copy of the entries of the bucket the given key belongs to.Returned list is a copy of the bucket list, closest first, with the actual buckets (not a copy).getClosest(int max) The keys closest to us.getClosest(int max, Collection<T> toIgnore) The keys closest to us.getClosest(T key, int max) The keys closest to the key.getClosest(T key, int max, Collection<T> toIgnore) The keys closest to the key.Snapshot of every bucket for the debug console, one row per bucket: {index, bucket count, range begin, range end, key count, keys} as strings.getExploreKeys(long age) For every bucket that hasn't been updated in this long, or isn't close to full, generate a random key that would be a member of that bucket.getExploreKeys(long age, int max) For every bucket that hasn't been updated in this long, or isn't close to full, generate a random key that would be a member of that bucket, up to max keys.intThe entry count of the fullest bucket, to spot eviction pressure.(package private) intThe number of bits minus 1 (range number) for the xor of the key.private voidTakes the read lock.getRoot()The key this set is rooted on; bucket ranges are bit positions of the XOR distance from this key.private booleanTry to acquire the write lock.private voidlocked_split(int r) Creates two or more new buckets.private TmakeKey(byte[] data) Make a new SimpleDataStructure from the data.private intpickBucket(int range) The bucket number that contains this range number Caller must hold read lock or write lock.private intpickBucket(T key) The bucket number (NOT the range number) that the xor of the key goes in Caller must hold read lock.private voidReleases the read lock.private voidReleases the write lock.booleanRemove a peer from its bucket.private booleanshouldSplit(KBucket<T> b) No lock required.intsize()The current number of entries.private voidsplit(int r) Grabs the write lock.toString()
Field Details
_log
Logger for the routing table._context
The app context._us
The local node's key._buckets
The bucket list is locked by _bucketsLock, however the individual buckets are not locked. Users may see buckets that have more than the maximum k entries, or may have adds and removes silently fail when they appear to succeed.Closest values are in bucket 0, furthest are in the last bucket.
_rangeCalc
Calculator of the bucket range for a key._trimmer
Trimmer deciding which entries to drop._bucketsLock
Locked for reading only when traversing all the buckets. Locked for writing only when splitting a bucket. Adds/removes/gets from individual buckets are not locked.KEYSIZE_BITS
private final int KEYSIZE_BITSNumber of bits in a key.NUM_BUCKETS
private final int NUM_BUCKETSNumber of buckets in the set.BUCKET_SIZE
private final int BUCKET_SIZEEntries one bucket holds.B_VALUE
private final int B_VALUEB value from the kademlia paper.B_FACTOR
private final int B_FACTORFactor scaling the bucket ranges.
Constructor Details
KBucketSet
Use the default trim strategy, which removes a random entry.- Parameters:
context- the router contextus- the local identity (typically a SHA1Hash or Hash) The class must have a zero-argument constructor.max- the Kademlia value "k", the max per bucket, must be > 4b- the Kademlia value "b", split buckets an extra 2**(b-1) times, 1 <= b <= 8, use 1 for bittorrent, Kademlia paper recommends 5- Throws:
IllegalArgumentException- if max <= 4, or b is outside 1..8RuntimeException- if the us class has no zero-argument constructor
KBucketSet
Use the supplied trim strategy.- Parameters:
context- the router contextus- the local identitymax- the Kademlia value "k", the max per bucket, must be > 4b- the Kademlia value "b", 1 <= b <= 8trimmer- the trim strategy- Throws:
IllegalArgumentException- if max <= 4, or b is outside 1..8RuntimeException- if the us class has no zero-argument constructor
Method Details
getReadLock
private void getReadLock()Takes the read lock.releaseReadLock
private void releaseReadLock()Releases the read lock.getWriteLock
private boolean getWriteLock()Try to acquire the write lock.- Returns:
- true if the lock was acquired
releaseWriteLock
private void releaseWriteLock()Releases the write lock.add
Add a peer to the appropriate bucket. Will split the bucket if necessary.- Parameters:
peer- the router hash to insert- Returns:
- true if the peer is new to the bucket it goes in, or false if it was already in it. Always returns false on an attempt to add ourselves.
shouldSplit
split
private void split(int r) Grabs the write lock. Caller must NOT have the read lock. The bucket should be splittable (range start != range end).- Parameters:
r- the range start of the bucket to be split
locked_split
private void locked_split(int r) Creates two or more new buckets. The old bucket is replaced and discarded.Caller must hold write lock The bucket should be splittable (range start != range end).
- Parameters:
r- the range start of the bucket to be split
size
public int size()The current number of entries.- Returns:
- total number of entries across all buckets
getBucketCount
public int getBucketCount()Number of buckets in this set.- Returns:
- the bucket count
getBucketCapacity
public int getBucketCapacity()The number of entries a single bucket holds before splitting.- Returns:
- the per-bucket capacity
getFullestBucketSize
public int getFullestBucketSize()The entry count of the fullest bucket, to spot eviction pressure.- Returns:
- zero or more
getRoot
The key this set is rooted on; bucket ranges are bit positions of the XOR distance from this key.- Returns:
- non-null
getBucketBValue
public int getBucketBValue()The B value: the number of subranges the key space is split into at the top level.- Returns:
- one or more
getDebugBucketRows
Snapshot of every bucket for the debug console, one row per bucket: {index, bucket count, range begin, range end, key count, keys} as strings. Ranges are bit positions of the XOR distance from the root key. Keys are the entry keys'toString()values, comma-separated, empty if the bucket is empty.- Returns:
- non-null, empty if no buckets exist
remove
Remove a peer from its bucket.- Parameters:
entry- the peer to remove- Returns:
- true if removed
clear
public void clear()Clear all buckets.getAll
getAll
getAll
Add all entries in all buckets to the provided collector.The read lock is held only long enough to copy the bucket references out of the list, then released before the collector runs. Collectors do netDb, banlist and profile lookups, so holding the lock across the pass blocked every
add(T)and bucket split in the router for the duration of a full traversal of the whole keyspace — and this is called once per DSRM reply and once per outbound explore query. A concurrent split may leave a snapshot entry already emptied and a concurrent add may not appear; both were already possible outcomes of the unsynchronized per-bucket access documented on_buckets.- Parameters:
collector- the collector to add entries to
getClosest
getClosest
The keys closest to us. Returned list will never contain us.- Parameters:
max- maximum number of keys to returntoIgnore- entries to exclude- Returns:
- non-null, closest first
getClosest
getClosest
The keys closest to the key. Returned list will never contain us.- Parameters:
key- the target keymax- maximum number of keys to returntoIgnore- entries to exclude- Returns:
- non-null, closest first
pickBucket
The bucket number (NOT the range number) that the xor of the key goes in Caller must hold read lock.- Parameters:
key- the key whose bucket is wanted- Returns:
- 0 to max-1 or -1 for us
getBuckets
Returned list is a copy of the bucket list, closest first, with the actual buckets (not a copy).Primarily for testing. You shouldn't ever need to get all the buckets. Use
getClosest(int)or getAll() instead to get the keys.- Returns:
- non-null
getBucket
pickBucket
private int pickBucket(int range) The bucket number that contains this range number Caller must hold read lock or write lock.- Parameters:
range- the range number whose bucket is wanted- Returns:
- 0 to max-1 or -1 for us
createBuckets
createBucket
getRange
The number of bits minus 1 (range number) for the xor of the key. Package private for testing only. Others shouldn't need this.- Parameters:
key- the router hash whose bucket range is wanted- Returns:
- 0 to max-1 or -1 for us
getExploreKeys
For every bucket that hasn't been updated in this long, or isn't close to full, generate a random key that would be a member of that bucket. The returned keys may be searched for to "refresh" the buckets.- Parameters:
age- the maximum age in milliseconds- Returns:
- non-null, closest first
getExploreKeys
For every bucket that hasn't been updated in this long, or isn't close to full, generate a random key that would be a member of that bucket, up to max keys. The closest bucket's key is always first. The remaining buckets are visited in random order so a capped pass does not always starve the far end of the keyspace.- Parameters:
age- the maximum age in millisecondsmax- the maximum number of keys to generate- Returns:
- non-null, closest bucket first
getBucketEntries
generateRandomKey
makeKey
Make a new SimpleDataStructure from the data.- Parameters:
data- size <= SDS length, else throws IAE, can be 1 bigger if top byte is zero- Returns:
- a new data structure holding the given key bytes
toString