Ga naar de hoofdinhoud

Spark lokaal draaien tegen ADLS Gen2 met Floci AZ

We implementeerden de ADLS Gen2 DFS-semantiek die nodig is voor hadoop-azure:x.x.x en valideerde het resultaat via de volledige Titan Spark-pipeline over abfss://.

Spark 3.5 / 4.x hadoop-azure 3.3 / 3.4 ADLS Gen2 DFS Titan E2E
lokaal compatibility-pad
echte clients
01

Titan / Spark

Analytics-ready

02

Hadoop ABFS

hadoop-azure:x.x.x

03

ADLS Gen2 DFS-semantiek

Fouten · ETags · list · rename · metadata

04

Floci AZ

Lokale Azure-compatibele storagegrens

Gevalideerd

Gevalideerd over drie Spark- en Hadoop-generaties

We hebben de DFS-implementatie niet slechts tegen één runtimecombinatie gevalideerd. Hetzelfde Floci AZ DFS-pad is uitgevoerd over onze Spark 3.5- en Spark 4.x-compatibiliteitsbaselines.

Baseline 1

DBR 16.4 LTS-compatibiliteit

Spark
3.5.2
Hadoop
3.3.4
Client
hadoop-azure:3.3.4
Delta Lake
3.3.1

Baseline 2

DBR 17.3 LTS-compatibiliteit

Spark
4.0.0
Hadoop
3.4.1
Client
hadoop-azure:3.4.1
Delta Lake
4.0.0

Baseline 3

DBR 18 LTS-compatibiliteit

Spark
4.1.0
Hadoop
3.4.2
Client
hadoop-azure:3.4.2
Delta Lake
4.2.0

De Databricks Runtime-referenties zijn benaderende omgevingsanalogen ter oriëntatie. Het zijn geen claims van bit-for-bit runtime-equivalentie.

Waarom Floci AZ en niet LocalStack of Azurite

Titan is afhankelijk van cloud-facing contracten, niet alleen storagebytes. In lokale development willen we onze normale SDK's, Spark-filesystemcode en infrastructuurtooling laten draaien tegen Azure-shaped endpoints zonder voor iedere iteratie Azure-infrastructuur te provisionen.

Beslissing lokale emulator

Hoe de drie opties zich tot elkaar verhouden

De belangrijke vraag was niet welke emulator de meeste features heeft, maar welke Titan's Azure-facing development path het beste behoudt.

LocalStack

Geen match
Cloudgrens
AWS-gericht
ADLS Gen2 DFS
× Niet het Azure DFS-contract
Platformscope
Brede service-emulatie, maar voor de verkeerde cloudgrens
Geschikt voor Titan
Nee

Azurite

Gedeeltelijke fit
Cloudgrens
Azure Storage
ADLS Gen2 DFS
× Buiten gedocumenteerde scope
Platformscope
Emulatie van Blob-, Queue- en Table-storage
Geschikt voor Titan
Gedeeltelijk

Floci AZ

Geselecteerd
Cloudgrens
Azure-gerichte services
ADLS Gen2 DFS
✓ Ondersteund via onze DFS-contributie
Platformscope
Bredere Azure-emulatorfamilie met echte SDK- en CLI-clients
Geschikt voor Titan
✓ Beste fit
✓

Beslissing: Met Floci AZ kunnen we het productiegerichte Spark-, Hadoop- en Azure-clientpad in de lokale ontwikkelcyclus houden, terwijl we toch een implementatie hebben die we kunnen uitbreiden wanneer het benodigde protocoloppervlak ontbreekt.

De eerste Floci AZ / Hadoop ABFS-compatibiliteitsfailure

Hadoop riep aan FileSystem.delete() via het DFS-endpoint. Vóór de patch liep die DELETE nog via floci-az's generieke Blob-deletepad. Een ontbrekend pad leek daardoor op een ontbrekende blob.

Waargenomen vóór de patch

Blob-achtige ontbrekende objectmelding

geverifieerde responsstructuur
HTTP 404
Content-Type: application/xml
x-ms-error-code: BlobNotFound

