Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Cockroachdb Bundle Laravel Package

fwolfsjaeger/doctrine-cockroachdb-bundle

Symfony bundle that adds CockroachDB support to Doctrine, providing DBAL configuration, platform integration, and service wiring so your Symfony app can use CockroachDB as a Doctrine-managed database.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and register

composer require fwolfsjaeger/doctrine-cockroachdb-bundle

There is likely no Symfony Flex recipe for a 2-star package, so expect to add it manually to config/bundles.php:

return [
    // ...
    Fwolfsjaeger\DoctrineCockroachDBBundle\DoctrineCockroachDBBundle::class => ['all' => true],
];

Open the bundle class first — it is small, and the only thing that matters is which DBAL Driver class it registers and under what URL scheme. That tells you everything else.

2. Point Doctrine at CockroachDB

The bundle exists to give Doctrine a driver that speaks CockroachDB's dialect on top of the PostgreSQL wire protocol. In .env.local:

DATABASE_URL="cockroachdb://[email protected]:26257/app?sslmode=disable"

If the scheme isn't picked up (or you prefer to be explicit), set the driver class on the connection in config/packages/doctrine.yaml:

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'
        driver_class: '%env(COCKROACH_DRIVER_CLASS)%'   # the class from the bundle
        server_version: '23.1'                          # pin it; avoids probing

3. Prove it works against a real node

Do not start with SQLite. Use a single-node cluster:

docker run -d --name crdb -p 26257:26257 -p 8080:8080 \
  cockroachdb/cockroach:latest start-single-node --insecure

Then the normal Doctrine loop:

bin/console doctrine:database:create
bin/console make:entity   # or your existing entities
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate

First use case: take one low-risk bounded context (a new module, a side service) and run its migrations + CRUD + one write transaction. That is the fastest way to find out whether the driver's retry and introspection behaviour actually holds for your schema.

Implementation Patterns

Register the driver once, reuse everywhere. The bundle's value is a single driver class. Use it for every connection (default, read, reporting) rather than mixing in postgresql:// for some — mixed drivers produce inconsistent schema diffs.

Wrap every write transaction in a retry loop. This is the single most important pattern with CockroachDB. Contention surfaces as SQLSTATE 40001 (restart transaction), and the whole transaction must be replayed — DBAL does not do this for you, and Connection::transactional() will not retry.

final class Retry
{
    public function __construct(private int $attempts = 5) {}

    public function run(callable $fn, Connection $conn): mixed
    {
        for ($i = 1; ; $i++) {
            try {
                return $conn->transactional($fn);
            } catch (\Throwable $e) {
                if ($i >= $this->attempts || !$this->isRetryable($e)) {
                    throw $e;
                }
                usleep((int) (10_000 * 2 ** $i)); // jittered backoff
            }
        }
    }

    private function isRetryable(\Throwable $e): bool
    {
        $sqlState = $e->getPrevious()?->getCode() ?? $e->getCode();
        // 40001 = serialization failure, 40003 = connection reset
        return in_array((string) $sqlState, ['40001', '40003'], true);
    }
}

Make this a decorator around the repository/service, not something sprinkled inside entities.

Prefer application-generated primary keys. CockroachDB has no sequences. If you rely on @GeneratedValue(strategy="IDENTITY") with SERIAL, you will get either an error or a slow unique_rowid() fallback depending on how the driver maps it. Use UUIDs:

#[ORM\Id]
#[ORM\Column(type: 'uuid', unique: true)]
private Uuid $id;

public function __construct() { $this->id = Uuid::v7(); }

Uuid::v7() (time-ordered) keeps index locality much better than v4 — worth the one-line change.

Keep transactions short and touch rows in a deterministic order. CockroachDB is optimistic by default. Long transactions and cross-table writes in arbitrary order are the two things that generate the most 40001s. Sort the IDs you update before looping.

Migrations: review the SQL, don't trust the diff. Run doctrine:migrations:diff --formatted and read the generated DDL before committing. CockroachDB supports most ALTER TABLE forms but not all, and its introspection surfaces types differently from PostgreSQL, so the schema manager may oscillate between two "correct" states. If that happens, hand-write the migration with $this->addSql() and add a doctrine:schema:validate allowance in CI.

Raw SQL for CockroachDB-only features. Follower reads, AS OF SYSTEM TIME, UNIQUE ROW region hints — use Connection::executeQuery() directly. They don't belong in the ORM, but they're often the reason you chose CockroachDB in the first place.

Test against a real node in CI. Testcontainers or a service container with start-single-node --insecure, then run migrations and a 40001-generating concurrent test. Without this, the retry path is untested.

Gotchas and Tips

  • The package is a dependency risk. Two stars, zero dependents. Pin an exact version in composer.json, read the driver source before adopting, and assume you may need to fork it. The surface is probably small enough that forking is cheap.
  • driver_class vs url scheme. If Doctrine falls back to the stock pdo_pgsql driver, you'll get no CockroachDB-specific handling — and no error, just subtly wrong behaviour. Verify with bin/console doctrine:query:sql 'SELECT version()' and by dumping get_class($conn->getDriver()) in a tinker session.
  • SQLSTATE is often on the previous exception. DBAL wraps driver errors. $e->getCode() frequently returns 0 while the pgcode lives on $e->getPrevious()->getCode(). Log both when you first integrate, then codify the check.
  • Schema introspection churn. The most common complaint with CockroachDB + Doctrine is migrations:diff generating phantom changes (default values, int8/bigint, timestamptz precision). Mitigate by disabling doctrine.dbal.schema_filter for CockroachDB's internal schemas and pinning server_version so the platform doesn't probe and mis-detect.
  • SSL on CockroachDB Cloud is stricter. sslmode=require is not enough; verify-full with sslrootcert is expected. Pass these via options: {sslrootcert: ...} in doctrine.yaml, not the URL, if the URL parser drops them.
  • Unsupported PostgreSQL surface. Ad-hoc locks (pg_advisory_lock), LISTEN/NOTIFY, and some SET statements are unsupported or partially supported. If a third-party bundle uses them (tenant switchers, queue packages), it will fail at runtime, not at boot.
  • Debugging: enable the Doctrine profiler panel and set doctrine.dbal.logging: true in dev. When a transaction fails, print SHOW TRANSACTION STATUS — it's the fastest way to distinguish a retryable conflict from an aborted session.
  • Extension points, in order of preference: (1) decorate the repository/service with the retry wrapper, (2) add a Doctrine\DBAL\Logging\Middleware for query telemetry, (3) subclass the bundle's driver class to add behaviour, (4) fork the driver. Steps 1–3 keep you off the fork path.
  • Don't reach for doctrine:schema:update --force. Against a distributed database with online schema changes, it's a footgun. Migrations only, always reviewed.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator