adminQueue.tsx 4.0 KB

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