<...>
  <Code>BlobNotFound</Code>
  ...
</...>

Alleen de velden die door de regressietooling zijn geverifieerd worden getoond. We claimen geen exacte originele XML-envelope die nooit als fixture is vastgelegd.

Wat Hadoop ABFS nodig had

Data Lake ontbrekend pad

DFS-response
HTTP 404
Content-Type: application/json
x-ms-error-code: PathNotFound

{
  "error": {
    "code": "PathNotFound",
    "message": "The specified path does not exist."
  }
}

Met DFS-semantiek vertaalt Hadoop de ontbrekende delete naar het normale filesystemresultaat: false.

Hoe één DELETE voor een ontbrekend pad wordt vertaald

HTTP-response → filesystem-resultaat

01

HTTP-transport

404 zegt dat de resource niet bestaat.

02

ADLS Gen2 DFS

PathNotFound + JSON identificeert het servicecontract van de Data Lake.

03

Hadoop ABFS

De client mapt de Azure-response naar Hadoop-filesystemsemantiek.

04

Caller-resultaat

fs.delete(path, true) == false

Voor Hadoop telt het contract achter de statuscode

ABFS vertaalt Azure-specifieke foutcodes, headers en de status van het bestandssysteem over meerdere verzoeken. Een correcte HTTP-status met de verkeerde servicebetekenis kan alsnog tot een onjuist Hadoop-bestandssysteemresultaat leiden. Dit contract geldt ook voor ETags, padtypen, hernoemingen en metadata-updates. Daarom moest de implementatie verder gaan dan alleen DELETE.

Draai lokaal het echte Spark- en Hadoop-ABFS-pad

Titan bereikt storage nog steeds via Spark en Hadoop ABFS. Lokale development verandert het endpoint dat de DFS-request ontvangt, niet het clientpad dat hem creëert.

Spark- en Hadoop ABFS-pad met productie- en lokale DFS-endpoints Titan roept Spark aan, Spark gebruikt Hadoop ABFS en Hadoop verstuurt het ADLS Gen2 DFS-request. Hetzelfde clientpad kan in productie eindigen bij Azure Data Lake Storage of lokaal via een loopback-Docker-route bij floci-az. ONGEWIJZIGD CLIENTPAD APPLICATIE Titan Pipeline + bestandsformaten UITVOERING Apache Spark 3.5.2 / 4.0.0 / 4.1.0 FILESYSTEM-CLIENT Hadoop ABFS hadoop-azure:x.x.x ADLS GEN2 DFS-AUTHORITY *.dfs.core.windows.net Azure-compatibele hostnaam en requestsemantiek PRODUCTIE-ENDPOINT Azure Data Lake Storage Gen2 <account>.dfs.core.windows.net Echte Azure DFS-service LOKAAL ENDPOINT Loopback- / Docker-route → floci-az :4577 devstoreaccount1.dfs.core.windows.net Lokale DFS-compatibele service

De emulator versterkt de inner development loop. Hij vervangt integratietesten tegen Azure niet.

Map het ABFS-contract in plaats van één fout tegelijk na te jagen

Na een paar cycli waarin we één operatie repareerden om vervolgens het volgende probleem bloot te leggen, waren we het zat om steeds één klein stukje van de puzzel op te lossen.

We namen hadoop-azure:x.x.x en hadoop-common:x.x.x:

De ABFS-codepaden getraced

De DFS-calls reverse-engineered

Uitgebreide Floci AZ DFS-afhandeling

Dat veranderde het werk van reactief debuggen in een begrensde compatibiliteitsronde. We wisten welke filesystemoperaties bereikbaar waren, welke servicesemantiek Hadoop parseerde en welke states tussen calls moesten blijven bestaan.

Analytics

Van clientanalyse naar Titan E2E

01

Trace de client

Volg Hadoop-filesystemmethoden naar de concrete ABFS-requestpaden.

02

Map het bereikbare DFS-oppervlak

