Skip to content

Commit

Permalink
Eliminate core/vendor/site directory configs
Browse files Browse the repository at this point in the history
Just go back to a default, and let the search path do all the work. The
`pg_config` option is `--extdir` and the special string to include in
`extension_search_path` is named `$extdir`.

Also:

*   Add context around Peter E's new patch
*   Mention the CNPG desire for read-only extension images in the
    "Challenges" section (renamed from "The Problem")
*   Remove `LINKBINS` and instead allow individual install locations to
    be customized by passing `datadir`, `bindir`, etc. to `make
    install`.
*   Fix the treatment of `MODULE_PATHNAME` to ignore the `$libdir`
    prefix. Restore the corresponding `module_pathname` control file
    variable with the preference to omit `$libdir`.
  • Loading branch information
theory committed Nov 18, 2024
1 parent b776520 commit 2c6f9e1
Showing 1 changed file with 118 additions and 82 deletions.
200 changes: 118 additions & 82 deletions content/post/postgres/rfc-extension-packaging-lookup.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ file organization and a search path [GUC] for finding extensions. The
inspired this proposal. These threads cover quite a lot of territory, so I
thought it would be useful to pull together a more unified, public proposal.

## The Problem
## Challenges

A number of challenges face extension users in various configurations, thanks
to extension file organization in the Postgres core. The common thread among
Expand All @@ -29,9 +29,9 @@ itself.

On Debian systems, the user account that creates extension packages lacks
permission to add files to Postgres install. But testing extensions requires
installing the extension files where Postgres can find them. Furthermore,
extensions should ideally be built against a clean Postgres install; adding an
extension in order to run `make installcheck` would pollute it.
installing extension files where Postgres can find them. Moreover, extensions
ideally build against a clean Postgres install; adding an extension in order
to run `make installcheck` would pollute it.

