mirror of
https://github.com/prymitive/karma
synced 2026-05-11 03:46:48 +00:00
refactor(ui): prevent concurrent fetches
Refactor the timer so instead of calling fetch directly every seconds we call it every 1 second, check if we could & should fetch (fetch isn't already/still in progress and seconds passed since last fetch) and if needed call fetch. This will also allow us to pause fetches when user is interacting with alerts so there's no whack-a-mole with buttons
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { toJS } from "mobx";
|
||||
import { observable, action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import moment from "moment";
|
||||
|
||||
import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
const Fetcher = observer(
|
||||
@@ -14,40 +16,53 @@ const Fetcher = observer(
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
};
|
||||
|
||||
timer = null;
|
||||
lastTick = observable(
|
||||
{
|
||||
time: moment(0),
|
||||
update() {
|
||||
this.time = moment();
|
||||
}
|
||||
},
|
||||
{
|
||||
update: action
|
||||
}
|
||||
);
|
||||
|
||||
interval = null;
|
||||
|
||||
setTimer() {
|
||||
fetchIfIdle = () => {
|
||||
const { alertStore, settingsStore } = this.props;
|
||||
|
||||
const newInterval = toJS(settingsStore.fetchConfig.config.interval);
|
||||
const nextTick = moment(this.lastTick.time).add(
|
||||
settingsStore.fetchConfig.config.interval,
|
||||
"seconds"
|
||||
);
|
||||
|
||||
if (this.interval !== newInterval) {
|
||||
if (this.timer !== null) clearInterval(this.timer);
|
||||
const pastDeadline = moment().isSameOrAfter(nextTick);
|
||||
|
||||
this.interval = newInterval;
|
||||
this.timer = setInterval(
|
||||
() => alertStore.fetchWithThrottle(),
|
||||
this.interval * 1000
|
||||
);
|
||||
const status = alertStore.status.value.toString();
|
||||
const updateInProgress =
|
||||
status === AlertStoreStatuses.Fetching.toString() ||
|
||||
status === AlertStoreStatuses.Processing.toString();
|
||||
|
||||
if (pastDeadline && !updateInProgress) {
|
||||
this.lastTick.update();
|
||||
alertStore.fetchWithThrottle();
|
||||
}
|
||||
};
|
||||
|
||||
timerTick = () => {
|
||||
this.fetchIfIdle();
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchIfIdle();
|
||||
this.timer = setInterval(this.timerTick, 1000);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { alertStore } = this.props;
|
||||
|
||||
this.lastTick.update();
|
||||
alertStore.fetchWithThrottle();
|
||||
|
||||
this.setTimer();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { alertStore } = this.props;
|
||||
|
||||
alertStore.fetchWithThrottle();
|
||||
|
||||
this.setTimer();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from "react";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import { advanceTo, advanceBy, clear } from "jest-date-mock";
|
||||
|
||||
import { EmptyAPIResponse } from "__mocks__/Fetch";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
@@ -9,24 +11,30 @@ import { Settings } from "Stores/Settings";
|
||||
|
||||
import { Fetcher } from ".";
|
||||
|
||||
let alertStore;
|
||||
let settingsStore;
|
||||
let fetchSpy;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
let alertStore;
|
||||
let settingsStore;
|
||||
|
||||
beforeEach(() => {
|
||||
fetch.mockResponse(JSON.stringify(EmptyAPIResponse()));
|
||||
advanceTo(new Date(2000, 1, 1, 0, 0, 0));
|
||||
|
||||
alertStore = new AlertStore(["label=value"]);
|
||||
fetchSpy = jest
|
||||
.spyOn(alertStore, "fetchWithThrottle")
|
||||
.mockImplementation(() => {});
|
||||
|
||||
settingsStore = new Settings();
|
||||
settingsStore.fetchConfig.config.interval = 30;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
|
||||
global.fetch.mockRestore();
|
||||
jest.clearAllMocks();
|
||||
clear();
|
||||
});
|
||||
|
||||
const MockEmptyAPIResponseWithoutFilters = () => {
|
||||
@@ -57,6 +65,30 @@ describe("<Fetcher />", () => {
|
||||
expect(tree.html()).toBe(FetcherSpan("label=value", 60));
|
||||
});
|
||||
|
||||
it("changing interval changes how often fetch is called", () => {
|
||||
settingsStore.fetchConfig.config.interval = 1;
|
||||
MountedFetcher();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
settingsStore.fetchConfig.config.interval = 600;
|
||||
|
||||
advanceBy(3 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
advanceBy(32 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
advanceBy(62 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
advanceBy(602 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("re-renders on filters change", () => {
|
||||
MockEmptyAPIResponseWithoutFilters();
|
||||
const tree = MountedFetcher();
|
||||
@@ -86,13 +118,19 @@ describe("<Fetcher />", () => {
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps calling alertStore.fetchWithThrottle after running pending timers", () => {
|
||||
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
|
||||
it("keeps calling alertStore.fetchWithThrottle every minute", () => {
|
||||
MountedFetcher();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
advanceBy(62 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
advanceBy(62 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(3);
|
||||
|
||||
advanceBy(62 * 1000);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user