adminQueue.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import {useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Button} from 'sentry/components/button';
  4. import ButtonBar from 'sentry/components/buttonBar';
  5. import SelectControl from 'sentry/components/forms/controls/selectControl';
  6. import InternalStatChart from 'sentry/components/internalStatChart';
  7. import LoadingError from 'sentry/components/loadingError';
  8. import LoadingIndicator from 'sentry/components/loadingIndicator';
  9. import Panel from 'sentry/components/panels/panel';
  10. import PanelBody from 'sentry/components/panels/panelBody';
  11. import PanelHeader from 'sentry/components/panels/panelHeader';
  12. import {t} from 'sentry/locale';
  13. import {useApiQuery} from 'sentry/utils/queryClient';
  14. const TIME_WINDOWS = ['1h', '1d', '1w'] as const;
  15. type TimeWindow = (typeof TIME_WINDOWS)[number];
  16. type State = {
  17. activeTask: string;
  18. resolution: string;
  19. since: number;
  20. timeWindow: TimeWindow;
  21. };
  22. export default function AdminQueue() {
  23. const [state, setState] = useState<State>({
  24. timeWindow: '1w',
  25. since: new Date().getTime() / 1000 - 3600 * 24 * 7,
  26. resolution: '1h',
  27. activeTask: '',
  28. });
  29. const {
  30. data: taskList,
  31. isLoading,
  32. isError,
  33. } = useApiQuery<string[]>(['/internal/queue/tasks/'], {
  34. staleTime: 0,
  35. });
  36. if (isError) {
  37. return <LoadingError />;
  38. }
  39. if (isLoading) {
  40. return <LoadingIndicator />;
  41. }
  42. const changeWindow = (timeWindow: TimeWindow) => {
  43. let seconds: number;
  44. if (timeWindow === '1h') {
  45. seconds = 3600;
  46. } else if (timeWindow === '1d') {
  47. seconds = 3600 * 24;
  48. } else if (timeWindow === '1w') {
  49. seconds = 3600 * 24 * 7;
  50. } else {
  51. throw new Error('Invalid time window');
  52. }
  53. setState(prevState => ({
  54. ...prevState,
  55. since: new Date().getTime() / 1000 - seconds,
  56. timeWindow,
  57. }));
  58. };
  59. const changeTask = (value: string) => {
  60. setState(prevState => ({...prevState, activeTask: value}));
  61. };
  62. const {activeTask} = state;
  63. return (
  64. <div>
  65. <Header>
  66. <h3>t{'Queue Overview'}</h3>
  67. <ButtonBar merged active={state.timeWindow}>
  68. {TIME_WINDOWS.map(r => (
  69. <Button size="sm" barId={r} onClick={() => changeWindow(r)} key={r}>
  70. {r}
  71. </Button>
  72. ))}
  73. </ButtonBar>
  74. </Header>
  75. <Panel>
  76. <PanelHeader>{t('Global Throughput')}</PanelHeader>
  77. <PanelBody withPadding>
  78. <InternalStatChart
  79. since={state.since}
  80. resolution={state.resolution}
  81. stat="jobs.all.started"
  82. label="jobs started"
  83. />
  84. </PanelBody>
  85. </Panel>
  86. <h3>t{'Task Details'}</h3>
  87. <div>
  88. <div className="m-b-1">
  89. <label>t{'Show details for task:'}</label>
  90. <SelectControl
  91. name="task"
  92. onChange={({value}) => changeTask(value)}
  93. value={activeTask}
  94. clearable
  95. options={taskList.map(value => ({value, label: value}))}
  96. />
  97. </div>
  98. {activeTask ? (
  99. <div>
  100. <Panel key={`jobs.started.${activeTask}`}>
  101. <PanelHeader>
  102. {t('Jobs Started')} <small>{activeTask}</small>
  103. </PanelHeader>
  104. <PanelBody withPadding>
  105. <InternalStatChart
  106. since={state.since}
  107. resolution={state.resolution}
  108. stat={`jobs.started.${activeTask}`}
  109. label="jobs"
  110. height={100}
  111. />
  112. </PanelBody>
  113. </Panel>
  114. <Panel key={`jobs.finished.${activeTask}`}>
  115. <PanelHeader>
  116. {t('Jobs Finished')} <small>{activeTask}</small>
  117. </PanelHeader>
  118. <PanelBody withPadding>
  119. <InternalStatChart
  120. since={state.since}
  121. resolution={state.resolution}
  122. stat={`jobs.finished.${activeTask}`}
  123. label="jobs"
  124. height={100}
  125. />
  126. </PanelBody>
  127. </Panel>
  128. </div>
  129. ) : null}
  130. </div>
  131. </div>
  132. );
  133. }
  134. const Header = styled('div')`
  135. display: flex;
  136. justify-content: space-between;
  137. align-items: center;
  138. `;