Persist and serve pdf captures from the GET _screenshot/ DSL - #6121
Conversation
Grafana previewPreview deployed for 1 dashboard in the staging Grafana.
Dashboards: Preview is torn down automatically when this PR is closed or merged. (Run: https://github.com/cardstack/boxel/actions/runs/35035467518) |
Observability diff (vs staging)Show diffdiff --git a/tmp/remote-canon.HMIcmO/dashboards/boxel-status/screenshot-captures.json b/tmp/committed-canon.00rFGK/dashboards/boxel-status/screenshot-captures.json
index ad87d7e..88b3287 100644
--- a/tmp/remote-canon.HMIcmO/dashboards/boxel-status/screenshot-captures.json
+++ b/tmp/committed-canon.00rFGK/dashboards/boxel-status/screenshot-captures.json
@@ -38,7 +38,6 @@
"x": 0,
"y": 0
},
- "id": 1,
"panels": [],
"title": "Overview",
"type": "row"
@@ -64,7 +63,6 @@
"x": 0,
"y": 1
},
- "id": 2,
"options": {
"colorMode": "none",
"graphMode": "area",
@@ -82,7 +80,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "sum(count_over_time({service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" [5m])) or vector(0)",
+ "expr": "sum(count_over_time({service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" [5m])) or vector(0)",
"queryType": "range",
"refId": "A"
}
@@ -111,7 +109,6 @@
"x": 6,
"y": 1
},
- "id": 3,
"options": {
"colorMode": "none",
"graphMode": "area",
@@ -129,7 +126,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "sum(count_over_time({service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" [5m])) or vector(0)",
+ "expr": "sum(count_over_time({service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" [5m])) or vector(0)",
"queryType": "range",
"refId": "A"
}
@@ -165,7 +162,6 @@
"x": 12,
"y": 1
},
- "id": 4,
"options": {
"legend": {
"displayMode": "list",
@@ -183,7 +179,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "sum by (outcome) (count_over_time({service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" [$__interval]))",
+ "expr": "sum by (outcome) (count_over_time({service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" [$__interval]))",
"legendFormat": "{{outcome}}",
"queryType": "range",
"refId": "A"
@@ -220,7 +216,6 @@
"x": 0,
"y": 5
},
- "id": 5,
"options": {
"legend": {
"displayMode": "list",
@@ -238,7 +233,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.50, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap totalMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.50, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap totalMs [$__interval]) by ()",
"legendFormat": "p50",
"queryType": "range",
"refId": "A"
@@ -248,7 +243,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap totalMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap totalMs [$__interval]) by ()",
"legendFormat": "p95",
"queryType": "range",
"refId": "B"
@@ -258,7 +253,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.99, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap totalMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.99, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap totalMs [$__interval]) by ()",
"legendFormat": "p99",
"queryType": "range",
"refId": "C"
@@ -275,7 +270,6 @@
"x": 0,
"y": 13
},
- "id": 6,
"panels": [],
"title": "Capture stage breakdown",
"type": "row"
@@ -308,7 +302,6 @@
"x": 0,
"y": 14
},
- "id": 7,
"options": {
"legend": {
"displayMode": "list",
@@ -326,7 +319,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap queueWaitMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap queueWaitMs [$__interval]) by ()",
"legendFormat": "queueWaitMs",
"queryType": "range",
"refId": "A"
@@ -336,7 +329,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap permissionsMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap permissionsMs [$__interval]) by ()",
"legendFormat": "permissionsMs",
"queryType": "range",
"refId": "B"
@@ -346,7 +339,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap launchMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap launchMs [$__interval]) by ()",
"legendFormat": "launchMs",
"queryType": "range",
"refId": "C"
@@ -356,7 +349,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap renderMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap renderMs [$__interval]) by ()",
"legendFormat": "renderMs",
"queryType": "range",
"refId": "D"
@@ -366,7 +359,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap navMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap navMs [$__interval]) by ()",
"legendFormat": "navMs",
"queryType": "range",
"refId": "E"
@@ -376,7 +369,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap settleMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap settleMs [$__interval]) by ()",
"legendFormat": "settleMs",
"queryType": "range",
"refId": "F"
@@ -386,7 +379,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap imagePaintMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap imagePaintMs [$__interval]) by ()",
"legendFormat": "imagePaintMs",
"queryType": "range",
"refId": "G"
@@ -396,7 +389,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap screenshotMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap screenshotMs [$__interval]) by ()",
"legendFormat": "screenshotMs",
"queryType": "range",
"refId": "H"
@@ -406,7 +399,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap decodeMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap decodeMs [$__interval]) by ()",
"legendFormat": "decodeMs",
"queryType": "range",
"refId": "I"
@@ -416,7 +409,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap persistMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap persistMs [$__interval]) by ()",
"legendFormat": "persistMs",
"queryType": "range",
"refId": "J"
@@ -453,7 +446,6 @@
"x": 0,
"y": 23
},
- "id": 8,
"options": {
"legend": {
"displayMode": "list",
@@ -471,7 +463,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap queueWaitMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap queueWaitMs [$__interval]) by ()",
"legendFormat": "queueWaitMs",
"queryType": "range",
"refId": "A"
@@ -481,7 +473,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap prerenderMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap prerenderMs [$__interval]) by ()",
"legendFormat": "prerenderMs (incl. transport)",
"queryType": "range",
"refId": "B"
@@ -491,7 +483,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap renderMs [$__interval]) by ()",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap renderMs [$__interval]) by ()",
"legendFormat": "renderMs (server-observed)",
"queryType": "range",
"refId": "C"
@@ -528,7 +520,6 @@
"x": 8,
"y": 23
},
- "id": 9,
"options": {
"legend": {
"displayMode": "list",
@@ -546,7 +537,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap launchMs [$__interval]) by (tabReused)",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap launchMs [$__interval]) by (tabReused)",
"legendFormat": "tabReused={{tabReused}}",
"queryType": "range",
"refId": "A"
@@ -583,7 +574,6 @@
"x": 16,
"y": 23
},
- "id": 10,
"options": {
"legend": {
"displayMode": "list",
@@ -601,7 +591,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "sum by (persistOutcome) (count_over_time({service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" [$__interval]))",
+ "expr": "sum by (persistOutcome) (count_over_time({service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" [$__interval]))",
"legendFormat": "{{persistOutcome}}",
"queryType": "range",
"refId": "A"
@@ -618,7 +608,6 @@
"x": 0,
"y": 31
},
- "id": 11,
"panels": [],
"title": "Slices",
"type": "row"
@@ -650,7 +639,6 @@
"x": 0,
"y": 32
},
- "id": 12,
"options": {
"legend": {
"displayMode": "list",
@@ -668,7 +656,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap totalMs [$__interval]) by (realmURL)",
+ "expr": "quantile_over_time(0.95, {service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap totalMs [$__interval]) by (realmURL)",
"legendFormat": "{{realmURL}}",
"queryType": "range",
"refId": "A"
@@ -705,7 +693,6 @@
"x": 12,
"y": 32
},
- "id": 13,
"options": {
"legend": {
"displayMode": "list",
@@ -723,7 +710,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "quantile_over_time(0.95, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap totalMs [$__interval]) by (surface)",
+ "expr": "quantile_over_time(0.95, {service=\"realm-server\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"request\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap totalMs [$__interval]) by (surface)",
"legendFormat": "{{surface}}",
"queryType": "range",
"refId": "A"
@@ -749,7 +736,6 @@
"x": 0,
"y": 41
},
- "id": 14,
"options": {
"showHeader": true,
"sortBy": [
@@ -765,7 +751,7 @@
"type": "loki",
"uid": "loki"
},
- "expr": "topk(20, max by (sourceURL) (max_over_time({service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | unwrap totalMs [$__range])))",
+ "expr": "topk(20, max by (sourceURL) (max_over_time({service=~\"realm-server|worker\", env=\"$env\"} |= \"boxel:screenshot-perf\" | json | line_format \"{{ if .log }}{{ .log }}{{ else }}{{ __line__ }}{{ end }}\" | json | channel=\"boxel:screenshot-perf\" | eventType=\"capture\" | realmURL=~\".*${realm}.*\" | surface=~\"$surface\" | contentType=~\"$contentType\" | unwrap totalMs [$__range])))",
"instant": true,
"queryType": "instant",
"refId": "A"
@@ -792,7 +778,6 @@
"x": 0,
"y": 51
},
- "id": 15,
"panels": [],
"title": "MediaCache storage",
"type": "row"
@@ -818,7 +803,6 @@
"x": 0,
"y": 52
},
- "id": 16,
"options": {
"colorMode": "none",
"graphMode": "none",
@@ -857,6 +841,65 @@
"title": "Bucket size (CloudWatch)",
"type": "stat"
},
+ {
+ "datasource": {
+ "type": "cloudwatch",
+ "uid": "cef5x9o3yzawwf"
+ },
+ "description": "NumberOfObjects for boxel-media-cache-${env}, reported daily. Compare with 'Ledger object keys': the bucket stores one object per distinct object_key (dedupe-on-write), so the two counts should track each other; a persistent excess of bucket objects means orphans no ledger row references — invisible to GC, which scans ledger rows and never enumerates the bucket. Spans 2d regardless of the dashboard range so lastNotNull always finds the newest daily sample.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 0,
+ "y": 56
+ },
+ "options": {
+ "colorMode": "none",
+ "graphMode": "none",
+ "justifyMode": "center",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "value"
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "cloudwatch",
+ "uid": "cef5x9o3yzawwf"
+ },
+ "dimensions": {
+ "BucketName": "boxel-media-cache-${env}",
+ "StorageType": "AllStorageTypes"
+ },
+ "id": "a",
+ "metricEditorMode": 0,
+ "metricName": "NumberOfObjects",
+ "metricQueryType": 0,
+ "namespace": "AWS/S3",
+ "period": "86400",
+ "refId": "A",
+ "region": "default",
+ "statistic": "Maximum"
+ }
+ ],
+ "timeFrom": "2d",
+ "title": "Bucket objects (CloudWatch)",
+ "type": "stat"
+ },
{
"datasource": {
"type": "grafana-postgresql-datasource",
@@ -878,7 +921,6 @@
"x": 6,
"y": 52
},
- "id": 18,
"options": {
"colorMode": "none",
"graphMode": "none",
@@ -908,6 +950,56 @@
"title": "Ledger-referenced bytes",
"type": "stat"
},
+ {
+ "datasource": {
+ "type": "grafana-postgresql-datasource",
+ "uid": "cef5v5sl9k7i8f"
+ },
+ "description": "COUNT(DISTINCT object_key) in media_cache_ledger — how many objects the ledger references. The bucket-object counterpart to 'Ledger-referenced bytes'.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 6,
+ "y": 56
+ },
+ "options": {
+ "colorMode": "none",
+ "graphMode": "none",
+ "justifyMode": "center",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "value"
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "grafana-postgresql-datasource",
+ "uid": "cef5v5sl9k7i8f"
+ },
+ "editorMode": "code",
+ "format": "table",
+ "rawQuery": true,
+ "rawSql": "SELECT COUNT(DISTINCT object_key) AS ledger_keys\n FROM media_cache_ledger;",
+ "refId": "A"
+ }
+ ],
+ "title": "Ledger object keys",
+ "type": "stat"
+ },
{
"datasource": {
"type": "cloudwatch",
@@ -954,7 +1046,6 @@
"x": 12,
"y": 52
},
- "id": 20,
"options": {
"legend": {
"calcs": [],
@@ -1010,122 +1101,11 @@
"timeFrom": "30d",
"title": "Bucket growth (daily)",
"type": "timeseries"
- },
- {
- "datasource": {
- "type": "cloudwatch",
- "uid": "cef5x9o3yzawwf"
- },
- "description": "NumberOfObjects for boxel-media-cache-${env}, reported daily. Compare with 'Ledger object keys': the bucket stores one object per distinct object_key (dedupe-on-write), so the two counts should track each other; a persistent excess of bucket objects means orphans no ledger row references — invisible to GC, which scans ledger rows and never enumerates the bucket. Spans 2d regardless of the dashboard range so lastNotNull always finds the newest daily sample.",
- "fieldConfig": {
- "defaults": {
- "color": {
- "mode": "palette-classic"
- },
- "unit": "short"
- },
- "overrides": []
- },
- "gridPos": {
- "h": 4,
- "w": 6,
- "x": 0,
- "y": 56
- },
- "id": 17,
- "options": {
- "colorMode": "none",
- "graphMode": "none",
- "justifyMode": "center",
- "reduceOptions": {
- "calcs": [
- "lastNotNull"
- ],
- "fields": "",
- "values": false
- },
- "textMode": "value"
- },
- "targets": [
- {
- "datasource": {
- "type": "cloudwatch",
- "uid": "cef5x9o3yzawwf"
- },
- "dimensions": {
- "BucketName": "boxel-media-cache-${env}",
- "StorageType": "AllStorageTypes"
- },
- "id": "a",
- "metricEditorMode": 0,
- "metricName": "NumberOfObjects",
- "metricQueryType": 0,
- "namespace": "AWS/S3",
- "period": "86400",
- "refId": "A",
- "region": "default",
- "statistic": "Maximum"
- }
- ],
- "timeFrom": "2d",
- "title": "Bucket objects (CloudWatch)",
- "type": "stat"
- },
- {
- "datasource": {
- "type": "grafana-postgresql-datasource",
- "uid": "cef5v5sl9k7i8f"
- },
- "description": "COUNT(DISTINCT object_key) in media_cache_ledger — how many objects the ledger references. The bucket-object counterpart to 'Ledger-referenced bytes'.",
- "fieldConfig": {
- "defaults": {
- "color": {
- "mode": "palette-classic"
- },
- "unit": "short"
- },
- "overrides": []
- },
- "gridPos": {
- "h": 4,
- "w": 6,
- "x": 6,
- "y": 56
- },
- "id": 19,
- "options": {
- "colorMode": "none",
- "graphMode": "none",
- "justifyMode": "center",
- "reduceOptions": {
- "calcs": [
- "lastNotNull"
- ],
- "fields": "",
- "values": false
- },
- "textMode": "value"
- },
- "targets": [
- {
- "datasource": {
- "type": "grafana-postgresql-datasource",
- "uid": "cef5v5sl9k7i8f"
- },
- "editorMode": "code",
- "format": "table",
- "rawQuery": true,
- "rawSql": "SELECT COUNT(DISTINCT object_key) AS ledger_keys\n FROM media_cache_ledger;",
- "refId": "A"
- }
- ],
- "title": "Ledger object keys",
- "type": "stat"
}
],
"preload": false,
"refresh": "1m",
- "schemaVersion": 42,
+ "schemaVersion": 41,
"tags": [
"screenshot",
"captures",
@@ -1136,7 +1116,7 @@
{
"hide": 2,
"name": "env",
- "query": "staging",
+ "query": "__ENV__",
"skipUrlSync": true,
"type": "constant"
},
@@ -1189,6 +1169,38 @@
],
"query": "get-dsl,post",
"type": "custom"
+ },
+ {
+ "allValue": ".*",
+ "current": {
+ "selected": true,
+ "text": "All",
+ "value": "$__all"
+ },
+ "description": "The capture's output encoding; events from before the field existed match All.",
+ "hide": 0,
+ "includeAll": true,
+ "multi": false,
+ "name": "contentType",
+ "options": [
+ {
+ "selected": true,
+ "text": "All",
+ "value": "$__all"
+ },
+ {
+ "selected": false,
+ "text": "image/png",
+ "value": "image/png"
+ },
+ {
+ "selected": false,
+ "text": "application/pdf",
+ "value": "application/pdf"
+ }
+ ],
+ "query": "image/png,application/pdf",
+ "type": "custom"
}
]
},
@@ -1198,7 +1210,6 @@
},
"timepicker": {},
"timezone": "browser",
- "title": "Screenshot Capture Performance",
- "weekStart": ""
+ "title": "Screenshot Capture Performance"
}
}
(Run: https://github.com/cardstack/boxel/actions/runs/35035467477) |
Host Test Results 1 files ±0 1 suites ±0 2h 32m 42s ⏱️ - 6m 40s Results for commit 8836abf. ± Comparison against earlier commit fd76fb0. Realm Server Test Results 1 files ±0 225 suites ±0 1h 22m 29s ⏱️ +25s Results for commit 8836abf. ± Comparison against earlier commit fd76fb0. |
lukemelia
left a comment
There was a problem hiding this comment.
[Claude Code 🤖]
Lens. Reviewed the pdf persist/serve path end-to-end against the base branch this stacks on: the capture identity round-trip (POST persist target ⇄ GET DSL served URL ⇄ the worker's re-derived persist hash), the content-type plumbing from render → ledger row → response headers, the reuse of the gate / congestion pre-check / sync-wait posture, and the telemetry + dashboard changes. I did not re-audit the pdf render leg itself (it lands in the base branch) or the arbitrary-screenshot cost model.
Bottom line: no blocking issues. type is part of the canonical spec identity (canonicalCaptureSpecString keeps it via canonicalOverrides, pdf ≠ the png default), so a pdf and a png key distinct ledger rows rather than colliding; the real engine sets contentType: 'application/pdf' in captureScreenshot (prerender/utils.ts), so the persist stores the paged content type instead of the response.contentType ?? 'image/png' fallback; pdfFilenameFor reduces the last path segment to a quoted-string-safe charset with no CRLF anywhere, so the Content-Disposition filename can't inject a header; and the ledger's width/height columns are already nullable, which the paged capture relies on. parseCaptureSpecParams has a single caller (the GET DSL serve path), so lifting the type=pdf refusal doesn't quietly admit pdf on any other surface. The new tests drive the full GET flow (capture-on-demand → persist as application/pdf with null dims → inline serve with filename → repeat is a zero-Chrome ledger hit, png identity untouched) and would fail without the change — the old parse refused type=pdf and the task hashed a pdf render to null and refused to persist.
Adjacent, out of scope. The ScreenshotPrerenderResponse.contentType doc in runtime-common/index.ts still reads "The engine produces only image/png today" — no longer true since the pdf capture leg landed, and this PR's persist now depends on the engine reporting application/pdf. Worth correcting whenever that type is next touched; not this PR's job.
da51bd0 to
fd76fb0
Compare
A pdf capture now persists to the MediaCache under its own spec identity
(content_type application/pdf, no pixel dimensions) and serves from
{realm}_screenshot/{path}?type=pdf: ledger hit on repeat, capture on miss
behind the same allowArbitraryScreenshots gate, congestion pre-check, and
sync-wait posture every capture uses. An edit re-captures on next fetch
because the identity pins the instance generation — the durable-URL use
case where cards embed always-current pdf links.
PDF responses carry Content-Disposition: inline with a filename derived
from the source card, so the browser viewer shows a sensible name; images
stay bare. The boxel:screenshot-perf events gain a contentType dimension
on both event types (request events derive it from the spec or served
row, capture events report what the render produced), and the screenshot
dashboard's queries grow a matching contentType template variable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fd76fb0 to
8836abf
Compare
Summary
Third PR in the Cards to PDF chain (CS-12977), stacked on #6120 (which stacks on #6118) — this diff reads clean against #6120.
Durable PDF URLs:
{realm}_screenshot/{path}?type=pdfnow serves from the ledger, capturing on miss — the use case where cards embed always-current PDF links, since the identity pins the instance generation and an edit re-captures on next fetch.captures[0].urlround-trips through the GET DSL as?type=pdf), and the worker task persistscontent_type: application/pdfwith null pixel dimensions. No schema change; the ledger, both adapters, and the GC arms are content-type-agnostic (none of the GC candidate queries readcontent_type).type=pdf; the miss path reuses theallowArbitraryScreenshotsgate, the congestion pre-check, and the 25s sync-wait / 503 + Retry-After posture unchanged. The service worker's_screenshot/503-absorbing retry covers pdf URLs by path prefix, untouched.inline; filename="<card>.pdf"(derived from the source URL's last segment, reduced to the quoted-string-safe charset) so the browser's viewer/download shows a sensible name; image responses stay bare.boxel:screenshot-perfevent types gain a requiredcontentTypedimension — request events derive it from the spec (or the served ledger row on the?name=path), capture events report what the render actually produced (null when it never got that far). The screenshot-captures Grafana dashboard gains a matchingcontentTypetemplate variable applied to all 24 queries; events from before the field existed match the All value (.*).Testing
media-cache-dsl-test.ts(45/45 locally): the pdf GET flow end-to-end (captures on demand, persists asapplication/pdfwith null dims, serves with content-type + content-disposition, repeat is a ledger hit with zero Chrome work, the png identity stays untouched), the task persisting under the pdf spec hash,?type=pdfround-tripping through the served-query parser, andpdfFilenameForunit coverage.screenshot-card-test.ts(64/64): a POST pdf capture's persist identity hashes the encoding in and returns the?type=pdfserved URL.media-cache-serving-test.tsandmedia-cache-gc-test.tsgreen;runtime-commonandrealm-servertypecheck clean.🤖 Generated with Claude Code