1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
const EventEmitter = require("events");
const safeJsonStringify = require("safe-json-stringify");
const toolbox = require("./toolbox");
class startupStateRecovery extends EventEmitter {
constructor(logger, requestProcessor) {
super();
this.logger = logger;
this.requestProcessor = requestProcessor;
}
// Recover all currently processing items and re-emit the last signal
// This will restore the process to the same step it was last on when
// the server shutdown.
restoreProcessingItems() {
let _this = this;
_this.logger.log("Restoring in-process items from the database.");
toolbox.getAllProcessingRequests((success, data) => {
if (success) {
let itemsRemaining = data.length;
_this.logger.log(`found ${itemsRemaining} in-process items to restore...`);
if (itemsRemaining == 0)
_this.emit("restoreProcessingDone");
data.forEach((element) => {
_this.logger.log(`restoring uuid ${element.uuid}`, "info", element.uuid);
let cherrypicks = toolbox.decodeBase64toJSON(element.cherrypick_results_json);
if (Object.keys(cherrypicks).length > 0) {
let cherrypickCount = 0;
for (var branch in cherrypicks) {
if (Object.prototype.hasOwnProperty.call(cherrypicks, branch)) {
// Emitting the event returns 1 if anything's listening
// otherwise, 0.
_this.logger.log(
`Emitting in-processing item recovery: ${cherrypicks[branch].state} with args: ${
safeJsonStringify(cherrypicks[branch].args)}`,
"debug", element.uuid
);
cherrypickCount += _this.requestProcessor.emit(
cherrypicks[branch].state,
...cherrypicks[branch].args || []
);
}
}
// If none of the signals we sent were listened to, make sure
// the item is marked as "complete" so it doesn't stay stuck.
if (cherrypickCount == 0) {
_this.logger.log(
`This in-process item had nothing to restore! Moving it to 'complete' state.`,
"verbose", element.uuid
);
toolbox.decrementPickCountRemaining(element.uuid);
}
} else {
_this.logger.log(
"This in-process item is being recovered as though it's a new item.",
"verbose", element.uuid
);
_this.emit("recoverFromStart", element.uuid);
}
--itemsRemaining;
if (itemsRemaining == 0)
_this.emit("restoreProcessingDone");
});
} else {
_this.logger.log(
`failed to restore processing items from the database! Database error: ${data}`,
"error"
);
_this.emit("restoreProcessingDone");
}
});
}
restoreActionListeners() {
let _this = this;
toolbox.getCachedListeners((success, data) => {
if (success) {
let itemsRemaining = data.length;
_this.logger.log(`found ${itemsRemaining} items with listeners to restore...`);
if (itemsRemaining == 0)
_this.emit("RestoreListenersDone");
data.forEach((element) => {
let jsonData = toolbox.decodeBase64toJSON(element.listener_cache);
if (Object.keys(jsonData).length > 0) {
for (var listenerData in jsonData) {
_this.logger.log(
`restoring listener ${listenerData}`,
"info", jsonData[listenerData][11]
);
_this.logger.log(
`Listener being restored with args: ${safeJsonStringify(jsonData[listenerData])}`,
"debug", jsonData[listenerData][11]
);
// requestProcessor is the only type that should be setting up,
// listeners, but this 'if' below allows for expansion.
if (jsonData[listenerData][0] == "requestProcessor")
jsonData[listenerData][0] = _this.requestProcessor;
// set up the listener with a flag that denotes that it was restored.
// This suppresses the initial gerrit comments.
jsonData[listenerData].push(true);
toolbox.setupListener.apply(this, jsonData[listenerData]);
}
}
--itemsRemaining;
if (itemsRemaining == 0)
_this.emit("RestoreListenersDone");
});
} else {
_this.logger.log(
`failed to restore listeners from the database! Database error: ${data}`,
"error"
);
_this.emit("RestoreListenersDone");
}
});
}
}
module.exports = startupStateRecovery;
|