presidium-db-client-server 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DatabaseHandler.js +56 -10
- package/DatabaseLoadBalancer.js +97 -0
- package/DatabaseServer.js +2 -6
- package/DiskHashTableClient.js +17 -12
- package/DiskSortedHashTableClient.js +107 -76
- package/errorHandler.js +18 -0
- package/loadBalancerHandler.js +40 -0
- package/package.json +8 -4
- package/parseString.js +18 -0
package/DatabaseHandler.js
CHANGED
|
@@ -127,7 +127,11 @@ function DatabaseHandler(options) {
|
|
|
127
127
|
},
|
|
128
128
|
|
|
129
129
|
eq("set"), async () => {
|
|
130
|
-
const
|
|
130
|
+
const key = request.headers["key"]
|
|
131
|
+
const sortValue = request.headers["sort-value"]
|
|
132
|
+
const value = request.headers["content-type"] == "text/plain"
|
|
133
|
+
? await Readable.Text(request)
|
|
134
|
+
: await Readable.Buffer(request)
|
|
131
135
|
await ht.set(key, value, sortValue)
|
|
132
136
|
response.writeHead(200, {
|
|
133
137
|
"Content-Type": "text/plain",
|
|
@@ -136,7 +140,7 @@ function DatabaseHandler(options) {
|
|
|
136
140
|
},
|
|
137
141
|
|
|
138
142
|
eq("get"), async () => {
|
|
139
|
-
const
|
|
143
|
+
const key = request.headers["key"]
|
|
140
144
|
const value = await ht.get(key)
|
|
141
145
|
if (value === undefined) {
|
|
142
146
|
response.writeHead(499, {
|
|
@@ -152,7 +156,7 @@ function DatabaseHandler(options) {
|
|
|
152
156
|
},
|
|
153
157
|
|
|
154
158
|
eq("getBinary"), async () => {
|
|
155
|
-
const
|
|
159
|
+
const key = request.headers["key"]
|
|
156
160
|
const value = await ht.getBinary(key)
|
|
157
161
|
response.writeHead(200, {
|
|
158
162
|
"Content-Type": "application/octet-stream",
|
|
@@ -161,7 +165,7 @@ function DatabaseHandler(options) {
|
|
|
161
165
|
},
|
|
162
166
|
|
|
163
167
|
eq("delete"), async () => {
|
|
164
|
-
const
|
|
168
|
+
const key = request.headers["key"]
|
|
165
169
|
const didDelete = await ht.delete(key)
|
|
166
170
|
response.writeHead(200, {
|
|
167
171
|
"Content-Type": "text/plain",
|
|
@@ -178,7 +182,8 @@ function DatabaseHandler(options) {
|
|
|
178
182
|
},
|
|
179
183
|
|
|
180
184
|
eq("iterator"), async () => {
|
|
181
|
-
const
|
|
185
|
+
const valueType = request.headers["value-type"]
|
|
186
|
+
const options = filter({ valueType }, __)
|
|
182
187
|
response.writeHead(200, {
|
|
183
188
|
"Content-Type": "application/octet-stream",
|
|
184
189
|
"Content-Encoding": "utf8",
|
|
@@ -192,7 +197,8 @@ function DatabaseHandler(options) {
|
|
|
192
197
|
},
|
|
193
198
|
|
|
194
199
|
eq("entriesIterator"), async () => {
|
|
195
|
-
const
|
|
200
|
+
const valueType = request.headers["value-type"]
|
|
201
|
+
const options = filter({ valueType }, __)
|
|
196
202
|
response.writeHead(200, {
|
|
197
203
|
"Content-Type": "application/octet-stream",
|
|
198
204
|
"Content-Encoding": "utf8",
|
|
@@ -208,7 +214,17 @@ function DatabaseHandler(options) {
|
|
|
208
214
|
},
|
|
209
215
|
|
|
210
216
|
eq("forwardIterator"), async () => {
|
|
211
|
-
const
|
|
217
|
+
const valueType = request.headers["value-type"]
|
|
218
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
219
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
220
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
221
|
+
const options = filter({
|
|
222
|
+
valueType,
|
|
223
|
+
exclusiveStartKey,
|
|
224
|
+
startingSortValue,
|
|
225
|
+
endingSortValue,
|
|
226
|
+
}, __)
|
|
227
|
+
|
|
212
228
|
response.writeHead(200, {
|
|
213
229
|
"Content-Type": "application/octet-stream",
|
|
214
230
|
"Content-Encoding": "utf8",
|
|
@@ -222,7 +238,17 @@ function DatabaseHandler(options) {
|
|
|
222
238
|
},
|
|
223
239
|
|
|
224
240
|
eq("reverseIterator"), async () => {
|
|
225
|
-
const
|
|
241
|
+
const valueType = request.headers["value-type"]
|
|
242
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
243
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
244
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
245
|
+
const options = filter({
|
|
246
|
+
valueType,
|
|
247
|
+
exclusiveStartKey,
|
|
248
|
+
startingSortValue,
|
|
249
|
+
endingSortValue,
|
|
250
|
+
}, __)
|
|
251
|
+
|
|
226
252
|
response.writeHead(200, {
|
|
227
253
|
"Content-Type": "application/octet-stream",
|
|
228
254
|
"Content-Encoding": "utf8",
|
|
@@ -236,7 +262,17 @@ function DatabaseHandler(options) {
|
|
|
236
262
|
},
|
|
237
263
|
|
|
238
264
|
eq("forwardEntriesIterator"), async () => {
|
|
239
|
-
const
|
|
265
|
+
const valueType = request.headers["value-type"]
|
|
266
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
267
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
268
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
269
|
+
const options = filter({
|
|
270
|
+
valueType,
|
|
271
|
+
exclusiveStartKey,
|
|
272
|
+
startingSortValue,
|
|
273
|
+
endingSortValue,
|
|
274
|
+
}, __)
|
|
275
|
+
|
|
240
276
|
response.writeHead(200, {
|
|
241
277
|
"Content-Type": "application/octet-stream",
|
|
242
278
|
"Content-Encoding": "utf8",
|
|
@@ -254,7 +290,17 @@ function DatabaseHandler(options) {
|
|
|
254
290
|
},
|
|
255
291
|
|
|
256
292
|
eq("reverseEntriesIterator"), async () => {
|
|
257
|
-
const
|
|
293
|
+
const valueType = request.headers["value-type"]
|
|
294
|
+
const exclusiveStartKey = request.headers["exclusive-start-key"]
|
|
295
|
+
const startingSortValue = request.headers["starting-sort-value"]
|
|
296
|
+
const endingSortValue = request.headers["ending-sort-value"]
|
|
297
|
+
const options = filter({
|
|
298
|
+
valueType,
|
|
299
|
+
exclusiveStartKey,
|
|
300
|
+
startingSortValue,
|
|
301
|
+
endingSortValue,
|
|
302
|
+
}, __)
|
|
303
|
+
|
|
258
304
|
response.writeHead(200, {
|
|
259
305
|
"Content-Type": "application/octet-stream",
|
|
260
306
|
"Content-Encoding": "utf8",
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presidium DB Client and Server may be freely distributed under the CFOSS license.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
require("rubico/global")
|
|
6
|
+
const HTTP = require("presidium/HTTP")
|
|
7
|
+
const loadBalancerHandler = require("./loadBalancerHandler")
|
|
8
|
+
const healthCheckHandler = require("./healthCheckHandler")
|
|
9
|
+
const notFoundHandler = require("./notFoundHandler")
|
|
10
|
+
const errorHandler = require("./errorHandler")
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @name DatabaseLoadbalancer
|
|
14
|
+
*
|
|
15
|
+
* @docs
|
|
16
|
+
* ```specscript
|
|
17
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
18
|
+
*
|
|
19
|
+
* HTTP.Handler(HTTP.Request, HTTP.Response) -> ()
|
|
20
|
+
*
|
|
21
|
+
* new DatabaseLoadbalancer(
|
|
22
|
+
* hash(tableName string, key string, length number) -> Promise|number,
|
|
23
|
+
* options {
|
|
24
|
+
* serverIps: Array<string>,
|
|
25
|
+
* },
|
|
26
|
+
* ...handlers Arguments<HTTP.Handler>
|
|
27
|
+
* )
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
class DatabaseLoadbalancer {
|
|
31
|
+
constructor(hash, options, ...handlers) {
|
|
32
|
+
this.hash = hash
|
|
33
|
+
this.serverIps = options.serverIps
|
|
34
|
+
this.handlers = handlers
|
|
35
|
+
this.clients = new Map()
|
|
36
|
+
this.closed = false
|
|
37
|
+
|
|
38
|
+
const router = tryCatch(
|
|
39
|
+
switchCase(
|
|
40
|
+
request => request.path == "/health",
|
|
41
|
+
healthCheckHandler,
|
|
42
|
+
|
|
43
|
+
request => request.pathDepth == 2,
|
|
44
|
+
curry.call(loadBalancerHandler, this),
|
|
45
|
+
|
|
46
|
+
notFoundHandler,
|
|
47
|
+
),
|
|
48
|
+
errorHandler
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const handler = all.series([
|
|
52
|
+
...this.handlers,
|
|
53
|
+
request => {
|
|
54
|
+
const { pathname } = new URL(`http://*${request.url}`)
|
|
55
|
+
request.path = decodeURIComponent(
|
|
56
|
+
pathname == "/" ? pathname
|
|
57
|
+
: pathname.endsWith("/") ? pathname.slice(0, -1)
|
|
58
|
+
: pathname
|
|
59
|
+
)
|
|
60
|
+
request.pathSegments = request.path.slice(1).split("/")
|
|
61
|
+
request.pathDepth = request.pathSegments.length
|
|
62
|
+
},
|
|
63
|
+
router,
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
this.server = HTTP.Server(handler)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @name listen
|
|
71
|
+
*
|
|
72
|
+
* @docs
|
|
73
|
+
* ```specscript
|
|
74
|
+
* listen(port number) -> ()
|
|
75
|
+
* listen(port number, callback function) -> ()
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
listen(port, callback) {
|
|
79
|
+
this.server.listen(port, callback)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @name close
|
|
84
|
+
*
|
|
85
|
+
* @docs
|
|
86
|
+
* ```specscript
|
|
87
|
+
* close() -> ()
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
close() {
|
|
91
|
+
this.server.close()
|
|
92
|
+
this.closed = true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = DatabaseLoadbalancer
|
package/DatabaseServer.js
CHANGED
|
@@ -9,6 +9,7 @@ const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
|
|
|
9
9
|
const DatabaseHandler = require("./DatabaseHandler")
|
|
10
10
|
const healthCheckHandler = require("./healthCheckHandler")
|
|
11
11
|
const notFoundHandler = require("./notFoundHandler")
|
|
12
|
+
const errorHandler = require("./errorHandler")
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @name DatabaseServer
|
|
@@ -84,12 +85,7 @@ class DatabaseServer {
|
|
|
84
85
|
|
|
85
86
|
notFoundHandler,
|
|
86
87
|
),
|
|
87
|
-
|
|
88
|
-
response.writeHead(error.code ?? 500, {
|
|
89
|
-
"Content-Type": "text/plain",
|
|
90
|
-
})
|
|
91
|
-
response.end(error.message)
|
|
92
|
-
}
|
|
88
|
+
errorHandler
|
|
93
89
|
)
|
|
94
90
|
|
|
95
91
|
const handler = all.series([
|
package/DiskHashTableClient.js
CHANGED
|
@@ -41,6 +41,7 @@ class DiskHashTableClient {
|
|
|
41
41
|
async clear() {
|
|
42
42
|
const response = await this.http.POST(`/${this.tableName}/clear`, {
|
|
43
43
|
headers: {
|
|
44
|
+
"Content-Type": "text/plain",
|
|
44
45
|
"Client-Name": "DiskHashTableClient",
|
|
45
46
|
},
|
|
46
47
|
})
|
|
@@ -60,10 +61,13 @@ class DiskHashTableClient {
|
|
|
60
61
|
async set(key, value) {
|
|
61
62
|
const response = await this.http.POST(`/${this.tableName}/set`, {
|
|
62
63
|
headers: {
|
|
63
|
-
"Content-Type":
|
|
64
|
+
"Content-Type": Buffer.isBuffer(value)
|
|
65
|
+
? "application/octet-stream"
|
|
66
|
+
: "text/plain",
|
|
64
67
|
"Client-Name": "DiskHashTableClient",
|
|
68
|
+
"Key": key,
|
|
65
69
|
},
|
|
66
|
-
body:
|
|
70
|
+
body: value,
|
|
67
71
|
})
|
|
68
72
|
await handleServerResponse(response)
|
|
69
73
|
}
|
|
@@ -81,10 +85,10 @@ class DiskHashTableClient {
|
|
|
81
85
|
async get(key) {
|
|
82
86
|
const response = await this.http.POST(`/${this.tableName}/get`, {
|
|
83
87
|
headers: {
|
|
84
|
-
"Content-Type": "
|
|
88
|
+
"Content-Type": "text/plain",
|
|
85
89
|
"Client-Name": "DiskHashTableClient",
|
|
90
|
+
"Key": key,
|
|
86
91
|
},
|
|
87
|
-
body: JSON.stringify({ key }),
|
|
88
92
|
})
|
|
89
93
|
if (response.status == 499) {
|
|
90
94
|
return undefined
|
|
@@ -105,10 +109,10 @@ class DiskHashTableClient {
|
|
|
105
109
|
async getBinary(key) {
|
|
106
110
|
const response = await this.http.POST(`/${this.tableName}/getBinary`, {
|
|
107
111
|
headers: {
|
|
108
|
-
"Content-Type": "
|
|
112
|
+
"Content-Type": "text/plain",
|
|
109
113
|
"Client-Name": "DiskHashTableClient",
|
|
114
|
+
"Key": key,
|
|
110
115
|
},
|
|
111
|
-
body: JSON.stringify({ key }),
|
|
112
116
|
})
|
|
113
117
|
return handleServerResponse(response)
|
|
114
118
|
}
|
|
@@ -126,10 +130,10 @@ class DiskHashTableClient {
|
|
|
126
130
|
async delete(key) {
|
|
127
131
|
const response = await this.http.POST(`/${this.tableName}/delete`, {
|
|
128
132
|
headers: {
|
|
129
|
-
"Content-Type": "
|
|
133
|
+
"Content-Type": "text/plain",
|
|
130
134
|
"Client-Name": "DiskHashTableClient",
|
|
135
|
+
"Key": key,
|
|
131
136
|
},
|
|
132
|
-
body: JSON.stringify({ key }),
|
|
133
137
|
})
|
|
134
138
|
const didDeleteText = await handleServerResponse(response)
|
|
135
139
|
return didDeleteText == "true"
|
|
@@ -146,6 +150,7 @@ class DiskHashTableClient {
|
|
|
146
150
|
async count() {
|
|
147
151
|
const response = await this.http.POST(`/${this.tableName}/count`, {
|
|
148
152
|
headers: {
|
|
153
|
+
"Content-Type": "text/plain",
|
|
149
154
|
"Client-Name": "DiskHashTableClient",
|
|
150
155
|
},
|
|
151
156
|
})
|
|
@@ -167,10 +172,10 @@ class DiskHashTableClient {
|
|
|
167
172
|
const { valueType } = options
|
|
168
173
|
const response = await this.http.POST(`/${this.tableName}/iterator`, {
|
|
169
174
|
headers: {
|
|
170
|
-
"Content-Type": "
|
|
175
|
+
"Content-Type": "text/plain",
|
|
171
176
|
"Client-Name": "DiskHashTableClient",
|
|
177
|
+
...valueType ? { "Value-Type": valueType } : {},
|
|
172
178
|
},
|
|
173
|
-
body: JSON.stringify(options),
|
|
174
179
|
})
|
|
175
180
|
if (valueType == "binary") {
|
|
176
181
|
return handleServerResponse(response, {
|
|
@@ -198,10 +203,10 @@ class DiskHashTableClient {
|
|
|
198
203
|
const { valueType } = options
|
|
199
204
|
const response = await this.http.POST(`/${this.tableName}/entriesIterator`, {
|
|
200
205
|
headers: {
|
|
201
|
-
"Content-Type": "
|
|
206
|
+
"Content-Type": "text/plain",
|
|
202
207
|
"Client-Name": "DiskHashTableClient",
|
|
208
|
+
...valueType ? { "Value-Type": valueType } : {},
|
|
203
209
|
},
|
|
204
|
-
body: JSON.stringify({ valueType }),
|
|
205
210
|
})
|
|
206
211
|
|
|
207
212
|
if (valueType == "binary") {
|
|
@@ -6,6 +6,7 @@ require("rubico/global")
|
|
|
6
6
|
const HTTP = require("presidium/HTTP")
|
|
7
7
|
const handleServerResponse = require("./handleServerResponse")
|
|
8
8
|
const buffersToEntry = require("./buffersToEntry")
|
|
9
|
+
const parseString = require("./parseString")
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @name DiskSortedHashTableClient
|
|
@@ -44,13 +45,14 @@ class DiskSortedHashTableClient {
|
|
|
44
45
|
async clear() {
|
|
45
46
|
const response = await this.http.POST(`/${this.tableName}/clear`, {
|
|
46
47
|
headers: {
|
|
48
|
+
"Content-Type": "text/plain",
|
|
47
49
|
"Client-Name": "DiskSortedHashTableClient",
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
|
|
51
|
+
...pipe(this, all({
|
|
52
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
53
|
+
"Degree": get("degree"),
|
|
54
|
+
}), filter(__), map(parseString)),
|
|
55
|
+
|
|
54
56
|
},
|
|
55
57
|
})
|
|
56
58
|
return handleServerResponse(response)
|
|
@@ -69,16 +71,20 @@ class DiskSortedHashTableClient {
|
|
|
69
71
|
async set(key, value, sortValue) {
|
|
70
72
|
const response = await this.http.POST(`/${this.tableName}/set`, {
|
|
71
73
|
headers: {
|
|
72
|
-
"Content-Type":
|
|
74
|
+
"Content-Type": Buffer.isBuffer(value)
|
|
75
|
+
? "application/octet-stream"
|
|
76
|
+
: "text/plain",
|
|
73
77
|
"Client-Name": "DiskSortedHashTableClient",
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
...this
|
|
78
|
-
"
|
|
79
|
-
|
|
78
|
+
"Key": key,
|
|
79
|
+
"Sort-Value": sortValue,
|
|
80
|
+
|
|
81
|
+
...pipe(this, all({
|
|
82
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
83
|
+
"Degree": get("degree"),
|
|
84
|
+
}), filter(__), map(parseString)),
|
|
85
|
+
|
|
80
86
|
},
|
|
81
|
-
body:
|
|
87
|
+
body: value,
|
|
82
88
|
})
|
|
83
89
|
await handleServerResponse(response)
|
|
84
90
|
}
|
|
@@ -96,16 +102,16 @@ class DiskSortedHashTableClient {
|
|
|
96
102
|
async get(key) {
|
|
97
103
|
const response = await this.http.POST(`/${this.tableName}/get`, {
|
|
98
104
|
headers: {
|
|
99
|
-
"Content-Type": "
|
|
105
|
+
"Content-Type": "text/plain",
|
|
100
106
|
"Client-Name": "DiskSortedHashTableClient",
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"Degree":
|
|
106
|
-
}
|
|
107
|
+
"Key": key,
|
|
108
|
+
|
|
109
|
+
...pipe(this, all({
|
|
110
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
111
|
+
"Degree": get("degree"),
|
|
112
|
+
}), filter(__), map(parseString)),
|
|
113
|
+
|
|
107
114
|
},
|
|
108
|
-
body: JSON.stringify({ key }),
|
|
109
115
|
})
|
|
110
116
|
if (response.status == 499) {
|
|
111
117
|
return undefined
|
|
@@ -126,16 +132,16 @@ class DiskSortedHashTableClient {
|
|
|
126
132
|
async getBinary(key) {
|
|
127
133
|
const response = await this.http.POST(`/${this.tableName}/getBinary`, {
|
|
128
134
|
headers: {
|
|
129
|
-
"Content-Type": "
|
|
135
|
+
"Content-Type": "text/plain",
|
|
130
136
|
"Client-Name": "DiskSortedHashTableClient",
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
"Degree":
|
|
136
|
-
}
|
|
137
|
+
"Key": key,
|
|
138
|
+
|
|
139
|
+
...pipe(this, all({
|
|
140
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
141
|
+
"Degree": get("degree"),
|
|
142
|
+
}), filter(__), map(parseString)),
|
|
143
|
+
|
|
137
144
|
},
|
|
138
|
-
body: JSON.stringify({ key }),
|
|
139
145
|
})
|
|
140
146
|
return handleServerResponse(response)
|
|
141
147
|
}
|
|
@@ -157,16 +163,22 @@ class DiskSortedHashTableClient {
|
|
|
157
163
|
const { valueType } = options
|
|
158
164
|
const response = await this.http.POST(`/${this.tableName}/forwardIterator`, {
|
|
159
165
|
headers: {
|
|
160
|
-
"Content-Type": "
|
|
166
|
+
"Content-Type": "text/plain",
|
|
161
167
|
"Client-Name": "DiskSortedHashTableClient",
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"
|
|
167
|
-
|
|
168
|
+
|
|
169
|
+
...pipe(options, all({
|
|
170
|
+
"Exclusive-Start-Key": get("exclusiveStartKey"),
|
|
171
|
+
"Starting-Sort-Value": get("startingSortValue"),
|
|
172
|
+
"Ending-Sort-Value": get("endingSortValue"),
|
|
173
|
+
"Value-Type": get("valueType"),
|
|
174
|
+
}), filter(__), map(parseString)),
|
|
175
|
+
|
|
176
|
+
...pipe(this, all({
|
|
177
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
178
|
+
"Degree": get("degree"),
|
|
179
|
+
}), filter(__), map(parseString)),
|
|
180
|
+
|
|
168
181
|
},
|
|
169
|
-
body: JSON.stringify(options),
|
|
170
182
|
})
|
|
171
183
|
if (valueType == "binary") {
|
|
172
184
|
return handleServerResponse(response, {
|
|
@@ -197,16 +209,22 @@ class DiskSortedHashTableClient {
|
|
|
197
209
|
const { valueType } = options
|
|
198
210
|
const response = await this.http.POST(`/${this.tableName}/reverseIterator`, {
|
|
199
211
|
headers: {
|
|
200
|
-
"Content-Type": "
|
|
212
|
+
"Content-Type": "text/plain",
|
|
201
213
|
"Client-Name": "DiskSortedHashTableClient",
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
"
|
|
207
|
-
|
|
214
|
+
|
|
215
|
+
...pipe(options, all({
|
|
216
|
+
"Exclusive-Start-Key": get("exclusiveStartKey"),
|
|
217
|
+
"Starting-Sort-Value": get("startingSortValue"),
|
|
218
|
+
"Ending-Sort-Value": get("endingSortValue"),
|
|
219
|
+
"Value-Type": get("valueType"),
|
|
220
|
+
}), filter(__), map(parseString)),
|
|
221
|
+
|
|
222
|
+
...pipe(this, all({
|
|
223
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
224
|
+
"Degree": get("degree"),
|
|
225
|
+
}), filter(__), map(parseString)),
|
|
226
|
+
|
|
208
227
|
},
|
|
209
|
-
body: JSON.stringify(options),
|
|
210
228
|
})
|
|
211
229
|
if (valueType == "binary") {
|
|
212
230
|
return handleServerResponse(response, {
|
|
@@ -237,16 +255,22 @@ class DiskSortedHashTableClient {
|
|
|
237
255
|
const { valueType } = options
|
|
238
256
|
const response = await this.http.POST(`/${this.tableName}/forwardEntriesIterator`, {
|
|
239
257
|
headers: {
|
|
240
|
-
"Content-Type": "
|
|
258
|
+
"Content-Type": "text/plain",
|
|
241
259
|
"Client-Name": "DiskSortedHashTableClient",
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
"
|
|
247
|
-
|
|
260
|
+
|
|
261
|
+
...pipe(options, all({
|
|
262
|
+
"Exclusive-Start-Key": get("exclusiveStartKey"),
|
|
263
|
+
"Starting-Sort-Value": get("startingSortValue"),
|
|
264
|
+
"Ending-Sort-Value": get("endingSortValue"),
|
|
265
|
+
"Value-Type": get("valueType"),
|
|
266
|
+
}), filter(__), map(parseString)),
|
|
267
|
+
|
|
268
|
+
...pipe(this, all({
|
|
269
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
270
|
+
"Degree": get("degree"),
|
|
271
|
+
}), filter(__), map(parseString)),
|
|
272
|
+
|
|
248
273
|
},
|
|
249
|
-
body: JSON.stringify(options),
|
|
250
274
|
})
|
|
251
275
|
if (valueType == "binary") {
|
|
252
276
|
const iter = await handleServerResponse(response, {
|
|
@@ -278,16 +302,22 @@ class DiskSortedHashTableClient {
|
|
|
278
302
|
const { valueType } = options
|
|
279
303
|
const response = await this.http.POST(`/${this.tableName}/reverseEntriesIterator`, {
|
|
280
304
|
headers: {
|
|
281
|
-
"Content-Type": "
|
|
305
|
+
"Content-Type": "text/plain",
|
|
282
306
|
"Client-Name": "DiskSortedHashTableClient",
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
"
|
|
288
|
-
|
|
307
|
+
|
|
308
|
+
...pipe(options, all({
|
|
309
|
+
"Exclusive-Start-Key": get("exclusiveStartKey"),
|
|
310
|
+
"Starting-Sort-Value": get("startingSortValue"),
|
|
311
|
+
"Ending-Sort-Value": get("endingSortValue"),
|
|
312
|
+
"Value-Type": get("valueType"),
|
|
313
|
+
}), filter(__), map(parseString)),
|
|
314
|
+
|
|
315
|
+
...pipe(this, all({
|
|
316
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
317
|
+
"Degree": get("degree"),
|
|
318
|
+
}), filter(__), map(parseString)),
|
|
319
|
+
|
|
289
320
|
},
|
|
290
|
-
body: JSON.stringify(options),
|
|
291
321
|
})
|
|
292
322
|
|
|
293
323
|
if (valueType == "binary") {
|
|
@@ -319,16 +349,16 @@ class DiskSortedHashTableClient {
|
|
|
319
349
|
async delete(key) {
|
|
320
350
|
const response = await this.http.POST(`/${this.tableName}/delete`, {
|
|
321
351
|
headers: {
|
|
322
|
-
"Content-Type": "
|
|
352
|
+
"Content-Type": "text/plain",
|
|
323
353
|
"Client-Name": "DiskSortedHashTableClient",
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
"Degree":
|
|
329
|
-
}
|
|
354
|
+
"Key": key,
|
|
355
|
+
|
|
356
|
+
...pipe(this, all({
|
|
357
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
358
|
+
"Degree": get("degree"),
|
|
359
|
+
}), filter(__), map(parseString)),
|
|
360
|
+
|
|
330
361
|
},
|
|
331
|
-
body: JSON.stringify({ key }),
|
|
332
362
|
})
|
|
333
363
|
const didDeleteText = await handleServerResponse(response)
|
|
334
364
|
return didDeleteText == "true"
|
|
@@ -345,13 +375,14 @@ class DiskSortedHashTableClient {
|
|
|
345
375
|
async count() {
|
|
346
376
|
const response = await this.http.POST(`/${this.tableName}/count`, {
|
|
347
377
|
headers: {
|
|
378
|
+
"Content-Type": "text/plain",
|
|
348
379
|
"Client-Name": "DiskSortedHashTableClient",
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
380
|
+
|
|
381
|
+
...pipe(this, all({
|
|
382
|
+
"Sort-Value-Type": get("sortValueType"),
|
|
383
|
+
"Degree": get("degree"),
|
|
384
|
+
}), filter(__), map(parseString)),
|
|
385
|
+
|
|
355
386
|
},
|
|
356
387
|
})
|
|
357
388
|
return Number(await handleServerResponse(response))
|
package/errorHandler.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name errorHandler
|
|
3
|
+
*
|
|
4
|
+
* @docs
|
|
5
|
+
* ```specscript
|
|
6
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
7
|
+
*
|
|
8
|
+
* errorHandler(error Error, request HTTP.Request, response HTTP.Response) -> ()
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
function errorHandler(error, request, response) {
|
|
12
|
+
response.writeHead(error.code ?? 500, {
|
|
13
|
+
"Content-Type": "text/plain",
|
|
14
|
+
})
|
|
15
|
+
response.end(error.message)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = errorHandler
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const HTTP = require("presidium/HTTP")
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @name loadBalancerHandler
|
|
5
|
+
*
|
|
6
|
+
* @docs
|
|
7
|
+
* ```specscript
|
|
8
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
9
|
+
*
|
|
10
|
+
* loadBalancerHandler(HTTP.Request, HTTP.Response) -> ()
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
async function loadBalancerHandler(request, response) {
|
|
14
|
+
const tableName = request.pathSegments[0]
|
|
15
|
+
const key = request.headers["key"]
|
|
16
|
+
|
|
17
|
+
let index = this.hash(tableName, key)
|
|
18
|
+
if (typeof index.then == "function") {
|
|
19
|
+
index = await index
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const serverIp = this.serverIps[index]
|
|
23
|
+
let client
|
|
24
|
+
if (this.clients.has(serverIp)) {
|
|
25
|
+
client = this.clients.get(serverIp)
|
|
26
|
+
} else {
|
|
27
|
+
client = new HTTP(serverIp)
|
|
28
|
+
this.clients.set(serverIp, client)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const serverResponse = await client[request.method](request.url, {
|
|
32
|
+
headers: request.headers,
|
|
33
|
+
body: request,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
response.writeHead(serverResponse.status, serverResponse.headers)
|
|
37
|
+
serverResponse.pipe(response)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = loadBalancerHandler
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "presidium-db-client-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Presidium DB client and server library",
|
|
5
5
|
"author": "Richard Tong",
|
|
6
6
|
"license": "CFOSS",
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
"AsyncIterator.js",
|
|
15
15
|
"BinaryEntriesStream.js",
|
|
16
16
|
"BinaryStream.js",
|
|
17
|
-
"DatabaseHandler.js",
|
|
18
17
|
"DatabaseServer.js",
|
|
18
|
+
"DatabaseHandler.js",
|
|
19
|
+
"DatabaseLoadBalancer.js",
|
|
20
|
+
"loadBalancerHandler.js",
|
|
19
21
|
"DiskHashTableClient.js",
|
|
20
22
|
"DiskSortedHashTableClient.js",
|
|
21
23
|
"TextEntriesStream.js",
|
|
@@ -23,7 +25,9 @@
|
|
|
23
25
|
"buffersToEntry.js",
|
|
24
26
|
"handleServerResponse.js",
|
|
25
27
|
"healthCheckHandler.js",
|
|
26
|
-
"notFoundHandler.js"
|
|
28
|
+
"notFoundHandler.js",
|
|
29
|
+
"errorHandler.js",
|
|
30
|
+
"parseString.js"
|
|
27
31
|
],
|
|
28
32
|
"keywords": [
|
|
29
33
|
"presidium",
|
|
@@ -41,7 +45,7 @@
|
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
43
47
|
"presidium": "^4.5.0",
|
|
44
|
-
"rubico": "^2.
|
|
48
|
+
"rubico": "^2.21.0"
|
|
45
49
|
},
|
|
46
50
|
"devDependencies": {
|
|
47
51
|
"codecov": "^3.8.2"
|
package/parseString.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const numberRegex = /^[0-9\.]+$/
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @name parseString
|
|
5
|
+
*
|
|
6
|
+
* @docs
|
|
7
|
+
* ```specscript
|
|
8
|
+
* parseString(s string) -> number|string
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
function parseString(s) {
|
|
12
|
+
if (numberRegex.test(s)) {
|
|
13
|
+
return Number(s)
|
|
14
|
+
}
|
|
15
|
+
return s
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = parseString
|