Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,21 @@
* limitations under the License.
*/

import { Subject } from 'rxjs';

export default {
data() {
return {
destroy$: new Subject<void>(),
};
},
created() {
this.subscribe();
},
beforeUnmount() {
// Emit to signal all subscriptions to complete
this.destroy$.next();
this.destroy$.complete();
this.unsubscribe();
},
Comment on lines 25 to 33
methods: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Instance {
public availableMetrics: string[] = [];
public tags: { [key: string]: string }[];
public statusTimestamp: string;
public version?: number;
public buildVersion: string;
public statusInfo: StatusInfo;

Expand Down Expand Up @@ -534,6 +535,7 @@ type StatusInfo = {

type InstanceData = {
id: string;
version?: number;
registration: Registration;
endpoints?: Endpoint[];
availableMetrics?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,39 @@ describe('DetailsCache', () => {
expect(queryByText(`${HITS[0]}`)).not.toBeInTheDocument();
});
});

it('should reinitialize metrics when instance version changes (SSE update)', async () => {
const application = new Application(applications[0]);
const instance1 = application.instances[0];

const { getByText, rerender, queryByText } = await render(DetailsCache, {
props: {
instance: instance1,
cacheName: CACHE_NAME,
index: 0,
},
});

// wait until initial fetch rendered a numeric value
await waitFor(() => {
expect(getByText(`${HITS[0]}`)).toBeTruthy();
});

const instance2 = new Application({
...application,
instances: [
{
...instance1,
id: instance1.id,
version: (instance1.version ?? 0) + 1,
},
],
}).instances[0];

await rerender({ instance: instance2, cacheName: CACHE_NAME, index: 0 });

await waitFor(() => {
expect(queryByText(`${HITS[0]}`)).not.toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@

<script>
import moment from 'moment';
import { take } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';

import SbaAccordion from '@/components/sba-accordion.vue';
import sbaAlert from '@/components/sba-alert.vue';
Expand Down Expand Up @@ -116,6 +117,7 @@ export default {
shouldFetchCacheMisses: true,
chartData: [],
currentInstanceId: null,
currentInstanceUpdateKey: null,
}),
computed: {
ratio() {
Expand All @@ -138,18 +140,36 @@ export default {
},
},
methods: {
initCacheMetrics() {
if (this.instance.id !== this.currentInstanceId) {
this.currentInstanceId = this.instance.id;
this.hasLoaded = false;
this.error = null;
this.current = null;
this.chartData = [];
this.shouldFetchCacheSize = true;
this.shouldFetchCacheHits = true;
this.shouldFetchCacheMisses = true;
}
},
initCacheMetrics() {
const updateKey =
this.instance.version ??
this.instance.statusTimestamp ??
this.instance.id;
const firstInit = this.currentInstanceId === null;
if (
this.instance.id !== this.currentInstanceId ||
updateKey !== this.currentInstanceUpdateKey
) {
this.currentInstanceId = this.instance.id;
this.currentInstanceUpdateKey = updateKey;
this.hasLoaded = false;
this.error = null;
this.current = null;
this.chartData = [];
this.shouldFetchCacheSize = true;
this.shouldFetchCacheHits = true;
this.shouldFetchCacheMisses = true;

// Restart polling immediately so SSE updates refresh the view.
if (!firstInit) {
// Stop old subscription and start fresh
this.unsubscribe();
// Recreate destroy$ so new subscription can use takeUntil properly
this.destroy$ = new Subject();
this.subscribe();
}
}
},
async fetchMetrics() {
const [hit, miss, size] = await Promise.all([
this.fetchCacheHits(),
Expand Down Expand Up @@ -230,31 +250,33 @@ export default {

return { ...data, hitsPerInterval, missesPerInterval, totalPerInterval };
},
createSubscription() {
return timer(0, sbaConfig.uiSettings.pollTimer.cache)
.pipe(
concatMap(this.fetchMetrics),
map(this.calculateMetricsPerInterval),
retryWhen((err) => {
return err.pipe(delay(1000), take(5));
}),
)
.subscribe({
next: (data) => {
this.hasLoaded = true;
this.current = data;
this.chartData.push({ ...data, timestamp: moment().valueOf() });
},
error: (error) => {
this.hasLoaded = true;
console.warn(
`Fetching cache ${this.cacheName} metrics failed:`,
error,
);
this.error = error;
},
});
},
createSubscription() {
return timer(0, sbaConfig.uiSettings.pollTimer.cache)
.pipe(
concatMap(this.fetchMetrics),
map(this.calculateMetricsPerInterval),
// Stop polling when destroy$ emits (on unmount or instance update)
takeUntil(this.destroy$),
retryWhen((err) => {
return err.pipe(delay(1000), take(5));
}),
)
.subscribe({
next: (data) => {
this.hasLoaded = true;
this.current = data;
this.chartData.push({ ...data, timestamp: moment().valueOf() });
},
error: (error) => {
this.hasLoaded = true;
console.warn(
`Fetching cache ${this.cacheName} metrics failed:`,
error,
);
this.error = error;
},
});
},
},
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,37 @@ describe('DetailsDatasource', () => {
expect(screen.queryByText(`${ACTIVE[0]}`)).not.toBeInTheDocument();
});
});

it('should reinitialize metrics when instance version changes (SSE update)', async () => {
const application = new Application(applications[0]);
const instance1 = application.instances[0];

const { rerender } = await render(DetailsDatasource, {
props: {
instance: instance1,
dataSource: DATA_SOURCE,
},
});

await waitFor(() => {
expect(screen.getByText(`${ACTIVE[0]}`)).toBeVisible();
});

const instance2 = new Application({
...application,
instances: [
{
...instance1,
id: instance1.id,
version: (instance1.version ?? 0) + 1,
},
],
}).instances[0];

await rerender({ instance: instance2, dataSource: DATA_SOURCE });

await waitFor(() => {
expect(screen.queryByText(`${ACTIVE[0]}`)).not.toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@

<script>
import moment from 'moment';
import { concatMap, delay, retryWhen, timer } from 'rxjs';
import { Subject, concatMap, delay, retryWhen, takeUntil, timer } from 'rxjs';
import { take } from 'rxjs/operators';

import SbaAccordion from '@/components/sba-accordion.vue';
Expand Down Expand Up @@ -96,6 +96,7 @@ export default {
current: null,
chartData: [],
currentInstanceId: null,
currentInstanceUpdateKey: null,
}),
watch: {
instance: {
Expand All @@ -104,15 +105,33 @@ export default {
},
},
methods: {
initDatasourceMetrics() {
if (this.instance.id !== this.currentInstanceId) {
this.currentInstanceId = this.instance.id;
this.hasLoaded = false;
this.error = null;
this.current = null;
this.chartData = [];
}
},
initDatasourceMetrics() {
const updateKey =
this.instance.version ??
this.instance.statusTimestamp ??
this.instance.id;
const firstInit = this.currentInstanceId === null;
if (
this.instance.id !== this.currentInstanceId ||
updateKey !== this.currentInstanceUpdateKey
) {
this.currentInstanceId = this.instance.id;
this.currentInstanceUpdateKey = updateKey;
this.hasLoaded = false;
this.error = null;
this.current = null;
this.chartData = [];

// Restart polling immediately so SSE updates refresh the view.
if (!firstInit) {
// Stop old subscription and start fresh
this.unsubscribe();
// Recreate destroy$ so new subscription can use takeUntil properly
this.destroy$ = new Subject();
this.subscribe();
}
}
},
async fetchMetrics() {
const responseActive = this.instance.fetchMetric(
'jdbc.connections.active',
Expand All @@ -131,30 +150,32 @@ export default {
max: (await responseMax).data.measurements[0].value,
};
},
createSubscription() {
return timer(0, sbaConfig.uiSettings.pollTimer.datasource)
.pipe(
concatMap(this.fetchMetrics),
retryWhen((err) => {
return err.pipe(delay(1000), take(5));
}),
)
.subscribe({
next: (data) => {
this.hasLoaded = true;
this.current = data;
this.chartData.push({ ...data, timestamp: moment().valueOf() });
},
error: (error) => {
this.hasLoaded = true;
console.warn(
`Fetching datasource ${this.dataSource} metrics failed:`,
error,
);
this.error = error;
},
});
},
createSubscription() {
return timer(0, sbaConfig.uiSettings.pollTimer.datasource)
.pipe(
concatMap(this.fetchMetrics),
// Stop polling when destroy$ emits (on unmount or instance update)
takeUntil(this.destroy$),
retryWhen((err) => {
return err.pipe(delay(1000), take(5));
}),
)
.subscribe({
next: (data) => {
this.hasLoaded = true;
this.current = data;
this.chartData.push({ ...data, timestamp: moment().valueOf() });
},
error: (error) => {
this.hasLoaded = true;
console.warn(
`Fetching datasource ${this.dataSource} metrics failed:`,
error,
);
this.error = error;
},
});
},
},
};
</script>
Expand Down
Loading
Loading