dashboard/shell/plugins/steve/web-worker.steve-sub-worker.js

130 lines
3.4 KiB
JavaScript

import * as Comlink from 'comlink';
import { SCHEMA } from '@shell/config/types';
const COUNTS_FLUSH_TIMEOUT = 5000;
const SCHEMA_FLUSH_TIMEOUT = 2500;
const state = {
store: '', // Store name
load: undefined, // Load callback to load a resource into the store
counts: [], // Buffer of count resources recieved in a given window
countTimer: undefined, // Tiemr to flush the count buffer
flushTimer: undefined, // Timer to flush the schema chaneg queue
queue: [], // Schema change queue
schemas: {} // Map of schema id to hash to track when a schema actually changes
};
// Quick, simple hash function
function hash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash &= hash;
}
return new Uint32Array([hash])[0].toString(36);
}
// Quick, simple hash function to generate hash for an object
function hashObj(obj) {
return hash(JSON.stringify(obj, null, 2));
}
function flush() {
state.queue.forEach((schema) => {
const hash = hashObj(schema);
const existing = state.schemas[schema.id];
if (!existing || (existing && existing !== hash)) {
// console.log(`${ schema.id } CHANGED ${ hash } > ${ existing }`);
state.schemas[schema.id] = hash;
const msg = {
data: schema,
resourceType: SCHEMA,
type: 'resource.change'
};
load(msg);
}
});
state.queue = [];
state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
}
state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
// Callback to the store's load function (in the main thread) to process a load
function load(data) {
if (state.load) {
state.load(data);
}
}
// Web Worker API
const fns = {
initWorker(storeName, loadFn) {
state.store = storeName;
state.load = loadFn;
},
destroyWorker() {
clearTimeout(state.countTimer);
clearTimeout(state.flushTimer);
// Web worker global function to terminate the web worker
close();
},
// Debounce counts messages so we only process at most 1 every 5 seconds
countsUpdate(resource) {
state.counts.push(resource);
if (!state.countTimer) {
state.countTimer = setTimeout(() => {
const last = state.counts.pop();
state.counts = [];
state.countTimer = null;
load(last);
}, COUNTS_FLUSH_TIMEOUT);
}
},
// Called to load schema
loadSchema(schemas) {
schemas.forEach((schema) => {
// These properties are added to the object, but aren't on the raw object, so remove them
// otherwise our comparison will show changes when there aren't any
delete schema._id;
delete schema._group;
state.schemas[schema.id] = hashObj(schema);
});
},
// Called when schema is updated
updateSchema(schema) {
// Add the schema to the queue to be checked to see if the schema really changed
state.queue.push(schema);
},
// Remove the cached schema
removeSchema(id) {
// Remove anything in the queue related to the schema - we don't want to send any pending updates later for a schema that has been removed
state.queue = state.queue.filter(schema => schema.id !== id);
// Delete the schema from the map, so if it comes back we don't ignore it if the hash is the same
delete state.schemas[id];
}
};
// Expose the Web Worker API - see: https://github.com/GoogleChromeLabs/comlink
Comlink.expose(fns);