@live-change/backup-service 0.8.137 → 0.8.139

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/backup.js CHANGED
@@ -33,7 +33,9 @@ async function writeDbBackup(stream) {
33
33
  //serverUrl: process.env.DB_URL || 'http://localhost:9417/api/ws',
34
34
  //db: process.env.DB_NAME || 'test',
35
35
  structure: true,
36
- verbose: true
36
+ verbose: true,
37
+ bucket: 128, // less database stress
38
+ delay: 10 // less database stress
37
39
  },
38
40
  (method, ...args) => write({ type: 'request', method, parameters: args }),
39
41
  () => write({ type: 'sync' })
package/clear.js ADDED
@@ -0,0 +1,95 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+
5
+ export async function getLastEventId() {
6
+ if(app.splitEvents) {
7
+ throw new Error("getLastEventId not supported in split events mode")
8
+ } else {
9
+ const lastLogs = await app.dao.get(['database', 'logRange', app.databaseName, 'events', { reverse: true, limit: 1 }])
10
+ const lastLog = lastLogs[0]
11
+ return lastLog?.id || ''
12
+ }
13
+ }
14
+
15
+ export async function removeOldEvents(before) {
16
+ if(app.splitEvents) {
17
+ throw new Error("removeOldEvents not supported in split events mode")
18
+ } else {
19
+ await app.dao.request(['database', 'clearLog'], app.databaseName, 'events', before)
20
+ }
21
+ }
22
+
23
+ export async function removeOldOpLogs(age = 60*60*1000) {
24
+ const clearLimit = 40
25
+ let cleared = 0
26
+ do {
27
+ const clearResults = await app.dao.requestWithSettings({ requestTimeout: 100*1e3 },
28
+ ['database', 'clearDatabaseOpLogs'], app.databaseName, Date.now() - age, clearLimit)
29
+ cleared = 0
30
+ for(const result of clearResults.results) if(result.count > cleared) cleared = result.count
31
+ //console.log("REMOVED", clearResults.count, "OP LOGS", cleared, "MAX")
32
+ await new Promise(resolve => setTimeout(resolve, 50)) // reduceDatabaseStress
33
+ } while(cleared === clearLimit)
34
+ }
35
+
36
+ export async function getLastTriggerTimestamp() {
37
+ if(app.splitTriggers) {
38
+ throw new Error("getLastTrigger not supported in split triggers mode")
39
+ }
40
+ const lastTriggers = await app.dao.get(
41
+ ['database', 'indexRange', app.databaseName, 'triggers_byTimestamp', { reverse: true, limit: 1 }])
42
+ const lastTrigger = lastTriggers[0]?.id
43
+ return lastTrigger ?? null
44
+ }
45
+
46
+ export async function getLastCommandTimestamp() {
47
+ if(app.splitCommands) {
48
+ throw new Error("getLastCommand not supported in split commands mode")
49
+ }
50
+ const lastCommands = await app.dao.get(
51
+ ['database', 'indexRange', app.databaseName, 'commands_byTimestamp', { reverse: true, limit: 1 }])
52
+ const lastCommand = lastCommands[0]?.id
53
+ return lastCommand ?? null
54
+ }
55
+
56
+ const clearQuery = `${async (input, output, { tableName, bucket, before }) => {
57
+ const indexReader = await input.index(tableName + '_byTimestamp')
58
+ const tableWriter = await output.table(tableName)
59
+ const range = { limit: bucket, lt: before }
60
+ const pointers = await indexReader.get(range)
61
+ for(const pointer of pointers) {
62
+ await tableWriter.delete(pointer.to)
63
+ }
64
+ await output.put({ count: pointers.length })
65
+ }}`
66
+
67
+ export async function removeOldTriggers(before, options) {
68
+ const bucket = options?.bucket || 128
69
+ if(app.splitTriggers) {
70
+ throw new Error("removeOldTriggers not supported in split triggers mode")
71
+ }
72
+ let more = true
73
+ while(more) {
74
+ const queryResult = await app.dao.request(['database', 'query'], app.databaseName, clearQuery,
75
+ { tableName: 'triggers', bucket, before })
76
+ const deleteStats = queryResult[0]
77
+ if(options.delay) await new Promise(resolve => setTimeout(resolve, options.delay))
78
+ more = deleteStats.count >= bucket
79
+ }
80
+ }
81
+
82
+ export async function removeOldCommands(before, options) {
83
+ const bucket = options?.bucket || 128
84
+ if(app.splitCommands) {
85
+ throw new Error("removeOldCommands not supported in split commands mode")
86
+ }
87
+ let more = true
88
+ while(more) {
89
+ const queryResult = await app.dao.request(['database', 'query'], app.databaseName, clearQuery,
90
+ { tableName: 'commands', bucket, before })
91
+ const deleteStats = queryResult[0]
92
+ if(options.delay) await new Promise(resolve => setTimeout(resolve, options.delay))
93
+ more = deleteStats.count >= bucket
94
+ }
95
+ }
package/index.js CHANGED
@@ -10,7 +10,10 @@ const expressApp = express()
10
10
  import fs from 'fs'
11
11
  import { createBackup, currentBackupPath, removeOldBackups } from './backup.js'
