-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split metrics from device
resource
#1371
base: master
Are you sure you want to change the base?
Conversation
65c5775
to
70730be
Compare
70730be
to
4b2e937
Compare
console.log( | ||
`patch metricsBody:${JSON.stringify(metricsBody, null, 2)}`, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need removing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks
const latestDeviceMetricsRecord = await resinApiTx.get({ | ||
resource: 'device_metrics_record', | ||
id: { is_reported_by__device: device.id }, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be merged into the fetchData
request rather than adding an extra one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, great hint also explicitly loading with readOnly transcation.
I've added it to the device request by expanding the reports__device_metrics_record
CREATE INDEX IF NOT EXISTS "device_metrics_record_by_device_idx" | ||
ON "device metrics record" ("is reported by-device"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be included in balena-init.sql
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Page- @fisehara Afaict we shouldn't need to add a manual index (neither here nor in the balena-init.sql
), since the "is reported by-device" INTEGER NOT NULL UNIQUE
part of the CREATE TABLE
does auto-generate the following index:
CREATE UNIQUE INDEX "device metrics record_is reported by-device_key" ON public."device metrics record" USING ("is reported by-device");
which makes device_metrics_record_by_device_idx
redundant/duplicate.
If we still prefer to explicitly be adding index as part of the migration just in case, then it should probably be matching the auto-generated one. (but tbh I don't think we should b/c pg adds it out of the box)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a unique constraint will automatically create a unique btree index on the column or group of columns used in the constraint.
See: https://www.postgresql.org/docs/current/sql-createtable.html
src/balena.sbvr
Outdated
Fact type: device has memory usage | ||
Necessity: each device has at most one memory usage | ||
Fact type: device has memory total | ||
Necessity: each device has at most one memory total | ||
Fact type: device has storage block device | ||
Necessity: each device has at most one storage block device | ||
Fact type: device has storage usage | ||
Necessity: each device has at most one storage usage | ||
Fact type: device has storage total | ||
Necessity: each device has at most one storage total | ||
Fact type: device has cpu usage | ||
Necessity: each device has at most one cpu usage | ||
Fact type: device has cpu temp | ||
Necessity: each device has at most one cpu temp | ||
Fact type: device is undervolted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this can be safely removed until the async migration relying on it is finalized
Remodel `device` resource and split metrics into `device metrics record` This reduces the load on the `device` resource when updating device metrics periodically. device metrics are far less frequently needed than the `device` resource. Thus writing to the heavily used `device` resource should be offloaded to the `device metrics record` resource. Change-type: major Signed-off-by: Harald Fischer <[email protected]>
4b2e937
to
533ab03
Compare
await resinApiTx.patch({ | ||
resource: 'device_metrics_record', | ||
id: { is_reported_by__device: device.id }, | ||
body: metricsBody, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding v both here and in the v2 patch?
body: metricsBody, | |
options: { | |
$filter: { | |
$not: metricsBody | |
} | |
}, | |
body: metricsBody, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this avoids an unnecessary postgres write in the case there are no changes
@@ -23,9 +23,11 @@ export { | |||
serviceInstallFromImage, | |||
} from './state-get-utils'; | |||
export { | |||
metricsPatchFields, | |||
validDeviceMetricsRecordPatchFields, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: Since this field is also exported from index.ts w/ export * as deviceState from './features/device-state';
, how about not renaming it so that we don't introduce a breaking change?
@@ -188,3 +188,6 @@ ON "application" ("slug" varchar_pattern_ops, "is public", "is host"); | |||
|
|||
CREATE INDEX IF NOT EXISTS "scheduled_job_run_start_timestamp_idx" | |||
ON "scheduled job run" (DATE_TRUNC('milliseconds', "start timestamp")); | |||
|
|||
CREATE INDEX IF NOT EXISTS "device_metrics_record_by_device_idx" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder about the discussion on this at
See: https://github.com/balena-io/open-balena-api/pull/1371/files#r1255500040
FROM | ||
"device metrics record" | ||
) | ||
FOR UPDATE SKIP LOCKED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FOR UPDATE SKIP LOCKED | |
FOR UPDATE |
The sync migration one should not leave anything pending.
reports__device_metrics_record: {}, | ||
}, | ||
}, | ||
})) as Array< | ||
Pick<Device, 'id' | 'uuid'> & { | ||
belongs_to__application: Array<Pick<Application, 'uuid'>>; | ||
reports__device_metrics_record: DeviceMetricsRecord[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't really care to fetch any of the rest columns.
reports__device_metrics_record: {}, | |
}, | |
}, | |
})) as Array< | |
Pick<Device, 'id' | 'uuid'> & { | |
belongs_to__application: Array<Pick<Application, 'uuid'>>; | |
reports__device_metrics_record: DeviceMetricsRecord[]; | |
reports__device_metrics_record: { | |
$select: 'id' | |
}, | |
}, | |
}, | |
})) as Array< | |
Pick<Device, 'id' | 'uuid'> & { | |
belongs_to__application: Array<Pick<Application, 'uuid'>>; | |
reports__device_metrics_record: reports__device_metrics_record: [Pick<DeviceMetricsRecord, 'id'>?]; |
const latestDeviceMetricsRecord = await resinApiTx.get({ | ||
resource: 'device_metrics_record', | ||
id: { is_reported_by__device: deviceId }, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const latestDeviceMetricsRecord = await resinApiTx.get({ | |
resource: 'device_metrics_record', | |
id: { is_reported_by__device: deviceId }, | |
}); | |
const latestDeviceMetricsRecord = await resinApiTx.get({ | |
resource: 'device_metrics_record', | |
id: { is_reported_by__device: deviceId }, | |
options: { | |
$select: 'id' | |
} | |
}); |
await resinApiTx.post({ | ||
resource: 'device_metrics_record', | ||
id: { is_reported_by__device: deviceId }, | ||
body: { | ||
...metricsBody, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that work? Shouldn't it be v ?
await resinApiTx.post({ | |
resource: 'device_metrics_record', | |
id: { is_reported_by__device: deviceId }, | |
body: { | |
...metricsBody, | |
}, | |
}); | |
await resinApiTx.post({ | |
resource: 'device_metrics_record', | |
body: { | |
...metricsBody, | |
is_reported_by__device: deviceId, | |
}, | |
}); |
resource: 'device_metrics_record', | ||
body: { | ||
...metricsBody, | ||
...{ is_reported_by__device: device.id }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...{ is_reported_by__device: device.id }, | |
is_reported_by__device: device.id, |
I less object to GC :)
} else { | ||
await resinApiTx.patch({ | ||
resource: 'device_metrics_record', | ||
id: { is_reported_by__device: deviceId }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id: { is_reported_by__device: deviceId }, | |
id: { is_reported_by__device: deviceId }, | |
options: { | |
$filter: { | |
$not: metricsBody | |
} | |
}, |
if (apps != null || Object.keys(deviceBody).length > 0) { | ||
if ( | ||
Object.keys(metricsBody).length > 0 && | ||
!(await shouldUpdateMetrics(uuid)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a special case I think where we always want to recognize changes to the is_undervolted
property which we currently always accept but this PR has moved to being treated like any of the other "metrics" fields
await resinApiTx.patch({ | ||
resource: 'device_metrics_record', | ||
id: { is_reported_by__device: device.id }, | ||
body: metricsBody, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this avoids an unnecessary postgres write in the case there are no changes
Remodel
device
resource and split metrics intodevice metrics record
This reduces the load on the
device
resource when updating device metrics periodically. device metrics are far less frequently needed than thedevice
resource. Thus writing to the heavily useddevice
resource should be offloaded to thedevice metrics record
resource.Change-type: major