Spot instances worden midden in een shuffle teruggevorderd en de foutmelding zegt niets bruikbaars
Elke kostenbesparende Databricks deployment gebruikt spot instances voor worker nodes. AWS trekt ze terug met twee minuten waarschuwing. Azure geeft dertig seconden.
Wanneer een spot node midden in een taak verdwijnt, herprobeert Spark de taak op de overgebleven executors. Dat werkt, totdat de teruggevorderde node shuffle data bevatte waar andere taken van afhankelijk zijn.
De foutmelding die je ziet is FetchFailedException: Failed to connect to host. Niet 'je spot instance is teruggevorderd'. Niet 'probeer on-demand nodes'. Een network fetch fout die eruitziet als een tijdelijke storing. Teams herstarten de job, stoten op hetzelfde reclamation window en proberen het opnieuw.
De oplossing hangt af van de schaal. Voor jobs onder de 2 uur zorgt spark.speculation op true zetten en spot instances met fallback naar on-demand (first_on_demand: 1 in cluster policy) ervoor dat de driver stabiel blijft terwijl worker churn getolereerd wordt. Voor langlopende jobs (alles met grote shuffles of het schrijven van honderden Delta partitions) kosten on-demand workers minder dan drie herpogingen van een job van 4 uur op spot-tarieven.
Databricks heeft in 2024 spot fall-back policies toegevoegd, maar de standaard cluster configuratie gebruikt nog steeds alleen spot voor workers. Als je je job template vóór die update hebt aangemaakt, draai je op de oude standaard. Controleer je cluster policy JSON op aws_attributes.availability of azure_attributes.availability. Als het SPOT of SPOT_WITH_FALLBACK_AZURE zegt, loop je risico. Verander het naar SPOT_WITH_FALLBACK op AWS of ON_DEMAND_AZURE voor kritieke nachtjobs.
Het patroon is voorspelbaar: spot reclamation rates schieten omhoog tijdens kantooruren in de US-East en EU-West regio's wanneer de algemene compute vraag stijgt. Je job om 2 uur EST die maandenlang zonder problemen liep, begint te mislukken wanneer klanten van andere cloudproviders hun ochtend workloads opschalen in overlappende tijdzones.
Autoscaling voegt workers te laat toe en je job raakt de timeout
Databricks autoscaling kijkt naar het aantal openstaande taken en voegt workers toe wanneer de achterstand de capaciteit overschrijdt. Het probleem: een nieuwe node provisionen, libraries installeren en registreren bij de Spark driver duurt 3-7 minuten op AWS en 5-12 minuten op Azure. Voor bursty workloads (jobs die plotseling 10x zoveel executors nodig hebben voor een grote join) arriveren de extra workers nadat de stage al minuten lang met verminderde parallelisme heeft gedraaid.
In development draaide je de job op een interactive cluster dat al warm was met 8 nodes. In productie start de job cluster met min_workers: 2 en max_workers: 20. De eerste stages lopen goed. Dan komt de brede transformatie, Spark plant 200 taken over 2 workers en de autoscaler begint 18 extra nodes aan te vragen. De helft van die aanvragen mislukt omdat de instance pool koud is of de cloudregio capaciteitsproblemen heeft. Je job ploetert door met 6 workers in plaats van 20, duurt drie keer zo lang als verwacht en raakt de timeout_seconds die je hebt ingesteld op basis van de development run.
De foutmelding is Run exceeded timeout of 7200 seconds. Niets wijst op autoscaling als root cause.
Twee aanpakken werken daadwerkelijk. Gebruik ten eerste instance pools met idle instances ingesteld op min_idle_instances: 4. Dit houdt warme nodes beschikbaar en verkort de provisioning tijd tot minder dan 90 seconden. Stel ten tweede autoscale.min_workers dichter bij je werkelijke steady-state behoefte in dan op het minimum. De kosten van 6 idle nodes gedurende 10 minuten voor de job start zijn verwaarloosbaar vergeleken met een mislukte job van 2 uur die volledig opnieuw moet worden gestart.
Uit de Stack Overflow 2024 survey blijkt dat slechts 1,9% van de professionele developers Databricks SQL gebruikt, wat betekent dat de meeste Databricks productie workloads Spark jobs zijn die worden beheerd door kleine gespecialiseerde teams. Wanneer de ene engineer die de cluster config heeft afgesteld vertrekt, driften deze instellingen weg.
Delta concurrent write conflicts escaleren wanneer je de mislukte job herstart
Delta Lake's ACID transacties gebruiken optimistic concurrency control. Twee jobs die tegelijkertijd naar dezelfde tabel schrijven slagen allebei, tenzij hun operaties conflicteren. Een ConcurrentAppendException betekent dat twee commits probeerden bestanden toe te voegen aan dezelfde partition. Een ConcurrentDeleteReadException betekent dat één job bestanden heeft gelezen die een andere job heeft verwijderd (via MERGE of DELETE).
In development draai je één job tegelijk. In productie overlapt de nachtelijke ingestion job met de vertraagd lopende transformatie van de dag ervoor. Of de herstart van de mislukte job van 2 uur botst met de job van 6 uur die dezelfde tabel leest.
En dan wordt het nog erger. Het standaard herstart-gedrag in Databricks Workflows herstart de volledige job vanaf het begin. Als de job bij taak 847 van 900 is mislukt door een tijdelijke spot reclamation, worden bij de herstart alle 900 taken opnieuw uitgevoerd, inclusief de schrijfacties die al naar Delta zijn gecommit. Die schrijfacties conflicteren nu met de gedeeltelijk gecommitte data van de eerste poging.
Voor ConcurrentAppendException is de oplossing partition isolation: zorg dat gelijktijdige writers verschillende partitions als doel hebben door replaceWhere te gebruiken met niet-overlappende predicaten. Voor ConcurrentDeleteReadException tijdens MERGE operaties schakel je spark.databricks.delta.merge.repartitionBeforeWrite.enabled in om het conflict surface op bestandsniveau te verkleinen.
Maar de echte oplossing is architectureel. Herstart schrijf-intensieve jobs niet blind. Gebruik idempotent_token op Databricks job runs (beschikbaar sinds eind 2025) om herstarts veilig te maken, of ontwerp je pipeline met een staging table patroon waarbij elke run naar een uniek pad schrijft en een finale atomaire RENAME het resultaat op zijn plek zet. De herstart overschrijft het staging pad dan zonder schade.
De commit log in _delta_log vertelt je precies welke transactie heeft gecolliseerd en wanneer. Maar alleen als je kijkt voordat de volgende succesvolle run de log compacteert.
Unity Catalog permissions propageren met vertraging die tests nooit blootleggen
Unity Catalog heeft workspace-niveau access controls vervangen door een gecentraliseerde governance laag. Permission grants propageren via een distributed cache. In de praktijk duurt het 30-120 seconden voordat een GRANT SELECT ON TABLE catalog.schema.table TO group effectief is op alle clusters in de workspace.
Je CI/CD pipeline verleent permissions in stap 3 en draait de integratietest in stap 4. In de testomgeving met één cluster is de propagation binnen 5 seconden voltooid. In productie met 40 gelijktijdige clusters duurt het 90 seconden. De test slaagt. De productie job (die na de deployment een nieuw cluster start) stuit op AnalysisException: User does not have permission to SELECT on table.
Dit mislukt met tussenpozen, wat de lastigste soort failure is. Dezelfde job slaagt bij de herstart omdat de permission dan al is gepropageerd. Teams noemen het flaky en voegen een herstart toe. De herstart maskeert het probleem totdat een job twee permission-afhankelijke stappen heeft en de herstart alleen opnieuw uitvoert vanaf de mislukte stap, waarbij de stale permission cache van de eerste stap intact blijft.
De oplossing is een permissions warm-up query. Voer na het verlenen van permissions in je deployment script een SELECT 1 FROM catalog.schema.table LIMIT 1 uit met de service principal die de productie job zal draaien. Wacht tot dit slaagt met een lus van 3 herstarts en 30 seconden backoff. Dit dwingt de permission in de cache van ten minste één cluster, en volgende clusters erven van de metastore cache in plaats van te wachten op volledige propagation.
Controleer ook de system.access.audit logs. Unity Catalog logt elke permission check. Als je DENIED entries ziet gevolgd door ALLOWED entries voor dezelfde principal en tabel binnen minuten, heb je een propagation timing probleem, geen fout in de configuratie.
De monitoring gap tussen job status en werkelijke data freshness
Databricks Workflows rapporteert job status: succeeded, failed, timed out of cancelled. Wat het niet rapporteert is of de job die slaagde daadwerkelijk de data heeft geproduceerd die je downstream consumenten verwachten.
Een job kan slagen terwijl hij nul rijen schrijft omdat de brontabel leeg was. Hij kan slagen terwijl hij stilletjes 40% van de records negeert door een schema evolution die een niet-nullable kolom naar nullable heeft geconverteerd. Hij kan 90 minuten te laat slagen door de hierboven beschreven autoscaling vertragingen, terwijl elk dashboard dat op die data vertrouwt stale cijfers toont tot en met de ochtendstandup.
De Databricks job run API (/api/2.1/jobs/runs/get) geeft result_state: SUCCESS terug in al deze gevallen. De run duur is beschikbaar, maar er is geen ingebouwde alerting voor 'deze job duurt gewoonlijk 45 minuten en vandaag duurde het 130 minuten'. Je zou dat zelf moeten bouwen door de runs list API op te vragen, een rolling average te berekenen en een drempelwaarde in te stellen.
MetricSign maakt verbinding met je Databricks workspace en volgt job execution patronen. Niet alleen pass/fail, maar ook duration anomalies en vertraagde completions. Wanneer een job die normaal voor 5:15 uur klaar is om 6:30 uur nog loopt, stuurt MetricSign een refresh_delayed signaal voordat de job een timeout krijgt of je stakeholders stale dashboards opmerken. Dat vroege signaal is het verschil tussen consumenten proactief informeren en achteraf uitleggen waarom het rapport van 8 uur de cijfers van gisteren liet zien.
De operationele realiteit voor de meeste Databricks teams: de job is geslaagd, de data klopt niet en niemand weet het totdat iemand een dashboard opent en ziet dat de cijfers niet zijn veranderd. Die kloof tussen job status en data freshness is waar het vertrouwen in productie verslechtert.