mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-18 13:48:58 +02:00
fix: decouple metrics from ofter-read table
Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
parent
70e7446dbe
commit
30c8b394fb
@ -349,7 +349,6 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
acc.project = r.project;
|
||||
acc.stale = r.stale;
|
||||
acc.lastSeenAt = r.last_seen_at;
|
||||
|
||||
acc.createdAt = r.created_at;
|
||||
acc.type = r.type;
|
||||
if (!acc.environments[r.environment]) {
|
||||
@ -414,6 +413,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
});
|
||||
|
||||
featureToggle.archived = archived;
|
||||
|
||||
return featureToggle;
|
||||
}
|
||||
throw new NotFoundError(
|
||||
@ -507,15 +507,21 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
'feature_environments.feature_name',
|
||||
'features.name',
|
||||
)
|
||||
.leftJoin(
|
||||
'feature_environments_metrics',
|
||||
'feature_environments_metrics.feature_name',
|
||||
'features.name',
|
||||
)
|
||||
.leftJoin('feature_environments_metrics', function () {
|
||||
this.on(
|
||||
'feature_environments_metrics.feature_name',
|
||||
'=',
|
||||
'feature_environments.feature_name',
|
||||
).andOn(
|
||||
'feature_environments_metrics.environment',
|
||||
'=',
|
||||
'feature_environments.environment',
|
||||
);
|
||||
})
|
||||
.leftJoin(
|
||||
'environments',
|
||||
'feature_environments.environment',
|
||||
'environments.name',
|
||||
'feature_environments.environment',
|
||||
)
|
||||
.leftJoin('feature_tag as ft', 'ft.feature_name', 'features.name');
|
||||
|
||||
@ -524,7 +530,6 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
'features.description as description',
|
||||
'features.type as type',
|
||||
'features.created_at as created_at',
|
||||
'features.last_seen_at as last_seen_at',
|
||||
'features.stale as stale',
|
||||
'features.impression_data as impression_data',
|
||||
'feature_environments.enabled as enabled',
|
||||
@ -559,26 +564,36 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
if (rows.length > 0) {
|
||||
const overview = rows.reduce((acc, row) => {
|
||||
if (acc[row.feature_name] !== undefined) {
|
||||
acc[row.feature_name].environments.push(
|
||||
FeatureStrategiesStore.getEnvironment(row),
|
||||
);
|
||||
const currentEnv =
|
||||
FeatureStrategiesStore.getEnvironment(row);
|
||||
acc[row.feature_name].environments.push(currentEnv);
|
||||
if (this.isNewTag(acc[row.feature_name], row)) {
|
||||
this.addTag(acc[row.feature_name], row);
|
||||
}
|
||||
|
||||
if (
|
||||
new Date(currentEnv.lastSeenAt) >
|
||||
new Date(acc[row.feature_name].lastSeenAt)
|
||||
) {
|
||||
acc[row.feature_name].lastSeenAt =
|
||||
currentEnv.lastSeenAt;
|
||||
}
|
||||
} else {
|
||||
const envOverview =
|
||||
FeatureStrategiesStore.getEnvironment(row);
|
||||
|
||||
acc[row.feature_name] = {
|
||||
type: row.type,
|
||||
description: row.description,
|
||||
favorite: row.favorite,
|
||||
name: row.feature_name,
|
||||
lastSeenAt: row.env_last_seen_at,
|
||||
createdAt: row.created_at,
|
||||
lastSeenAt: row.last_seen_at,
|
||||
stale: row.stale,
|
||||
impressionData: row.impression_data,
|
||||
environments: [
|
||||
FeatureStrategiesStore.getEnvironment(row),
|
||||
],
|
||||
environments: [envOverview],
|
||||
};
|
||||
|
||||
if (this.isNewTag(acc[row.feature_name], row)) {
|
||||
this.addTag(acc[row.feature_name], row);
|
||||
}
|
||||
|
@ -77,10 +77,9 @@ export default class FeatureToggleClientStore
|
||||
'features.project as project',
|
||||
'features.stale as stale',
|
||||
'features.impression_data as impression_data',
|
||||
'features.last_seen_at as last_seen_at',
|
||||
'features.created_at as created_at',
|
||||
'fe.variants as variants',
|
||||
'fe.last_seen_at as env_last_seen_at',
|
||||
'fem.last_seen_at as env_last_seen_at',
|
||||
'fe.enabled as enabled',
|
||||
'fe.environment as environment',
|
||||
'fs.id as strategy_id',
|
||||
@ -96,6 +95,15 @@ export default class FeatureToggleClientStore
|
||||
'df.parent as parent',
|
||||
'df.variants as parent_variants',
|
||||
'df.enabled as parent_enabled',
|
||||
this.db.raw(`(
|
||||
SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN MAX(last_seen_at)
|
||||
ELSE NULL
|
||||
END
|
||||
FROM feature_environments_metrics
|
||||
WHERE features.name = feature_environments_metrics.feature_name
|
||||
) as last_seen_at`),
|
||||
] as (string | Raw<any>)[];
|
||||
|
||||
let query = this.db('features')
|
||||
@ -115,13 +123,18 @@ export default class FeatureToggleClientStore
|
||||
'enabled',
|
||||
'environment',
|
||||
'variants',
|
||||
'last_seen_at',
|
||||
)
|
||||
.where({ environment })
|
||||
.as('fe'),
|
||||
'fe.feature_name',
|
||||
'features.name',
|
||||
)
|
||||
.leftJoin('feature_environments_metrics as fem', function () {
|
||||
this.on('fem.feature_name', '=', 'features.name').andOnVal(
|
||||
'fem.environment',
|
||||
environment,
|
||||
);
|
||||
})
|
||||
.leftJoin(
|
||||
'feature_strategy_segment as fss',
|
||||
`fss.feature_strategy_id`,
|
||||
|
@ -20,7 +20,6 @@ const FEATURE_COLUMNS = [
|
||||
'stale',
|
||||
'created_at',
|
||||
'impression_data',
|
||||
'last_seen_at',
|
||||
'archived_at',
|
||||
];
|
||||
|
||||
@ -86,7 +85,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
|
||||
async get(name: string): Promise<FeatureToggle> {
|
||||
return this.db
|
||||
.first(FEATURE_COLUMNS)
|
||||
.first(this.columnsWithMetrics())
|
||||
.from(TABLE)
|
||||
.where({ name })
|
||||
.then(this.rowToFeature);
|
||||
@ -101,7 +100,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
): Promise<FeatureToggle[]> {
|
||||
const { archived, ...rest } = query;
|
||||
const rows = await this.db
|
||||
.select(FEATURE_COLUMNS)
|
||||
.select(this.columnsWithMetrics())
|
||||
.from(TABLE)
|
||||
.where(rest)
|
||||
.modify(FeatureToggleStore.filterByArchived, archived);
|
||||
@ -109,9 +108,11 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
}
|
||||
|
||||
async getAllByNames(names: string[]): Promise<FeatureToggle[]> {
|
||||
const query = this.db<FeaturesTable>(TABLE).orderBy('name', 'asc');
|
||||
query.whereIn('name', names);
|
||||
const rows = await query;
|
||||
const rows = await this.db
|
||||
.select(this.columnsWithMetrics())
|
||||
.from(TABLE)
|
||||
.orderBy('name', 'asc')
|
||||
.whereIn('name', names);
|
||||
return rows.map(this.rowToFeature);
|
||||
}
|
||||
|
||||
@ -187,18 +188,6 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
.forUpdate()
|
||||
.skipLocked(),
|
||||
);
|
||||
|
||||
// Updating the toggle's last_seen_at also for backwards compatibility
|
||||
await this.db(TABLE)
|
||||
.update({ last_seen_at: now })
|
||||
.whereIn(
|
||||
'name',
|
||||
this.db(TABLE)
|
||||
.select('name')
|
||||
.whereIn('name', toggleNames)
|
||||
.forUpdate()
|
||||
.skipLocked(),
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error('Could not update lastSeen, error: ', err);
|
||||
@ -286,7 +275,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
try {
|
||||
const row = await this.db(TABLE)
|
||||
.insert(this.dtoToRow(project, data))
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
|
||||
return this.rowToFeature(row[0]);
|
||||
} catch (err) {
|
||||
@ -310,7 +299,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
const row = await this.db(TABLE)
|
||||
.where({ name: data.name })
|
||||
.update(this.dtoToRow(project, data))
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
|
||||
return this.rowToFeature(row[0]);
|
||||
}
|
||||
@ -320,7 +309,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
const row = await this.db(TABLE)
|
||||
.where({ name })
|
||||
.update({ archived_at: now })
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
return this.rowToFeature(row[0]);
|
||||
}
|
||||
|
||||
@ -329,7 +318,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
const rows = await this.db(TABLE)
|
||||
.whereIn('name', names)
|
||||
.update({ archived_at: now })
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
return rows.map((row) => this.rowToFeature(row));
|
||||
}
|
||||
|
||||
@ -340,7 +329,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
const rows = await this.db(TABLE)
|
||||
.whereIn('name', names)
|
||||
.update({ stale })
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
return rows.map((row) => this.rowToFeature(row));
|
||||
}
|
||||
|
||||
@ -362,7 +351,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
const row = await this.db(TABLE)
|
||||
.where({ name })
|
||||
.update({ archived_at: null })
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
return this.rowToFeature(row[0]);
|
||||
}
|
||||
|
||||
@ -370,7 +359,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
const rows = await this.db(TABLE)
|
||||
.whereIn('name', names)
|
||||
.update({ archived_at: null })
|
||||
.returning(FEATURE_COLUMNS);
|
||||
.returning(this.columnsWithMetrics());
|
||||
return rows.map((row) => this.rowToFeature(row));
|
||||
}
|
||||
|
||||
@ -418,11 +407,13 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
.update('variants', variantsString)
|
||||
.where('feature_name', featureName);
|
||||
|
||||
const row = await this.db(TABLE)
|
||||
.select(FEATURE_COLUMNS)
|
||||
const rows = await this.db(TABLE)
|
||||
.select(this.columnsWithMetrics())
|
||||
.where({ project: project, name: featureName });
|
||||
|
||||
const toggle = this.rowToFeature(row[0]);
|
||||
const toggle = this.rowToFeature(
|
||||
(rows as unknown as FeaturesTable[])[0],
|
||||
);
|
||||
toggle.variants = newVariants;
|
||||
|
||||
return toggle;
|
||||
@ -483,6 +474,21 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
|
||||
return result?.potentially_stale ?? false;
|
||||
}
|
||||
|
||||
private columnsWithMetrics = () => {
|
||||
return [
|
||||
...FEATURE_COLUMNS,
|
||||
this.db.raw(`(
|
||||
SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN MAX(last_seen_at)
|
||||
ELSE NULL
|
||||
END
|
||||
FROM feature_environments_metrics
|
||||
WHERE features.name = feature_environments_metrics.feature_name
|
||||
) as last_seen_at`),
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = FeatureToggleStore;
|
||||
|
@ -34,7 +34,14 @@ exports.up = function (db, cb) {
|
||||
features.impression_data as impression_data,
|
||||
features.created_at as created_at,
|
||||
features.archived_at as archived_at,
|
||||
features.last_seen_at as last_seen_at,
|
||||
(
|
||||
SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN MAX(last_seen_at)
|
||||
END
|
||||
FROM feature_environments_metrics
|
||||
WHERE features.name = feature_environments_metrics.feature_name
|
||||
) as last_seen_at,
|
||||
feature_environments_metrics.last_seen_at as env_last_seen_at,
|
||||
feature_environments.enabled as enabled,
|
||||
feature_environments.environment as environment,
|
||||
@ -53,9 +60,10 @@ exports.up = function (db, cb) {
|
||||
feature_strategies.variants as strategy_variants
|
||||
FROM features
|
||||
LEFT JOIN feature_environments ON feature_environments.feature_name = features.name
|
||||
LEFT JOIN feature_environments_metrics ON feature_environments_metrics.feature_name = features.name
|
||||
LEFT JOIN feature_strategies ON feature_strategies.feature_name = feature_environments.feature_name
|
||||
and feature_strategies.environment = feature_environments.environment
|
||||
AND feature_strategies.environment = feature_environments.environment
|
||||
LEFT JOIN feature_environments_metrics ON feature_environments_metrics.feature_name = feature_environments.feature_name
|
||||
AND feature_environments_metrics.environment = feature_environments.environment
|
||||
LEFT JOIN environments ON feature_environments.environment = environments.name
|
||||
LEFT JOIN feature_strategy_segment as fss ON fss.feature_strategy_id = feature_strategies.id;
|
||||
`,
|
||||
|
Loading…
Reference in New Issue
Block a user