Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance #371

Merged
merged 2 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions packages/api/src/handlers/queues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,21 @@ async function getAppQueues(
pairs: [string, BaseAdapter][],
query: Record<string, any>
): Promise<AppQueue[]> {
return await Promise.all(
return Promise.all(
pairs.map(async ([queueName, queue]) => {
const isActiveQueue = query.activeQueue === queueName;

const status =
!query[queueName] || query[queueName] === 'latest'
? allStatuses
: [query[queueName] as JobStatus];
!isActiveQueue || query.status === 'latest' ? allStatuses : [query.status as JobStatus];
const currentPage = +query.page || 1;

const counts = await queue.getJobCounts(...allStatuses);

const isPaused = await queue.isPaused();

const pagination = getPagination(status, counts, currentPage);
const jobs = await queue.getJobs(status, pagination.range.start, pagination.range.end);
const jobs = isActiveQueue
? await queue.getJobs(status, pagination.range.start, pagination.range.end)
: [];

return {
name: queueName,
Expand Down
13 changes: 13 additions & 0 deletions packages/ui/src/hooks/useActiveQueue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { matchPath } from 'react-router';
import { useLocation } from 'react-router-dom';

export function useActiveQueue(): string | undefined {
const { pathname } = useLocation();
const match = matchPath<{ name: string }>(pathname, {
path: '/queue/:name',
exact: true,
strict: true,
});

return match?.params.name;
}
22 changes: 19 additions & 3 deletions packages/ui/src/hooks/useInterval.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useEffect, useRef } from 'react';
import { useEffect, useLayoutEffect, useRef } from 'react';

/**
* Based on https://usehooks-ts.com/react-hook/use-interval
* */

export function useInterval(callback: () => void, delay: number | null, deps: any[] = []): void {
const savedCallback = useRef(callback);

// Remember the latest callback if it changes.
useEffect(() => {
useLayoutEffect(() => {
savedCallback.current = callback;
}, [callback]);

Expand All @@ -16,8 +20,20 @@ export function useInterval(callback: () => void, delay: number | null, deps: an
}

savedCallback.current();
let isLastFinished = true;

const id = setInterval(async () => {
if (!isLastFinished) {
return;
}

const id = setInterval(() => savedCallback.current(), delay);
isLastFinished = false;
try {
await savedCallback.current();
} finally {
isLastFinished = true;
}
}, delay);

return () => {
clearInterval(id);
Expand Down
39 changes: 24 additions & 15 deletions packages/ui/src/hooks/useStore.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { AppJob } from '@bull-board/api/typings/app';
import { GetQueuesResponse } from '@bull-board/api/typings/responses';
import { useState } from 'react';
import { QueueActions, SelectedStatuses } from '../../typings/app';

import { Api } from '../services/Api';
import { useActiveQueue } from './useActiveQueue';
import { ConfirmApi, useConfirm } from './useConfirm';
import { useInterval } from './useInterval';
import { useSelectedStatuses } from './useSelectedStatuses';
import { QueueActions, SelectedStatuses } from '../../typings/app';
import { AppJob } from '@bull-board/api/typings/app';
import { GetQueuesResponse } from '@bull-board/api/typings/responses';
import { useQuery } from './useQuery';
import { ConfirmApi, useConfirm } from './useConfirm';
import { useSelectedStatuses } from './useSelectedStatuses';

const interval = 5000;

Expand All @@ -25,6 +26,8 @@ export interface Store {

export const useStore = (api: Api): Store => {
const query = useQuery();
const activeQueue = useActiveQueue();

const [state, setState] = useState<State>({
data: null,
loading: true,
Expand All @@ -35,7 +38,11 @@ export const useStore = (api: Api): Store => {

const update = () =>
api
.getQueues({ status: selectedStatuses, page: query.get('page') || '1' })
.getQueues({
activeQueue,
status: activeQueue ? selectedStatuses[activeQueue] : undefined,
page: query.get('page') || '1',
})
.then((data) => {
setState({ data, loading: false });
})
Expand Down Expand Up @@ -101,15 +108,17 @@ export const useStore = (api: Api): Store => {
'Are you sure that you want to clean all completed jobs?'
);

const pauseQueue = (queueName: string) =>withConfirmAndUpdate(
() => api.pauseQueue(queueName),
'Are you sure that you want to pause queue processing?'
);
const pauseQueue = (queueName: string) =>
withConfirmAndUpdate(
() => api.pauseQueue(queueName),
'Are you sure that you want to pause queue processing?'
);

const resumeQueue = (queueName: string) =>withConfirmAndUpdate(
() => api.resumeQueue(queueName),
'Are you sure that you want to resume queue processing?'
);
const resumeQueue = (queueName: string) =>
withConfirmAndUpdate(
() => api.resumeQueue(queueName),
'Are you sure that you want to resume queue processing?'
);

const getJobLogs = (queueName: string) => (job: AppJob) => () =>
api.getJobLogs(queueName, job.id);
Expand All @@ -126,7 +135,7 @@ export const useStore = (api: Api): Store => {
cleanAllCompleted,
getJobLogs,
pauseQueue,
resumeQueue
resumeQueue,
},
confirmProps,
selectedStatuses,
Expand Down
25 changes: 17 additions & 8 deletions packages/ui/src/services/Api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { AppJob } from '@bull-board/api/typings/app';
import { AppJob, Status } from '@bull-board/api/typings/app';
import { GetQueuesResponse } from '@bull-board/api/typings/responses';
import Axios, { AxiosInstance, AxiosResponse } from 'axios';
import { toast } from 'react-toastify';
import { SelectedStatuses } from '../../typings/app';

export class Api {
private axios: AxiosInstance;
Expand All @@ -13,13 +12,15 @@ export class Api {
}

public getQueues({
activeQueue,
status,
page,
}: {
status: SelectedStatuses;
activeQueue?: string;
status?: Status;
page: string;
}): Promise<GetQueuesResponse> {
return this.axios.get(`/queues`, { params: { ...status, page } });
return this.axios.get(`/queues`, { params: { activeQueue, status, page } });
}

public retryAll(queueName: string): Promise<void> {
Expand All @@ -39,19 +40,27 @@ export class Api {
}

public cleanJob(queueName: string, jobId: AppJob['id']): Promise<void> {
return this.axios.put(`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/clean`);
return this.axios.put(
`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/clean`
);
}

public retryJob(queueName: string, jobId: AppJob['id']): Promise<void> {
return this.axios.put(`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/retry`);
return this.axios.put(
`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/retry`
);
}

public promoteJob(queueName: string, jobId: AppJob['id']): Promise<void> {
return this.axios.put(`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/promote`);
return this.axios.put(
`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/promote`
);
}

public getJobLogs(queueName: string, jobId: AppJob['id']): Promise<string[]> {
return this.axios.get(`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/logs`);
return this.axios.get(
`/queues/${encodeURIComponent(queueName)}/${encodeURIComponent(`${jobId}`)}/logs`
);
}

public pauseQueue(queueName: string) {
Expand Down