crono_trigger 0.3.2 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/README.md +40 -0
- data/Rakefile +17 -0
- data/crono_trigger.gemspec +4 -1
- data/exe/crono_trigger-web +33 -0
- data/lib/crono_trigger.rb +20 -2
- data/lib/crono_trigger/cli.rb +8 -3
- data/lib/crono_trigger/models/signal.rb +52 -0
- data/lib/crono_trigger/models/worker.rb +16 -0
- data/lib/crono_trigger/polling_thread.rb +58 -20
- data/lib/crono_trigger/railtie.rb +15 -0
- data/lib/crono_trigger/schedulable.rb +69 -17
- data/lib/crono_trigger/version.rb +1 -1
- data/lib/crono_trigger/web.rb +163 -0
- data/lib/crono_trigger/worker.rb +118 -8
- data/lib/generators/crono_trigger/install/install_generator.rb +16 -0
- data/lib/generators/crono_trigger/install/templates/install.rb +23 -0
- data/lib/generators/crono_trigger/migration/templates/create_table_migration.rb +1 -0
- data/lib/generators/crono_trigger/migration/templates/migration.rb +1 -0
- data/web/app/.gitignore +21 -0
- data/web/app/README.md +2448 -0
- data/web/app/images.d.ts +3 -0
- data/web/app/package-lock.json +12439 -0
- data/web/app/package.json +36 -0
- data/web/app/public/favicon.ico +0 -0
- data/web/app/public/index.html +45 -0
- data/web/app/public/manifest.json +8 -0
- data/web/app/src/App.css +5 -0
- data/web/app/src/App.test.tsx +9 -0
- data/web/app/src/App.tsx +91 -0
- data/web/app/src/Models.tsx +61 -0
- data/web/app/src/SchedulableRecord.tsx +208 -0
- data/web/app/src/SchedulableRecordTableCell.tsx +19 -0
- data/web/app/src/SchedulableRecords.tsx +110 -0
- data/web/app/src/Signal.tsx +21 -0
- data/web/app/src/Signals.tsx +74 -0
- data/web/app/src/Worker.tsx +106 -0
- data/web/app/src/Workers.tsx +78 -0
- data/web/app/src/index.css +5 -0
- data/web/app/src/index.tsx +15 -0
- data/web/app/src/interfaces.ts +77 -0
- data/web/app/tsconfig.json +30 -0
- data/web/app/tsconfig.prod.json +3 -0
- data/web/app/tsconfig.test.json +6 -0
- data/web/app/tslint.json +13 -0
- data/web/public/asset-manifest.json +6 -0
- data/web/public/favicon.ico +0 -0
- data/web/public/manifest.json +8 -0
- data/web/public/service-worker.js +1 -0
- data/web/public/static/css/main.0f826673.css +2 -0
- data/web/public/static/css/main.0f826673.css.map +1 -0
- data/web/public/static/js/main.1413dc51.js +2 -0
- data/web/public/static/js/main.1413dc51.js.map +1 -0
- data/web/views/index.erb +1 -0
- data/web/views/signals.erb +9 -0
- data/web/views/workers.erb +9 -0
- metadata +89 -3
@@ -0,0 +1,19 @@
|
|
1
|
+
import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles';
|
2
|
+
import TableCell from '@material-ui/core/TableCell';
|
3
|
+
import * as React from 'react';
|
4
|
+
|
5
|
+
const styles = createStyles({
|
6
|
+
property: {
|
7
|
+
padding: "4px 16px 4px 8px"
|
8
|
+
}
|
9
|
+
});
|
10
|
+
|
11
|
+
interface ITableCellProps extends WithStyles<typeof styles> {
|
12
|
+
children: string | number | null | React.ReactNode | React.ReactNode[],
|
13
|
+
}
|
14
|
+
|
15
|
+
const SchedulableRecordTableCell = withStyles(styles)((props: ITableCellProps) => (
|
16
|
+
<TableCell className={props.classes.property}>{props.children}</TableCell>
|
17
|
+
));
|
18
|
+
|
19
|
+
export default SchedulableRecordTableCell;
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import FormControl from '@material-ui/core/FormControl';
|
2
|
+
import Paper from '@material-ui/core/Paper';
|
3
|
+
import Table from '@material-ui/core/Table';
|
4
|
+
import TableBody from '@material-ui/core/TableBody';
|
5
|
+
import TableHead from '@material-ui/core/TableHead';
|
6
|
+
import TableRow from '@material-ui/core/TableRow';
|
7
|
+
import TextField from '@material-ui/core/TextField';
|
8
|
+
import { debounce } from 'lodash';
|
9
|
+
import * as React from 'react';
|
10
|
+
|
11
|
+
import { IGlobalWindow, ISchedulableRecordsProps, ISchedulableRecordsStates } from './interfaces';
|
12
|
+
import SchedulableRecord from './SchedulableRecord';
|
13
|
+
import SchedulableRecordTableCell from './SchedulableRecordTableCell';
|
14
|
+
|
15
|
+
declare var window: IGlobalWindow;
|
16
|
+
|
17
|
+
class SchedulableRecords extends React.Component<ISchedulableRecordsProps, ISchedulableRecordsStates> {
|
18
|
+
private fetchLoop: any;
|
19
|
+
|
20
|
+
private handleTimeRangeFilterChange = debounce((event: any) => {
|
21
|
+
this.setState({timeRangeMinute: parseInt(event.target.value, 10)});
|
22
|
+
this.fetchSchedulableRecord();
|
23
|
+
}, 500)
|
24
|
+
|
25
|
+
constructor(props: ISchedulableRecordsProps) {
|
26
|
+
super(props);
|
27
|
+
|
28
|
+
this.state = {records: [], timeRangeMinute: 10};
|
29
|
+
}
|
30
|
+
|
31
|
+
public componentDidMount() {
|
32
|
+
this.fetchSchedulableRecord();
|
33
|
+
this.setFetchSchedulableRecordLoop();
|
34
|
+
}
|
35
|
+
|
36
|
+
public componentWillUnmount() {
|
37
|
+
if (this.fetchLoop) {
|
38
|
+
clearTimeout(this.fetchLoop);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
public setFetchSchedulableRecordLoop(): void {
|
43
|
+
this.fetchLoop = setTimeout(() => {
|
44
|
+
this.fetchSchedulableRecord();
|
45
|
+
this.setFetchSchedulableRecordLoop();
|
46
|
+
}, 3000);
|
47
|
+
}
|
48
|
+
|
49
|
+
public fetchSchedulableRecord(): void {
|
50
|
+
const that = this;
|
51
|
+
fetch(`${window.mountPath}/models/${this.props.model_name}.json?after=${this.state.timeRangeMinute}`)
|
52
|
+
.then((res) => res.json())
|
53
|
+
.then((data) => {
|
54
|
+
that.setState(data);
|
55
|
+
}).catch((err) => {
|
56
|
+
console.error(err);
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
public render() {
|
61
|
+
return (
|
62
|
+
<div id="schedulable-models">
|
63
|
+
<FormControl className="filter-form">
|
64
|
+
<TextField
|
65
|
+
id="time-range-input"
|
66
|
+
label="Time Range"
|
67
|
+
type="number"
|
68
|
+
defaultValue={this.state.timeRangeMinute}
|
69
|
+
helperText="minute after"
|
70
|
+
margin="normal"
|
71
|
+
onChange={this.wrappedHandleTimeRangeFilterChange}
|
72
|
+
/>
|
73
|
+
</FormControl>
|
74
|
+
<Paper className="models-container" style={{marginTop: "8px"}}>
|
75
|
+
<Table className="models">
|
76
|
+
<TableHead>
|
77
|
+
<TableRow>
|
78
|
+
<SchedulableRecordTableCell>Status</SchedulableRecordTableCell>
|
79
|
+
<SchedulableRecordTableCell>ID</SchedulableRecordTableCell>
|
80
|
+
<SchedulableRecordTableCell>Cron</SchedulableRecordTableCell>
|
81
|
+
<SchedulableRecordTableCell>Next Execute At</SchedulableRecordTableCell>
|
82
|
+
<SchedulableRecordTableCell>Delay Sec</SchedulableRecordTableCell>
|
83
|
+
<SchedulableRecordTableCell>Execute Lock</SchedulableRecordTableCell>
|
84
|
+
<SchedulableRecordTableCell>Time To Unlock</SchedulableRecordTableCell>
|
85
|
+
<SchedulableRecordTableCell>Last Executed At</SchedulableRecordTableCell>
|
86
|
+
<SchedulableRecordTableCell>Locked By</SchedulableRecordTableCell>
|
87
|
+
<SchedulableRecordTableCell>Last Error Time</SchedulableRecordTableCell>
|
88
|
+
<SchedulableRecordTableCell>Retry Count</SchedulableRecordTableCell>
|
89
|
+
<SchedulableRecordTableCell>Detail</SchedulableRecordTableCell>
|
90
|
+
<SchedulableRecordTableCell>Ops</SchedulableRecordTableCell>
|
91
|
+
</TableRow>
|
92
|
+
</TableHead>
|
93
|
+
<TableBody>
|
94
|
+
{this.state.records.map((record) => (
|
95
|
+
<SchedulableRecord key={record.id} model_name={this.props.model_name} record={record} />
|
96
|
+
))}
|
97
|
+
</TableBody>
|
98
|
+
</Table>
|
99
|
+
</Paper>
|
100
|
+
</div>
|
101
|
+
)
|
102
|
+
}
|
103
|
+
|
104
|
+
private wrappedHandleTimeRangeFilterChange = (event: any) => {
|
105
|
+
event.persist();
|
106
|
+
this.handleTimeRangeFilterChange(event);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
export default SchedulableRecords;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import TableCell from '@material-ui/core/TableCell';
|
2
|
+
import TableRow from '@material-ui/core/TableRow';
|
3
|
+
import * as React from 'react';
|
4
|
+
|
5
|
+
import { ISignalProps } from './interfaces';
|
6
|
+
|
7
|
+
class Signal extends React.Component<ISignalProps, any> {
|
8
|
+
public render() {
|
9
|
+
const signal = this.props.signal;
|
10
|
+
return (
|
11
|
+
<TableRow>
|
12
|
+
<TableCell>{signal.worker_id}</TableCell>
|
13
|
+
<TableCell>{signal.signal}</TableCell>
|
14
|
+
<TableCell>{signal.sent_at}</TableCell>
|
15
|
+
<TableCell>{signal.received_at}</TableCell>
|
16
|
+
</TableRow>
|
17
|
+
)
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
export default Signal;
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import Paper from '@material-ui/core/Paper';
|
2
|
+
import Table from '@material-ui/core/Table';
|
3
|
+
import TableBody from '@material-ui/core/TableBody';
|
4
|
+
import TableCell from '@material-ui/core/TableCell';
|
5
|
+
import TableHead from '@material-ui/core/TableHead';
|
6
|
+
import TableRow from '@material-ui/core/TableRow';
|
7
|
+
import * as React from 'react';
|
8
|
+
|
9
|
+
import { IGlobalWindow, ISignalsState } from './interfaces';
|
10
|
+
import Signal from './Signal';
|
11
|
+
|
12
|
+
declare var window: IGlobalWindow;
|
13
|
+
|
14
|
+
class Signals extends React.Component<any, ISignalsState> {
|
15
|
+
private fetchLoop: any;
|
16
|
+
|
17
|
+
constructor(props: any) {
|
18
|
+
super(props)
|
19
|
+
this.state = {records: []}
|
20
|
+
}
|
21
|
+
|
22
|
+
public componentDidMount() {
|
23
|
+
this.fetchSignals();
|
24
|
+
this.setFetchSignalLoop();
|
25
|
+
}
|
26
|
+
|
27
|
+
public componentWillUnmount() {
|
28
|
+
if (this.fetchLoop) {
|
29
|
+
clearTimeout(this.fetchLoop);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
public setFetchSignalLoop(): void {
|
34
|
+
this.fetchLoop = setTimeout(() => {
|
35
|
+
this.fetchSignals();
|
36
|
+
this.setFetchSignalLoop();
|
37
|
+
}, 3000);
|
38
|
+
}
|
39
|
+
|
40
|
+
public fetchSignals(): void {
|
41
|
+
const that = this;
|
42
|
+
fetch(`${window.mountPath}/signals.json`)
|
43
|
+
.then((res) => res.json())
|
44
|
+
.then((data) => {
|
45
|
+
that.setState(data);
|
46
|
+
}).catch((err) => {
|
47
|
+
console.error(err);
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
public render() {
|
52
|
+
return (
|
53
|
+
<Paper className="signals-container">
|
54
|
+
<Table className="signals">
|
55
|
+
<TableHead>
|
56
|
+
<TableRow>
|
57
|
+
<TableCell>Worker ID</TableCell>
|
58
|
+
<TableCell>Signal</TableCell>
|
59
|
+
<TableCell>Sent At</TableCell>
|
60
|
+
<TableCell>Received At</TableCell>
|
61
|
+
</TableRow>
|
62
|
+
</TableHead>
|
63
|
+
<TableBody>
|
64
|
+
{this.state.records.map((record) => {
|
65
|
+
return <Signal key={`${record.worker_id}-${record.sent_at}`} signal={record} />
|
66
|
+
})}
|
67
|
+
</TableBody>
|
68
|
+
</Table>
|
69
|
+
</Paper>
|
70
|
+
)
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
export default Signals;
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import Button from '@material-ui/core/Button';
|
2
|
+
import Snackbar from '@material-ui/core/Snackbar';
|
3
|
+
import TableCell from '@material-ui/core/TableCell';
|
4
|
+
import TableRow from '@material-ui/core/TableRow';
|
5
|
+
import * as React from 'react';
|
6
|
+
|
7
|
+
import { IGlobalWindow, IWorkerProps } from "./interfaces";
|
8
|
+
|
9
|
+
declare var window: IGlobalWindow;
|
10
|
+
|
11
|
+
class Worker extends React.Component<IWorkerProps, any> {
|
12
|
+
constructor(props: IWorkerProps) {
|
13
|
+
super(props)
|
14
|
+
this.handleQuietClick = this.handleQuietClick.bind(this)
|
15
|
+
this.handleStopClick = this.handleStopClick.bind(this)
|
16
|
+
this.handleNotificationClose = this.handleNotificationClose.bind(this)
|
17
|
+
this.state = {
|
18
|
+
notificationMessage: <span />,
|
19
|
+
notificationOpen: false,
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
public render() {
|
24
|
+
const worker = this.props.worker;
|
25
|
+
return (
|
26
|
+
<TableRow>
|
27
|
+
<TableCell>{worker.worker_id}</TableCell>
|
28
|
+
<TableCell numeric={true}>{worker.max_thread_size}</TableCell>
|
29
|
+
<TableCell numeric={true}>{worker.current_queue_size}</TableCell>
|
30
|
+
<TableCell numeric={true}>{worker.current_executing_size}</TableCell>
|
31
|
+
<TableCell>{worker.executor_status}</TableCell>
|
32
|
+
<TableCell>{worker.polling_model_names}</TableCell>
|
33
|
+
<TableCell>{worker.last_heartbeated_at}</TableCell>
|
34
|
+
<TableCell>
|
35
|
+
<Button variant="contained" style={{"marginRight": "8px"}} onClick={this.handleQuietClick}>Quiet</Button>
|
36
|
+
<Button variant="contained" color="secondary" onClick={this.handleStopClick}>Stop</Button>
|
37
|
+
<Snackbar
|
38
|
+
anchorOrigin={{vertical: "bottom", horizontal: "right"}}
|
39
|
+
open={this.state.notificationOpen}
|
40
|
+
autoHideDuration={3000}
|
41
|
+
onClose={this.handleNotificationClose}
|
42
|
+
message={this.state.notificationMessage}
|
43
|
+
/>
|
44
|
+
</TableCell>
|
45
|
+
|
46
|
+
</TableRow>
|
47
|
+
)
|
48
|
+
}
|
49
|
+
|
50
|
+
private handleQuietClick(event: any) {
|
51
|
+
const worker = this.props.worker;
|
52
|
+
fetch(`${window.mountPath}/signals`, {
|
53
|
+
body: JSON.stringify({"worker_id": worker.worker_id, "signal": "TSTP"}),
|
54
|
+
headers: {"content-type": "application/json"},
|
55
|
+
method: "POST"
|
56
|
+
}).then(this.handleResponseStatus).then((res) => {
|
57
|
+
this.setState({
|
58
|
+
notificationMessage: <span>Quiet {worker.worker_id}</span>,
|
59
|
+
notificationOpen: true,
|
60
|
+
})
|
61
|
+
}).catch((err) => {
|
62
|
+
this.setState({
|
63
|
+
notificationMessage: <span>Failed to Quiet ({err.message})</span>,
|
64
|
+
notificationOpen: true,
|
65
|
+
})
|
66
|
+
})
|
67
|
+
}
|
68
|
+
|
69
|
+
private handleStopClick(event: any) {
|
70
|
+
const worker = this.props.worker;
|
71
|
+
fetch(`${window.mountPath}/signals`, {
|
72
|
+
body: JSON.stringify({"worker_id": worker.worker_id, "signal": "TERM"}),
|
73
|
+
headers: {"content-type": "application/json"},
|
74
|
+
method: "POST"
|
75
|
+
}).then(this.handleResponseStatus).then((res) => {
|
76
|
+
this.setState({
|
77
|
+
notificationMessage: <span>Stop {worker.worker_id}</span>,
|
78
|
+
notificationOpen: true,
|
79
|
+
})
|
80
|
+
}).catch((err) => {
|
81
|
+
this.setState({
|
82
|
+
notificationMessage: <span>Failed to Stop ({err.message})</span>,
|
83
|
+
notificationOpen: true,
|
84
|
+
})
|
85
|
+
})
|
86
|
+
}
|
87
|
+
|
88
|
+
private handleResponseStatus(res: Response): Promise<Response | Error> {
|
89
|
+
if (!res.ok) {
|
90
|
+
return res.json().then((data: any) => {
|
91
|
+
throw new Error(data.error);
|
92
|
+
})
|
93
|
+
} else {
|
94
|
+
return Promise.resolve(res);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
private handleNotificationClose(ev: any, reason: any) {
|
99
|
+
this.setState({
|
100
|
+
...this.state,
|
101
|
+
notificationOpen: false,
|
102
|
+
})
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
export default Worker;
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import Paper from '@material-ui/core/Paper';
|
2
|
+
import Table from '@material-ui/core/Table';
|
3
|
+
import TableBody from '@material-ui/core/TableBody';
|
4
|
+
import TableCell from '@material-ui/core/TableCell';
|
5
|
+
import TableHead from '@material-ui/core/TableHead';
|
6
|
+
import TableRow from '@material-ui/core/TableRow';
|
7
|
+
import * as React from 'react';
|
8
|
+
|
9
|
+
import { IGlobalWindow, IWorkersState } from "./interfaces";
|
10
|
+
import Worker from "./Worker";
|
11
|
+
|
12
|
+
declare var window: IGlobalWindow;
|
13
|
+
|
14
|
+
class Workers extends React.Component<any, IWorkersState> {
|
15
|
+
private fetchLoop: any;
|
16
|
+
|
17
|
+
constructor(props: any) {
|
18
|
+
super(props);
|
19
|
+
this.state = {records: []};
|
20
|
+
}
|
21
|
+
|
22
|
+
public componentDidMount() {
|
23
|
+
this.fetchWorkers();
|
24
|
+
this.setFetchWorkerLoop();
|
25
|
+
}
|
26
|
+
|
27
|
+
public componentWillUnmount() {
|
28
|
+
if (this.fetchLoop) {
|
29
|
+
clearTimeout(this.fetchLoop);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
public setFetchWorkerLoop(): void {
|
34
|
+
this.fetchLoop = setTimeout(() => {
|
35
|
+
this.fetchWorkers();
|
36
|
+
this.setFetchWorkerLoop();
|
37
|
+
}, 3000);
|
38
|
+
}
|
39
|
+
|
40
|
+
public fetchWorkers(): void {
|
41
|
+
const that = this;
|
42
|
+
fetch(`${window.mountPath}/workers.json`)
|
43
|
+
.then((res) => res.json())
|
44
|
+
.then((data) => {
|
45
|
+
that.setState(data);
|
46
|
+
}).catch((err) => {
|
47
|
+
console.error(err);
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
public render() {
|
52
|
+
return (
|
53
|
+
<Paper className="workers-container">
|
54
|
+
<Table className="workers">
|
55
|
+
<TableHead>
|
56
|
+
<TableRow>
|
57
|
+
<TableCell>Worker ID</TableCell>
|
58
|
+
<TableCell numeric={true}>Thread</TableCell>
|
59
|
+
<TableCell numeric={true}>Internal Queue</TableCell>
|
60
|
+
<TableCell numeric={true}>Executing</TableCell>
|
61
|
+
<TableCell>Status</TableCell>
|
62
|
+
<TableCell>Polling Models</TableCell>
|
63
|
+
<TableCell>Last Heatbeated At</TableCell>
|
64
|
+
<TableCell> </TableCell>
|
65
|
+
</TableRow>
|
66
|
+
</TableHead>
|
67
|
+
<TableBody>
|
68
|
+
{this.state.records.map((record) => {
|
69
|
+
return <Worker key={record.worker_id} worker={record} />
|
70
|
+
})}
|
71
|
+
</TableBody>
|
72
|
+
</Table>
|
73
|
+
</Paper>
|
74
|
+
)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
export default Workers;
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import * as React from 'react';
|
2
|
+
import * as ReactDOM from 'react-dom';
|
3
|
+
import { BrowserRouter as Router } from "react-router-dom";
|
4
|
+
import App from './App';
|
5
|
+
import './index.css';
|
6
|
+
import { IGlobalWindow } from './interfaces';
|
7
|
+
|
8
|
+
declare var window: IGlobalWindow
|
9
|
+
|
10
|
+
ReactDOM.render(
|
11
|
+
<Router basename={window.mountPath || "/"}>
|
12
|
+
<App />
|
13
|
+
</Router>
|
14
|
+
, document.getElementById('root') as HTMLElement
|
15
|
+
);
|
@@ -0,0 +1,77 @@
|
|
1
|
+
interface IGlobalWindow {
|
2
|
+
mountPath: string
|
3
|
+
}
|
4
|
+
|
5
|
+
interface IWorkerRecord {
|
6
|
+
worker_id: string,
|
7
|
+
max_thread_size: number,
|
8
|
+
current_queue_size: number,
|
9
|
+
current_executing_size: number,
|
10
|
+
polling_model_names: string[],
|
11
|
+
executor_status: string,
|
12
|
+
last_heartbeated_at: string,
|
13
|
+
}
|
14
|
+
interface IWorkersState {
|
15
|
+
records: IWorkerRecord[]
|
16
|
+
}
|
17
|
+
interface IWorkerProps {
|
18
|
+
worker: IWorkerRecord
|
19
|
+
}
|
20
|
+
|
21
|
+
interface ISignalRecord {
|
22
|
+
worker_id: string,
|
23
|
+
signal: string,
|
24
|
+
sent_at: string,
|
25
|
+
received_at: string,
|
26
|
+
}
|
27
|
+
interface ISignalsState {
|
28
|
+
records: ISignalRecord[]
|
29
|
+
}
|
30
|
+
interface ISignalProps {
|
31
|
+
signal: ISignalRecord
|
32
|
+
}
|
33
|
+
|
34
|
+
interface ISchedulableRecord {
|
35
|
+
crono_trigger_status: string,
|
36
|
+
id: number,
|
37
|
+
cron: string | null,
|
38
|
+
next_execute_at: string | null,
|
39
|
+
last_executed_at: string | null,
|
40
|
+
timezone: string | null,
|
41
|
+
execute_lock: number,
|
42
|
+
locked_by: string | null,
|
43
|
+
started_at: string,
|
44
|
+
finished_at: string,
|
45
|
+
last_error_name: string,
|
46
|
+
last_error_reason: string,
|
47
|
+
last_error_time: string,
|
48
|
+
retry_count: number,
|
49
|
+
time_to_unlock: number,
|
50
|
+
delay_sec: number,
|
51
|
+
}
|
52
|
+
interface ISchedulableRecordsProps {
|
53
|
+
model_name: string
|
54
|
+
}
|
55
|
+
interface ISchedulableRecordsStates {
|
56
|
+
records: ISchedulableRecord[],
|
57
|
+
timeRangeMinute: number,
|
58
|
+
}
|
59
|
+
|
60
|
+
interface ISchedulableRecordProps {
|
61
|
+
model_name: string,
|
62
|
+
record: ISchedulableRecord
|
63
|
+
}
|
64
|
+
|
65
|
+
export {
|
66
|
+
IGlobalWindow,
|
67
|
+
IWorkerRecord,
|
68
|
+
IWorkersState,
|
69
|
+
IWorkerProps,
|
70
|
+
ISignalRecord,
|
71
|
+
ISignalsState,
|
72
|
+
ISignalProps,
|
73
|
+
ISchedulableRecord,
|
74
|
+
ISchedulableRecordsProps,
|
75
|
+
ISchedulableRecordsStates,
|
76
|
+
ISchedulableRecordProps
|
77
|
+
}
|