The distributed hash table (DHT) is a lightweight distributed key-value store, that can store record across nodes.
The DHT Service
from hyveos_sdk import Connection
async with Connection () as connection:
dht = connection. get_dht_service ()
# continue with usage of dht
use hyveos_sdk :: Connection;
let connection = Connection :: new () . await . unwrap ();
let mut dht = connection . dht () ;
// continue with usage of dht
import { Client } from ' hyveos-sdk '
import { Connection } from " hyveos-web "
const transport = new Connection ( ' http://localhost:8080 ' )
const client = new Client (transport)
const dhtService = client . dht
// continue with usage of dhtService
Example 1. Obtain the DHT service handler. Note the async
environment.
Put & Retrieve a record
The DHT is grouped into topics. You store a record into the DHT under a specific topic
.
from hyveos_sdk import Connection
async with Connection () as connection:
dht = connection. get_dht_service ()
discovery = connection. get_discovery_service ()
id = discovery. get_own_id ()
await dht. put_record ( " charging_pause " , id , " 1h " )
use hyveos_sdk :: Connection;
let connection = Connection :: new () . await . unwrap ();
let mut dht = connection . dht ();
let mut discovery = connection . discovery ();
let id = discovery . get_own_id () . await . unwrap ();
dht . put_record ( " charging_pause " , id , " 1h " ) . await . unwrap ();
import { Client } from ' hyveos-sdk '
import { Connection } from " hyveos-web "
const transport = new Connection ( ' http://localhost:8080 ' )
const client = new Client (transport)
const dhtService = client . dht
const discoveryService = client . discovery
const id = await discoveryService . getOwnId ()
await dhtService . putRecord ( ' charging_pause ' , id , ' 1h ' )
main () . catch (console . error )
Example 2. Put a record in the DHT. The runtime uses the
discovery service to find its own peer id as a string,
and then use it as a key. The value of "1h"
is the duration of charging.
Rust: Data Encoding Formats
The Rust SDK allows the user to control in which format the data is encoded internally during transport.
The choice depends on your use case: cbor is more encoding efficient, json is human-readable.
dht . put_record_json ( " topic " , " key " , & value ) . await . unwrap ();
dht . put_record_cbor ( " topic " , " key " , & value ) . await . unwrap ();
Next, retreiving a record works with get_record(topic, key)
.
from hyveos_sdk import Connection
async with Connection () as connection:
dht = connection. get_dht_service ()
record = await dht. get_record ( " charging_pause " , " 012345678ABCDEF " )
if record is not None and record.data is not None :
value = record.data. decode ( " utf-8 " )
print ( f "012345678ABCDEF is charging for: {value} " )
except UnicodeDecodeError :
print ( " Record data is not valid UTF-8 " )
print ( " Record not found " )
use hyveos_sdk :: Connection;
let connection = Connection :: new () . await . unwrap ();
let mut dht = connection . dht ();
let value = dht . get_record ( " charging_pause " , " 012345678ABCDEF " ) . await . unwrap ();
// convert the returned raw Vec<u8> into a UTF-8 string
if let Some( value ) = value . and_then ( | value | String :: from_utf8 ( value ) . ok ()) {
println! ( " 012345678ABCDEF is charging for: {value} " );
println! ( " Record not found " );
import { Client } from ' hyveos-sdk ' ;
import { Connection } from " hyveos-web " ;
const transport = new Connection ( ' http://localhost:8080 ' );
const client = new Client (transport);
const dhtService = client . dht ;
const record : Uint8Array | null = await dhtService . getRecord ( " charging_pause " , " 012345678ABCDEF " ) ;
const decoded = new TextDecoder ( " utf-8 " ) . decode (record);
console . log ( ` 012345678ABCDEF is charging for: ${ decoded } ` );
console . error ( " Record data is not valid UTF-8 " , error);
console . log ( " Record not found " );
main () . catch (console . error );
Example 3. Retreive records from the DHT. The runtime looks for how long
the peer with exemplary peer id "0123456789ABCDEF"
will be charging, if even.
Correct peer id
In the example for get_record(topic, key)
above, the peer id "0123456789ABCDEF"
of the peer is an example string. If you use get_record(topic, key)
, with key
being a peer id, you
should first retreive the actual peer id. For this, use the
Discovery Service , or reuse the peer id if you have already
stored it within the current runtime.
Provide
Apart from storing and retrieving records, the DHT also exposes the interface to mark
nodes as “providers” of a key.
This pattern can be useful in assigning and advertising nodes to specific semantic roles
Again, the provide(topic, key)
interface marks the node as a provider
for a key in the DHT, under a specified topic.
from hyveos_sdk import Connection
async with Connection () as connection:
dht = connection. get_dht_service ()
await dht. provide ( ' capabilities ' , ' camera ' )
use hyveos_sdk :: Connection;
let connection = Connection :: new () . await . unwrap ();
let mut dht_service = connection . dht ();
dht_service . provide ( " capabilities " , " camera " ) . await . unwrap ();
import { Client } from ' hyveos-sdk ' ;
import { Connection } from " hyveos-web " ;
const transport = new Connection ( ' http://localhost:8080 ' );
const client = new Client (transport);
const dhtService = client . dht ;
const key = new TextEncoder () . encode ( " camera " );
await dhtService . provide ( " capabilities " , key) ;
// await transport.close();
main () . catch (console . error );
Example 4. The node stores its role as a potential camera provider into the DHT.
The particular node that runs this code takes on the role
of a robot or drone with visual capabilities.
If another node wants to retrieve which node provides the camera
capability, it can search for those providers in the DHT.
get_providers(topic, key)
returns an asynchronous iterator
representing the providers of a record under a specific topic.
from hyveos_sdk import Connection
async with Connection () as connection:
dht = connection. get_dht_service ()
async with dht. get_providers ( " capabilities " , " camera " ) as providers:
async for provider in providers:
print ( f 'Found a node with a camera: {provider} ' )
use hyveos_sdk :: Connection;
use futures :: TryStreamExt as _;
let connection = Connection :: new () . await . unwrap ();
let dht_service = connection . dht ();
let mut providers = dht_service . get_providers ( " capabilities " , " camera " ) . await . unwrap ();
while let Some( provider ) = providers . try_next () . await . unwrap () {
println! ( " Found a node with a camera: {provider} " );
import { Client } from ' hyveos-sdk ' ;
import { Connection } from " hyveos-web " ;
const transport = new Connection ( ' http://localhost:8080 ' );
const client = new Client (transport);
const dhtService = client . dht ;
const providers = await dhtService . getProviders ( ' capabilities ' , ' camera ' )
for await ( const provider of providers) {
console . log ( ' Found a node with a camera: ${provider} ' );
// await transport.close()
main () . catch (console . error );
Example 5. The node looks for nodes with a camera and prints them out.
The DHT optimizes key-value storage and retrieval across large networks
such as the ones you want to build. Query, put, get, and provide are standard operations
supported. The DHT can be a leightweight data store or role discovery mechanism.
The latter is needed when you want to give different nodes different roles and hence
functionalities in the network, per scenario.
The hyveOS DHT is based on the Kademlia DHT .