Neo4jCompositeDatabase¶
A composite database is a single query endpoint over several constituent databases. It stores no data of its own — no topology, no store, no indexes or constraints, no seeding. Its content is entirely the constituent aliases declared in its spec.
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jCompositeDatabase
metadata:
name: cineasts
namespace: neo4j
spec:
clusterRef: prod-cluster
constituents:
- name: latest
targetDatabase: movies-latest
- name: upcoming
targetDatabase: movies-upcoming
Queried from a session on the composite — a constituent is not reachable
from any other database's session (42N04), and connecting to the constituent
by its qualified name does not work either (22N51), despite what the first
error suggests:
Why this is a separate Kind¶
Neo4jDatabase is almost entirely topology, options, storage and seeding —
every one of which is meaningless on a composite, and rejected by the server.
A composite: true flag would leave most of that spec silently inert.
Spec¶
| Field | Type | Description |
|---|---|---|
clusterRef |
string |
Required. The Neo4jEnterpriseCluster or Neo4jEnterpriseStandalone in the same namespace that hosts this composite and its constituents. |
name |
string |
The composite database name in Neo4j. Defaults to metadata.name. 3–63 characters matching ^[a-zA-Z][a-zA-Z0-9\-]*$ (letters, digits and dashes, starting with a letter) — the CRD schema refuses anything else at apply time. Underscores are rejected by the server, and dots are rejected here although Neo4j allows them in a database name: <composite>.<constituent> is how constituents are addressed, so a dotted composite name could never have any. system is reserved (the inline validator refuses it at reconcile; a schema pattern cannot express it). When name is empty and metadata.name is used instead, the schema pattern does not apply to it, but the inline validator still refuses dots and underscores. |
constituents |
[]CompositeConstituent |
Required, at least one. The databases this composite exposes. Each becomes an alias <composite>.<name>. |
enforceConstituents |
bool |
Default true. Makes constituents authoritative: a constituent of this composite that is absent from spec is dropped. Set false to leave out-of-band constituents alone, in which case this CR only ever adds. Same posture as Neo4jRole.enforcePrivileges. An ordinary alias that merely looks like a constituent (<composite>.<x> with no composite behind it — Neo4j's composite column is NULL) is never dropped: it is not a constituent, and it is reported in status.message instead. |
defaultCypherLanguage |
string |
"5" or "25". The DEFAULT LANGUAGE CYPHER clause exists only on CalVer — it does not parse on the 5.26 LTS — so the value is checked against the target's image, the same way Neo4jDatabase.spec.defaultCypherLanguage is. On the LTS "25" is rejected by validation (spec.defaultCypherLanguage) rather than letting the server emit a raw syntax error, and "5" is accepted with a warning but has no effect: every LTS database already runs Cypher 5, and the operator leaves the clause out of CREATE COMPOSITE DATABASE and ALTER DATABASE. On CalVer both values are applied. This is the only property of a composite that can be altered after creation. |
wait |
bool |
Default true. Blocks creation until the composite is available, via the Cypher WAIT clause. |
deletionPolicy |
string |
Delete (default) or Retain. See Deletion. |
CompositeConstituent¶
| Field | Type | Description |
|---|---|---|
name |
string |
Required. The constituent's name within the composite. The resulting alias is <composite>.<name>, which is also how queries address it. 1–63 characters, ^[a-zA-Z][a-zA-Z0-9\-]*$ (no dots or underscores — it is already namespaced). Must be unique within the composite. |
targetDatabase |
string |
Required. 1–63 characters. The database the constituent resolves to — in this DBMS when remote is unset, or on the remote DBMS when it is set. A local target need not exist yet: the controller skips that constituent and retries. A remote target is never checked, because the operator cannot see the other DBMS. May not be system, nor the composite itself. |
remote |
RemoteConstituent |
Points the constituent at a database in another Neo4j DBMS. Omit for a local constituent. |
RemoteConstituent¶
| Field | Type | Description |
|---|---|---|
url |
string |
Required. The remote DBMS's Bolt endpoint, e.g. neo4j+s://other.example.com:7687. Must be neo4j+s:// or neo4j+ssc:// (self-signed certificate): Neo4j refuses every other scheme for a remote alias — neo4j://, bolt:// and even bolt+s:// — on the 5.26 LTS and CalVer alike, so the CRD refuses them at apply time. |
oidcCredentialForwarding |
bool |
Forwards the querying user's own OIDC token instead of storing any credential. Requires Cypher 25, so CalVer only. Mutually exclusive with credentialsSecretRef; exactly one of the two must be set — a remote constituent with neither is rejected. |
credentialsSecretRef |
string |
A Secret in this namespace with username and password keys. Requires spec.remoteAliasKeystore on the deployment. Mutually exclusive with oidcCredentialForwarding; exactly one of the two must be set. |
driverSettings |
map[string]string |
Passed through to the alias's DRIVER clause (e.g. connection_timeout). Emitted verbatim as Cypher map values. Keys must match [a-zA-Z][a-zA-Z0-9_]* (they are Cypher map keys and cannot be parameterised). |
Choosing a mode¶
Prefer oidcCredentialForwarding where both DBMSs trust the same identity
provider. No credential is stored, so there is nothing to leak, rotate or
encrypt — and it needs no keystore at all.
credentialsSecretRef stores a username and password on the alias. Neo4j
encrypts them in the system database, which is why it requires
spec.remoteAliasKeystore; without one the server refuses alias creation
outright. The operator passes the password as a Cypher parameter, never
interpolated into the statement text, so it does not reach the query log.
SHOW ALIASES never returns a password on any alias.
A composite with a credentialsSecretRef constituent on a deployment that has
no keystore is rejected by the operator's inline validation before any Cypher
runs (the CR reports Failed / Ready=False, reason ValidationFailed), naming
the field to set — rather than failing later inside the server with an internal
50N09/50N00 error that mentions neither the CR nor the constituent. There is
no admission webhook, so kubectl apply itself succeeds.
Ordering: the composite always comes first¶
Neo4j accepts CREATE ALIAS cineasts.latest even when no composite
cineasts exists. The result is an ordinary alias whose name merely contains
a dot — listed by SHOW ALIASES, and even queryable. But the composite can
then never be created:
42N87: The database or alias name `cineasts` conflicts with the name
`cineasts.latest` of an existing database or alias.
This CR owns the composite and its constituents together, which is what makes
the ordering guaranteed rather than hoped for. If a stray dotted alias already
occupies the namespace, the CR reports Failed with reason
CompositeDatabaseNameBlocked and names the alias to drop — the server's own
error never mentions ordering.
Deletion¶
deletionPolicy: Delete issues DROP COMPOSITE DATABASE <name> CASCADE
ALIASES. CASCADE ALIASES is not optional: a plain drop is refused while
constituents exist. It removes the constituent aliases only — the databases
they target are untouched.
deletionPolicy: Retain leaves everything in Neo4j and releases the finalizer.
If the referenced deployment is gone or not Ready at deletion time, the
finalizer is released anyway rather than wedging the CR in Terminating for a
database that may no longer exist.
Privileges do not go on the composite¶
This is the most common mistake, and Neo4j does not warn about it:
Graph privileges belong on the constituent target databases. A user reading
through a composite needs ACCESS on the composite and ACCESS plus the
graph privileges on each constituent's target. A Neo4jRole whose privileges
name a composite reports PrivilegesResolve=False.
Status¶
| Field | Type | Description |
|---|---|---|
phase |
string |
Pending, Creating, Ready, Failed. |
message |
string |
Human-readable detail for the current phase. |
observedConstituents |
[]string |
The constituent list read back from the server, fully qualified (cineasts.latest). This is what the DBMS actually exposes, which legitimately differs from spec when enforceConstituents is false. |
observedGeneration |
int64 |
The metadata.generation this status reflects. |
conditions |
[]Condition |
See below. |
Conditions¶
| Type | Reasons | Meaning |
|---|---|---|
Ready |
CompositeDatabaseReady, CompositeDatabaseFailed, CompositeDatabaseNameBlocked, ClusterNotFound (phase Pending), ClusterNotReady (phase Pending), Creating (phase Creating), ConnectionFailed, ValidationFailed |
True when the composite exists and its constituents match spec. |
ClusterNotReady |
ClusterNotReady, ClusterReady |
Mirrors the readiness of the referenced deployment. |
Limitations¶
- Remote constituents need setup. OIDC credential forwarding requires Cypher 25 (CalVer only). Stored native credentials require
spec.remoteAliasKeystoreon the deployment — without it Neo4j refuses to create the alias. - No
OPTIONS. Composites have none; the operator never emits the clause. - No topology. A composite has no store to place.
- Composites cannot be nested, and cannot hold data themselves.