summaryrefslogtreecommitdiff
path: root/scripts/gerrit/cherry-pick_automation/startupStateRecovery.js
blob: 12f4ecbd14367e7acb65ed1b2a0c5a0572dfbc17 (plain)
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;