MetricSign
NL|ENStart free →
Best Practices8 min·

Databricks R-plots verdwijnen zonder foutmelding — het graphics device faalt stilzwijgend

Je R-code wordt foutloos uitgevoerd. De cell is klaar. Het plotgebied is leeg. Databricks vertelt je niet waarom — want vanuit het perspectief van de runtime is er niets fout gegaan.

Read this article in English →

De cell slaagt, de plot verdwijnt, niemand krijgt een melding

Een Databricks community thread documenteerde een patroon dat R-gebruikers op het platform maar al te goed kennen: plot(1:3, 5:7) wordt zonder fout uitgevoerd op Runtime 14.3 LTS, de cell toont een groen vinkje en het output-gebied is leeg. Geen waarschuwing. Geen traceback. Hetzelfde cluster rendert Python matplotlib-plots wel gewoon.

Dit is geen zeldzame randgebeurtenis. Het duikt keer op keer op in Databricks Community-forums en de oplossing volgt bijna altijd hetzelfde vage patroon. Het probleem verschijnt na een platform-update, houdt dagenlang aan en verdwijnt dan stilletjes. De oorspronkelijke poster in die thread bevestigde dat het verdween zonder dat zij iets hadden veranderd. Databricks heeft nooit bekendgemaakt wat er kapot was gegaan.

De zakelijke gevolgen zijn reëel. R notebooks die ggplot2-visualisaties genereren voor wekelijkse rapportages, QA-grafieken ingebouwd in data-validatieworkflows, verkennende plots tijdens feature engineering: al deze notebooks produceren onzichtbare output. Het notebook ziet eruit alsof het is uitgevoerd. De scheduler markeert de job als geslaagd. Een stakeholder opent het notebook in de verwachting een grafiek te zien en vindt niets.

Wat dit failure mode gevaarlijk maakt, is de onzichtbaarheid. Een job die een exception gooit, wordt opgevangen door alerting. Een job die succesvol afsluit maar lege output produceert, passeert elk controlepunt. Tenzij je expliciete validatie hebt gebouwd rondom gerenderde output, kom je er pas achter als iemand het notebook handmatig opent en de lege plek ziet.

Databricks verwerkt R-graphics via een PNG device dat je niet beheert

Als je een plot-opdracht uitvoert in een R-cell, rendert Databricks die niet zoals RStudio dat doet. RStudio stuurt graphics-instructies naar een lokale rendering backend (het RStudioGD-device) die je kunt inspecteren en configureren. Databricks onderschept de graphics output van R via een server-side PNG device.

Het mechanisme werkt als volgt: vóór de uitvoering van je R-cell opent Databricks een PNG graphics device. Je plotting-code schrijft naar dat device. Na de uitvoering leest Databricks de resulterende PNG-bytes en embedt ze als base64-gecodeerde afbeeldingen in de output-JSON van het notebook. De R REPL ziet het gerenderde beeld zelf nooit. Hij schrijft alleen naar een file descriptor die Databricks beheert.

Daarom geeft dev.list() een device terug, ook als plots niet renderen. Het device bestaat. Het accepteert write-aanroepen. Het produceert alleen geen bruikbare output. De fout treedt op in de laag tussen de device output en de notebook frontend, een laag die volledig buiten het bereik van R's foutafhandeling valt.

Meerdere situaties kunnen deze pipeline breken. Een Databricks Runtime-update kan veranderen hoe de notebook server graphics devices initialiseert. Een onderbroken cell kan het device in een half-open staat achterlaten, waarbij volgende plots naar een gecorrumpeerde buffer schrijven. Library-conflicten tussen de ingebouwde grDevices van R en packages zoals Cairo of ragg kunnen output omleiden naar een device dat Databricks niet uitleest.

De png()- en dev.off()-cyclus die werkt in zelfstandige R-scripts helpt hier niet, omdat Databricks zijn eigen device al heeft geopend vóórdat je code wordt uitgevoerd. dev.off() aanroepen op het device van Databricks zonder een vervanging te openen laat volgende cells zonder device, maar geeft nog steeds geen foutmelding.

R Plot Rendering Pipeline in Databricks Notebooks Databricks opent server-side PNG graphics device R plotting code schrijft naar het graphics device Cell-uitvoering voltooid — R REPL rapporteert succes Databricks leest PNG bytes van het device buffer PNG-bytes als base64 gecodeerd en ingebed in notebook output Notebook frontend rendert base64-beeld in output-gebied FOUTPUNT: Elke breuk tussen stap 3-6 geeft lege output
R Plot Rendering Pipeline in Databricks Notebooks

De DBFS-workaround werkt, tot hij het niet meer doet

De meest aanbevolen workaround in community-threads is de notebook rendering pipeline volledig te omzeilen: schrijf plots als PNG-bestanden naar DBFS en toon ze met displayHTML().

