Add run retry task, run scheduled task functionality

This commit is contained in:
Ken Hibino 2020-12-19 06:35:08 -08:00
parent f527b0c6d8
commit a454f2f094
7 changed files with 207 additions and 2 deletions

View File

@ -29,6 +29,8 @@ import {
runAllRetryTasks, runAllRetryTasks,
runAllScheduledTasks, runAllScheduledTasks,
runDeadTask, runDeadTask,
runRetryTask,
runScheduledTask,
} from "../api"; } from "../api";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
@ -51,6 +53,12 @@ export const LIST_DEAD_TASKS_ERROR = "LIST_DEAD_TASKS_ERROR";
export const CANCEL_ACTIVE_TASK_BEGIN = "CANCEL_ACTIVE_TASK_BEGIN"; export const CANCEL_ACTIVE_TASK_BEGIN = "CANCEL_ACTIVE_TASK_BEGIN";
export const CANCEL_ACTIVE_TASK_SUCCESS = "CANCEL_ACTIVE_TASK_SUCCESS"; export const CANCEL_ACTIVE_TASK_SUCCESS = "CANCEL_ACTIVE_TASK_SUCCESS";
export const CANCEL_ACTIVE_TASK_ERROR = "CANCEL_ACTIVE_TASK_ERROR"; export const CANCEL_ACTIVE_TASK_ERROR = "CANCEL_ACTIVE_TASK_ERROR";
export const RUN_SCHEDULED_TASK_BEGIN = "RUN_DEAD_TASK_BEGIN";
export const RUN_SCHEDULED_TASK_SUCCESS = "RUN_DEAD_TASK_SUCCESS";
export const RUN_SCHEDULED_TASK_ERROR = "RUN_DEAD_TASK_ERROR";
export const RUN_RETRY_TASK_BEGIN = "RUN_RETRY_TASK_BEGIN";
export const RUN_RETRY_TASK_SUCCESS = "RUN_RETRY_TASK_SUCCESS";
export const RUN_RETRY_TASK_ERROR = "RUN_RETRY_TASK_ERROR";
export const RUN_DEAD_TASK_BEGIN = "RUN_DEAD_TASK_BEGIN"; export const RUN_DEAD_TASK_BEGIN = "RUN_DEAD_TASK_BEGIN";
export const RUN_DEAD_TASK_SUCCESS = "RUN_DEAD_TASK_SUCCESS"; export const RUN_DEAD_TASK_SUCCESS = "RUN_DEAD_TASK_SUCCESS";
export const RUN_DEAD_TASK_ERROR = "RUN_DEAD_TASK_ERROR"; export const RUN_DEAD_TASK_ERROR = "RUN_DEAD_TASK_ERROR";
@ -215,6 +223,43 @@ interface CancelActiveTaskErrorAction {
taskId: string; taskId: string;
error: string; error: string;
} }
interface RunScheduledTaskBeginAction {
type: typeof RUN_SCHEDULED_TASK_BEGIN;
queue: string;
taskKey: string;
}
interface RunScheduledTaskSuccessAction {
type: typeof RUN_SCHEDULED_TASK_SUCCESS;
queue: string;
taskKey: string;
}
interface RunScheduledTaskErrorAction {
type: typeof RUN_SCHEDULED_TASK_ERROR;
queue: string;
taskKey: string;
error: string;
}
interface RunRetryTaskBeginAction {
type: typeof RUN_RETRY_TASK_BEGIN;
queue: string;
taskKey: string;
}
interface RunRetryTaskSuccessAction {
type: typeof RUN_RETRY_TASK_SUCCESS;
queue: string;
taskKey: string;
}
interface RunRetryTaskErrorAction {
type: typeof RUN_RETRY_TASK_ERROR;
queue: string;
taskKey: string;
error: string;
}
interface RunDeadTaskBeginAction { interface RunDeadTaskBeginAction {
type: typeof RUN_DEAD_TASK_BEGIN; type: typeof RUN_DEAD_TASK_BEGIN;
@ -522,6 +567,12 @@ export type TasksActionTypes =
| CancelActiveTaskBeginAction | CancelActiveTaskBeginAction
| CancelActiveTaskSuccessAction | CancelActiveTaskSuccessAction
| CancelActiveTaskErrorAction | CancelActiveTaskErrorAction
| RunScheduledTaskBeginAction
| RunScheduledTaskSuccessAction
| RunScheduledTaskErrorAction
| RunRetryTaskBeginAction
| RunRetryTaskSuccessAction
| RunRetryTaskErrorAction
| RunDeadTaskBeginAction | RunDeadTaskBeginAction
| RunDeadTaskSuccessAction | RunDeadTaskSuccessAction
| RunDeadTaskErrorAction | RunDeadTaskErrorAction
@ -703,6 +754,42 @@ export function cancelActiveTaskAsync(queue: string, taskId: string) {
}; };
} }
export function runScheduledTaskAsync(queue: string, taskKey: string) {
return async (dispatch: Dispatch<TasksActionTypes>) => {
dispatch({ type: RUN_SCHEDULED_TASK_BEGIN, queue, taskKey });
try {
await runScheduledTask(queue, taskKey);
dispatch({ type: RUN_SCHEDULED_TASK_SUCCESS, queue, taskKey });
} catch (error) {
console.error("runScheduledTaskAsync: ", error);
dispatch({
type: RUN_SCHEDULED_TASK_ERROR,
error: `Could not run task: ${taskKey}`,
queue,
taskKey,
});
}
};
}
export function runRetryTaskAsync(queue: string, taskKey: string) {
return async (dispatch: Dispatch<TasksActionTypes>) => {
dispatch({ type: RUN_RETRY_TASK_BEGIN, queue, taskKey });
try {
await runRetryTask(queue, taskKey);
dispatch({ type: RUN_RETRY_TASK_SUCCESS, queue, taskKey });
} catch (error) {
console.error("runRetryTaskAsync: ", error);
dispatch({
type: RUN_RETRY_TASK_ERROR,
error: `Could not run task: ${taskKey}`,
queue,
taskKey,
});
}
};
}
export function runDeadTaskAsync(queue: string, taskKey: string) { export function runDeadTaskAsync(queue: string, taskKey: string) {
return async (dispatch: Dispatch<TasksActionTypes>) => { return async (dispatch: Dispatch<TasksActionTypes>) => {
dispatch({ type: RUN_DEAD_TASK_BEGIN, queue, taskKey }); dispatch({ type: RUN_DEAD_TASK_BEGIN, queue, taskKey });

View File

@ -253,6 +253,16 @@ export async function listDeadTasks(
return resp.data; return resp.data;
} }
export async function runScheduledTask(
qname: string,
taskKey: string
): Promise<void> {
await axios({
method: "post",
url: `${BASE_URL}/queues/${qname}/scheduled_tasks/${taskKey}:run`,
});
}
export async function deleteScheduledTask( export async function deleteScheduledTask(
qname: string, qname: string,
taskKey: string taskKey: string
@ -305,6 +315,16 @@ export async function runAllScheduledTasks(qname: string): Promise<void> {
}); });
} }
export async function runRetryTask(
qname: string,
taskKey: string
): Promise<void> {
await axios({
method: "post",
url: `${BASE_URL}/queues/${qname}/retry_tasks/${taskKey}:run`,
});
}
export async function deleteRetryTask( export async function deleteRetryTask(
qname: string, qname: string,
taskKey: string taskKey: string

View File

@ -29,6 +29,7 @@ import {
runAllRetryTasksAsync, runAllRetryTasksAsync,
listRetryTasksAsync, listRetryTasksAsync,
deleteRetryTaskAsync, deleteRetryTaskAsync,
runRetryTaskAsync,
} from "../actions/tasksActions"; } from "../actions/tasksActions";
import { AppState } from "../store"; import { AppState } from "../store";
import TablePaginationActions, { import TablePaginationActions, {
@ -63,6 +64,7 @@ const mapDispatchToProps = {
runAllRetryTasksAsync, runAllRetryTasksAsync,
listRetryTasksAsync, listRetryTasksAsync,
deleteRetryTaskAsync, deleteRetryTaskAsync,
runRetryTaskAsync,
}; };
const connector = connect(mapStateToProps, mapDispatchToProps); const connector = connect(mapStateToProps, mapDispatchToProps);
@ -193,6 +195,7 @@ function RetryTasksTable(props: Props & ReduxProps) {
<Row <Row
key={task.id} key={task.id}
task={task} task={task}
allActionPending={props.allActionPending}
isSelected={selectedKeys.includes(task.key)} isSelected={selectedKeys.includes(task.key)}
onSelectChange={(checked: boolean) => { onSelectChange={(checked: boolean) => {
if (checked) { if (checked) {
@ -203,6 +206,9 @@ function RetryTasksTable(props: Props & ReduxProps) {
); );
} }
}} }}
onRunClick={() => {
props.runRetryTaskAsync(task.queue, task.key);
}}
onDeleteClick={() => { onDeleteClick={() => {
props.deleteRetryTaskAsync(task.queue, task.key); props.deleteRetryTaskAsync(task.queue, task.key);
}} }}
@ -246,6 +252,8 @@ interface RowProps {
isSelected: boolean; isSelected: boolean;
onSelectChange: (checked: boolean) => void; onSelectChange: (checked: boolean) => void;
onDeleteClick: () => void; onDeleteClick: () => void;
onRunClick: () => void;
allActionPending: boolean;
} }
function Row(props: RowProps) { function Row(props: RowProps) {
@ -281,7 +289,16 @@ function Row(props: RowProps) {
<TableCell>{task.retried}</TableCell> <TableCell>{task.retried}</TableCell>
<TableCell>{task.max_retry}</TableCell> <TableCell>{task.max_retry}</TableCell>
<TableCell> <TableCell>
<Button disabled={task.requestPending} onClick={props.onDeleteClick}> <Button
onClick={props.onRunClick}
disabled={task.requestPending || props.allActionPending}
>
Run
</Button>
<Button
disabled={task.requestPending}
onClick={props.onDeleteClick || props.allActionPending}
>
Delete Delete
</Button> </Button>
</TableCell> </TableCell>

View File

@ -29,6 +29,7 @@ import {
runAllScheduledTasksAsync, runAllScheduledTasksAsync,
listScheduledTasksAsync, listScheduledTasksAsync,
deleteScheduledTaskAsync, deleteScheduledTaskAsync,
runScheduledTaskAsync,
} from "../actions/tasksActions"; } from "../actions/tasksActions";
import { AppState } from "../store"; import { AppState } from "../store";
import TablePaginationActions, { import TablePaginationActions, {
@ -63,6 +64,7 @@ const mapDispatchToProps = {
batchRunScheduledTasksAsync, batchRunScheduledTasksAsync,
deleteAllScheduledTasksAsync, deleteAllScheduledTasksAsync,
runAllScheduledTasksAsync, runAllScheduledTasksAsync,
runScheduledTaskAsync,
}; };
const connector = connect(mapStateToProps, mapDispatchToProps); const connector = connect(mapStateToProps, mapDispatchToProps);
@ -190,6 +192,7 @@ function ScheduledTasksTable(props: Props & ReduxProps) {
<Row <Row
key={task.id} key={task.id}
task={task} task={task}
allActionPending={props.allActionPending}
isSelected={selectedKeys.includes(task.key)} isSelected={selectedKeys.includes(task.key)}
onSelectChange={(checked: boolean) => { onSelectChange={(checked: boolean) => {
if (checked) { if (checked) {
@ -200,6 +203,9 @@ function ScheduledTasksTable(props: Props & ReduxProps) {
); );
} }
}} }}
onRunClick={() => {
props.runScheduledTaskAsync(queue, task.key);
}}
onDeleteClick={() => { onDeleteClick={() => {
props.deleteScheduledTaskAsync(queue, task.key); props.deleteScheduledTaskAsync(queue, task.key);
}} }}
@ -242,7 +248,9 @@ interface RowProps {
task: ScheduledTaskExtended; task: ScheduledTaskExtended;
isSelected: boolean; isSelected: boolean;
onSelectChange: (checked: boolean) => void; onSelectChange: (checked: boolean) => void;
onRunClick: () => void;
onDeleteClick: () => void; onDeleteClick: () => void;
allActionPending: boolean;
} }
function Row(props: RowProps) { function Row(props: RowProps) {
@ -275,7 +283,16 @@ function Row(props: RowProps) {
<TableCell>{task.type}</TableCell> <TableCell>{task.type}</TableCell>
<TableCell>{durationBefore(task.next_process_at)}</TableCell> <TableCell>{durationBefore(task.next_process_at)}</TableCell>
<TableCell> <TableCell>
<Button onClick={props.onDeleteClick} disabled={task.requestPending}> <Button
onClick={props.onRunClick}
disabled={task.requestPending || props.allActionPending}
>
Run
</Button>
<Button
onClick={props.onDeleteClick}
disabled={task.requestPending || props.allActionPending}
>
Delete Delete
</Button> </Button>
</TableCell> </TableCell>

View File

@ -35,6 +35,8 @@ import {
RUN_ALL_RETRY_TASKS_SUCCESS, RUN_ALL_RETRY_TASKS_SUCCESS,
RUN_ALL_SCHEDULED_TASKS_SUCCESS, RUN_ALL_SCHEDULED_TASKS_SUCCESS,
RUN_DEAD_TASK_SUCCESS, RUN_DEAD_TASK_SUCCESS,
RUN_RETRY_TASK_SUCCESS,
RUN_SCHEDULED_TASK_SUCCESS,
TasksActionTypes, TasksActionTypes,
} from "../actions/tasksActions"; } from "../actions/tasksActions";
import { DailyStat, Queue } from "../api"; import { DailyStat, Queue } from "../api";
@ -163,6 +165,40 @@ function queuesReducer(
return { ...state, data: newData }; return { ...state, data: newData };
} }
case RUN_SCHEDULED_TASK_SUCCESS: {
const newData = state.data.map((queueInfo) => {
if (queueInfo.name !== action.queue) {
return queueInfo;
}
return {
...queueInfo,
currentStats: {
...queueInfo.currentStats,
pending: queueInfo.currentStats.pending + 1,
scheduled: queueInfo.currentStats.scheduled - 1,
},
};
});
return { ...state, data: newData };
}
case RUN_RETRY_TASK_SUCCESS: {
const newData = state.data.map((queueInfo) => {
if (queueInfo.name !== action.queue) {
return queueInfo;
}
return {
...queueInfo,
currentStats: {
...queueInfo.currentStats,
pending: queueInfo.currentStats.pending + 1,
retry: queueInfo.currentStats.retry - 1,
},
};
});
return { ...state, data: newData };
}
case RUN_DEAD_TASK_SUCCESS: { case RUN_DEAD_TASK_SUCCESS: {
const newData = state.data.map((queueInfo) => { const newData = state.data.map((queueInfo) => {
if (queueInfo.name !== action.queue) { if (queueInfo.name !== action.queue) {

View File

@ -19,6 +19,8 @@ import {
RUN_ALL_RETRY_TASKS_SUCCESS, RUN_ALL_RETRY_TASKS_SUCCESS,
RUN_ALL_SCHEDULED_TASKS_SUCCESS, RUN_ALL_SCHEDULED_TASKS_SUCCESS,
RUN_DEAD_TASK_SUCCESS, RUN_DEAD_TASK_SUCCESS,
RUN_RETRY_TASK_SUCCESS,
RUN_SCHEDULED_TASK_SUCCESS,
TasksActionTypes, TasksActionTypes,
} from "../actions/tasksActions"; } from "../actions/tasksActions";
@ -45,6 +47,20 @@ function snackbarReducer(
isOpen: false, isOpen: false,
}; };
case RUN_SCHEDULED_TASK_SUCCESS:
return {
isOpen: true,
// TODO: show only task id
message: `Scheduled task ${action.taskKey} is now pending`,
};
case RUN_RETRY_TASK_SUCCESS:
return {
isOpen: true,
// TODO: show only task id
message: `Retry task ${action.taskKey} is now pending`,
};
case RUN_DEAD_TASK_SUCCESS: case RUN_DEAD_TASK_SUCCESS:
return { return {
isOpen: true, isOpen: true,

View File

@ -66,6 +66,12 @@ import {
RUN_ALL_SCHEDULED_TASKS_SUCCESS, RUN_ALL_SCHEDULED_TASKS_SUCCESS,
DELETE_ALL_SCHEDULED_TASKS_BEGIN, DELETE_ALL_SCHEDULED_TASKS_BEGIN,
RUN_ALL_SCHEDULED_TASKS_BEGIN, RUN_ALL_SCHEDULED_TASKS_BEGIN,
RUN_RETRY_TASK_BEGIN,
RUN_RETRY_TASK_SUCCESS,
RUN_RETRY_TASK_ERROR,
RUN_SCHEDULED_TASK_BEGIN,
RUN_SCHEDULED_TASK_SUCCESS,
RUN_SCHEDULED_TASK_ERROR,
} from "../actions/tasksActions"; } from "../actions/tasksActions";
import { import {
ActiveTask, ActiveTask,
@ -389,6 +395,7 @@ function tasksReducer(
}, },
}; };
case RUN_SCHEDULED_TASK_BEGIN:
case DELETE_SCHEDULED_TASK_BEGIN: case DELETE_SCHEDULED_TASK_BEGIN:
return { return {
...state, ...state,
@ -403,6 +410,7 @@ function tasksReducer(
}, },
}; };
case RUN_SCHEDULED_TASK_SUCCESS:
case DELETE_SCHEDULED_TASK_SUCCESS: case DELETE_SCHEDULED_TASK_SUCCESS:
return { return {
...state, ...state,
@ -414,6 +422,7 @@ function tasksReducer(
}, },
}; };
case RUN_SCHEDULED_TASK_ERROR:
case DELETE_SCHEDULED_TASK_ERROR: case DELETE_SCHEDULED_TASK_ERROR:
return { return {
...state, ...state,
@ -525,6 +534,7 @@ function tasksReducer(
}, },
}; };
case RUN_RETRY_TASK_BEGIN:
case DELETE_RETRY_TASK_BEGIN: case DELETE_RETRY_TASK_BEGIN:
return { return {
...state, ...state,
@ -539,6 +549,7 @@ function tasksReducer(
}, },
}; };
case RUN_RETRY_TASK_SUCCESS:
case DELETE_RETRY_TASK_SUCCESS: case DELETE_RETRY_TASK_SUCCESS:
return { return {
...state, ...state,
@ -550,6 +561,7 @@ function tasksReducer(
}, },
}; };
case RUN_RETRY_TASK_ERROR:
case DELETE_RETRY_TASK_ERROR: case DELETE_RETRY_TASK_ERROR:
return { return {
...state, ...state,