Skip to content

Commit 499702a

Browse files
authored
Merge pull request #14 from share/merge-fork
Merge fork
2 parents 317ad9e + 77f58a4 commit 499702a

8 files changed

+612
-100
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
## 4.0.1
2+
3+
- upgrade sharedb
4+
5+
6+
## 4.0.0
7+
8+
- upgrade `pg` from 7.4.1 to 8.5.1 to prevent silent failure for node v14 or later, reported in [this issue](https://github.com/brianc/node-postgres/issues/2317).
9+
- fix submit ops failed due to version mismatched - https://github.com/share/sharedb-postgres/issues/8
10+
11+
## Note about re-versioning
12+
13+
Original `sharedb-postgres` seems to have been not maintained for a long time since 2018. Thus we made a fork and maintain it as `@plotdb/sharedb-postgre`.
14+
15+
16+
# Change log in original repo
17+
118
## 3.0.0
219

320
Thanks to @billwashere, we upgraded to a more modern version of `pg` (4.5.1 ->

README.md

+52-6
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,68 @@
1-
# sharedb-postgres
1+
# @plotdb/sharedb-postgres
22

3-
PostgreSQL database adapter for [sharedb](https://github.com/share/sharedb). This
4-
driver can be used both as a snapshot store and oplog.
3+
PostgreSQL database adapter for [sharedb](https://github.com/share/sharedb). This driver can be used both as a snapshot store and oplog.
54

65
Doesn't support queries (yet?).
76

87
Moderately experimental. (This drives [Synaptograph](https://www.synaptograph.com)'s backend, and [@nornagon](https://github.com/nornagon) hasn't noticed any issues so far.)
98

109

10+
## Note about versioning
11+
12+
This is a fork from the [original `sharedb-postgres`](https://github.com/share/sharedb-postgres) and its relative forks (see [billwashere](https://github.com/billwashere/sharedb-postgres-jsonb), [zbryikt](https://github.com/zbryikt/sharedb-postgres-jsonb). It seems to have been not maintained for a long time since 2018, Thus we decide to fork it and maintain it as `@plotdb/sharedb-postgre`.
13+
14+
15+
## Installation
16+
17+
```cmd
18+
npm i @plotdb/sharedb-postgres
19+
```
20+
21+
22+
## Requirements
23+
24+
Due to the fix to resolve [high concurency issues](https://github.com/share/sharedb-postgres/issues/1) Postgres 9.5+ is now required.
25+
26+
## Migrating older versions
27+
28+
Older versions of this adaptor used the data type json. You will need to alter the data type prior to using if you are upgrading.
29+
30+
```PLpgSQL
31+
ALTER TABLE ops
32+
ALTER COLUMN operation
33+
SET DATA TYPE jsonb
34+
USING operation::jsonb;
35+
36+
ALTER TABLE snapshots
37+
ALTER COLUMN data
38+
SET DATA TYPE jsonb
39+
USING data::jsonb;
40+
```
41+
1142
## Usage
1243

13-
`sharedb-postgres` wraps native [node-postgres](https://github.com/brianc/node-postgres), and it supports the same configuration options.
44+
`sharedb-postgres-jsonb` wraps native [node-postgres](https://github.com/brianc/node-postgres), and it supports the same configuration options.
1445

1546
To instantiate a sharedb-postgres wrapper, invoke the module and pass in your
16-
PostgreSQL configuration as an argument. For example:
47+
PostgreSQL configuration as an argument or use environmental arguments.
48+
49+
For example using environmental arugments:
50+
51+
```js
52+
var db = require('@plotdb/sharedb-postgres')();
53+
var backend = require('sharedb')({db: db})
54+
```
55+
56+
Then executing via the command line
57+
58+
```
59+
PGUSER=dbuser PGPASSWORD=secretpassword PGHOST=database.server.com PGDATABASE=mydb PGPORT=5433 npm start
60+
```
61+
62+
Example using an object
1763

1864
```js
19-
var db = require('sharedb-postgres')({host: 'localhost', database: 'mydb'});
65+
var db = require('@plotdb/sharedb-postgres')({host: 'localhost', database: 'mydb'});
2066
var backend = require('sharedb')({db: db})
2167
```
2268

index.js

+77-84
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ function PostgresDB(options) {
99

1010
this.closed = false;
1111

12+
this.pg_config = options;
1213
this.pool = new pg.Pool(options);
1314
};
1415
module.exports = PostgresDB;
@@ -22,11 +23,6 @@ PostgresDB.prototype.close = function(callback) {
2223
if (callback) callback();
2324
};
2425

25-
function rollback(client, done) {
26-
client.query('ROLLBACK', function(err) {
27-
return done(err);
28-
})
29-
}
3026

3127
// Persists an op and snapshot if it is for the next version. Calls back with
3228
// callback(err, succeeded)
@@ -41,84 +37,78 @@ PostgresDB.prototype.commit = function(collection, id, op, snapshot, options, ca
4137
* }
4238
* snapshot: PostgresSnapshot
4339
*/
44-
this.pool.connect(function(err, client, done) {
45-
if (err) {
46-
done(client);
47-
callback(err);
48-
return;
49-
}
50-
function commit() {
51-
client.query('COMMIT', function(err) {
52-
done(err);
53-
if (err) {
54-
callback(err);
55-
} else {
56-
callback(null, true);
57-
}
58-
})
40+
this.pool.connect((err, client, done) => {
41+
if (err) {
42+
done(client);
43+
callback(err);
44+
return;
45+
}
46+
/*
47+
* This query uses common table expression to upsert the snapshot table
48+
* (iff the new version is exactly 1 more than the latest table or if
49+
* the document id does not exists)
50+
*
51+
* It will then insert into the ops table if it is exactly 1 more than the
52+
* latest table or it the first operation and iff the previous insert into
53+
* the snapshot table is successful.
54+
*
55+
* This result of this query the version of the newly inserted operation
56+
* If either the ops or the snapshot insert fails then 0 rows are returned
57+
*
58+
* If 0 zeros are return then the callback must return false
59+
*
60+
* Casting is required as postgres thinks that collection and doc_id are
61+
* not varchar
62+
*/
63+
const query = {
64+
name: 'sdb-commit-op-and-snap',
65+
text: `WITH snapshot_id AS (
66+
INSERT INTO snapshots (collection, doc_id, doc_type, version, data)
67+
SELECT $1::varchar collection, $2::varchar doc_id, $4 doc_type, $3 v, $5 d
68+
WHERE $3 = (
69+
SELECT version+1 v
70+
FROM snapshots
71+
WHERE collection = $1 AND doc_id = $2
72+
FOR UPDATE
73+
) OR NOT EXISTS (
74+
SELECT 1
75+
FROM snapshots
76+
WHERE collection = $1 AND doc_id = $2
77+
FOR UPDATE
78+
)
79+
ON CONFLICT (collection, doc_id) DO UPDATE SET version = $3, data = $5, doc_type = $4
80+
RETURNING version
81+
)
82+
INSERT INTO ops (collection, doc_id, version, operation)
83+
SELECT $1::varchar collection, $2::varchar doc_id, $3 v, $6 operation
84+
WHERE (
85+
$3 = (
86+
SELECT max(version)+1
87+
FROM ops
88+
WHERE collection = $1 AND doc_id = $2
89+
) OR NOT EXISTS (
90+
SELECT 1
91+
FROM ops
92+
WHERE collection = $1 AND doc_id = $2
93+
)
94+
) AND EXISTS (SELECT 1 FROM snapshot_id)
95+
RETURNING version`,
96+
values: [collection,id,snapshot.v, snapshot.type, snapshot.data,op]
5997
}
60-
client.query(
61-
'SELECT max(version) AS max_version FROM ops WHERE collection = $1 AND doc_id = $2',
62-
[collection, id],
63-
function(err, res) {
64-
var max_version = res.rows[0].max_version;
65-
if (max_version == null)
66-
max_version = 0;
67-
if (snapshot.v !== max_version + 1) {
68-
return callback(null, false);
69-
}
70-
client.query('BEGIN', function(err) {
71-
client.query(
72-
'INSERT INTO ops (collection, doc_id, version, operation) VALUES ($1, $2, $3, $4)',
73-
[collection, id, snapshot.v, op],
74-
function(err, res) {
75-
if (err) {
76-
// TODO: if err is "constraint violation", callback(null, false) instead
77-
rollback(client, done);
78-
callback(err);
79-
return;
80-
}
81-
if (snapshot.v === 1) {
82-
client.query(
83-
'INSERT INTO snapshots (collection, doc_id, doc_type, version, data) VALUES ($1, $2, $3, $4, $5)',
84-
[collection, id, snapshot.type, snapshot.v, snapshot.data],
85-
function(err, res) {
86-
// TODO:
87-
// if the insert was successful and did insert, callback(null, true)
88-
// if the insert was successful and did not insert, callback(null, false)
89-
// if there was an error, rollback and callback(error)
90-
if (err) {
91-
rollback(client, done);
92-
callback(err);
93-
return;
94-
}
95-
commit();
96-
}
97-
)
98-
} else {
99-
client.query(
100-
'UPDATE snapshots SET doc_type = $3, version = $4, data = $5 WHERE collection = $1 AND doc_id = $2 AND version = ($4 - 1)',
101-
[collection, id, snapshot.type, snapshot.v, snapshot.data],
102-
function(err, res) {
103-
// TODO:
104-
// if any rows were updated, success
105-
// if 0 rows were updated, rollback and not success
106-
// if error, rollback and not success
107-
if (err) {
108-
rollback(client, done);
109-
callback(err);
110-
return;
111-
}
112-
commit();
113-
}
114-
)
115-
}
116-
}
117-
)
118-
})
98+
client.query(query, (err, res) => {
99+
if (err) {
100+
callback(err)
101+
} else if(res.rows.length === 0) {
102+
done(client);
103+
callback(null,false)
104+
}
105+
else {
106+
done(client);
107+
callback(null,true)
119108
}
120-
)
121-
})
109+
})
110+
111+
})
122112
};
123113

124114
// Get the named document from the database. The callback is called with (err,
@@ -181,9 +171,12 @@ PostgresDB.prototype.getOps = function(collection, id, from, to, options, callba
181171
callback(err);
182172
return;
183173
}
184-
client.query(
185-
'SELECT version, operation FROM ops WHERE collection = $1 AND doc_id = $2 AND version >= $3 AND version < $4',
186-
[collection, id, from, to],
174+
175+
var cmd = 'SELECT version, operation FROM ops WHERE collection = $1 AND doc_id = $2 AND version > $3 ';
176+
var params = [collection, id, from];
177+
if(to || to == 0) { cmd += ' AND version <= $4'; params.push(to)}
178+
cmd += ' order by version';
179+
client.query( cmd, params,
187180
function(err, res) {
188181
done();
189182
if (err) {

0 commit comments

Comments
 (0)