@datalayer/core
Version:
[](https://datalayer.io)
181 lines (180 loc) • 6.56 kB
JavaScript
/*
* Copyright (c) 2023-2025 Datalayer, Inc.
* Distributed under the terms of the Modified BSD License.
*/
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';
import { JSONExt } from '@lumino/coreutils';
import { Poll } from '@lumino/polling';
import { getRuntimes } from '../../stateful/runtimes';
import { coreStore } from './CoreState';
import { iamStore } from './IAMState';
/**
* Kernel store
*/
export const runtimesStore = createStore((set, get) => {
return {
configuration: {
maxNotebookRuntimes: 5,
maxCellRuntimes: 3,
},
setConfiguration: (configuration) => {
set(state => JSONExt.deepEqual(state.configuration, configuration)
? {}
: { configuration: { ...configuration } });
},
runtimesRunUrl: coreStore.getState().configuration?.runtimesRunUrl,
tab: 0.0,
getIntTab: () => Math.floor(get().tab),
setTab: (tab) => set(state => ({ tab })),
/**
* Remote runtime pods.
*/
runtimePods: [],
/**
* Refresh the runtime pods.
*/
refreshRuntimePods: async () => {
const servers = await getRuntimes();
// Update the state with the Remote Kernels.
if (!JSONExt.deepEqual(get().runtimePods, servers)) {
set({ runtimePods: [...servers] });
}
},
/**
* Cached runtime models.
*/
runtimeModels: [],
/**
* Add a runtime model
*/
addRuntimeModel: (model) => {
const kernels = get().runtimeModels;
// TODO
// We need to review the IRuntimeModel/IRuntimePod/Kernel.IModel and their id/uid handling.
// The id is the kernel id, which is no more always present for some reasons.
// So we need to also check the uid of the model.
const index = kernels.findIndex(m => (model.id === m.id || model.uid === m.uid) ?? -1);
if (index < 0) {
set({ runtimeModels: [...kernels, model] });
}
},
/**
* Remove a runtime model by ID.
*/
removeRuntimeModel: (id) => {
const kernels = [...get().runtimeModels];
const index = kernels?.findIndex(model => id === model.id) ?? -1;
if (index >= 0) {
kernels.splice(index, 1);
set({ runtimeModels: kernels });
}
},
setRuntimeModels: (models) => {
if (!JSONExt.deepEqual(get().runtimeModels, models)) {
set({ runtimeModels: [...models] });
}
},
multiServiceManager: undefined,
setMultiServiceManager: multiServiceManager => {
set(state => ({ multiServiceManager }));
},
showDisclaimer: false,
setShowDisclaimer: showDisclaimer => {
set(state => ({ showDisclaimer }));
},
/**
* Kernel Snapshots.
*/
runtimeSnapshots: [],
/**
* Add a Kernel Snapshot
*/
addRuntimeSnapshot: (snapshot) => {
const snapshots = get().runtimeSnapshots;
const index = snapshots.findIndex(s => s.id === snapshot.id);
if (index < 0) {
const kernelSnapshots = [...snapshots, snapshot];
set({ runtimeSnapshots: kernelSnapshots });
}
else if (!JSONExt.deepEqual(snapshots[index], snapshot)) {
const kernelSnapshots = [...snapshots];
kernelSnapshots.splice(index, 1, snapshot);
set({ runtimeSnapshots: kernelSnapshots });
}
},
/**
* Remove a Kernel Snapshot.
*/
removeRuntimeSnapshot: (id) => {
const snapshots = get().runtimeSnapshots;
const index = snapshots.findIndex(s => s.id === id);
if (index >= 0) {
const kernelSnapshots = [...snapshots];
kernelSnapshots.splice(index, 1);
set({ runtimeSnapshots: kernelSnapshots });
}
},
/**
* Set Kernel Snapshots.
*/
setRuntimeSnapshots: (snapshots) => {
if (!JSONExt.deepEqual(get().runtimeSnapshots, snapshots)) {
set({ runtimeSnapshots: [...snapshots] });
}
},
version: '',
setVersion: version => {
if (version && !get().version) {
set(state => ({ version }));
}
},
};
});
// Poll remote kernels
const kernelsPoll = new Poll({
auto: true,
factory: () => runtimesStore.getState().refreshRuntimePods(),
frequency: {
interval: 61 * 1000,
backoff: true,
max: 300 * 1000,
},
name: '@datalayer/jupyter-kernels:KernelsManager#kernels',
standby: () => iamStore.getState().token || runtimesStore.getState().runtimesRunUrl
? 'when-hidden'
: true,
});
// Force refresh at expiration date if next tick is after it.
runtimesStore.subscribe((state, prevState) => {
if (!JSONExt.deepEqual(state.runtimePods, prevState.runtimePods)) {
const now = Date.now();
const minExpiredAt = Math.min(...state.runtimePods.map(kernel => kernel.expired_at ? parseFloat(kernel.expired_at) : Infinity)) * 1_000;
// Refresh 2 sec after the closest expiration time
// to let some times to the system to dispose the resources.
if (now + kernelsPoll.frequency.interval > minExpiredAt + 2_000) {
setTimeout(() => {
kernelsPoll.refresh();
}, minExpiredAt + 2_000 - now);
}
}
});
coreStore.subscribe((state, prevState) => {
if (state.configuration.runtimesRunUrl &&
state.configuration.runtimesRunUrl !==
prevState.configuration.runtimesRunUrl) {
const runtimesRunUrl = state.configuration.runtimesRunUrl;
console.log(`Updating runtimesRunUrl with new value ${runtimesRunUrl}`);
runtimesStore.setState({ runtimesRunUrl });
kernelsPoll
.refresh()
.then(() => kernelsPoll.tick)
.catch(reason => {
console.error('Failed to refresh kernel servers list following service URL changed.', reason);
});
}
});
export function useRuntimesStore(selector) {
return useStore(runtimesStore, selector);
}
export default useRuntimesStore;