MisterRaindrop opened a new pull request, #2044:
URL: https://github.com/apache/cloudberry/pull/2044

   ### What does this PR do?
   
   `contrib/datalake_fdw` reads and writes Parquet (#1951), but only on a local
   file system. This adds the storage layer underneath: an `s3` backend over the
   AWS SDK for C++, and a published contract so a backend this extension does 
not
   ship can be added from outside it.
   
   Closes #2009.
   
   Decisions worth a look:
   
   - **A backend answers one question**: given a location and its options, which
     `arrow::fs::FileSystem` reads and writes it. Opening files, listing,
     classifying errors, accounting for memory and translating a URI into a 
native
     path all stay on this extension's side of the boundary, so a backend is a
     mount function and nothing else.
   - **`s3` is our own `arrow::fs::FileSystem` over the AWS SDK**, not Arrow's.
     No RPM of Arrow is built with S3 support -- EPEL's and the Arrow project's
     own both set `use_s3 0` -- so depending on it would mean asking every user 
to
     build Arrow. The SDK is a `BUILD_ONLY="s3;sts"` static build that takes 
well
     under a minute, links statically, and adds libcurl, OpenSSL and zlib to
     `NEEDED` and nothing else.
   - **Timeouts and retries are set by this module**, not inherited: connect 5 
s,
     request 300 s, three retries, so a black-holed endpoint answers in about 24
     seconds rather than hanging a session, on every Arrow version.
   - **A writer only ever removes what it created.** The local backend opens 
with
     `O_EXCL` and unlinks that path alone; the S3 one keys cleanup on whether an
     upload is still live rather than on whether the stream is closed, so a
     `Close()` that fails at the last part still aborts the upload instead of
     leaving parts to be billed for.
   - **Credentials are scrubbed at one place.** Every value under a key 
containing
     `secret`, `token` or `password` is remembered per process, and 
`dl_error_set`
     -- the single exit every `DlErrCode` error goes through -- removes it from
     the message. An unexpected C++ exception and a third-party backend's own
     status are covered by construction rather than by remembering to call
     something. A `base_path` that carries a password in its userinfo is 
redacted
     the same way, in the message as well as the detail.
   - **Which schemes a volume may name comes from the registry**, not a list in
     the parser: a backend that registered `mine` can be written into a
     `base_path` without this extension being changed.
   - **The plug-in contract is versioned three ways**: an ABI version, an Arrow
     version, and a fingerprint over the compiler major and 
`_GLIBCXX_USE_CXX11_ABI`.
     Registration happens during preload and in any order -- the registering 
side
     pulls in `datalake_fdw` through `load_external_function`, inside a `PG_TRY`
     so an `ereport` cannot unwind through the plug-in's C++ frames.
   - **Every Arrow allocation still goes through the tracked pool**, including a
     backend's: `mount` is handed a host struct carrying it, and the five
     installed headers give a backend no way to reach Arrow's default pool by
     accident.
   
   ### Type of Change
   - [ ] Bug fix (non-breaking change)
   - [x] New feature (non-breaking change)
   - [ ] Breaking change (fix or feature with breaking changes)
   - [ ] Documentation update
   
   ### Test Plan
   
   One body of storage behaviour runs against every backend rather than once per
   backend: `storage_conformance` parameterises a shared SQL file over a URI
   prefix and a volume, and runs it for `file://`, for `s3://` and for the test
   backend.
   
   - [x] Integration tests added -- three new categories. `storage_local` covers
         the facade and the registry: four kinds of bad registration, a 
duplicate
         scheme, path escapes, a name already in use, a missing file.
         `storage_s3` covers the backend against a real service: round trip,
         9 MB through multipart, `ListObjectsV2` paging, `HeadObject`,
         `DeleteObject`, deleting twice, a missing key, a missing bucket, a 
wrong
         secret (and that the message does not contain it), path style with an
         endpoint override, and a black-holed endpoint inside 30 s.
         `storage_conformance` adds the type round trip, field-id projection, a
         row-group range, listing, a rejected overwrite, a write that fails
         halfway leaving nothing behind, a prefix nothing was written under and
         one whose objects were just deleted (both not-found, which is the 
point:
         object storage and a filesystem disagree and the facade decides), and
         volume resolution: no `USAGE`, a `PUBLIC` mapping, a URI outside its
         volume, a volume that does not exist, and a rejected `base_path` that
         must not echo its own password.
   - [x] Passed `make installcheck` -- every category, on a three-segment 
cluster
         with the module preloaded, against MinIO and against SeaweedFS, on
         Arrow 9.0.0 and Arrow 17.0.0.
   - [ ] Unit tests added/updated
   - [ ] Passed `make -C src/test installcheck-cbdb-parallel` (not run)
   
   Beyond the suite:
   
   - **The plug-in contract was used from outside.** A `.cc` file that includes
     only the five installed headers and PostgreSQL's server headers compiles 
to a
     `.so` that registers a backend; put *before* `datalake_fdw` in
     `shared_preload_libraries` the cluster starts and the scheme works, and
     built with a stale `abi_fingerprint` the registration is refused with both
     values in the message.
   - **Memory was measured, S3 against local.** Writing and reading the same
     Parquet file through `s3://` costs about 15 MiB of resident memory more 
than
     through `file://` -- and that difference stays flat as the object grows
     (7.9 MiB for a 2 MB file, 14.8 MiB for a 149 MB one), so neither the 
response
     body nor the multipart buffer accumulates.
   - **The credential chain was exercised.** With no user mapping the same
     statement fails; with `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in 
the
     postmaster's environment it succeeds.
   - **Nothing new is exported.** `nm -D` lists the PostgreSQL entry points, one
     registration function and the test extension's UDFs; no Arrow and no AWS
     symbols, on all three build variants.
   
   ### Impact
   
   **Dependencies:** an optional build dependency on the AWS SDK for C++. 
Without
   it the extension builds as before and says so, and opening an `s3://` 
location
   reports that it was built without it; naming a prefix that has no SDK in it 
is
   an error rather than a silent fallback. The module is off by default and not 
in
   the RPM, so packaging is unchanged.
   
   CI builds the SDK from source once per distribution and architecture and 
caches
   it, starts SeaweedFS for the s3 cases, and now takes Arrow from the Arrow
   project's own repository on Rocky 9 and 10 as well, pinned -- 17.0.0 and
   21.0.0, so a version change in EPEL cannot arrive without a commit.
   
   What that costs the `ic-datalake-fdw` job, measured on all three legs: 
building
   the SDK takes 175-228 s on a cache miss and the cache restores in 1 s (it is
   4 MB); SeaweedFS is up 6 s after the step starts; the s3 cases add about 35 
s,
   of which 22 s is one deliberate connect timeout.
   
   **User-facing changes:** a volume's `base_path` now accepts `file://` as well
   as `s3://`, and the scheme it rejects is named against what is registered
   rather than against a fixed list. Three `iceberg_am_reject` DETAIL lines and
   two ERROR lines change wording, the latter because they used to quote back a
   URI that can carry a password.
   
   ### Checklist
   - [x] Followed [contribution 
guide](https://cloudberry.apache.org/contribute/code)
   - [x] Added/updated documentation
   - [x] Reviewed code for security implications
   - [ ] This PR contains AI-assisted code generation
   - [ ] Requested review from [cloudberry 
committers](https://github.com/orgs/apache/teams/cloudberry-committers)
   
   `contrib/datalake_fdw/README.md` documents volumes, where credentials come
   from, building with S3 support, and writing a backend -- the example in it is
   compiled against the installed headers rather than written out by hand. The
   test extension's new functions name a path on the server's file system and 
are
   revoked from `PUBLIC` like the two that were already there.
   
   ### Additional Context
   
   Known limits, deliberately rather than by oversight:
   
   - **A `file://` volume must be the same directory on every host.** Nothing
     checks it; the README says so.
   - **Between checking that a name is free and creating it, another writer can
     take it.** Iceberg's file names are unique by construction, so it does not
     arise there.
   - **An upload abandoned by a crashed backend leaves its parts behind.** A
     bucket lifecycle rule that expires incomplete multipart uploads is the 
usual
     answer; the README says so.
   - **About 15 MiB per backend process is outside `gp_vmem_protect_limit`** --
     the SDK client, its connection and one part buffer are allocated by the SDK
     rather than through the tracked pool.
   - **The fingerprint catches the mismatches that occur in practice**, not 
every
     possible one.
   - **No HDFS.** The scheme is gone from the parser rather than half-supported;
     it returns as a backend when someone needs it.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to