@muze-nl/simplystore 0.5.3 → 0.5.5
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/package.json +1 -1
- package/src/command-worker-module.mjs +26 -22
- package/src/command-worker.mjs +2 -6
- package/src/fastParse.mjs +5 -3
- package/src/server.mjs +86 -71
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import JSONTag from '@muze-nl/jsontag'
|
|
2
|
-
import {source} from './symbols.mjs'
|
|
2
|
+
import {source, isChanged} from './symbols.mjs'
|
|
3
3
|
import fastParse from './fastParse.mjs'
|
|
4
4
|
import {stringToSAB,resultSetStringify} from './fastStringify.mjs'
|
|
5
5
|
import writeFileAtomic from 'write-file-atomic'
|
|
@@ -25,7 +25,7 @@ export const metaIdProxy = {
|
|
|
25
25
|
})
|
|
26
26
|
},
|
|
27
27
|
set: (id,ref) => {
|
|
28
|
-
//
|
|
28
|
+
//FIXME: is this correct?
|
|
29
29
|
meta.index.id.set(id, resultSet.length-1)
|
|
30
30
|
},
|
|
31
31
|
get: (id) => {
|
|
@@ -46,7 +46,10 @@ export const metaIdProxy = {
|
|
|
46
46
|
export const FastJSONTag = {
|
|
47
47
|
getType: (obj) => JSONTag.getType(obj[source]),
|
|
48
48
|
getAttribute: (obj, attr) => JSONTag.getAttribute(obj[source],attr),
|
|
49
|
-
setAttribute: (obj, attr, value) =>
|
|
49
|
+
setAttribute: (obj, attr, value) => {
|
|
50
|
+
obj[isChanged] = true
|
|
51
|
+
return JSONTag.setAttribute(obj[source], attr, value)
|
|
52
|
+
},
|
|
50
53
|
getAttributes: (obj) => JSONTag.getAttributes(obj[source]),
|
|
51
54
|
getAttributeString: (obj) => JSONTag.getAttributesString(obj[source]),
|
|
52
55
|
getTypeString: (obj) => JSONTag.getTypeString(obj[source])
|
|
@@ -59,6 +62,7 @@ export async function initialize(task) {
|
|
|
59
62
|
metaProxy.index.id = metaIdProxy
|
|
60
63
|
datafile = task.datafile
|
|
61
64
|
commands = await import(task.commandsFile).then(mod => {
|
|
65
|
+
console.log('commands loaded:',Object.keys(mod.default))
|
|
62
66
|
return mod.default
|
|
63
67
|
})
|
|
64
68
|
}
|
|
@@ -71,29 +75,29 @@ export default async function runCommand(commandStr, request) {
|
|
|
71
75
|
jsontag: true
|
|
72
76
|
}
|
|
73
77
|
if (commands[task.name]) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
let time = Date.now()
|
|
79
|
+
commands[task.name](dataspace, task, request, metaProxy)
|
|
80
|
+
//TODO: if command/task makes no changes, skip updating data.jsontag and writing it, skip response.data
|
|
81
|
+
FastJSONTag.setAttribute(dataspace, 'command', task.id)
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
83
|
+
const strData = resultSetStringify(resultSet)
|
|
84
|
+
const uint8sab = stringToSAB(strData)
|
|
85
|
+
response.data = uint8sab
|
|
86
|
+
response.meta = {
|
|
87
|
+
index: {
|
|
88
|
+
id: meta.index.id
|
|
85
89
|
}
|
|
86
|
-
|
|
87
|
-
await writeFileAtomic(datafile, uint8sab)
|
|
88
|
-
} catch(err) {
|
|
89
|
-
console.error('error',err)
|
|
90
|
-
response.code = 422;
|
|
91
|
-
response.body = '<object class="Error">{"message":'+JSON.stringify(''+err)+',"code":422}'
|
|
92
90
|
}
|
|
91
|
+
//TODO: write data every x commands or x minutes, in seperate thread
|
|
92
|
+
await writeFileAtomic(datafile, uint8sab)
|
|
93
|
+
let end = Date.now()
|
|
94
|
+
console.log('task time',end-time)
|
|
93
95
|
} else {
|
|
94
|
-
console.error('Command not found', task.name
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
console.error('Command not found', task.name)
|
|
97
|
+
throw {
|
|
98
|
+
code: 404,
|
|
99
|
+
message: "Command "+task.name+" not found"
|
|
100
|
+
}
|
|
97
101
|
}
|
|
98
102
|
return response
|
|
99
103
|
}
|
package/src/command-worker.mjs
CHANGED
|
@@ -3,11 +3,7 @@ import runCommand, { initialize } from '../src/command-worker-module.mjs'
|
|
|
3
3
|
|
|
4
4
|
parentPort.on('message', async data => {
|
|
5
5
|
let result
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
result = await runCommand(data.command)
|
|
9
|
-
} catch(err) {
|
|
10
|
-
result = { error: err.message }
|
|
11
|
-
}
|
|
6
|
+
await initialize(data)
|
|
7
|
+
result = await runCommand(data.command)
|
|
12
8
|
parentPort.postMessage(result)
|
|
13
9
|
})
|
package/src/fastParse.mjs
CHANGED
|
@@ -838,10 +838,12 @@ export default function parse(input, meta, immutable=true)
|
|
|
838
838
|
set(target, prop, value) {
|
|
839
839
|
if (!immutable) {
|
|
840
840
|
firstParse()
|
|
841
|
-
if (
|
|
842
|
-
value
|
|
841
|
+
if (prop!==isChanged) {
|
|
842
|
+
if (JSONTag.getType(value)==='object' && !value[isProxy]) {
|
|
843
|
+
value = getNewValueProxy(value)
|
|
844
|
+
}
|
|
845
|
+
target[prop] = value
|
|
843
846
|
}
|
|
844
|
-
target[prop] = value
|
|
845
847
|
targetIsChanged = true
|
|
846
848
|
return true
|
|
847
849
|
}
|
package/src/server.mjs
CHANGED
|
@@ -29,43 +29,43 @@ async function main(options) {
|
|
|
29
29
|
const commandLog = options.commandLog || './command-log.jsontag'
|
|
30
30
|
const commandStatus = options.commandStatus || './command-status.jsontag'
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
server.use(express.static(wwwroot))
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// allow access to raw body, used to parse a query send as post body
|
|
35
35
|
server.use(express.raw({
|
|
36
36
|
type: (req) => true // parse body on all requests
|
|
37
37
|
}))
|
|
38
38
|
|
|
39
39
|
function loadData() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
return new Promise((resolve,reject) => {
|
|
41
|
+
let worker = new Worker(loadWorker)
|
|
42
|
+
worker.on('message', result => {
|
|
43
|
+
resolve(result)
|
|
44
|
+
worker.terminate()
|
|
45
|
+
})
|
|
46
|
+
worker.on('error', error => {
|
|
47
|
+
reject(error)
|
|
48
|
+
worker.terminate()
|
|
49
|
+
})
|
|
50
|
+
worker.postMessage(datafile)
|
|
51
|
+
})
|
|
52
52
|
}
|
|
53
53
|
try {
|
|
54
54
|
let data = await loadData()
|
|
55
|
-
|
|
55
|
+
jsontagBuffer = data.data
|
|
56
56
|
meta = data.meta
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
} catch(err) {
|
|
58
|
+
console.error('ERROR: SimplyStore cannot load '+datafile, err)
|
|
59
|
+
process.exit(1)
|
|
60
|
+
}
|
|
61
61
|
|
|
62
62
|
const queryWorkerInitTask = () => {
|
|
63
63
|
return {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
name: 'init',
|
|
65
|
+
req: {
|
|
66
|
+
body: jsontagBuffer,
|
|
67
67
|
meta
|
|
68
|
-
|
|
68
|
+
}
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -94,19 +94,19 @@ async function main(options) {
|
|
|
94
94
|
let path = req.path.substr(6) // cut '/query'
|
|
95
95
|
console.log('query',path)
|
|
96
96
|
let request = {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
method: req.method,
|
|
98
|
+
url: req.originalUrl,
|
|
99
|
+
query: req.query,
|
|
100
|
+
path: path
|
|
101
101
|
}
|
|
102
102
|
if (accept(req,res,['application/jsontag'])) {
|
|
103
103
|
request.jsontag = true
|
|
104
104
|
}
|
|
105
105
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
let result = await queryWorkerPool.run('query', request)
|
|
107
|
+
sendResponse(result, res)
|
|
108
108
|
} catch(error) {
|
|
109
|
-
|
|
109
|
+
sendError(error, res)
|
|
110
110
|
}
|
|
111
111
|
let end = Date.now()
|
|
112
112
|
console.log(path, (end-start), process.memoryUsage())
|
|
@@ -122,20 +122,20 @@ async function main(options) {
|
|
|
122
122
|
}
|
|
123
123
|
let path = req.path.substr(6) // cut '/query'
|
|
124
124
|
let request = {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
method: req.method,
|
|
126
|
+
url: req.originalUrl,
|
|
127
|
+
query: req.query,
|
|
128
|
+
path: path,
|
|
129
|
+
body: req.body.toString()
|
|
130
130
|
}
|
|
131
131
|
if (accept(req,res,['application/jsontag'])) {
|
|
132
132
|
request.jsontag = true
|
|
133
133
|
}
|
|
134
134
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
let result = await queryWorkerPool.run('query', request)
|
|
136
|
+
sendResponse(result, res)
|
|
137
137
|
} catch(error) {
|
|
138
|
-
|
|
138
|
+
sendError(error, res)
|
|
139
139
|
}
|
|
140
140
|
let end = Date.now()
|
|
141
141
|
console.log(path, (end-start), process.memoryUsage())
|
|
@@ -153,9 +153,13 @@ async function main(options) {
|
|
|
153
153
|
let lines = file.split("\n").filter(Boolean) //filter clears empty lines
|
|
154
154
|
for(let line of lines) {
|
|
155
155
|
let command = JSONTag.parse(line)
|
|
156
|
-
status.set(command.
|
|
156
|
+
status.set(command.command, command)
|
|
157
157
|
}
|
|
158
|
+
} else {
|
|
159
|
+
console.error('Could not open command status',commandStatusFile)
|
|
158
160
|
}
|
|
161
|
+
} else {
|
|
162
|
+
console.log('no command status', commandStatusFile)
|
|
159
163
|
}
|
|
160
164
|
return status
|
|
161
165
|
}
|
|
@@ -208,31 +212,38 @@ async function main(options) {
|
|
|
208
212
|
start(
|
|
209
213
|
// resolve()
|
|
210
214
|
(data) => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
let s
|
|
216
|
+
if (!data || (data.code>=300 && data.code<=499)) {
|
|
217
|
+
console.error('ERROR: SimplyStore cannot run command ', command.id, data)
|
|
218
|
+
if (!data?.code) {
|
|
219
|
+
s = {code: 500, status: "failed"}
|
|
216
220
|
} else {
|
|
217
|
-
status.
|
|
218
|
-
|
|
221
|
+
s = {code: data.code, status: "failed", message: data.message, details: data.details}
|
|
222
|
+
}
|
|
223
|
+
status.set(command.id, s)
|
|
224
|
+
} else {
|
|
225
|
+
s = {code: 200, status: "done"}
|
|
226
|
+
status.set(command.id, s)
|
|
227
|
+
if (data.data) { // data has changed, commands may do other things instead of changing data
|
|
228
|
+
jsontagBuffer = data.data
|
|
229
|
+
meta = data.meta
|
|
230
|
+
// restart query workers with new data
|
|
231
|
+
let oldPool = queryWorkerPool
|
|
232
|
+
queryWorkerPool = new WorkerPool(maxWorkers, queryWorker, queryWorkerInitTask())
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
oldPool.close()
|
|
235
|
+
}, 2000)
|
|
219
236
|
}
|
|
220
237
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
appendFile(commandStatus, JSONTag.stringify({command:command.id, status: 'done'}))
|
|
225
|
-
// restart query workers with new data
|
|
226
|
-
let oldPool = queryWorkerPool
|
|
227
|
-
queryWorkerPool = new WorkerPool(maxWorkers, queryWorker, queryWorkerInitTask())
|
|
228
|
-
setTimeout(() => {
|
|
229
|
-
oldPool.close()
|
|
230
|
-
}, 2000)
|
|
238
|
+
let l = Object.assign({command:command.id}, s)
|
|
239
|
+
console.log('appending status ',l,s)
|
|
240
|
+
appendFile(commandStatus, JSONTag.stringify(Object.assign({command:command.id}, s)))
|
|
231
241
|
},
|
|
232
242
|
//reject()
|
|
233
243
|
(error) => {
|
|
234
|
-
status.
|
|
235
|
-
|
|
244
|
+
let s = {status: "failed", code: error.code, message: error.message, details: error.details}
|
|
245
|
+
status.set(command.id, s)
|
|
246
|
+
appendFile(commandStatus, JSONTag.stringify(Object.assign({command:command.id}, s)))
|
|
236
247
|
}
|
|
237
248
|
)
|
|
238
249
|
} else {
|
|
@@ -270,8 +281,9 @@ async function main(options) {
|
|
|
270
281
|
|
|
271
282
|
runNextCommand()
|
|
272
283
|
} catch(err) {
|
|
273
|
-
|
|
274
|
-
status.set(commandId,
|
|
284
|
+
let s = {code:err.code||500, status:'failed', message:err.message, details:err.details}
|
|
285
|
+
status.set(commandId, s)
|
|
286
|
+
appendFile(commandStatus, JSONTag.stringify(Object.assign({command:commandId}, s)))
|
|
275
287
|
console.error('ERROR: SimplyStore cannot run command ', commandId, err)
|
|
276
288
|
}
|
|
277
289
|
})
|
|
@@ -279,32 +291,35 @@ async function main(options) {
|
|
|
279
291
|
function checkCommand(req, res) {
|
|
280
292
|
let commandStr = req.body.toString() // raw body through express.raw()
|
|
281
293
|
let command = JSONTag.parse(commandStr)
|
|
294
|
+
let commandOK = {
|
|
295
|
+
command: command?.id,
|
|
296
|
+
code: 202,
|
|
297
|
+
status: 'accepted'
|
|
298
|
+
}
|
|
282
299
|
if (!command || !command.id) {
|
|
283
300
|
error = {
|
|
284
301
|
code: 422,
|
|
285
|
-
message: "Command has no id"
|
|
302
|
+
message: "Command has no id",
|
|
303
|
+
details: command
|
|
286
304
|
}
|
|
287
305
|
sendResponse({code: 422, body: JSON.stringify(error)}, res)
|
|
288
306
|
return false
|
|
289
307
|
} else if (status.has(command.id)) {
|
|
290
|
-
|
|
291
|
-
sendResponse({body: JSON.stringify(result)}, res)
|
|
308
|
+
sendResponse({body: JSON.stringify(s)}, res)
|
|
292
309
|
return false
|
|
293
310
|
} else if (!command.name) {
|
|
294
311
|
error = {
|
|
295
312
|
code: 422,
|
|
296
|
-
message: "Command has no name"
|
|
313
|
+
message: "Command has no name",
|
|
314
|
+
details: command
|
|
297
315
|
}
|
|
298
316
|
sendResponse({code:422, body: JSON.stringify(error)}, res)
|
|
299
317
|
return false
|
|
300
318
|
}
|
|
301
319
|
appendFile(commandLog, JSONTag.stringify(command))
|
|
302
|
-
appendFile(commandStatus, JSONTag.stringify(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}))
|
|
306
|
-
status.set(command.id, 'accepted')
|
|
307
|
-
sendResponse({code: 202, body: '"Accepted"'}, res)
|
|
320
|
+
appendFile(commandStatus, JSONTag.stringify(commandOK))
|
|
321
|
+
status.set(command.id, commandOK)
|
|
322
|
+
sendResponse({code: 202, body: JSON.stringify(commandOK)}, res)
|
|
308
323
|
return command.id
|
|
309
324
|
}
|
|
310
325
|
|
|
@@ -319,7 +334,7 @@ async function main(options) {
|
|
|
319
334
|
sendResponse({
|
|
320
335
|
code: 404,
|
|
321
336
|
jsontag: false,
|
|
322
|
-
body: JSON.stringify({code: 404, message: "Command not found"})
|
|
337
|
+
body: JSON.stringify({code: 404, message: "Command not found", details: req.params.id})
|
|
323
338
|
}, res)
|
|
324
339
|
}
|
|
325
340
|
})
|