```r png("/dbfs/FileStore/plots/weekly_report.png", width=800, height=600) ggplot(df, aes(x=date, y=value)) + geom_line() dev.off()

displayHTML('') ```

Dit werkt voor losse plots. Voor samengestelde visualisaties faalt het. De thread wees specifiek op ggarrange() uit het ggpubr-package, een functie die meerdere ggplot-objecten samenvoegt tot één figuur. Als ggarrange() naar een handmatig geopend PNG device schrijft op Databricks, is de output vaak onvolledig of leeg. De functie beheert zijn eigen interne graphics device-staat en die staat conflicteert met het handmatig geopende png()-device.

De DBFS-aanpak introduceert ook zijn eigen failure modes. Het /dbfs/ FUSE mount heeft bekende betrouwbaarheidsproblemen op clusters met hoge I/O-belasting. Schrijfacties kunnen stilzwijgend mislukken of bestanden van nul bytes produceren. Als je notebook een plot schrijft naar /dbfs/FileStore/plots/chart.png en het FUSE mount is gedegradeerd, rendert displayHTML() een kapotte image-tag, maar slaagt de cell nog steeds.

Een robuuster patroon schrijft eerst naar het lokale bestandssysteem van de driver node en kopieert daarna naar DBFS:

``r png("/tmp/chart.png", width=800, height=600) print(ggplot(df, aes(x=date, y=value)) + geom_line()) dev.off() file.copy("/tmp/chart.png", "/dbfs/FileStore/plots/chart.png") ``

Je kunt vervolgens controleren of het bestand is geschreven door file.info("/dbfs/FileStore/plots/chart.png")$size > 0 te checken vóór je displayHTML() aanroept. Dit converteert een silent failure tenminste naar een expliciete fout die je kunt opvangen.

Ontdek lege output voordat je stakeholders dat doen

Het kernprobleem is dat Databricks plot rendering behandelt als een bijeffect, niet als een resultaat. Een cell die een grafiek moet produceren, heeft dezelfde exit code ongeacht of de grafiek rendert of niet. Om hier betrouwbaarheid omheen te bouwen, moet je gerenderde output behandelen als een artefact dat verificatie vereist.

Voeg voor R notebooks in productie een render-check cell toe na elke plotsectie:

``r dev_info <- dev.list() if (is.null(dev_info) || length(dev_info) == 0) { stop("RENDER_FAILURE: No active graphics device. Plots in preceding cells did not render.") } ``

Dit vangt niet elke failure op (het device kan bestaan maar toch lege output produceren), dus combineer het met de DBFS bestandsgrootte-check voor kritieke visualisaties. Voor ggplot-objecten kun je ook het plot-object zelf valideren vóór rendering:

``r p <- ggplot(df, aes(x=date, y=value)) + geom_line() if (is.null(ggplot_build(p)$data[[1]]) || nrow(ggplot_build(p)$data[[1]]) == 0) { stop("RENDER_FAILURE: Plot contains no renderable data.") } print(p) ``

Deze checks converteren silent rendering failures naar job failures, wat precies is wat je wilt. Een mislukte job wordt opgepikt door monitoring. Een geslaagde job met lege output niet.

Voor teams die R notebooks planmatig uitvoeren via Databricks Jobs detecteert MetricSign wanneer een job afsluit maar de downstream dependencies onverwachte patronen vertonen. Denk aan een report notebook dat historisch gezien 45 seconden nodig heeft om te renderen, maar plotseling in 3 seconden afsluit omdat er geen plots zijn gegenereerd. Die duuranomalie, gecorreleerd over de run history van de job, verschijnt als root-cause signaal voordat een stakeholder een leeg notebook opent.

Runtime-updates breken R-rendering zonder changelog-vermelding

De tijdlijn in de thread spreekt voor zich. Het probleem verscheen plotseling, raakte alle R-plots in meerdere notebooks en browsers en verdween zonder tussenkomst van de gebruiker. Dit patroon wijst op een wijziging aan Databricks-zijde, hoogstwaarschijnlijk een Runtime-patch die de initialisatie van het graphics device van de notebook server heeft aangepast.

Databricks Runtime-releases volgen een vaste cadans: grote versies per kwartaal, onderhoudsupdates vaker. Onderhoudsupdates op LTS runtimes zoals 14.3 kunnen interne componenten wijzigen zonder dat dit in de klantgerichte release notes verschijnt. De graphics device pipeline voor R is zo'n intern component. Het maakt geen deel uit van het Spark API-oppervlak, dus wijzigingen daarin leiden niet tot documentatie-updates.

