Skip to content

Commit 9f65c0c

Browse files
authored
Merge pull request #7 from gregperk/postgres
add postgres as another backing store
2 parents 2bfbbc1 + 05e802e commit 9f65c0c

File tree

10 files changed

+99
-5
lines changed

10 files changed

+99
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ dist
77
*.swp
88
.cscope_db
99
npm-debug.log
10+
.vscode

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Change Log
22

3+
## [4.0.0]
4+
### Changed
5+
- Added Postgres as a supported backing store
6+
(doing so required adding an async `initialize()` to the storage interface
7+
and making the interface's `shutdown()` routine async).
8+
- Note: **THIS IS A BREAKING CHANGE** because `storage.initialize()` now
9+
needs to be called to ensure your backing store is up and running
10+
(well, if your code needs to work properly across all possible backing stores).
11+
12+
313
## [3.4.0]
414
### Changed
515
- AtlasClient.authenticate[...] classmethods now return an instance.
@@ -22,6 +32,7 @@
2232

2333

2434
[unreleased]: https://github.com/ForstaLabs/librelay-node/tree/master
35+
[4.0.0]: https://github.com/ForstaLabs/librelay-node/tree/v4.0.0
2536
[3.4.0]: https://github.com/ForstaLabs/librelay-node/tree/v3.4.0
2637
[3.3.0]: https://github.com/ForstaLabs/librelay-node/tree/v3.3.0
2738
[3.2.0]: https://github.com/ForstaLabs/librelay-node/tree/v3.2.0

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ librelay on a single backing store use
3636
`librelay.storage.setLabel('<something-unique>')` to shard your storage into
3737
a unique namespace.
3838

39+
You'll need to ensure your backing store is running properly with a call
40+
to (async) `librelay.storage.initialize()`, and if possible you should
41+
tear it down before quitting, with (async) `librelay.storage.shutdown()`.
42+
3943

4044
Provisioning
4145
-------

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "librelay",
3-
"version": "3.4.4",
3+
"version": "3.5.0",
44
"description": "Signal based end-to-end crypto for Forsta messaging platform",
55
"repository": "ForstaLabs/librelay-node",
66
"engines": {
@@ -20,6 +20,7 @@
2020
"libsignal": "1.2.1",
2121
"long": "3.2.0",
2222
"node-fetch": "1.7.3",
23+
"pg": "^7.4.1",
2324
"protobufjs": "6.8.0",
2425
"redis": "2.8.0",
2526
"utf-8-validate": "3.0.3",

src/storage/backing/fs.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,6 @@ class FSBacking extends StorageInterface {
140140
}
141141
return regex ? keys.filter(x => x.match(regex)) : keys;
142142
}
143-
144-
shutdown() {
145-
}
146143
}
147144

148145
module.exports = FSBacking;

src/storage/backing/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module.exports = {
22
RedisBacking: require('./redis'),
33
FSBacking: require('./fs'),
4+
PostgresBacking: require('./postgres'),
45
BackingInterface: require('./interface')
56
};

src/storage/backing/interface.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ class StorageInterface {
55
this.label = label;
66
}
77

8+
async initialize() {
9+
}
10+
811
async set(ns, key, value) {
912
throw new Error("Not Implemented");
1013
}

src/storage/backing/postgres.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const StorageInterface = require('./interface');
2+
const { Client } = require('pg');
3+
4+
class PostgresBacking extends StorageInterface {
5+
6+
constructor(label) {
7+
super(label);
8+
this.tableName = 'faux_redis_' + this.label.toLowerCase().replace(/[^a-z0-9_]/g, '_');
9+
this.client = new Client({ connectionString: process.env.DATABASE_URL });
10+
this.queryCreateTableIfNeeded = `
11+
CREATE TABLE IF NOT EXISTS ${this.tableName} (
12+
namespace TEXT,
13+
key TEXT,
14+
value TEXT,
15+
PRIMARY KEY (namespace, key)
16+
);`;
17+
18+
this.querySetValue = `
19+
INSERT INTO ${this.tableName} (namespace, key, value)
20+
VALUES ($1::text, $2::text, $3::text)
21+
ON CONFLICT (namespace, key)
22+
DO UPDATE SET value=$3::text`;
23+
24+
this.queryGetValue = `
25+
SELECT value FROM ${this.tableName} WHERE namespace=$1::text AND key=$2::text`;
26+
27+
this.queryRemoveValue = `
28+
DELETE FROM ${this.tableName} WHERE namespace=$1::text AND key=$2::text`;
29+
30+
this.queryGetKeys = `
31+
SELECT key FROM ${this.tableName} WHERE namespace=$1::text`;
32+
}
33+
34+
async initialize() {
35+
await this.client.connect();
36+
return this.client.query(this.queryCreateTableIfNeeded);
37+
}
38+
39+
async set(ns, key, value) {
40+
if (value === undefined) throw new Error("Tried to store undefined");
41+
const result = await this.client.query(this.querySetValue, [ns, key, value]);
42+
if (result.rowCount !== 1) throw new Error('Failure in postgres set');
43+
}
44+
45+
async get(ns, key) {
46+
const result = await this.client.query(this.queryGetValue, [ns, key]);
47+
if (result.rowCount !== 1) throw new ReferenceError(key);
48+
return result.rows[0].value;
49+
}
50+
51+
async has(ns, key) {
52+
const result = await this.client.query(this.queryGetValue, [ns, key]);
53+
return result.rowCount === 1;
54+
}
55+
56+
async remove(ns, key) {
57+
const result = await this.client.query(this.queryRemoveValue, [ns, key]);
58+
return result.rowCount === 1;
59+
}
60+
61+
async keys(ns, regex) {
62+
const result = await this.client.query(this.queryGetKeys, [ns]);
63+
const keys = result.rows.map(r => r.key);
64+
return regex ? keys.filter(x => x.match(regex)) : keys;
65+
}
66+
67+
async shutdown() {
68+
await this.client.end();
69+
this.client = null;
70+
}
71+
}
72+
73+
module.exports = PostgresBacking;

src/storage/backing/redis.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class RedisBacking extends StorageInterface {
8686
return regex ? keys.filter(x => x.match(regex)) : keys;
8787
}
8888

89-
shutdown() {
89+
async shutdown() {
9090
this.client.quit();
9191
this.client = null;
9292
}

src/storage/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ exports.get = async (ns, key, defaultValue) => {
6565
}
6666
return data && decode(data);
6767
};
68+
exports.initialize = () => _backing.initialize();
6869
exports.set = (ns, key, value) => _backing.set(ns, key, encode(value));
70+
exports.has = (ns, key, value) => _backing.has(ns, key);
6971
exports.remove = (ns, key) => _backing.remove(ns, key);
7072
exports.keys = (ns, re) => _backing.keys(ns, re);
7173
exports.shutdown = () => _backing.shutdown();
@@ -237,6 +239,7 @@ exports.getDeviceIds = async function(addr) {
237239
function getBackingClass(name) {
238240
return {
239241
redis: exports.backing.RedisBacking,
242+
postgres: exports.backing.PostgresBacking,
240243
fs: exports.backing.FSBacking
241244
}[name];
242245
}

0 commit comments

Comments
 (0)