fix: include rendered_at in 304 cache check so re-renders invalidate
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
After re-cropping an image, the renderer regenerates the .bin and advances the asset's rendered_at, but the device's 304 short-circuit still matched on (image_id, orientation) only — so the device kept serving the old upside-down/stale bytes from its local cache despite the server having freshly-rendered correct ones. Adds device.current_rendered_at, populated whenever a 200 response is served, and tightens the 304 condition to require all three (image id, orientation, rendered_at) to match. The asset lookup now happens before the 304 check so its rendered_at is in scope for the comparison. No firmware change — this is server-side cache logic. Existing devices get null current_rendered_at after the migration; their next poll falls through 304 and re-fetches once, then the cache is in sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -68,23 +68,9 @@ class DeviceImageController extends AbstractController
|
||||
return $r;
|
||||
}
|
||||
|
||||
// 304: device already has this image — skip the binary transfer and redraw.
|
||||
// Both the image and the orientation it was last rendered at must match;
|
||||
// otherwise the device's cached .bin is stale and we must re-send.
|
||||
if ($image->getId() === $currentImageId
|
||||
&& $device->getCurrentImageOrientation() === $device->getOrientation()) {
|
||||
$this->logger->info('device.poll.no_change', [
|
||||
'device_id' => $device->getId(),
|
||||
'mac' => $mac,
|
||||
'image_id' => $image->getId(),
|
||||
'interval_ms' => $intervalMs,
|
||||
]);
|
||||
$r = new Response(null, Response::HTTP_NOT_MODIFIED);
|
||||
$r->headers->set('X-Image-Id', (string) $image->getId());
|
||||
$r->headers->set('X-Interval-Ms', (string) $intervalMs);
|
||||
return $r;
|
||||
}
|
||||
|
||||
// Asset lookup is needed before the 304 check so we can compare its
|
||||
// rendered_at timestamp — otherwise a re-cropped image (same id, same
|
||||
// orientation, new bytes) would be incorrectly served as 304.
|
||||
$asset = $em->getRepository(RenderedAsset::class)->findOneBy([
|
||||
'image' => $image,
|
||||
'deviceModel' => $device->getModel(),
|
||||
@@ -105,6 +91,27 @@ class DeviceImageController extends AbstractController
|
||||
return $r;
|
||||
}
|
||||
|
||||
// 304: device already has this image — skip the binary transfer and redraw.
|
||||
// The image id, the orientation we last served at, AND the asset's
|
||||
// rendered_at must all match. A re-render (e.g. after re-crop) advances
|
||||
// rendered_at, so the device's cached bytes are stale and we re-send.
|
||||
$renderedAt = $asset->getRenderedAt();
|
||||
if ($image->getId() === $currentImageId
|
||||
&& $device->getCurrentImageOrientation() === $device->getOrientation()
|
||||
&& $renderedAt !== null
|
||||
&& $device->getCurrentRenderedAt()?->getTimestamp() === $renderedAt->getTimestamp()) {
|
||||
$this->logger->info('device.poll.no_change', [
|
||||
'device_id' => $device->getId(),
|
||||
'mac' => $mac,
|
||||
'image_id' => $image->getId(),
|
||||
'interval_ms' => $intervalMs,
|
||||
]);
|
||||
$r = new Response(null, Response::HTTP_NOT_MODIFIED);
|
||||
$r->headers->set('X-Image-Id', (string) $image->getId());
|
||||
$r->headers->set('X-Interval-Ms', (string) $intervalMs);
|
||||
return $r;
|
||||
}
|
||||
|
||||
$binPath = $this->projectDir . '/' . $asset->getFilePath();
|
||||
if (!file_exists($binPath)) {
|
||||
$this->logger->error('device.poll.file_missing', [
|
||||
@@ -118,10 +125,10 @@ class DeviceImageController extends AbstractController
|
||||
return $r;
|
||||
}
|
||||
|
||||
// Record the orientation we're serving the image at so the next poll's
|
||||
// 304 check can detect a flip and force a re-fetch. Flushed via the
|
||||
// controller's EM (markSeen() above already dirties the row).
|
||||
// Record the orientation and rendered_at we're serving at so the next
|
||||
// poll's 304 check can detect a flip or a re-render and force a re-fetch.
|
||||
$device->setCurrentImageOrientation($device->getOrientation());
|
||||
$device->setCurrentRenderedAt($renderedAt);
|
||||
$em->flush();
|
||||
|
||||
$this->logger->info('device.poll.served', [
|
||||
|
||||
Reference in New Issue
Block a user