@certik/skynet 0.10.8 → 0.10.11

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.11
4
+
5
+ - Added distributed lock for `producer` and `api` type apps
6
+
7
+ ## 0.10.10
8
+
9
+ - Updated `every` helper function in `app` library to support usage like `every(1).days` and `every(10).day`
10
+
11
+ ## 0.10.9
12
+
13
+ - Improved `inline.log` to print one level of object
14
+
3
15
  ## 0.10.8
4
16
 
5
17
  - Added logMiddleware to `api` apps by default
package/api.js CHANGED
@@ -2,6 +2,7 @@ const express = require("express");
2
2
  const meow = require("meow");
3
3
  const { 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, 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
  };
@@ -0,0 +1,111 @@
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 lockAvailable = await hasLock(name);
83
+
84
+ if (!lockAvailable) {
85
+ console.log("only one process with the same name should be running, quit");
86
+ process.exit(0);
87
+ }
88
+
89
+ let uid = await acquireLock(name, ttl);
90
+
91
+ setInterval(async () => {
92
+ if (!(await hasLock(name, uid))) {
93
+ console.log("only one process with the same name should be running, quit");
94
+ process.exit(0);
95
+ }
96
+
97
+ if (uid) {
98
+ if (verbose) {
99
+ console.log("renewing", name, uid);
100
+ }
101
+
102
+ uid = await renewLock(uid);
103
+ } else {
104
+ uid = await acquireLock(name, ttl);
105
+ }
106
+ }, (ttl * 1000) / 2);
107
+
108
+ return uid;
109
+ }
110
+
111
+ module.exports = { useLock };
package/kafka.js CHANGED
@@ -5,6 +5,7 @@ const { Kafka, logLevel } = require("kafkajs");
5
5
  const { 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, ttl: 120, verbose });
223
+
221
224
  if (!from) {
222
225
  const prevId = await getProducerLatestId(name, selectorFlags);
223
226
 
package/log.js CHANGED
@@ -1,3 +1,19 @@
1
+ function isObject(a) {
2
+ return !!a && a.constructor === Object;
3
+ }
4
+
5
+ function print(o) {
6
+ if (Array.isArray(o)) {
7
+ return `[${o.map(print).join(", ")}]`
8
+ }
9
+
10
+ if (isObject(o)) {
11
+ return `{${Object.keys(o).map(k => `${k}: ${o[k]}`).join(", ")}}`;
12
+ }
13
+
14
+ return `${o}`;
15
+ }
16
+
1
17
  function getLine(params) {
2
18
  let line = "";
3
19
 
@@ -5,7 +21,7 @@ function getLine(params) {
5
21
  for (let i = 0, l = params.length; i < l; i++) {
6
22
  // Certain objects don't get converted
7
23
  // Note using JSON.stringfy may be too slow for large objects
8
- line += `${params[i]} `.replace(/\n/gm, "\t");
24
+ line += `${print(params[i])} `.replace(/\n/gm, "\t");
9
25
  }
10
26
 
11
27
  return line.trim();
@@ -25,5 +41,6 @@ const inline = {
25
41
  };
26
42
 
27
43
  module.exports = {
44
+ print,
28
45
  inline,
29
46
  };
package/monitor.js CHANGED
@@ -106,7 +106,12 @@ ${
106
106
 
107
107
  async function runCheck({ verbose, production, mode, ...selectorFlags }) {
108
108
  const startTime = Date.now();
109
- console.log(`[MONITOR] starting check, ${toSelectorString(selectorFlags, ", ")}`);
109
+
110
+ if (Object.keys(selectorFlags).length > 0) {
111
+ console.log(`[MONITOR] starting check, ${toSelectorString(selectorFlags, ", ")}`);
112
+ } else {
113
+ console.log(`[MONITOR] starting check`);
114
+ }
110
115
 
111
116
  let checkState = {};
112
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@certik/skynet",
3
- "version": "0.10.8",
3
+ "version": "0.10.11",
4
4
  "description": "Skynet Shared JS library",
5
5
  "main": "index.js",
6
6
  "author": "CertiK Engineering",