From bbd9edab266833b9f655d19cf439707edd10a09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Sun, 14 Oct 2018 11:16:08 +0100 Subject: [PATCH] refactor(ui): speed up updates with large number of alerts Comparing group dicts with large number of alerts is expensive, doing it per group and using hash checks will speed up updates, especially when alerts don't change often --- ui/src/Stores/AlertStore.js | 29 +++++++++++++----- ui/src/Stores/AlertStore.test.js | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/ui/src/Stores/AlertStore.js b/ui/src/Stores/AlertStore.js index 7bd470fd6..f4ebb3568 100644 --- a/ui/src/Stores/AlertStore.js +++ b/ui/src/Stores/AlertStore.js @@ -285,13 +285,7 @@ class AlertStore { let updates = {}; // update data dicts if they changed - for (const key of [ - "colors", - "counters", - "groups", - "silences", - "upstreams" - ]) { + for (const key of ["colors", "counters", "silences", "upstreams"]) { if (!equal(this.data[key], result[key])) { updates[key] = result[key]; } @@ -300,6 +294,27 @@ class AlertStore { this.data = Object.assign(this.data, updates); } + // update groups, it can be huge so we have custom logic with cheaper + // comparision logic running per group using content hashes from the API + // response + for (const key of Object.keys(result.groups)) { + // set/update each group if: + // * it's not yet stored in AlertStore + // * it's stored but hash is different than in the API response + if ( + !(key in this.data.groups) || + (key in this.data.groups && + result.groups[key].hash !== this.data.groups[key].hash) + ) { + this.data.groups[key] = result.groups[key]; + } + } + for (const key of Object.keys(this.data.groups).filter( + k => !(k in result.groups) + )) { + delete this.data.groups[key]; + } + // before storing new version check if we need to reload if ( this.info.version !== "unknown" && diff --git a/ui/src/Stores/AlertStore.test.js b/ui/src/Stores/AlertStore.test.js index a9df5f260..2d692c927 100644 --- a/ui/src/Stores/AlertStore.test.js +++ b/ui/src/Stores/AlertStore.test.js @@ -333,4 +333,56 @@ describe("AlertStore.fetch", () => { await expect(store.fetch()).resolves.toBeUndefined(); expect(store.info.upgradeNeeded).toBe(true); }); + + it("adds new groups to the store after fetch", () => { + const response = EmptyAPIResponse(); + response.groups = { foo: "foo", bar: "bar" }; + const store = new AlertStore(["label=value"]); + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ foo: "foo", bar: "bar" }); + }); + + it("is no-op for groups that didn't change", () => { + const store = new AlertStore(["label=value"]); + store.data.groups = { foo: { hash: "foo" }, bar: { hash: "bar" } }; + + const response = EmptyAPIResponse(); + response.groups = { foo: { hash: "foo" }, bar: { hash: "bar" } }; + + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ + foo: { hash: "foo" }, + bar: { hash: "bar" } + }); + }); + + it("removes old groups from the store after fetch", () => { + const store = new AlertStore(["label=value"]); + store.data.groups = { foo: "foo", delete: "me", bar: "bar" }; + expect(Object.keys(store.data.groups)).toHaveLength(3); + + const response = EmptyAPIResponse(); + response.groups = { foo: "foo", bar: "bar" }; + + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ foo: "foo", bar: "bar" }); + }); + + it("updates groups with new hash after fetch", () => { + const store = new AlertStore(["label=value"]); + store.data.groups = { foo: { hash: "foo" }, bar: { hash: "bar" } }; + + const response = EmptyAPIResponse(); + response.groups = { foo: { hash: "newFoo" }, bar: { hash: "newBar" } }; + + store.parseAPIResponse(response); + expect(Object.keys(store.data.groups)).toHaveLength(2); + expect(store.data.groups).toMatchObject({ + foo: { hash: "newFoo" }, + bar: { hash: "newBar" } + }); + }); });