[Christoph's patch][destdir] solves these problems by adding a second lookup
path for extensions and dynamic modules, so that Postgres can load them
Expand All @@ -44,6 +44,19 @@ the patch will search in `/tmp/build/myext/opt/share`. This approach works for
the packaging use case, which explicitly uses full paths with a prefix, but
would be weird for other use cases.

Peter Eisentraut proposed a [new patch] with a new `GUC`,
`extension_control_path`, that provides a more typical search path pattern to
find extension control files, but doesn't account for shared modules that ship
with an extension, requiring that they still live in the
[`dynamic_library_path`]. Installing into custom directories requires the
`datadir` and `pkglibdir` variables:

``` sh
make install datadir=/else/where/share pkglibdir=/else/where/lib
```

This pattern can probably be simplified.

### OCI Immutability

[OCI] (née Docker) images are immutable, while a container running an image
Expand All @@ -62,25 +75,39 @@ deployment configuration complexity. It would be preferable to have all the
files for an extension in one place, rather than scattered across multiple
persistent volumes.

Peter Eisentraut's [new patch] addresses much of this issue by adding a search
path for extension control files and related data/share files (generally SQL
files). One can create a single volume with a `lib` directory for shared
modules and `share/extension` directory for control and data/share files.

However, an additional wrinkle is the ambition from the [CloutNativePg][CNPG]
([CNPG]) community to eliminate the need for a persistent volume, and rely
instead on mounting images that each contain all the files for a single
extension as their own volumes (perhaps using [Kubernetes image volume
feature], (currently in alpha)).

### Postgres.app Immutability

The macOS [Postgres.app] supports extensions. But installing one into
`SHAREDIR/extensions` changes the contents of the Postgres.app bundle,
breaking Apple-required signature validation. The OS will no longer be able to
validate that the app is legit and refuse to start it.

Peter Eisentraut's [new patch] addresses this issue as well, with all the same
caveats as for the [packager testing](#packager-testing) challenges.

## Solution

To address these issues, this RFC proposes to change file organization and
lookup patterns for PostgreSQL extensions.
To further address these issues, this RFC proposes to change file organization
and lookup patterns for PostgreSQL extensions.

### Extension Directories

First, when an extension is installed, all of its files will live in a single
directory named for the extension. The contents include:
First, when an extension is installed, by default all of its files will live
in a single directory named for the extension. The contents include:

* The Control file that describes extension
* Subdirectories for SQL, shared modules, docs, binaries
* Subdirectories for SQL, shared modules, docs, binaries, etc.

Subdirectories roughly correspond to the `pg_config --*dir` options:

Expand All @@ -96,42 +123,33 @@ This layout reduces the cognitive overhead for understanding what files belong
to what extension. Want to know what's included in the `widget` extension?
Everything is in the `widget` directory.

### Configuration Parameters
### Configuration Parameter

Add three new `pg_config` values:
Add a new `pg_config` value:

```
--extdir-core show location of core extensions
--extdir-vendor show location of vendor extensions
--extdir-site show location of site extensions
--extdir show location of extensions
```

All core-distributed extensions, including contrib extensions, will be
installed in the `--extdir-core` directory, each in its own directory as
described [above](#extension-directories). Its contents would look something
like this:
This is the directory into which extensions will by default be installed. Its
default value would be `$(pg_config --sharedir)/extension`, but could be set
at compile time like other configuration parameters. Its contents consist of
subdirectories that each contain an extension, as described in [Extension
Directories](#extension-directories). With a few extensions installed, it
would look something like:

``` console
ls -1 "$(pg_config --extdir-core)"
ls -1 "$(pg_config --extdir)"
auto_explain
bloom
intagg
isn
pair
plperl
plpgsql
plv8
xml2
```

OS vendor and packaging systems would install non-core extensions into
`--extdir-vendor`, while user-installed extensions will be put into
`--extdir-site`.

Like all other `pg_config` options, these values can be customized at compile
time. By default, they'll point to different directories, so that core,
vendor, and end-user extensions are always kept separate. Perhaps default to:

```
PG_INSTALL_ROOT/extensions/(core|site|vendor)
semver
vector
```

### Extension Path
Expand All @@ -141,13 +159,12 @@ Add an extension lookup path GUC akin to [`dynamic_library_path`], called
extensions and their files. The default value for this GUC will be:

``` ini
extension_path = '$extdir_site,$extdir_vendor,$extdir_core'
extension_path = '$extdir'
```

The special values `$extdir_site`, `$extdir_vendor`, and `$extdir_core`
correspond to the `pg_config` options `--extdir-site`, `--extdir-vendor`, and
`--extdir-core`, respectively, and function exactly as `$libdir` does for the
`dynamic_library_path` GUC, substituting the appropriate values.
The special string `$extdir` corresponds to the `pg_config` option of the same
name, and function exactly as `$libdir` does for the `dynamic_library_path`
GUC, substituting the appropriate values.

### Lookup Execution

Expand All @@ -170,10 +187,9 @@ appropriate subdirectories, e.g.:
### PGXS

Update the extension installation behavior of [PGXS] to install extension
files into the new locations. A new variable, `EXTDIR`, will define the
directory into which to install an extension, and will default to
`--extdir-site`. It can be set to the values `$extdir_site`, `$extdir_vendor`,
or `$extdir_core`, or to any literal path.
files into the new locations by default. A new variable, `EXTDIR`, will define
the directory into which to install an extension, and default to
`$(pg_config --extdir)`. It can be set to any literal path.

The `$EXTENSION` variable will be changed to allow only one extension name. If
it's set, the installation behavior will be changed for the following
Expand All @@ -190,37 +206,40 @@ variables:
* `PROGRAM`, `SCRIPTS` and `SCRIPTS_built`: Installed into
`$EXTDIR/$EXTENSION/bin`

Another new variable, `LINKBINS`, will default to true and symlink
`$EXTDIR/$EXTENSION/bin` files in `pg_config --bindir`. Installers can set it
to false to skip the symlinking, e.g., for immutable Postgres installs.
Each of these locations can still be overridden by setting one of the
(currently undocumented) [installation location options].

> [!NOTE] External projects that install extensions without using PGXS, like
> [pgrx], must also be updated to either follow the same pattern or to
> delegate installation to [PGXS].
### MODULE_PATHNAME

Update the installer to replace `MODULE_PATHNAME` in SQL scripts with the new
install path for shared modules, `$EXTDIR/$EXTENSION/lib`.
Change the behavior of the `MODULE_PATHNAME` string in extension SQL files to
ignore `$libdir` and instead assume named modules are found in the `lib`
subdirectory of the extension's directory.

### Control File

The `directory` and `module_pathname` control file variables will be
deprecated and ignored.
The `directory` control file variable will be deprecated and ignored.

The `module_pathname` variable should only name a shared module in the `lib`
subdirectory of an extension directory. Any existing use of a `$libdir` prefix
will be stripped out and ignored.

## Use Cases

Here’s how the proposed file layout and `extension_path` GUC address the [use
cases](#the-problem) that inspired this RFC.
cases](#challenges) that inspired this RFC.

### Packager Testing

A packager who wants to run tests without modifying a PostgreSQL install would
follow these steps:

* Set the `extension_path` GUC to search the site extension directory under
* Set the `extension_path` GUC to search a directory under
the packaging install. Something like
`$RPM_BUILD_ROOT/$(pg_config --extdir-vendor)`
`$RPM_BUILD_ROOT/$(pg_config --extdir)`
* Install the extension into that directory:
`make install EXTDIR=$RPM_BUILD_ROOT`
* Run `make installcheck`
Expand All @@ -234,35 +253,45 @@ The Postgres installation will not have been modified; only the
To allow extensions to be added to a OCI container and to persist beyond its
lifetime, one or more [volumes] could be used. A couple of options:

* Mount the `--extdir-site` and/or `--extdir-vendor` directories as
persistent volumes (or one volume and a subdirectory for each). Then any
extensions installed into them will persist. Files for any one extension
will live on a single volume. If a new container spins up, as long as it
uses the same persistent volume(s), can access the same extensions.

* Create separate images for each extension, and then "install" them by
using the [Kubernetes image volume feature] (currently in alpha) to mount
them as read-only volumes in the appropriate subdirectory of
`--extdir-site` or `--extdir-vendor`. Thereafter, any new containers would
simply have to mount all the same extension image volumes persistently
provide the same extensions to all containers.
* Mount the a persistent volume for extensions and append the path to that
directory to the `extension_search_path` GUC. Then Postgres can find any
extensions installed there, and they will persist. Files for all
extensions extension will live on a single volume. Or, to meet a desire to
keep some extensions separate (e.g., to keep open-source and
company-internal extensions separate), two or more persistent volumes
could be mounted, as long as they're all included in
`extension_search_path` and users take care to install extensions in the
proper locations.

* To meet the [CNPG] ambition, create separate images for each extension,
and then "install" them by using the [Kubernetes image volume feature]
(currently in alpha) to mount them as read-only volumes in the appropriate
subdirectory of `--extdir`. Thereafter, any new containers would simply
have to mount all the same extension image volumes persistently provide
the same extensions to all containers.

### Postgres.app

To allow extension installation without invalidating the Postgres.app bundle's
signature, the app could be compiled to have `--extdir-site` and
`--extdir-vendor` point to subdirectories well-known directories outside the
app bundle, such as `/Library/Application Support/Postgres`.
To allow extension installation without invalidating the Postgres.app bundle
signature, the default configuration would specify, in addition to `$extdir`,
a well-known directories outside the app bundle, such as
`/Library/Application Support/Postgres`. Users wishing to install new
extensions would then need to point the `EXTDIR` parameter to that location,
e.g.,

```console
$ make install EXTDIR="/Library/Application Support/Postgres"`
```

Any vendor or user extensions installed would be placed in those
subdirectories, without changing the contents of the Postgres.app bundle.
Postgres.app would know to find extensions in that location thanks to the
inclusion of `$extdir_site` in the `extension_path` GUC.
Or the app could get even trickier, setting the `--extdir` value to that
location so that users don't need to use `EXTDIR`. As long as
`extension_search_path` includes both the bundle's extension directory and
this external directory, Postgres will be able to find and load extensions.

## Extension Directory Examples

A core extension, like [citext], would live in
`$(pg_config --extdir-core)/citext`, and have a structure such as:
A core extension, like [citext], would live in `$(pg_config --extdir)/citext`,
and have a structure such as:

``` tree
citext
Expand Down Expand Up @@ -364,8 +393,8 @@ example:
shared_preload_libraries = '$extension_path:pg_partman_bgw'
```

But this overloads the semantics of `shared_preload_libraries` and friends
rather heavily, not to mention the [`LOAD`] command.
But this overloads the semantics of `shared_preload_libraries` and the code
that processes it rather heavily, not to mention the [`LOAD`] command.

Therefore, as a follow up to the [solution](#solution) proposed above, this
RFC proposes additional changes to PostgreSQL.
Expand Down Expand Up @@ -413,7 +442,7 @@ upgrade.
For a future change, consider modifying `CREATE EXTENSION` to support shared
module-only extensions. This would allow extensions with no SQL component,
such as `auto_explain` to be handled like any other extension; it would live
in `--extdir-core` with a directory structure like this:
in `--extdir` with a directory structure like this:

``` tree
auto_explain
Expand All @@ -426,18 +455,23 @@ auto_explain
└── auto_explain.index.bc
```

Note the `auto_explain.control` file. We would need to add a variable to
indicate that the extension includes no SQL files, so `CREATE EXTENSION` and
related commands wouldn't try to find them.
Note the `auto_explain.control` file. We would need to add a new control file
parameter to indicate that the extension includes no SQL files, so
`CREATE EXTENSION` and related commands wouldn't try to find them.

With these changes, extensions could become the primary, recommended interface
for extending PostgreSQL. Perhaps the `LOAD` command could be deprecated, and
the `*_preload_libraries` GUCs along with it.

## Compatibility Issues

* The `directory` and `module_pathname` control file variables and the
`MODULEDIR` PGXS variable would be deprecated and ignored.
* The behavior of the `MODULE_PATHNAME` string in SQL files would be changed
to ignore a `$libdir` prefix and instead be read as a shared library name
to find in `lib` subdirectory of the extension directory.
* The `module_pathname` control file variable would prefer the name of a
shared module, as `$libdir` would be ignored.
* The `directory` control file variable and the `MODULEDIR` PGXS variable
would be deprecated and ignored.
* `*_preload_libraries` would no longer be used to find extension modules
without full paths. Administrators would have to remove module names from
these GUCs and add the relevant extension names to the
Expand Down Expand Up @@ -470,6 +504,7 @@ This RFC does not include or attempt to address the following issues:
[discussion]: https://postgr.es/m/[email protected]
[Christoph Berg]: https://www.df7cb.de
[destdir]: https://commitfest.postgresql.org/50/4913/
[new patch]: https://postgr.es/m/[email protected]
[OCI]: https://opencontainers.org "Open Container Initiative"
[symlink magic]: https://speakerdeck.com/ongres/postgres-extensions-in-kubernetes?slide=14
"Postgres Extensions in Kubernetes: StackGres"
Expand All @@ -495,4 +530,5 @@ This RFC does not include or attempt to address the following issues:
"PostgreSQL Docs: LOAD"
[auto_explain]: https://www.postgresql.org/docs/current/auto-explain.html
"PostgreSQL Docs: auto_explain— log execution plans of slow queries"

[installation location options]: https://github.com/postgres/postgres/blob/master/src/Makefile.global.in#L82-L90
[CNPG]: https://cloudnative-pg.io "CloudNativePG — PostgreSQL Operator for Kubernetes"

0 comments on commit 2c6f9e1

Please sign in to comment.