Identificeer filesystem lifecycle, path I/O, listing, metadata, ACL, leases, rename en delete.

03

Match client-observed semantics

Implementeer error envelopes, ETags, resource types, continuation en state transitions.

04

Voeg regressietests toe

Zet ieder ontdekt contract om in repository-level coverage.

05

Draai de echte Hadoop-client

Gebruik AzureBlobFileSystem als compatibiliteitscontrole op clientniveau.

06

Draai Titan end-to-end

Valideer iedere ondersteunde ABFSS-workflow van Source → Semantic.

Het DFS-contract dat hadoop-azure en hadoop-common vereisen

Het resultaat is breder dan het oorspronkelijke DELETE-issue omdat de echte ABFS-client afhankelijk is van een coherent DFS-filesystemcontract. We implementeerden de operaties die door de gecombineerde hadoop-azure en hadoop-common clientpad en valideerde dat contract vervolgens tegen de Spark 3.5.2-, 4.0.0- en 4.1.0-compatibiliteitsbaselines.

Bestandssysteem

Create en properties

Bestandssysteem aanmaken/verwijderen plus filesystem-properties en rootstatus ophalen/instellen.

Path I/O

Create read append flush

Bestands- en directorycreatie, Path Read, sequentieel toevoegen en flushen.

Voorwaarden

ETags en overwrite

Conditional create en Hadoop's 409 → status/ETag → If-Match-overwriteflow.

Namespace

Toon status rename delete

Exacte filelisting, continuation handling, recursive delete en file/directory rename.

Metadata

Properties en XAttrs

Metadata-updates maken een round-trip zonder de onderliggende filebytes te wijzigen.

Analyseer welke orders geraakt worden

ACL-ownerpermission

Wire-compatible owner-, group-, permission- en ACL-metadata plus checkAccess.

HNS + leases

Autodetectie en Path Lease

HNS-detectie, root-ACL/status en flows voor lease verkrijgen/vernieuwen/vrijgeven/breken/wijzigen.

Errorcontract

DFS JSON-normalisatie

Data Lake-errorcodes en JSON-envelopes worden genormaliseerd voordat Hadoop ze ontvangt.

Scheid DFS-requests van Blob-gedrag

De belangrijke architectuurwijziging is servicescheiding. Een request voor *.dfs.core.windows.net moet worden geïdentificeerd voordat opslaggedrag wordt gekozen. Niet-ondersteunde DFS-updates worden expliciet geweigerd in plaats van door te vallen naar generieke Blob-mutatielogica.

Fail closed

Metadata-calls mogen nooit file uploads worden

Hadoop-metadataoperaties hebben vaak lege request bodies. Als een onbekende DFS PUT doorviel naar generieke PutBlob, kon de emulator een bestaand bestand vervangen door nul bytes.

Regel: ondersteunde DFS-operatie of expliciete DFS-fout. Geen stille Blob-fallback.

HTTP/2-routing

De DFS-hostname kan via HTTP/2 authority binnenkomen

Het routingfilter legt de opgeloste request-URI-host vast vóór async dispatch. Dat werkt voor HTTP/2 authority én HTTP/1.1, met Host als fallback.

Leg de DFS-host vast vóór async dispatch

Dit is de daadwerkelijke branchcode. De resolved host wordt vastgelegd terwijl de JAX-RS requestcontext beschikbaar is en opgeslagen op AzureRequest en vervolgens genormaliseerd door BlobServiceHandler.

werkelijk implementatiefragment
// AzureRoutingFilter.java
String h = requestContext.getUriInfo().getRequestUri().getHost();
if (h == null || h.isBlank()) {
    h = requestContext.getHeaders().getFirst("Host");
    if (h == null) {
        h = requestContext.getHeaders().getFirst("host");
    }
}
final String capturedHost = h;

// BlobServiceHandler.java
private static boolean isDataLakeRequest(AzureRequest request) {
    String host = request.host();
    if (host == null || host.isBlank()) {
        return false;
    }
    String normalizedHost = host.trim().toLowerCase(Locale.ROOT);
    int portSeparator = normalizedHost.indexOf(':');
    if (portSeparator >= 0) {
        normalizedHost = normalizedHost.substring(0, portSeparator);
    }
    return normalizedHost.endsWith(".dfs.core.windows.net");
}

