@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muze-nl/simplystore",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "main": "src/server.mjs",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -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
- //FICME: is this correct?
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) => JSONTag.setAttribute(obj[source], 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
- try {
75
- commands[task.name](dataspace, task, request, metaProxy)
76
- FastJSONTag.setAttribute(dataspace, 'command', task.id)
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
- const strData = resultSetStringify(resultSet)
79
- const uint8sab = stringToSAB(strData)
80
- response.data = uint8sab
81
- response.meta = {
82
- index: {
83
- id: meta.index.id
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, commands)
95
- response.code = 404
96
- response.body = '<object class="Error">{"message":"Command '+task.name+' not found","code":404}'
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
  }
@@ -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
- try {
7
- await initialize(data)
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 (JSONTag.getType(value)==='object' && !value[isProxy]) {
842
- value = getNewValueProxy(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
- server.use(express.static(wwwroot))
32
+ server.use(express.static(wwwroot))
33
33
 
34
- // allow access to raw body, used to parse a query send as post body
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
- 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
- })
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
- jsontagBuffer = data.data
55
+ jsontagBuffer = data.data
56
56
  meta = data.meta
57
- } catch(err) {
58
- console.error('ERROR: SimplyStore cannot load '+datafile, err)
59
- process.exit(1)
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
- name: 'init',
65
- req: {
66
- body: jsontagBuffer,
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
- method: req.method,
98
- url: req.originalUrl,
99
- query: req.query,
100
- path: path
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
- let result = await queryWorkerPool.run('query', request)
107
- sendResponse(result, res)
106
+ let result = await queryWorkerPool.run('query', request)
107
+ sendResponse(result, res)
108
108
  } catch(error) {
109
- sendError(error, res)
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
- method: req.method,
126
- url: req.originalUrl,
127
- query: req.query,
128
- path: path,
129
- body: req.body.toString()
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
- let result = await queryWorkerPool.run('query', request)
136
- sendResponse(result, res)
135
+ let result = await queryWorkerPool.run('query', request)
136
+ sendResponse(result, res)
137
137
  } catch(error) {
138
- sendError(error, res)
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.id, command.status)
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
- if (!data || data.error) {
212
- console.error('ERROR: SimplyStore cannot run command ', command.id, err)
213
- if (!data.error) {
214
- status.set(command.id, 'failed')
215
- throw new Error('Unexpected command failure')
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.set(command.id, data.error)
218
- throw data.error
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
- jsontagBuffer = data.data
222
- meta = data.meta
223
- status.set(command.id, 'done')
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.set(command.id, error)
235
- appendFile(commandStatus, JSONTag.stringify({command:command.id, status: error}))
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
- appendFile(commandStatus, JSONTag.stringify({command:commandId, status: 'ERROR: '+err.message}))
274
- status.set(commandId, 'ERROR: '+err.message)
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
- result = "OK"
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
- command: command.id,
304
- status: 'accepted'
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
  })