Skip to content

Commit

Permalink
Improves discoverability of errors in jobs (#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
pushchris authored Mar 29, 2024
1 parent 2466b30 commit 3b74380
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 1 deletion.
4 changes: 4 additions & 0 deletions apps/platform/src/organizations/OrganizationController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ router.get('/performance/jobs/:job', async ctx => {
ctx.body = await App.main.stats.list(ctx.params.job)
})

router.get('/performance/failed', async ctx => {
ctx.body = await App.main.queue.failed()
})

router.get('/integrations', async ctx => {
ctx.body = await organizationIntegrations(ctx.state.organization)
})
Expand Down
2 changes: 1 addition & 1 deletion apps/platform/src/providers/MessageTriggerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export const failSend = async ({ campaign, user, context }: MessageTriggerHydrat
})
await createEvent(user, {
name: campaign.eventName('failed'),
data: { ...context },
data: { ...context, error },
}, true, ({ result, ...data }) => data)
if (shouldNotify(error)) App.main.error.notify(error)
}
Expand Down
4 changes: 4 additions & 0 deletions apps/platform/src/queue/Queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,8 @@ export default class Queue {
async metrics(period = MetricPeriod.FOUR_HOURS): Promise<QueueMetric | undefined> {
return await this.provider.metrics?.(period)
}

async failed(): Promise<QueueMetric | undefined> {
return await this.provider.failed?.()
}
}
1 change: 1 addition & 0 deletions apps/platform/src/queue/QueueProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ export default interface QueueProvider {
start(): void
close(): void
metrics?(period: MetricPeriod): Promise<QueueMetric>
failed?(): Promise<any>
}
4 changes: 4 additions & 0 deletions apps/platform/src/queue/RedisQueueProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,8 @@ export default class RedisQueueProvider implements QueueProvider {
waiting,
}
}

async failed() {
return this.bull.getFailed()
}
}
3 changes: 3 additions & 0 deletions apps/ui/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ const api = {
jobPerformance: async (job: string) => await client
.get<Metric[]>(`/admin/organizations/performance/jobs/${job}`)
.then(r => r.data),
failed: async () => await client
.get<any>('/admin/organizations/performance/failed')
.then(r => r.data),
},

locales: createProjectEntityPath<Locale>('locales'),
Expand Down
31 changes: 31 additions & 0 deletions apps/ui/src/views/organization/Performance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PreferencesContext } from '../../ui/PreferencesContext'
import Tile, { TileGrid } from '../../ui/Tile'
import PageContent from '../../ui/PageContent'
import { SingleSelect } from '../../ui/form/SingleSelect'
import { DataTable, JsonPreview, Modal } from '../../ui'

interface Series {
label: string
Expand All @@ -24,6 +25,9 @@ export default function Performance() {
const [currentJob, setCurrentJob] = useState<string | undefined>()
const [jobMetrics, setJobMetrics] = useState<Series[] | undefined>()

const [failed, setFailed] = useState<Array<Record<string, any>>>([])
const [selectedFailed, setSelectedFailed] = useState<Record<string, any> | undefined>()

useEffect(() => {
api.organizations.metrics()
.then(({ waiting, data }) => {
Expand All @@ -46,6 +50,12 @@ export default function Performance() {
setCurrentJob(jobs[0])
})
.catch(() => {})

api.organizations.failed()
.then((failed) => {
setFailed(failed)
})
.catch(() => {})
}, [])

useEffect(() => {
Expand Down Expand Up @@ -124,6 +134,27 @@ export default function Performance() {
}}
/>
</div>}

{failed.length && <>
<Heading size="h4" title="Failed" />
<div className="failed">
<DataTable items={failed} columns={[
{ key: 'id', title: 'ID' },
{ key: 'name', title: 'Name' },
{ key: 'attemptsMade', title: 'Attempts Made' },
{ key: 'failedReason', title: 'Reason' },
{ key: 'timestamp', title: 'Timestamp' },
]} onSelectRow={row => setSelectedFailed(row) }/>
</div>
</>}

<Modal
title="Failed Job"
size="large"
open={!!selectedFailed}
onClose={() => setSelectedFailed(undefined)}>
{selectedFailed && <JsonPreview value={selectedFailed} />}
</Modal>
</PageContent>
)
}

0 comments on commit 3b74380

Please sign in to comment.