Hoe een DFS-request door de implementatie loopt

De request wordt geclassificeerd als Data Lake-verkeer, naar de DFS-padimplementatie gestuurd, toegepast op lokale state en terugvertaald naar het responsecontract dat Hadoop verwacht.

Hadoop ABFS-requestflow door de floci-az DFS-implementatie Hadoop ABFS verstuurt een ADLS Gen2 DFS-request. AzureRoutingFilter legt de host vast, BlobServiceHandler classificeert de DFS-operatie, DataLakePathOperations past padsemantiek toe, de storagestatus wordt bijgewerkt en AzureErrorResponse maakt de juiste DFS-respons. Niet-ondersteunde DFS-updates worden fail-closed afgehandeld. CLIENT FLOCI-AZ REQUESTPIPELINE STATUS + RESPONS 1 · HADOOP ABFS DFS-request create · status · list · rename · delete 2 · ROUTING AzureRoutingFilter URI-host van request vastleggen / Host-fallback Authority opslaan op AzureRequest 3 · DISPATCH BlobServiceHandler methode + actie + query + headers filesystem- of padoperatie selecteren 4 · DFS-SEMANTIEK DataLakePathOperations create · read · append · flush list · properties · ACL · access rename · delete · continuation 5 · STATUS StorageBackend + leases bytes · ETags · resourcetype · metadata Path Lease-status waar van toepassing 6 · DFS-RESPONS Azure-compatibel resultaat status · headers · JSON-foutsemantiek FAIL CLOSED Niet-ondersteunde DFS-update DFS-fout retourneren. Nooit PutBlob.

Gebruik de echte Hadoop ABFS-client

REST-tests bewijzen endpointgedrag. Deze test bewijst dat de endpointsemantiek samen een filesystem vormt dat de daadwerkelijke Hadoop 3.3.4-client kan gebruiken.

HNS + rootstatus
Conditional overwrite
XAttrs + exacte listing
Hernoemen + ontbrekende delete

Werkelijke testharness

Behoud Hadoop's DFS-hostname en wireshape

De Docker-compatibiliteitssetup mapt devstoreaccount1.dfs.core.windows.net naar loopback. Een transparante TCP-relay forwardt poort 80 naar floci-az:4577, zodat Hadoop nog steeds zijn normale DFS-request verstuurt.

Hadoop 3.3.4-standaarden fs.azure.always.use.https=true, daarom schakelt de test HTTPS alleen uit voor deze plaintext-loopback-harness.

alleen-loopback-forwarder
void start() throws IOException {
    serverSocket = new ServerSocket(
            listenPort,
            50,
            InetAddress.getByName("127.0.0.1"));
    running.set(true);
    executor.submit(this::acceptLoop);
}

Expliciet binden aan 127.0.0.1 houdt de testrelay weg van de andere interfaces van de container.

HadoopAbfsCompatibilityTest.java · daadwerkelijk fragment
String filesystem =
        "hadoop-" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
URI uri = URI.create("abfs://" + filesystem + "@" + ACCOUNT_HOST);

assertEquals("3.3.4", VersionInfo.getVersion());

Configuration conf = new Configuration();
conf.set("fs.abfs.impl",
        "org.apache.hadoop.fs.azurebfs.AzureBlobFileSystem");
conf.set("fs.azure.account.key." + ACCOUNT_HOST, EmulatorConfig.DEV_KEY);
conf.setBoolean("fs.azure.always.use.https", false);
conf.setBoolean("fs.azure.createRemoteFileSystemDuringInitialization", true);
conf.setBoolean("fs.azure.enable.conditional.create.overwrite", true);
conf.unset("fs.azure.account.hns.enabled." + ACCOUNT_HOST);
conf.unset("fs.azure.account.hns.enabled");