12
12
  import { restoreBackup } from './restore.js'
13
- import { getLastEventId, removeOldEvents } from './clearEvents.js'
13
+ import {
14
+ getLastEventId, removeOldEvents, removeOldOpLogs,
15
+ getLastCommandTimestamp, getLastTriggerTimestamp, removeOldTriggers, removeOldCommands
16
+ } from './clear.js'
14
17
 
15
18
  import progress from "progress-stream"
16
19
 
@@ -46,6 +49,7 @@ if(Object.keys(users) > 0) {
46
49
  }))
47
50
  }
48
51
 
52
+
49
53
  function doBackup() {
50
54
  console.log("DOING BACKUP!")
51
55
  if(currentBackup) return currentBackup
@@ -54,10 +58,45 @@ function doBackup() {
54
58
  currentBackup = {
55
59
  path, filename,
56
60
  promise: queue.add(async () => {
57
- const lastEventId = await getLastEventId()
61
+ let lastEventId = await getLastEventId()
62
+ let lastTriggerTimestamp = await getLastTriggerTimestamp()
63
+ let lastCommandTimestamp = await getLastCommandTimestamp()
64
+
58
65
  await removeOldBackups(backupsDir, 10 * TWENTY_FOUR_HOURS, 10)
59
66
  await createBackup(path)
67
+
68
+ console.log("====== CLEAR STATS:")
69
+ console.log("Last event id:", lastEventId)
70
+ console.log("Last trigger timestamp:", lastTriggerTimestamp)
71
+ console.log("Last command timestamp:", lastCommandTimestamp)
72
+
73
+ if(Number.isInteger(config.clearEvents) && lastEventId) {
74
+ const ts = (+(lastEventId.split(':')[0]) - config.clearEvents)
75
+ console.log("Clear events before", new Date(ts))
76
+ lastEventId = ts.toFixed(0).padStart(16, '0')+':'
77
+ }
78
+ if(Number.isInteger(config.clearTriggers) && lastTriggerTimestamp) {
79
+ const date = new Date(lastTriggerTimestamp)
80
+ const old = new Date(date.getTime() - config.clearTriggers)
81
+ console.log("Clear triggers before", old)
82
+ lastTriggerTimestamp = old.toISOString()
83
+ }
84
+ if(Number.isInteger(config.clearCommands) && lastCommandTimestamp) {
85
+ const date = new Date(lastCommandTimestamp)
86
+ const old = new Date(date.getTime() - config.clearCommands)
87
+ console.log("Clear commands before", old)
88
+ lastCommandTimestamp = old.toISOString()
89
+ }
90
+
91
+ console.log("====== CLEARING:")
92
+ console.log("Last remaining event id:", lastEventId)
93
+ console.log("Last remaining trigger timestamp:", lastTriggerTimestamp)
94
+ console.log("Last remaining command timestamp:", lastCommandTimestamp)
95
+
96
+ if(config.clearCommands) await removeOldCommands(lastCommandTimestamp, { delay: 10 })
97
+ if(config.clearTriggers) await removeOldTriggers(lastTriggerTimestamp, { delay: 10 })
60
98
  if(config.clearEvents) await removeOldEvents(lastEventId)
99
+ if(config.clearOpLogs) await removeOldOpLogs()
61
100
  })
62
101
  }
63
102
  currentBackup.promise.then(done => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/backup-service",
3
- "version": "0.8.137",
3
+ "version": "0.8.139",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -9,14 +9,14 @@
9
9
  "author": "Michał Łaszczewski <michal@emikse.com>",
10
10
  "license": "BSD-3-Clause",
11
11
  "dependencies": {
12
- "@live-change/db-client": "^0.8.137",
13
- "@live-change/framework": "^0.8.137",
12
+ "@live-change/db-client": "^0.8.139",
13
+ "@live-change/framework": "^0.8.139",
14
14
  "express": "^4.18.2",
15
15
  "express-basic-auth": "^1.2.1",
16
16
  "fs-extra": "^11.1.1",
17
17
  "p-queue": "^8.0.1",
18
18
  "progress-stream": "^2.0.0"
19
19
  },
20
- "gitHead": "cb42255fbe7aa794dc5d7a6e57250ce8c959acf9",
20
+ "gitHead": "7f54396b91dc7b596380ec4d8b182c12c765fc6f",
21
21
  "type": "module"
22
22
  }
package/clearEvents.js DELETED
@@ -1,21 +0,0 @@
1
- import App from '@live-change/framework'
2
- const app = App.app()
3
- import definition from './definition.js'
4
-
5
- export async function getLastEventId() {
6
- if(app.splitEvents) {
7
- throw new Error("getLastEventId not supported in split events mode")
8
- } else {
9
- const lastLogs = app.dao.get(['database', 'logRange', app.databaseName, 'events', { reverse: true, limit: 1 }])
10
- const lastLog = lastLogs[0]
11
- return lastLog?.id || ''
12
- }
13
- }
14
-
15
- export async function removeOldEvents(before) {
16
- if(app.splitEvents) {
17
- throw new Error("removeOldEvents not supported in split events mode")
18
- } else {
19
- await app.dao.request(['database', 'clearLog'], app.databaseName, 'events', before)
20
- }
21
- }