fix(oracle): Thick mode never loaded in a build, plus the Linux client recipe (#538) - #575
Merged
Conversation
…538) Thick mode has never worked in a real build. Two packaging defects, both measured on the published 0.13.4 and 0.13.7 images. Turbopack bundled oracledb into the server chunk and rewrote its __dirname to the literal string /ROOT/node_modules/oracledb/lib. initOracleClient() then looked for its native addon under /ROOT and threw NJS-045 before the Instant Client was ever consulted, so every recipe in the docs was unreachable. oracledb now joins serverExternalPackages. Externalizing is not enough on its own. Next's output file tracing copies index.js, lib/ and package.json but not build/Release/*.node, because the addon is reached through a computed require(path). The Dockerfile runner stage and the standalone payload script copy the whole package, the way better-sqlite3, @libredb/libredb and the @duckdb scope already are. The whole package because build/ carries one binary per platform and arch, so naming one would break the ARM64 leg. The published image stays Thin only. No Instant Client, no libaio. Adds tests/unit/packaging-oracledb-thick.test.ts, modelled on the DuckDB guard, and corrects the CLAUDE.md claim that every driver is external in both build configs. It was not true: mssql and ioredis still are not.
…538) mapDatabaseError() recognised only NJS-138. The other refusals a DBA turns into "you must use Thick mode" fell through to the generic retryable ConnectionError, which is the defect #228 fixed for NJS-138: a permanent configuration problem reported as something to retry. NJS-116 (the account has only a 10G password verifier), NJS-533 (the server requires native network encryption or checksumming), NJS-089 and any message saying "not supported by node-oracledb in Thin mode" now map to a non-retryable DatabaseConfigError naming ORACLE_CLIENT_LIB_DIR. NJS-116 also names the fix that needs no Instant Client: a password reset writes a 12C verifier. The check runs before the generic authentication branch on purpose, since NJS-116's own text contains the word password. The constructor's Instant Client failure said one thing for every error, "verify the path", and that advice is wrong for both failures that actually happen. NJS-045 means this build has no Thick-mode addon, which is a packaging defect and not a path; DPI-1047 means the libraries could not load from a directory that is usually correct but not on the loader path. describeOracleClientLoadFailure() says which one it is.
Section 4.4 told operators to set ORACLE_CLIENT_LIB_DIR and build a derived
image. Neither step was sufficient. On Linux initOracleClient({ libDir })
cannot load Instant Client at all: libclntsh.so.19.1 has no RUNPATH, so its
own siblings are not found and the driver fails with DPI-1047. node-oracledb
documents this and says never to rely on libDir there. The directory has to
be on the system library search path as well, through /etc/ld.so.conf.d plus
ldconfig or through LD_LIBRARY_PATH set before Node starts. Debian 13 adds a
second step: it ships libaio.so.1t64 only, and the client asks for
libaio.so.1.
The derived image recipe now does all of it. The mount alternative says
LD_LIBRARY_PATH has to be set on the container. Both were verified with
Instant Client 19.28 against Oracle Free 23ai: client_driver reports
node-oracledb 6.10.0 thk.
Also adds the 12.2 case the reporter hit. Thin mode covers 12.1 and later as
a version, but a server can still force Thick mode, and the doc now lists
those reasons with their driver codes. Section 11 gains rows for NJS-045,
DPI-1047, NJS-116, NJS-533 and NJS-089, and section 14 states plainly that
the published images could not enter Thick mode at all before this fix.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Review finding on #575. The "a 12.1+ server can still force Thick mode" table attributed three cases to NJS-089 that node-oracledb 6.10.0 never reports that way. Corrected against the driver's own source. Kerberos, RADIUS and OS authentication have no driver code at all. Thin mode implements externalAuth for token-based authentication, but nothing for these, so the server refuses the logon and what arrives is an ORA logon error. LDAP naming is not a Thin-mode refusal either: there is no LDAP code under lib/thin, so an ldap:// identifier fails during connect-string resolution, typically NJS-516. Both are Thick mode only, and neither can be mapped, because nothing in their text identifies the cause. An sso-only wallet is NJS-529, not NJS-089. Its text says nothing about Thin mode, so the generic substring never caught it. It is now mapped by code, with a message naming both ways out: convert the wallet to ewallet.pem, which avoids Thick mode entirely, or use Thick mode and keep the sso wallet. NJS-089 stays mapped but is described honestly. In Thin mode it covers client-side features the driver has not implemented, heterogeneous pooling, some database object types and Advanced Queuing among them, none of which a server can demand at connect time. Second finding, same section: the derived-image recipe did not build. The base image has no ca-certificates and --no-install-recommends does not pull it in with curl, so the Instant Client download died with curl (77). Added to the apt-get line and pinned by the packaging guard.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Thick mode has never worked in a build, for four independent reasons, all measured on the published 0.13.4 and 0.13.7 images. Turbopack bundled
oracledbinto the server chunk and rewrote its__dirnameto the literal/ROOT/node_modules/oracledb/lib, soinitOracleClient()threw NJS-045 before the Instant Client was consulted and every recipe in the docs was unreachable. Even externalized, Next's file tracing copies the driver's JavaScript but notbuild/Release/*.node, since the addon is reached through a computedrequire(path). On LinuxinitOracleClient({ libDir })cannot load Instant Client on its own either:libclntsh.so.19.1has no RUNPATH, so it fails with DPI-1047 unless the directory is also on the system library search path. And on Debian 13 the client asks forlibaio.so.1while the distribution ships onlylibaio.so.1t64. Separately,mapDatabaseErrorrecognised only NJS-138, so the other Thin-mode refusals a DBA turns into "you must use Thick mode" arrived as a retryable ConnectionError, which is the defect #228 fixed for NJS-138.Changed:
next.config.tsaddsoracledbtoserverExternalPackages; theDockerfilerunner stage andscripts/build-standalone-payload.shcopy the whole package with a target-addon probe, arch-agnostically, so the ARM64 leg keeps working;src/lib/db/errors.tsgainsdescribeOracleThinModeRefusal(NJS-116, NJS-533, NJS-089 and the generic Thin-mode substring join NJS-138 as non-retryableDatabaseConfigError, checked before the generic password branch because NJS-116's own text contains "password") anddescribeOracleClientLoadFailure(NJS-045 reads as a packaging defect naming platform and arch, DPI-1047 points at the loader path,libaio.so.1and section 4.4);src/lib/db/providers/sql/oracle.tsuses it;docs/providers/oracle.mdrewrites section 4.4 with a recipe that works, adds the 12.2 case and its driver codes, and extends sections 11 and 14;.env.exampleandCLAUDE.mdare corrected. New guardtests/unit/packaging-oracledb-thick.test.ts, plus tests intests/unit/db/errors.test.tsand the Thick-mode block oftests/integration/db/oracle-provider.test.ts. The published image stays Thin only, no Instant Client and no libaio.Verified locally:
format,lint,typecheck,knip,chart:check,channels:showcase:check,readme:check,security:check,test:ci(all 34 groups),test:coveragepluscoverage:checkat 45569/45569 lines (100.00%),build,build:libandattwall pass. AfterDOCKER_BUILD=true bun run build,grep -l "/ROOT/node_modules/oracledb" .next/server/chunks/*.js | wc -lis 0 (was 1), and.next/standalone/node_modules/oracledb/package.jsonexists whilebuild/Releaseis absent from the traced tree, which is what the explicit COPY is for. The live Thick-mode run against a real Instant Client is not part of this PR.Fixes #538