try (FileSystem fs = FileSystem.newInstance(uri, conf)) {
    assertTrue(fs.getFileStatus(new Path("/")).isDirectory());

    Path dir = new Path("/compat");
    Path file = new Path(dir, "file.txt");
    assertTrue(fs.mkdirs(dir));

    write(fs, file, "first");
    write(fs, file, "second");
    assertEquals("second", read(fs, file));

    byte[] xattr = "v8".getBytes(StandardCharsets.UTF_8);
    fs.setXAttr(file, "user.floci_smoke", xattr);
    assertArrayEquals(xattr, fs.getXAttr(file, "user.floci_smoke"));

    FileStatus[] exact = fs.listStatus(file);
    assertEquals(1, exact.length);

    Path renamed = new Path(dir, "renamed.txt");
    assertTrue(fs.rename(file, renamed));

    assertFalse(fs.delete(new Path(dir, "missing.txt"), true));
    assertTrue(fs.delete(dir, true));
}

Valideer de volledige Titan-pipeline op ABFSS

De volledige Titan application-level E2E-gate die hier wordt getoond gebruikt Spark 3.5.2 + Hadoop 3.3.4 + SecureAzureBlobFileSystem over abfss://. Dezelfde DFS-implementatie is ook gevalideerd tegen de hierboven vermelde compatibiliteitsbaselines Spark 4.0.0 / Hadoop 3.4.1 en Spark 4.1.0 / Hadoop 3.4.2.

Titan kruist herhaaldelijk de filesystemboundary tijdens het ontdekken van bronbestanden, lezen van meerdere formaten, schrijven van intermediate datasets, listen van resultaten en committen van Spark-output. De E2E-run verifieert dat die contracten samen werken over de complete pipeline.

Bronformaten end-to-end getest

Negen inputworkflows. Eén ABFSS-backed Titan-pipeline.

Avro CSV CSV.GZ Delimited JSON OCR Parquet XML XLSX
Volledige Titan ABFSS end-to-end-dataflow Alle negen geteste bronformaten komen binnen in de Source-laag en gaan vervolgens door Bronze, Silver, Technical Decoupling, Gold en Semantic. 01 Bron 02 Bronze 03 Silver 04 Technical Decoupling 05 Gold 06 Semantisch
930 / 930floci-az-repositorytests
263 tests · 0 foutenJava-compatibility-suite inclusief echte Hadoop ABFS 3.3.4
9 / 9 workflowsTitan ABFSS E2E via Source → Semantic

Houd de compatibility-claim uitvoerbaar

Het nuttige resultaat is niet één groene run. De repository bevat het Hadoop-compatibiliteitspad zodat een toekomstige wijziging tegen hetzelfde clientcontract kan worden gecontroleerd.

repository-suite
./mvnw test

# final result
Tests run: 930
Failures: 0
Errors: 0
Skipped: 0
Java-compatibility-suite
make test-java-compat

# final result
Tests run: 263
Failures: 0
Errors: 0
Skipped: 2
BUILD SUCCESS

Breng filesystem-failures naar de lokale feedbackloop

Het belangrijkste voordeel is niet dat Azure ineens op een laptop draait. Het voordeel is dat hetzelfde Spark- en Hadoop-filesystempad nu kan worden uitgeoefend voordat een deployment Azure bereikt, waardoor protocol- en integratiefouten veel dichter bij de codewijziging komen die ze veroorzaakte.

Echt clientgedrag lokaal. Spark en Hadoop ABFS blijven in het uitvoeringspad in plaats van te worden vervangen door storage mocks.

Eerdere protocolfeedback. Onjuiste DFS-errors, listinggedrag, metadata-updates en rename-semantiek kunnen lokaal of in CI falen.

Schonere Azure-validatie. Cloudtests kunnen focussen op identity, private networking, servicelimieten en productiespecifiek gedrag.

Het resultaat is een kortere feedbackcyclus zonder te doen alsof een lokale emulator een vervanging is voor Azure zelf.

Kortere feedbackloop

Vang het storagecontract af vóór clouddeployment

