@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 +3 -1
- package/clear.js +95 -0
- package/index.js +41 -2
- package/package.json +4 -4
- package/clearEvents.js +0 -21
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 {
|
|
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
|
-
|
|
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.
|
|
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.
|
|
13
|
-
"@live-change/framework": "^0.8.
|
|
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": "
|
|
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
|
-
}
|