Skip to content

Commit

Permalink
[GEN-2141]: remove instrumentation config modified-event batcher; upd…
Browse files Browse the repository at this point in the history
…ate source-update logic (#2108)
  • Loading branch information
BenElferink authored Dec 31, 2024
1 parent 10d35d6 commit 6d3d447
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 36 deletions.
32 changes: 0 additions & 32 deletions frontend/kube/watchers/instrumentation_config_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
)

var instrumentationConfigAddedEventBatcher *EventBatcher
var instrumentationConfigModifiedEventBatcher *EventBatcher
var instrumentationConfigDeletedEventBatcher *EventBatcher

func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) error {
Expand All @@ -34,21 +33,6 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er
},
)

instrumentationConfigModifiedEventBatcher = NewEventBatcher(
EventBatcherConfig{
MinBatchSize: 1,
Duration: 10 * time.Second,
Event: sse.MessageEventModified,
CRDType: consts.InstrumentationConfig,
SuccessBatchMessageFunc: func(batchSize int, crd string) string {
return fmt.Sprintf("Successfully updated %d sources", batchSize)
},
FailureBatchMessageFunc: func(batchSize int, crd string) string {
return fmt.Sprintf("Failed to update %d sources", batchSize)
},
},
)

instrumentationConfigDeletedEventBatcher = NewEventBatcher(
EventBatcherConfig{
MinBatchSize: 1,
Expand Down Expand Up @@ -76,7 +60,6 @@ func StartInstrumentationConfigWatcher(ctx context.Context, namespace string) er
func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.Interface) {
ch := watcher.ResultChan()
defer instrumentationConfigAddedEventBatcher.Cancel()
defer instrumentationConfigModifiedEventBatcher.Cancel()
defer instrumentationConfigDeletedEventBatcher.Cancel()
for {
select {
Expand All @@ -90,8 +73,6 @@ func handleInstrumentationConfigWatchEvents(ctx context.Context, watcher watch.I
switch event.Type {
case watch.Added:
handleAddedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig))
case watch.Modified:
handleModifiedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig))
case watch.Deleted:
handleDeletedInstrumentationConfig(event.Object.(*v1alpha1.InstrumentationConfig))
}
Expand All @@ -112,19 +93,6 @@ func handleAddedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConf
instrumentationConfigAddedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target)
}

func handleModifiedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) {
namespace := instruConfig.Namespace
name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name)
if err != nil {
genericErrorMessage(sse.MessageEventAdded, consts.InstrumentationConfig, err.Error())
return
}

target := fmt.Sprintf("namespace=%s&name=%s&kind=%s", namespace, name, kind)
data := fmt.Sprintf(`Source "%s" updated`, name)
instrumentationConfigModifiedEventBatcher.AddEvent(sse.MessageTypeSuccess, data, target)
}

func handleDeletedInstrumentationConfig(instruConfig *v1alpha1.InstrumentationConfig) {
namespace := instruConfig.Namespace
name, kind, err := commonutils.ExtractWorkloadInfoFromRuntimeObjectName(instruConfig.Name)
Expand Down
23 changes: 19 additions & 4 deletions frontend/webapp/hooks/sources/useSourceCRUD.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ interface Params {
}

export const useSourceCRUD = (params?: Params) => {
const { data } = useComputePlatform();
const { persistNamespace } = useNamespace();
const { addPendingItems } = usePendingStore();
const { data, refetch } = useComputePlatform();
const { setConfiguredSources } = useAppStore();
const { addPendingItems, removePendingItems } = usePendingStore();
const { addNotification, removeNotifications } = useNotificationStore();

const notifyUser = (type: NOTIFICATION_TYPE, title: string, message: string, id?: WorkloadId, hideFromHistory?: boolean) => {
Expand Down Expand Up @@ -59,7 +59,22 @@ export const useSourceCRUD = (params?: Params) => {

const [updateSource, uState] = useMutation<{ updateK8sActualSource: boolean }>(UPDATE_K8S_ACTUAL_SOURCE, {
onError: (error) => handleError(ACTION.UPDATE, error.message),
onCompleted: () => handleComplete(ACTION.UPDATE),
onCompleted: (res, req) => {
handleComplete(ACTION.UPDATE);

// This is instead of using a k8s modified-event watcher...
// If we do use a watcher, we can't guarantee an SSE will be sent for this update alone.
// It will definitely include SSE for all updates, that can be instrument/uninstrument, conditions changed etc.
// Not that there's anything about a watcher that would break the UI, it's just that we would receive unexpected events with ridiculous amounts,
// (example: instrument 5 apps, update the name of 2, then uninstrument the other 3, we would get an SSE with minimum 10 updated sources, when we expect it to show only 2 due to name change).
setTimeout(() => {
const id = req?.variables?.sourceId;

refetch();
notifyUser(NOTIFICATION_TYPE.SUCCESS, ACTION.UPDATE, 'Successfully updated 1 source', id);
removePendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: id }]);
}, 2000);
},
});

return {
Expand Down Expand Up @@ -88,7 +103,7 @@ export const useSourceCRUD = (params?: Params) => {
},

updateSource: async (sourceId: WorkloadId, patchSourceRequest: PatchSourceRequestInput) => {
notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating sources...', undefined, true);
notifyUser(NOTIFICATION_TYPE.INFO, 'Pending', 'Updating source...', undefined, true);
addPendingItems([{ entityType: OVERVIEW_ENTITY_TYPES.SOURCE, entityId: sourceId }]);
await updateSource({ variables: { sourceId, patchSourceRequest } });
},
Expand Down
11 changes: 11 additions & 0 deletions frontend/webapp/store/usePendingStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { create } from 'zustand';
import { OVERVIEW_ENTITY_TYPES, WorkloadId } from '@/types';

// This store is used to keep track of pending items that are being created, updated, or deleted.
// This is used for entities that require an SSE event to be sent from the backend after a CRUD action.
// ---
// Imagine a user instruments a few sources, we want to show loading spinners, toasts etc.
// The CLI will finish processing the CRDs and send an SSE event to the frontend.
// The frontend will then remove the pending item from the store and update the UI by refetching the data.
// ---
// This can be used for non-SSE entities (like actions & rules), but it's not necessary as we refetch-instantly in those cases.

export interface PendingItem {
entityType: OVERVIEW_ENTITY_TYPES;
entityId?: string | WorkloadId;
Expand Down Expand Up @@ -32,6 +41,8 @@ export const usePendingStore = create<StoreState>((set, get) => ({
addPendingItems: (arr) => set((state) => ({ pendingItems: state.pendingItems.concat(arr.filter((addItem) => !state.pendingItems.some((existingItem) => itemsAreEqual(existingItem, addItem)))) })),
removePendingItems: (arr) => set((state) => ({ pendingItems: state.pendingItems.filter((existingItem) => !arr.find((removeItem) => itemsAreEqual(existingItem, removeItem))) })),

// Pass an item to check if it's in the pending items array.
// This is used to show loading spinners, toasts etc.
isThisPending: (item) => {
const { pendingItems } = get();
let bool = false;
Expand Down

0 comments on commit 6d3d447

Please sign in to comment.