Voor
wijzigingscode → build → deploy Azure → draai pipeline → ontdek ABFS-probleem → remote debuggen
Nu
wijzigingscode → draai lokale Titan + Hadoop → vang contractprobleem af

Vragen over lokale ADLS Gen2, Hadoop ABFS en Spark

Praktische antwoorden over protocolkeuzes, testharness en scope van de Floci AZ-bijdrage.

Kan Apache Spark lokaal ADLS Gen2 gebruiken zonder een echt Azure Storage Account?

Ja, mits de emulator de DFS-wire-semantiek reproduceert die Hadoop ABFS verwacht. Het gaat niet alleen om dezelfde URLs accepteren; servicespecifieke errors, ETags, resourcetypen, listinggedrag en path-update-semantiek moeten eveneens overeenkomen met het clientcontract.

Waarom zorgde BlobNotFound versus PathNotFound ervoor dat FileSystem.delete() faalde?

Hadoop ABFS interpreteert de ADLS-servicefoutcode en JSON-body. Een Blob-achtige 404 is semantisch niet gelijk aan een DFS PathNotFound-response. Met de correcte DFS-response wordt het verwijderen van een ontbrekend pad vertaald naar het normale Hadoop-filesystemresultaat: false.

Welke Hadoop-implementaties zijn gevalideerd?

De repository-compatibiliteitssuite gebruikt direct hadoop-common 3.3.4 en hadoop-azure 3.3.4 met de echte org.apache.hadoop.fs.azurebfs.AzureBlobFileSystem-class. Aanvullende compatibiliteitsbaselines dekken Hadoop 3.4.1 met hadoop-azure 3.4.1 en Hadoop 3.4.2 met hadoop-azure 3.4.2 naast Spark 4.0.0 en 4.1.0.

Is abfss:// ook getest?

Ja. De Docker-compatibility harness gebruikt abfs:// over loopback-HTTP zodat de DFS-host netjes behouden blijft. Externe Spark-validatie gebruikte org.apache.hadoop.fs.azurebfs.SecureAzureBlobFileSystem en abfss://-paden.

Waarom staat fs.azure.always.use.https=false ingesteld in de compatibiliteitstest?

In de Hadoop 3.3.4 repository-compatibility-harness staat deze instelling standaard op true, zelfs voor abfs://. Die lokale harness stuurt bewust plaintext loopback-verkeer over poort 80, dus HTTPS wordt alleen voor die test uitgeschakeld. Dit verandert het runtime-routinggedrag van Floci niet.

Implementeert dit volledige ADLS Gen2 POSIX-security?

Nee. Owner-, group-, permission- en ACL-metadata maken een round-trip via het DFS/Hadoop-wireprotocol, maar de emulator probeert niet de volledige Azure POSIX-authorization-engine te reproduceren.

Hoe is de definitieve implementatie gevalideerd?

De definitieve branch doorstond 930 repositorytests en 263 Java-compatibiliteitstests zonder failures. De repositorysuite oefent direct de echte Hadoop 3.3.4 ABFS-client uit, de Titan application-level E2E-gate draait Spark 3.5.2 over abfss:// voor negen sourceworkflows en aanvullende compatibiliteitsbaselines valideren de DFS-implementatie met Spark 4.0.0 / Hadoop 3.4.1 en Spark 4.1.0 / Hadoop 3.4.2.

Waar staat de open-source-implementatie?

De implementatie is ontwikkeld voor het Floci AZ-project en is beschikbaar op de publieke featurebranch die vanuit dit artikel is gelinkt terwijl de upstream-contributie wordt beoordeeld.

Laat lokale dataplatformtests de echte integratiegrens testen

We ontwerpen Spark-, Databricks- en Azure-dataplatforms met herhaalbare infrastructuur en testgrenzen die verder gaan dan alleen applicatiecode.

Een strengere testgrens

Applicatie- en Spark-gedrag
Echte Hadoop-filesystemclient
ADLS Gen2 DFS-wire-semantiek
Reproduceerbare lokale + CI-validatie