Dit creëert een monitoring blind spot. Je pinnt je cluster aan Runtime 14.3 LTS in de verwachting van stabiliteit. Een onderhoudspatch wordt uitgerold. Je Python workloads lopen gewoon door omdat matplotlib een ander rendering-pad gebruikt. Je R notebooks produceren twee dagen lang stilzwijgend lege output totdat iemand er een opent.

De enige verdediging is R-rendering behandelen als een volwaardige pipeline output. Pin je cluster aan een specifieke patch-versie als die beschikbaar is (bijvoorbeeld 14.3.x-scala2.12 in plaats van 14.3 LTS). Draai een canary notebook dat een bekende plot genereert en de bestandsgrootte van de output op een schema verifieert. Log de Runtime-versie aan het begin van elke notebook run met spark.conf.get("spark.databricks.clusterUsageTags.sparkVersion") zodat je rendering failures achteraf kunt correleren met patch-wijzigingen.

Databricks SQL heeft een adoptie van 1,9% in de Stack Overflow 2024 Developer Survey, wat de relatief nichestatus van het platform weerspiegelt. Maar de teams die het gebruiken, doen dat intensief, met complexe multi-language notebooks die SQL, Python en R combineren. Precies die teams worden het hardst geraakt door taalspecifieke rendering-regressies die het dominante Python-pad niet raken.

R's tweede-klas status op Databricks is een betrouwbaarheidsprobleem

Databricks heeft fors geïnvesteerd in Python- en Scala-ondersteuning. Unity Catalog, MLflow-integratie, Delta Live Tables: dat alles is Python-first. R-ondersteuning bestaat, maar krijgt minder testdekking, minder documentatie-updates en minder community-aandacht voor randgevallen.

Serverless compute ondersteunt R helemaal niet. Shared access mode clusters beperken R-functionaliteit. De graphics rendering pipeline voor R loopt door een ander codepad dan de matplotlib-integratie van Python, die %matplotlib inline en IPython's display framework gebruikt. Dat pad wordt door ordes van grootte meer gebruikers getest.

Dit is geen reden om R op Databricks te verlaten. Veel statistische computing workflows, met name in farma, verzekeringen en academisch onderzoek, zijn afhankelijk van R-packages zonder Python-equivalent. Het survival-package, lme4 voor mixed-effects modellen, domeinspecifieke Bioconductor-packages: die houden R notebooks in productie, ook op platforms die R als bijzaak beschouwen.

Maar het is wel een reden om meer defensieve infrastructuur te bouwen rondom R workloads. Ga ervan uit dat R-rendering periodiek kapotgaat zonder waarschuwing. Ga ervan uit dat de fix van Databricks komt zonder uitleg, dagen later. Ontwerp je notebooks zo dat rendering failures job failures worden en job failures alerts worden.

Schrijf kritieke visualisaties naar DBFS en verifieer bestandsgroottes. Voeg device-check cells toe. Log runtime-versies. En leid je R notebook jobs door monitoring die onderscheid kan maken tussen een job die zijn werk heeft afgerond en een job die afsloot omdat hij zijn werk oversloeg. Want Databricks maakt dat onderscheid niet voor je.

Veelgestelde vragen

Waarom stoppen R-plots in Databricks met renderen zonder foutmelding?+
Databricks beheert R-plot rendering via een server-side PNG graphics device dat buiten het bereik van R's foutafhandeling valt. Als dit device faalt door een Runtime-patch, een onderbroken cell die het device in een gecorrumpeerde staat achterlaat, of een library-conflict, rapporteert de R REPL nog steeds succes omdat de plotting-code zonder exception is uitgevoerd. De fout treedt op in de laag tussen de device output en de notebook frontend, die geen foutrapportage-mechanisme heeft terug naar de R-sessie.
Werkt de DBFS-workaround voor alle R-plot typen in Databricks?+
Nee. Plots schrijven naar DBFS met `png()` en weergeven met `displayHTML()` werkt voor losse plots uit base R en ggplot2, maar samengestelde visualisaties met functies zoals `ggarrange()` uit ggpubr produceren vaak lege of onvolledige output. Deze functies beheren hun eigen interne graphics device-staat, die conflicteert met een handmatig geopend PNG device. Bovendien kan het `/dbfs/` FUSE mount bij hoge I/O-belasting stilzwijgend bestanden van nul bytes produceren.
Hoe maak ik Databricks R notebook failures zichtbaar voor monitoring?+
Voeg expliciete render-verificatie toe na plotcells: controleer of `dev.list()` een actief device teruggeeft, valideer ggplot-objecten met `ggplot_build()` vóór rendering en verifieer dat DBFS output-bestandsgroottes groter zijn dan nul. Deze checks converteren silent rendering failures naar exceptions die de job laten falen, waardoor ze zichtbaar worden voor elk job monitoring-systeem.

Gerelateerde integraties

Gerelateerde artikelen