@certik/skynet 0.10.9 → 0.10.12
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/CHANGELOG.md +12 -0
- package/api.js +5 -2
- package/app.js +10 -1
- package/deploy.js +1 -25
- package/distributed-lock.js +113 -0
- package/kafka.js +4 -1
- package/package.json +1 -1
- package/selector.js +25 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.12
|
|
4
|
+
|
|
5
|
+
- Fixed distributed lock name for `producer` and `api` type apps
|
|
6
|
+
|
|
7
|
+
## 0.10.11
|
|
8
|
+
|
|
9
|
+
- Added distributed lock for `producer` and `api` type apps
|
|
10
|
+
|
|
11
|
+
## 0.10.10
|
|
12
|
+
|
|
13
|
+
- Updated `every` helper function in `app` library to support usage like `every(1).days` and `every(10).day`
|
|
14
|
+
|
|
3
15
|
## 0.10.9
|
|
4
16
|
|
|
5
17
|
- Improved `inline.log` to print one level of object
|
package/api.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const express = require("express");
|
|
2
2
|
const meow = require("meow");
|
|
3
|
-
const { getSelectorFlags, getSelectorDesc } = require("./selector");
|
|
3
|
+
const { getJobName, getSelectorFlags, getSelectorDesc } = require("./selector");
|
|
4
4
|
const { isProduction } = require("./env");
|
|
5
|
+
const { useLock } = require("./distributed-lock");
|
|
5
6
|
const { inline } = require("./log");
|
|
6
7
|
|
|
7
8
|
async function logMiddleware(req, res, next) {
|
|
@@ -54,7 +55,7 @@ const apiKeyMiddleware = (key) => {
|
|
|
54
55
|
return requireAPIKey;
|
|
55
56
|
};
|
|
56
57
|
|
|
57
|
-
function startApiApp({ binaryName, name, selector = {}, routes, serve }) {
|
|
58
|
+
async function startApiApp({ binaryName, name, selector = {}, routes, serve }) {
|
|
58
59
|
const app = express();
|
|
59
60
|
app.use(express.json());
|
|
60
61
|
|
|
@@ -82,6 +83,8 @@ ${getSelectorDesc(selector)}
|
|
|
82
83
|
|
|
83
84
|
const { verbose, ...selectorFlags } = cli.flags;
|
|
84
85
|
|
|
86
|
+
await useLock({ name: getJobName(name, selectorFlags), ttl: 120, verbose });
|
|
87
|
+
|
|
85
88
|
// for health check
|
|
86
89
|
app.get("/", (req, res) => {
|
|
87
90
|
res.send("ok");
|
package/app.js
CHANGED
|
@@ -635,7 +635,7 @@ function api({ name, routes, serve, env = {}, region = "us-east-1" }) {
|
|
|
635
635
|
env,
|
|
636
636
|
check,
|
|
637
637
|
onRun: () => {
|
|
638
|
-
startApiApp({
|
|
638
|
+
return startApiApp({
|
|
639
639
|
binaryName: `${getBinaryName()} run`,
|
|
640
640
|
name,
|
|
641
641
|
selector,
|
|
@@ -691,17 +691,26 @@ const every = (n = 1) => {
|
|
|
691
691
|
if (n === 1) {
|
|
692
692
|
return {
|
|
693
693
|
second: "*/1 * * * * * *",
|
|
694
|
+
seconds: "*/1 * * * * * *",
|
|
694
695
|
minute: "0 * * * * * *",
|
|
696
|
+
minutes: "0 * * * * * *",
|
|
695
697
|
hour: "0 0 * * * * *",
|
|
698
|
+
hours: "0 0 * * * * *",
|
|
696
699
|
day: "0 0 0 * * * *",
|
|
700
|
+
days: "0 0 0 * * * *",
|
|
697
701
|
week: "0 0 0 * * 0 *",
|
|
702
|
+
weeks: "0 0 0 * * 0 *",
|
|
698
703
|
};
|
|
699
704
|
}
|
|
700
705
|
|
|
701
706
|
return {
|
|
707
|
+
second: `*/${n} * * * * * *`,
|
|
702
708
|
seconds: `*/${n} * * * * * *`,
|
|
709
|
+
minute: `0 */${n} * * * * *`,
|
|
703
710
|
minutes: `0 */${n} * * * * *`,
|
|
711
|
+
hour: `0 0 */${n} * * * *`,
|
|
704
712
|
hours: `0 0 */${n} * * * *`,
|
|
713
|
+
day: `0 0 0 */${n} * * *`,
|
|
705
714
|
days: `0 0 0 */${n} * * *`,
|
|
706
715
|
};
|
|
707
716
|
};
|
package/deploy.js
CHANGED
|
@@ -4,7 +4,7 @@ const execa = require("execa");
|
|
|
4
4
|
const meow = require("meow");
|
|
5
5
|
const chalk = require("chalk");
|
|
6
6
|
const which = require("which");
|
|
7
|
-
const { getSelectorFlags, getSelectorDesc } = require("./selector");
|
|
7
|
+
const { getJobName, getSelectorFlags, getSelectorDesc } = require("./selector");
|
|
8
8
|
const { getEnvOrThrow } = require("./env");
|
|
9
9
|
const { getBinaryName, detectSkynetDirectory } = require("./cli");
|
|
10
10
|
|
|
@@ -207,30 +207,6 @@ EOH
|
|
|
207
207
|
}
|
|
208
208
|
}`;
|
|
209
209
|
|
|
210
|
-
function normalizeSelectorValue(v) {
|
|
211
|
-
return v.replace(/[^A-Za-z0-9]+/g, "-");
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function getJobName(name, selectorFlags, mode = null) {
|
|
215
|
-
const selectorNamePart = Object.keys(selectorFlags)
|
|
216
|
-
.sort()
|
|
217
|
-
.map((name) => selectorFlags[name])
|
|
218
|
-
.join("-");
|
|
219
|
-
|
|
220
|
-
let jobName = name;
|
|
221
|
-
|
|
222
|
-
if (mode) {
|
|
223
|
-
jobName += `-${mode}`;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (selectorNamePart.length > 0) {
|
|
227
|
-
// handle special case
|
|
228
|
-
jobName += `-${normalizeSelectorValue(selectorNamePart)}`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return jobName;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
210
|
async function prepareNomad(isProduction) {
|
|
235
211
|
if (isProduction) {
|
|
236
212
|
console.log("Deploy to Production");
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const fetch = require("node-fetch");
|
|
2
|
+
|
|
3
|
+
async function acquireLock(name, ttl) {
|
|
4
|
+
try {
|
|
5
|
+
const res = await fetch("http://localhost:8500/v1/session/create", {
|
|
6
|
+
method: "PUT",
|
|
7
|
+
body: JSON.stringify({
|
|
8
|
+
Name: name,
|
|
9
|
+
TTL: `${ttl}s`,
|
|
10
|
+
Behavior: "delete",
|
|
11
|
+
}),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (res.ok) {
|
|
15
|
+
const { ID: id } = await res.json();
|
|
16
|
+
|
|
17
|
+
return id;
|
|
18
|
+
} else {
|
|
19
|
+
console.log(res, await res.text());
|
|
20
|
+
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
} catch (fetchErr) {
|
|
24
|
+
console.log("error fetching", fetchErr);
|
|
25
|
+
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function renewLock(uid) {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(`http://localhost:8500/v1/session/renew/${uid}`, {
|
|
33
|
+
method: "PUT",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (res.ok) {
|
|
37
|
+
return uid;
|
|
38
|
+
} else {
|
|
39
|
+
console.log(res, await res.text());
|
|
40
|
+
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
} catch (fetchErr) {
|
|
44
|
+
console.log("error fetching", fetchErr);
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function hasLock(name, uid = null) {
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`http://localhost:8500/v1/session/list`);
|
|
53
|
+
|
|
54
|
+
if (res.ok) {
|
|
55
|
+
const sessions = await res.json();
|
|
56
|
+
|
|
57
|
+
const sessionsWithTheSameName = sessions.filter((s) => s.Name === name);
|
|
58
|
+
|
|
59
|
+
if (!uid) {
|
|
60
|
+
return sessionsWithTheSameName.length === 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (sessionsWithTheSameName.length !== 1) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// only one should have the same uid
|
|
68
|
+
return sessionsWithTheSameName[0].ID === uid;
|
|
69
|
+
} else {
|
|
70
|
+
console.log(await res.text());
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
} catch (fetchErr) {
|
|
75
|
+
console.log("error fetching", fetchErr);
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function useLock({ name, ttl, verbose }) {
|
|
82
|
+
const lockWarningMessage = `only one process with the same lock name ${name} should be running, terminate current process`;
|
|
83
|
+
|
|
84
|
+
const lockAvailable = await hasLock(name);
|
|
85
|
+
|
|
86
|
+
if (!lockAvailable) {
|
|
87
|
+
console.log(lockWarningMessage);
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let uid = await acquireLock(name, ttl);
|
|
92
|
+
|
|
93
|
+
setInterval(async () => {
|
|
94
|
+
if (!(await hasLock(name, uid))) {
|
|
95
|
+
console.log(lockWarningMessage);
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (uid) {
|
|
100
|
+
if (verbose) {
|
|
101
|
+
console.log("renewing", name, uid);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
uid = await renewLock(uid);
|
|
105
|
+
} else {
|
|
106
|
+
uid = await acquireLock(name, ttl);
|
|
107
|
+
}
|
|
108
|
+
}, (ttl * 1000) / 2);
|
|
109
|
+
|
|
110
|
+
return uid;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { useLock };
|
package/kafka.js
CHANGED
|
@@ -2,9 +2,10 @@ const meow = require("meow");
|
|
|
2
2
|
const { getEnvironment, getEnvOrThrow } = require("./env");
|
|
3
3
|
const { wait } = require("./availability");
|
|
4
4
|
const { Kafka, logLevel } = require("kafkajs");
|
|
5
|
-
const { getSelectorFlags, getSelectorDesc, toSelectorString } = require("./selector");
|
|
5
|
+
const { getJobName, getSelectorFlags, getSelectorDesc, toSelectorString } = require("./selector");
|
|
6
6
|
const { createRecord, getRecordByKey, deleteRecordsByHashKey } = require("./dynamodb");
|
|
7
7
|
const { exponentialRetry } = require("./availability");
|
|
8
|
+
const { useLock } = require("./distributed-lock");
|
|
8
9
|
const { getBinaryName } = require("./cli");
|
|
9
10
|
const { inline } = require("./log");
|
|
10
11
|
|
|
@@ -218,6 +219,8 @@ ${getSelectorDesc(selector)}
|
|
|
218
219
|
process.exit(0);
|
|
219
220
|
}
|
|
220
221
|
|
|
222
|
+
await useLock({ name: getJobName(name, selectorFlags), ttl: 120, verbose });
|
|
223
|
+
|
|
221
224
|
if (!from) {
|
|
222
225
|
const prevId = await getProducerLatestId(name, selectorFlags);
|
|
223
226
|
|
package/package.json
CHANGED
package/selector.js
CHANGED
|
@@ -46,7 +46,32 @@ function toSelectorString(selectorFlags, delim = ",") {
|
|
|
46
46
|
.join(delim);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function normalizeSelectorValue(v) {
|
|
50
|
+
return v.replace(/[^A-Za-z0-9]+/g, "-");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getJobName(name, selectorFlags, mode = null) {
|
|
54
|
+
const selectorNamePart = Object.keys(selectorFlags)
|
|
55
|
+
.sort()
|
|
56
|
+
.map((name) => selectorFlags[name])
|
|
57
|
+
.join("-");
|
|
58
|
+
|
|
59
|
+
let jobName = name;
|
|
60
|
+
|
|
61
|
+
if (mode) {
|
|
62
|
+
jobName += `-${mode}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (selectorNamePart.length > 0) {
|
|
66
|
+
// handle special case
|
|
67
|
+
jobName += `-${normalizeSelectorValue(selectorNamePart)}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return jobName;
|
|
71
|
+
}
|
|
72
|
+
|
|
49
73
|
module.exports = {
|
|
74
|
+
getJobName,
|
|
50
75
|
getSelectorDesc,
|
|
51
76
|
getSelectorFlags,
|
|
52
77
|
toSelectorString,
|