Skip to content

Commit 848fcd6

Browse files
committed
Working kv store cli.
1 parent c2d4b9f commit 848fcd6

File tree

9 files changed

+269
-94
lines changed

9 files changed

+269
-94
lines changed

README.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,58 @@ npm i upring-kv --save
1616

1717
## Usage
1818

19-
See [./example.js](./example.js) for exposing upring-kv over HTTP.
19+
See [./bin.js](./bin.js) for exposing upring-kv over HTTP.
20+
This file contains a small http API to get/put data into the
21+
key-value store. Each URL equals to a given key.
22+
23+
To use is, follow these instructions. First, install some
24+
dependencies:
25+
26+
```
27+
npm i upring-kv pino baseswim -g
28+
```
29+
30+
Then, we need to figure out what is our ip.
31+
32+
On Linux:
33+
34+
```sh
35+
export MYIP=`ip addr show wlan0 | grep -Po 'inet \K[\d.]+'`
36+
```
37+
38+
On Mac:
39+
40+
```sh
41+
export MYIP=`ipconfig getifaddr en0`
42+
```
43+
44+
The export phase needs to be done for every opened shell.
45+
46+
Then we can start our upring cluster. We will use a
47+
[baseswim](http://npm.im/baseswim) node to simplify bootstrapping.
48+
49+
```sh
50+
# on one shell
51+
baseswim --host $MYIP --port 7979 | pino
52+
# on another shell
53+
upring-kv -p 3042 $MYIP:7979 | pino
54+
# on another shell
55+
upring-kv -p 3043 $MYIP:7979 | pino
56+
# on another shell
57+
upring-kv -p 3044 $MYIP:7979 | pino
58+
```
59+
60+
Then we can query our key/value storage using basic curl.
61+
62+
```
63+
curl -v localhost:3042
64+
curl -X POST -d 'hello upring' localhost:3043
65+
curl -v localhost:3044
66+
# on another shell
67+
curl localhost:3042?live=true # use SSE to send updates
68+
# one more shell
69+
curl -X POST -d 'by Matteo' localhost:3043
70+
```
2071

2172
## API
2273

bin.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#! /usr/bin/env node
2+
'use strict'
3+
4+
const UpRingKV = require('.')
5+
const fs = require('fs')
6+
const path = require('path')
7+
const http = require('http')
8+
const querystring = require('querystring')
9+
const Transform = require('readable-stream').Transform
10+
const pino = require('pino-http')
11+
const pump = require('pump')
12+
const args = require('minimist')(process.argv.slice(2), {
13+
boolean: ['help', 'verbose'],
14+
default: {
15+
port: 0,
16+
points: 100,
17+
timeout: 200,
18+
verbose: false
19+
},
20+
alias: {
21+
port: 'p',
22+
points: 'P',
23+
help: 'h',
24+
timeout: 't',
25+
verbose: 'V'
26+
}
27+
})
28+
29+
if (args.help) {
30+
console.log(fs.readFileSync(path.join(__dirname, 'help.txt')))
31+
process.exit(1)
32+
}
33+
34+
if (args.version) {
35+
console.log('upring-kv', 'v' + require('./package').version)
36+
process.exit(1)
37+
}
38+
39+
const db = UpRingKV({
40+
base: args._,
41+
logLevel: args.verbose ? 'debug' : 'info',
42+
hashring: {
43+
replicaPoints: args.points,
44+
joinTimeout: args.timeout
45+
}
46+
})
47+
48+
db.upring.on('up', function () {
49+
console.log('to start a new peer, copy and paste the following in a new terminal:')
50+
console.log('node example', this.whoami())
51+
52+
const logger = pino(db.upring.logger)
53+
54+
const server = http.createServer(function (req, res) {
55+
logger(req, res)
56+
switch (req.method) {
57+
case 'PUT':
58+
case 'POST':
59+
handlePost(req, res)
60+
break
61+
case 'GET':
62+
handleGet(req, res)
63+
break
64+
default:
65+
res.statusCode = 404
66+
res.end()
67+
}
68+
})
69+
70+
server.listen(args.port, function (err) {
71+
if (err) {
72+
throw err
73+
}
74+
75+
console.log('server listening on', server.address())
76+
})
77+
78+
function handleGet (req, res) {
79+
const split = req.url.split('?')
80+
const key = split[0]
81+
const query = querystring.parse(split[1])
82+
if (query.live) {
83+
res.writeHead(200, {
84+
'Content-Type': 'text/event-stream',
85+
'Cache-Control': 'no-cache',
86+
'Connection': 'keep-alive'
87+
})
88+
var transform = new Transform({
89+
objectMode: true,
90+
transform (chunk, enc, cb) {
91+
this.push('data:' + JSON.stringify(chunk.value) + '\n\n')
92+
cb()
93+
}
94+
})
95+
pump(db.liveUpdates(key), transform, res)
96+
return
97+
} else {
98+
db.get(key, function (err, data) {
99+
if (err) {
100+
res.statusCode = 500
101+
res.end(err.message)
102+
return
103+
}
104+
105+
if (!data) {
106+
res.statusCode = 404
107+
res.end()
108+
return
109+
}
110+
111+
res.setHeader('Content-Type', data.contentType)
112+
res.end(data.value)
113+
})
114+
}
115+
}
116+
117+
function handlePost (req, res) {
118+
var str = ''
119+
120+
req.on('data', function (chunk) {
121+
str += chunk.toString()
122+
})
123+
124+
req.on('error', function (err) {
125+
res.statusCode = 500
126+
res.end(err.message)
127+
})
128+
129+
req.on('end', function () {
130+
db.put(req.url, {
131+
contentType: req.headers['content-type'],
132+
value: str
133+
}, function (err) {
134+
if (err) {
135+
res.statusCode = 500
136+
res.end(err.message)
137+
} else {
138+
res.statusCode = 200
139+
res.end()
140+
}
141+
})
142+
})
143+
}
144+
})

example.js

Lines changed: 0 additions & 86 deletions
This file was deleted.

help.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Usage: upring-kv [options] base..
2+
3+
Options:
4+
-h/--help print this help
5+
-p/--port PORT the HTTP port to listen to
6+
-P/--points POINTS the number of points each peer has
7+
-t/--timeout MS millis to wait the peer to join the base
8+
-V/--verbose verbose mode on
9+
-v/--version the version of upring-kv

kv.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict'
22

33
const UpRing = require('upring')
4-
const commands = require('./lib/commands')
54
const clone = require('clone')
65
const nes = require('never-ending-stream')
6+
const commands = require('./lib/commands')
77
const ns = 'kv'
88

99
function UpRingKV (opts) {

lib/commands.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ function load (kv) {
4343
}
4444

4545
function sendData (peer, cb) {
46-
cb = cb || bigError
46+
if (typeof cb !== 'function') {
47+
cb = bigError
48+
}
4749
upring.peerConn(peer).request(req, function (err) {
4850
if (err) {
4951
cb(err)
@@ -60,10 +62,13 @@ function load (kv) {
6062
upring.add('ns:kv,cmd:get', function (req, reply) {
6163
const value = pairs.get(req.key)
6264
const key = req.key
63-
const dest = upring._hashring.next(key)
64-
if (value || !dest) {
65+
req.skipList = req.skipList || []
66+
req.skipList.push(upring.whoami())
67+
const dest = upring._hashring.next(key, req.skipList)
68+
69+
if (value || !(dest && upring.allocatedToMe(key))) {
6570
reply(null, { key, value })
66-
} else if (upring.allocatedToMe(key)) {
71+
} else {
6772
logger.debug({ key }, 'checking if we are in the middle of a migration')
6873
upring.peerConn(dest)
6974
.request(req, function (err, res) {
@@ -85,10 +90,11 @@ function load (kv) {
8590
}
8691

8792
const updates = new Readable({
88-
objectMode: true,
89-
read: function () {}
93+
objectMode: true
9094
})
9195

96+
updates._read = function () {}
97+
9298
eos(updates, function () {
9399
array.splice(array.indexOf(updates), 1)
94100
})

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"version": "0.1.0",
44
"description": "Key-Value store on top of UpRing",
55
"main": "kv.js",
6+
"bin": {
7+
"upring-kv": "./bin.js"
8+
},
69
"scripts": {
710
"test": "standard | snazzy && tap test/*test.js",
811
"coverage": "tap --cov --coverage-report=html test/*.test.js",
@@ -37,7 +40,10 @@
3740
"end-of-stream": "^1.1.0",
3841
"flush-write-stream": "^1.0.0",
3942
"level": "^1.4.0",
43+
"minimist": "^1.2.0",
4044
"never-ending-stream": "^2.0.0",
45+
"pino-http": "^1.3.0",
46+
"pump": "^1.0.1",
4147
"readable-stream": "^2.1.5",
4248
"upring": "^0.12.0"
4349
}

0 commit comments

Comments
 (0)