This commit is contained in:
dmc
2024-02-01 00:05:53 +01:00
parent 95145e31cd
commit a3a498a07e
3 changed files with 87 additions and 47 deletions

View File

@@ -23,7 +23,7 @@ pear init -y -t terminal
npm install hypercore hyperswarm b4a npm install hypercore hyperswarm b4a
``` ```
Alter the generated `writer-app/index.js` to the following: Alter the generated `writer-app/index.js` file to the following:
```javascript ```javascript
import Hyperswarm from 'hyperswarm' import Hyperswarm from 'hyperswarm'
@@ -60,7 +60,7 @@ pear init -y -t terminal
npm install hypercore hyperswarm npm install hypercore hyperswarm
``` ```
Alter the generated `reader-app/index.js` to the following: Alter the generated `reader-app/index.js` file to the following:
```javascript ```javascript

View File

@@ -3,28 +3,37 @@
[Hyperbee](../building-blocks/hyperbee.md) is an append-only B-tree based on Hypercore. It provides a key/value-store API with methods to insert and get key/value pairs, perform atomic batch insertions, and create sorted iterators. [Hyperbee](../building-blocks/hyperbee.md) is an append-only B-tree based on Hypercore. It provides a key/value-store API with methods to insert and get key/value pairs, perform atomic batch insertions, and create sorted iterators.
The example consists of three files: `writer.js` , `bee-reader.js` and `core-reader.js`. This How-to consists of three applications: `bee-writer-app` , `bee-reader-app` and `core-reader-app`.
`writer.js` stores 100k entries from a given dictionary file into a Hyperbee instance. The Corestore instance used to create the Hyperbee instance is replicated using Hyperswarm. This enables other peers to replicate their Corestore instance and download the dictionary data into their local Hyperbee instances. The `bee-writer-app` stores 100k entries from a given dictionary file into a Hyperbee instance. The Corestore instance used to create the Hyperbee instance is replicated using Hyperswarm. This enables other peers to replicate their Corestore instance and sparsely (on-demand) download the dictionary data into their local Hyperbee instances.
> Download the `dict.json.gz` compressed file from the [GitHub repository](https://github.com/holepunchto/examples/blob/main/quick-start/hyperbee/dict.json.gz) to the folder where the `writer.js`is present. The compressed file contains 100K dictionary words. Start the `bee-writer-app` project with the following commands:
```
mkdir bee-writer-app
cd bee-writer-app
pear init -y -t terminal
npm install corestore hyperswarm hyperbee b4a
```
[Click here to save `dict.json`](./assets/dict.json).
Save it into `bee-writer-app` directory. The `dict.json` file contains 100K dictionary words.
Alter the generated `bee-writer-app/index.js` file to the following
```javascript ```javascript
//writer.js import fsp from 'fs/promises'
import fs from 'fs'
import zlib from 'zlib'
import Hyperswarm from 'hyperswarm' import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore' import Corestore from 'corestore'
import Hyperbee from 'hyperbee' import Hyperbee from 'hyperbee'
import goodbye from 'graceful-goodbye'
import b4a from 'b4a' import b4a from 'b4a'
// create a corestore instance with the given location // create a corestore instance with the given location
const store = new Corestore('./writer-storage') const store = new Corestore(Pear.config.storage)
const swarm = new Hyperswarm() const swarm = new Hyperswarm()
goodbye(() => swarm.destroy()) Pear.teardown(() => swarm.destroy())
// replication of corestore instance // replication of corestore instance
swarm.on('connection', conn => store.replicate(conn)) swarm.on('connection', conn => store.replicate(conn))
@@ -53,7 +62,7 @@ discovery.flushed().then(() => {
// The first block will always be the Hyperbee header block // The first block will always be the Hyperbee header block
if (core.length <= 1) { if (core.length <= 1) {
console.log('importing dictionary...') console.log('importing dictionary...')
const dict = await loadDictionary() const dict = JSON.parse(await fsp.readFile('./dict.json'))
const batch = bee.batch() const batch = bee.batch()
for (const { key, value } of dict) { for (const { key, value } of dict) {
await batch.put(key, value) await batch.put(key, value)
@@ -63,43 +72,51 @@ if (core.length <= 1) {
// Otherwise just seed the previously-imported dictionary // Otherwise just seed the previously-imported dictionary
console.log('seeding dictionary...') console.log('seeding dictionary...')
} }
async function loadDictionary() {
const compressed = await fs.promises.readFile('./dict.json.gz')
return new Promise((resolve, reject) => {
// unzip the compressed file and return the content
zlib.unzip(compressed, (err, dict) => {
if (err) return reject(err)
return resolve(JSON.parse(b4a.toString(dict)))
})
})
}
``` ```
Open the app with `pear dev`:
`bee-reader.js` creates a Corestore instance and replicates it using the Hyperswarm instance to the same topic as `writer.js`. On every word entered in the command line, it will download the respective data to the local Hyperbee instance. ```
cd bee-writer-app
pear dev
```
Start the `bee-reader-app` project with the following commands:
```
mkdir bee-reader-app
cd bee-reader-app
pear init -y -t terminal
npm install corestore hyperswarm hyperbee b4a
```
The `bee-reader-app` creates a `Corestore` instance and replicates it using the `Hyperswarm` instance to the same topic as `bee-writer-app`. On every word entered in the command line, it will download the respective data to the local `Hyperbee` instance.
Alter the generated `bee-reader-app/index.js` file to the following
Try looking at disk space the `reader-storage` directory is using after each query. notice that it's significantly smaller than `writer-storage`! This is because Hyperbee only downloads the Hypercore blocks it needs to satisfy each query, a feature we call **sparse downloading.**
```javascript ```javascript
bee-reader.js
import Hyperswarm from 'hyperswarm' import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore' import Corestore from 'corestore'
import Hyperbee from 'hyperbee' import Hyperbee from 'hyperbee'
import goodbye from 'graceful-goodbye'
import b4a from 'b4a' import b4a from 'b4a'
const key = Pear.config.args[0]
if (!key) throw new Error('provide a key')
// creation of a corestore instance // creation of a corestore instance
const store = new Corestore('./reader-storage') const store = new Corestore(Pear.config.storage)
const swarm = new Hyperswarm() const swarm = new Hyperswarm()
goodbye(() => swarm.destroy()) Pear.teardown(() => swarm.destroy())
// replication of the corestore instance on connection with other peers // replication of the corestore instance on connection with other peers
swarm.on('connection', conn => store.replicate(conn)) swarm.on('connection', (conn) => store.replicate(conn))
// create or get the hypercore using the public key supplied as command-line argument // create or get the hypercore using the public key supplied as command-line argument
const core = store.get({ key: b4a.from(process.argv[2], 'hex') }) const core = store.get({ key: b4a.from(key, 'hex') })
// create a hyperbee instance using the hypercore instance // create a hyperbee instance using the hypercore instance
const bee = new Hyperbee(core, { const bee = new Hyperbee(core, {
@@ -116,10 +133,7 @@ console.log('core key here is:', core.key.toString('hex'))
// Attempt to connect to peers // Attempt to connect to peers
swarm.join(core.discoveryKey) swarm.join(core.discoveryKey)
// Do a single Hyperbee.get for every line of stdin data Pear.stio.in.on('data', data => {
// Each `get` will only download the blocks necessary to satisfy the query
process.stdin.setEncoding('utf-8')
process.stdin.on('data', data => {
const word = data.trim() const word = data.trim()
if (!word.length) return if (!word.length) return
bee.get(word).then(node => { bee.get(word).then(node => {
@@ -129,17 +143,34 @@ process.stdin.on('data', data => {
}) })
``` ```
Importantly, a Hyperbee is **just** a Hypercore, where the tree nodes are stored as Hypercore blocks. Now examine the Hyperbee as if it were just a Hypercore and log out a few blocks. Open the `bee-reader-app` and pass it the core key:
`core-reader.js` will continually download and log the last block of the Hypercore containing the Hyperbee data. Note that these blocks are encoded using Hyperbee's Node encoding, which we can easily import and use. ```
cd bee-reader-app
pear dev -- <SUPPLY KEY HERE>
```
Query the database by entering a key to lookup into the `bee-reader-app` terminal and hitting return.
Each application has dedicated storage at `Pear.config.storage`. Try logging out `Pear.config.storage` for the `bee-reader-app` and then look at the disk space for that storage path after each query. notice that it's significantly smaller than `writer-storage`! This is because Hyperbee only downloads the Hypercore blocks it needs to satisfy each query, a feature we call **sparse downloading.**
Importantly, a Hyperbee is **just** a Hypercore, where the tree nodes are stored as Hypercore blocks.
Finally create a `core-reader-app` project:
```
mkdir core-reader-app
cd core-reader-app
pear init -y -t terminal
npm install corestore hyperswarm b4a
```
Alter the generated `core-reader-app/index.js` file to the following
```javascript ```javascript
core-reader.js
import Hypercore from 'hypercore'
import Hyperswarm from 'hyperswarm' import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore' import Corestore from 'corestore'
import goodbye from 'graceful-goodbye'
import b4a from 'b4a' import b4a from 'b4a'
import { Node } from 'hyperbee/lib/messages.js' import { Node } from 'hyperbee/lib/messages.js'
@@ -148,7 +179,7 @@ import { Node } from 'hyperbee/lib/messages.js'
const store = new Corestore('./reader-storage') const store = new Corestore('./reader-storage')
const swarm = new Hyperswarm() const swarm = new Hyperswarm()
goodbye(() => swarm.destroy()) Pear.teardown(() => swarm.destroy())
// replication of the corestore instance on connection with other peers // replication of the corestore instance on connection with other peers
swarm.on('connection', conn => store.replicate(conn)) swarm.on('connection', conn => store.replicate(conn))
@@ -172,4 +203,15 @@ const lastBlock = await core.get(core.length - 1)
// print the information about the last block or the latest block of the hypercore instance // print the information about the last block or the latest block of the hypercore instance
console.log(`Raw Block ${seq}:`, lastBlock) console.log(`Raw Block ${seq}:`, lastBlock)
console.log(`Decoded Block ${seq}`, Node.decode(lastBlock)) console.log(`Decoded Block ${seq}`, Node.decode(lastBlock))
``` ```
Open the `core-reader-app` with `pear dev`, passing the core key to it:
```
cd core-reader-app
pear dev -- <SUPPLY KEY HERE>
```
Now we can examine the Hyperbee as if it were just a Hypercore.
The `core-reader-app` will continually download and log the last block of the Hypercore containing the Hyperbee data. Note that these blocks are encoded using Hyperbee's `Node` encoding, which has been imported directly from `Hyperbee` here for the purposes of explanation.

View File

@@ -16,7 +16,7 @@ pear init -y -t terminal
npm install corestore hyperswarm b4a npm install corestore hyperswarm b4a
``` ```
Alter the generated `multicore-writer-app/index.js` to the following Alter the generated `multicore-writer-app/index.js` file to the following
```javascript ```javascript
import Hyperswarm from 'hyperswarm' import Hyperswarm from 'hyperswarm'
@@ -73,10 +73,9 @@ mkdir multicore-reader-app
cd multicore-reader-app cd multicore-reader-app
pear init -y -t terminal pear init -y -t terminal
npm install corestore hyperswarm b4a npm install corestore hyperswarm b4a
``` ```
Alter the generated `multicore-reader-app/index.js` to the following Alter the generated `multicore-reader-app/index.js` file to the following
```javascript ```javascript
import Corestore from 'corestore' import Corestore from 'corestore'
@@ -129,7 +128,6 @@ for (const key of otherKeys) {
} }
``` ```
In one terminal, open `multicore-writer-app` with `pear dev`. In one terminal, open `multicore-writer-app` with `pear dev`.
``` ```