This is a more detailed documentation that includes private methods for reference.
Node ids in Discover are represented as base64 encoded Strings. This is because the default generated node ids (SHA-1 hashes) could be unsafe to print. base64
encoding was picked over hex
encoding because it takes up less space when printed or serialized in ASCII over the wire.
- new Discover(options)
- discover.add(remoteContact)
- discover.executeQuery(query, callback)
- discover.find(nodeId, callback, [announce])
- discover.findViaSeeds(nodeId, callback, [announce])
- discover.getClosestContacts(nodeId, closestKBuckets)
- discover.getClosestKBuckets(nodeId)
- discover.queryCompletionCheck(query, callback)
- discover.register(contact)
- discover.timerEndInMilliseconds(type, key)
- discover.timerStart(type, key)
- discover.trace(message)
- discover.unreachable(contact)
- discover.unregister(contact)
- Event 'stats.timers.find.ms'
- Event 'stats.timers.find.request.ms'
- Event 'stats.timers.find.round.ms'
options
:CONCURRENCY_CONSTANT
: Integer (Default: 3) Number of concurrent FIND-NODE requests to the network perfind
request.arbiter
: Function (Default: vector clock arbiter)function (incumbent, candidate) {}
An optional arbiter function.arbiter
function is used in three places. First, it is used as the k-bucketarbiter
function. Second, it is used to determine whether a new remote contact should be inserted into the LRU cache (ifarbiter
returns something!==
to the cached contact the remote contact will be inserted). Third, it is used to determine if unregistering a contact will succeed (ifarbiter
returns contact===
to the stored contact, unregister will fail).arbiterDefaults
: Function (Default: vector clock arbiter defaults)function (contact) {}
An optional arbiter defaults function that setscontact
arbiter defaults when acontact
is first registered. Remote contacts that are added viaadd
are assumed to have appropriate arbiter properties already set.eventTrace
: Boolean (Default: false) If set totrue
, Discover will emit~trace
events for debugging purposes.inlineTrace
: Boolean (Default: false) If set totrue
, Discover will log to console~trace
messages for debugging purposes.maxCacheSize
: Number (Default: 1000) Maximum number ofcontacts
to keep in non-kBucket cache (see #6)noCache
: Boolean (Default: false) Iftrue
, non-kBucket cache is not used.seeds
: Array (Default: []) An array of seedcontact
Objects that thetransport
understands.transport
: Object (Default:discover-tcp-transport
) An optional initialized and ready to use transport module for sending communications that conforms to the Transport Protocol. Iftransport
is not provided, a new instance ofdiscover-tcp-transport
will be created and used with default settings.
Creates a new Discover instance.
The seeds
are necessary if joining an existing Discover cluster. Discover will use these seeds
to announce itself to other nodes in the cluster. If seeds
are not provided, then it is assumed that this is a seed node, and other nodes will include this node's address in their seeds
option. It all has to start somewhere.
remoteContact
: Object Contact object to add that is not managed by this Discover node.id
: String (base64) The contact id, base64 encoded.data
: Any Data to be included with the contact, it is guaranteed to be returned for anyone querying for thiscontact
byid
.
- Return: Object Contact that was added.
Adds the remoteContact
. This is different from discover.register(contact)
in that adding a remoteContact
means that the remoteContact
is not managed by this Discover node.
The use-case motivating existence of this method is being able to hint where to send a response in a request-response type of asynchronous messaging between nodes that are part of the same Discover DHT. More precisely:
- Server A creates a contact Alpha and registers it with Discover.
- Server A queries Discover to find contact Beta (already existing).
- Discover responds that contact Beta is on Server B.
- Server A sends a message to contact Beta (on Server B) expecting a response to contact Alpha.
- Server B wants to respond to Alpha "quickly". At this point, the contact Alpha information has not propagated through the DHT, so Server B will have to wait for it's Discover instance to query the DHT and make multiple trips looking for contact Alpha.
In order to "speed up" step 5 above, we'd like to be able to hint information that is known, but maybe has not propagated yet. This means, that as part of step 4 above, we could also send a "hint" containing information on contact Alpha. This way, when Server B receives the message with a "hint", it can use discover.add(remoteContact)
to populate it's local Discover cache without additional network traffic.
CAUTION: reserved for internal use
query
: Object Object containing query state for this request.nodeId
: String (base64) Base64 encoded node id to find.nodes
: Arraycontact
s to query fornodeId
arranged from closest to furthestnode
:id
: String (base64) Base64 encoded contact id.
nodesMap
: Object A map to the samecontact
s already present innodes
for O(1) access.
callback
: Function The callback to call with result.
Used internally by discover.find()
to maintain query state when looking for a specific node on the network. It will launch up to CONCURRENCY_CONSTANT
findNode
requests via the transport
and keep going until the node is found or there are no longer any nodes closer to it (not found). Additionally, this function reports unreachable and reached nodes to the appropriate KBucket
which maintains the local node's awareness of the state of the network.
nodeId
: String (base64) The node id to find, base64 encoded.callback
: Function The callback to call with the result of searching fornodeId
.announce
: Object (Default: undefined) CAUTION: reserved for internal use Contact object, if specified, it indicates an announcement to the network so we ask the network instead of satisfying request locally and the sender is theannounce
contact object.
The callback
is called with the result of searching for nodeId
. The result will be a contact
containing contact.id
, contact.data
, contact.transport
of the node. If an error occurs, only error
will be provided.
discover.find('bm9kZS5pZC50aGF0LmltLmxvb2tpbmcuZm9y', function (error, contact) {
if (error) return console.error(error);
console.dir(contact);
});
CAUTION: reserved for internal use
nodeId
: String (base64) Base64 encoded node id to find.callback
: Function The callback to call with the result of searching fornodeId
.announce
: Object (Default: undefined) Contact object, if specified, it indicates an announcement and the sender is theannounce
contact object.
Uses seeds
instead of closest contacts (because those don't exist) to find the node with nodeId
. The callback
is called with the result of searching for nodeId
. The result will be a contact
containing contact.id
and contact.data
of the node. If an error occurs, only error
will be provided.
CAUTION: reserved for internal use
nodeId
: String (base64) Base64 encoded node id to find closest contacts to.closestKBuckets
: Array Sorted array ofKBucket
s from closest to furthest fromnodeId
.- Return: Array List of closest contacts.
Retrieves maximum of three closest contacts from the closest KBucket
.
CAUTION: reserved for internal use
nodeId
: String (base64) Base64 encoded node id to find closest contacts to.- Return: Array List of closest
KBucket
s.
Retrieves a sorted list of all KBucket
s from closest to furthest.
CAUTION: reserved for internal use
query
: Object Object containing query state for this request.nodeId
: String (base64) Base64 encoded node id to find.nodes
: Arraycontact
s to query fornodeId
arranged from closest to furthest.node
:id
: String (base64) Base64 encoded contact id.
nodesMap
: Object A map to the samecontact
s already present innodes
for O(1) access.
callback
: Function The callback to call with result.
Checks if query completion criteria are met. If there are any new nodes to add to the query, organizes them accordingly and sets the query state to incorporate new node information. Stops and returns failure or success otherwise.
contact
: Object Contact object to register.id
: String (base64) (Default:crypto.randomBytes(20).toString('base64'
) The contact id, base 64 encoded; will be created if not present.data
: Any Data to be included with the contact, it is guaranteed to be returned for anyone querying for thiscontact
byid
.
- Return: Object Contact that was registered with
id
and generated arbiter defaults if necessary.
Registers a new node on the network with contact.id
. Returns a contact
:
discover.register({
id: 'Zm9v', // base64 encoded String representing nodeId
data: 'foo'
});
NOTE: Current implementation creates a new k-bucket for every registered node id. It is important to remember that a k-bucket could store up to k*lg(n) contacts, where lg is log base 2, n is the number of registered node ids on the network, and k is the size of each k-bucket (by default 20). For 1 billion registered nodes on the network, each k-bucket could store around 20 * lg (1,000,000,000) = ~ 598 contacts. This isn't bad, until you have 1 million local entities for a total of 598,000,000 contacts plus k-bucket overhead, which starts to put real pressure on Node.js/V8 memory limit.
CAUTION: reserved for internal use
type
: String Timer type.key
: String Timer key.- Return: Number Milliseconds since the first time in the timer.
Calculates a millisecond interval between now and the first timer that was stored at type
and key
.
CAUTION: reserved for internal use
type
: String Timer type.key
: String Timer key.
Starts a new timer indexed by type
and key
. Multiple starts will result in start times being stored in an array for use by discover.timerEndInMilliseconds()
later.
message
: String Message to trace.
Logs or emits a ~trace
for debugging purposes.
contact
: Object Contact object to report unreachableid
: String (base64) The previously registered contact id, base 64 encoded.
Reports the contact
as unreachable in case Discover is storing outdated information. This can happen because Discover is a local cache of the global state of the network. If a change occurs, it may not immediately propagate to the local Discover instance.
If it is desired to get the latest contact
that is unreachable, the following code shows an example:
discover.find("Zm9v", function (error, contact) {
// got contact
// attempt to connect ... and fail :(
discover.unreachable(contact);
discover.find(contact.id, function (error, contact) {
// new contact will be found in the network
// or an error if it cannot be found
});
});
contact
: Object Contact object to registerid
: String (base64) The previously registered contact id, base 64 encoded.
Unregisters previously registered contact
(if arbiter
returns contact
and not other stored value) from the network.
function (latency) {}
latency
: Number Latency ofdiscover.find()
in milliseconds.
function (latency) {}
latency
: Number Latency of a single request to another Discover noder as part of a round ofdiscover.find()
DHT lookups.
function (latency) {}
latency
: Number Latency of a single round ofdiscover.find()
DHT lookups in milliseconds.