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

Psr6 Dynamo Db Bundle Laravel Package

rikudou/psr6-dynamo-db-bundle

A Symfony bundle that provides a PSR-6 cache adapter backed by AWS DynamoDB, letting applications use a DynamoDB table for scalable cache storage and integrate the cache pool with Symfony's configuration and services.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, low-adoption bundle (20 stars, no dependents), so treat the source as the documentation. Read, in order:

  1. vendor/rikudou/psr6-dynamo-db-bundle/README.md — usually the only prose available.
  2. src/DependencyInjection/Configuration.php — the authoritative list of config keys and defaults.
  3. src/DependencyInjection/*Extension.php and src/Resources/config/services.yaml — the service IDs you'll actually inject.
  4. The wrapped library (rikudou/psr6-dynamo-db, now >= 3.2) — the table schema, key attribute name, TTL handling and serialization live there, not in the bundle.

Minimal setup

composer require rikudou/psr6-dynamo-db-bundle:^4.2

Pin the constraint explicitly. v4.2.0 is the first release with Symfony 8 coverage and a raised floor on the base library (>= 3.2); if you leave the constraint loose you can silently pull a base-library minor that changes TTL/consistency behaviour under you.

Register the bundle in config/bundles.php unless the Flex recipe does it:

Rikudou\Psr6DynamoDbBundle\RikudouPsr6DynamoDbBundle::class => ['all' => true],

Create the DynamoDB table before the app runs. The primary key name must match what the library expects — confirm it in the pool source, but it is typically a string hash key:

aws dynamodb create-table \
  --table-name app_cache \
  --attribute-definitions AttributeName=key,AttributeType=S \
  --key-schema AttributeName=key,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

Configure the bundle (key names vary by version — check Configuration.php):

# config/packages/rikudou_psr6_dynamo_db.yaml
rikudou_psr6_dynamo_db:
    table_name: '%env(DYNAMODB_CACHE_TABLE)%'
    region: '%env(AWS_REGION)%'
    # endpoint: 'http://localhost:8000'   # DynamoDB Local / LocalStack

Then find and alias the pool:

bin/console debug:container --show-private | grep -i dynamo
services:
    Psr\Cache\CacheItemPoolInterface: '@rikudou_psr6_dynamo_db.cache'      # confirm ID
    Psr\SimpleCache\CacheInterface:   '@rikudou_psr6_dynamo_db.simple_cache'

First use case: replacing cache.app for a workload on ephemeral, multi-instance infrastructure (Lambda, ECS, Kubernetes) where the filesystem cache is per-container and useless. A shared DynamoDB-backed cache for third-party API responses or computed aggregates is the sweet spot.

Implementation Patterns

Read-through with PSR-6 (best when you need isHit(), deferred writes, or item metadata):

final class ExchangeRateProvider
{
    public function __construct(private CacheItemPoolInterface $cache) {}

    public function rates(string $base): array
    {
        $item = $this->cache->getItem('rates_' . strtolower($base));

        if (!$item->isHit()) {
            $item->set($this->fetchFromApi($base));
            $item->expiresAfter(300);
            $this->cache->save($item);
        }

        return $item->get();
    }
}

Batching to cut round trips. Every save() is a DynamoDB write. When populating several keys at once, use deferred saves and a single commit() — this is the main lever you have on both latency and cost:

foreach ($rows as $id => $row) {
    $item = $this->cache->getItem("row_$id");
    $item->set($row)->expiresAfter(600);
    $this->cache->saveDeferred($item);
}
$this->cache->commit();

PSR-16 for simple callers. For scalar values, inject CacheInterface and use get/set/delete with has() guards. Use PSR-6 in libraries that may be consumed elsewhere; PSR-16 is friendlier in controllers and small services.

Key namespacing. One table, many consumers. Prefix every key with the app and environment (sprintf('%s:%s:rates_%s', $app, $env, $base)) either via a thin decorating wrapper or a bundle option if one exists. This makes clearing/segregating environments possible without a second table.

Local development. Point endpoint at DynamoDB Local or LocalStack, and create the table as part of the container startup. Gate this on %kernel.environment% so production never picks up the local endpoint. Keep a separate DYNAMODB_CACHE_TABLE value per env in Symfony Secrets/env vars.

Integration tip — layered cache. DynamoDB round trips are milliseconds, not nanoseconds. For hot keys, put a request-scoped array cache or a Symfony array adapter in front of the DynamoDB pool and memoize within the request; only the misses go to the network.

Warming. If you have known hot keys (config, tax rates, feature flags), add a small console command that computes and writes them on deploy. This avoids a cold-start thundering herd hitting the origin.

Gotchas and Tips

  • Cost is the primary design constraint. Every getItem is a read request; every save a write. Caching infrequently-read values in DynamoDB is more expensive than not caching at all. Use it for shared state with a real hit rate, not for in-request memoization.
  • The table must exist before traffic. A missing table throws at runtime (ResourceNotFoundException), not at container compile time. Add table creation/validation to your deploy pipeline or a health check.
  • TTL is not immediate deletion. DynamoDB's TTL attribute removes items asynchronously (often within minutes, but the SLA is much longer). Make sure the pool itself compares the stored expiration on read — check the library source. If it relies purely on DynamoDB TTL, you can read stale items after expiry. v4.2.0 bumps the base library to >= 3.2, so re-verify this behaviour against the installed rikudou/psr6-dynamo-db after upgrading rather than trusting notes you took on an older version.
  • Cache keys have DynamoDB constraints. Partition keys are limited in length and character set. Long, serialized, or object-derived keys can fail. Hash long keys (hash('sha256', $key)) before storing, but keep a readable prefix for debugging by hand in the AWS console.
  • 400 KB item limit. PSR-6 values are serialized before storage; large arrays/HTML/blobs will blow up. Store references (IDs, S3 keys) or compressed payloads instead.
  • Serialization quirks. PSR-6 mandates serializable values, so closures, resources and some DTOs will fail. Confirm what the library uses (PHP serialize preserves types; JSON loses DateTime and object identity) and add an explicit serializer in your values if needed.
  • Eventual consistency on reads. Default DynamoDB reads are eventually consistent; read-after-write across instances can miss. If your workflow requires immediately visible writes, check whether the client/pool exposes ConsistentRead.
  • Don't use it as a counter or lock. A PSR-6 get → set → save sequence is read-modify-write and loses updates under concurrency. For counters or mutual exclusion, talk to DynamoDB conditional writes directly.
  • Wrap request-critical calls in try/catch. Network errors, throttling and access-denied all surface as PSR cache exceptions. Degrade to a local array cache rather than 500-ing the request:
try {
    $item = $this->cache->getItem($key);
} catch (\Psr\Cache\CacheException $e) {
    $this->logger->warning('DynamoDB cache unavailable', ['exception' => $e]);
    return $fallback->get($key);
}
  • Credentials: use the SDK's default provider chain. Environment variables, IAM instance/task role, ECS task role, or SSO — no keys in YAML. If you already have an Aws\DynamoDb\DynamoDbClient service defined, reference it from the bundle config instead of letting the bundle build one.
  • Debugging checklist: debug:container for service IDs, debug:autowiring cache to verify the alias, AWS SDK logging (Aws\Middleware / Monolog handler) for the actual request parameters, and CloudWatch ThrottledRequests / ConsumedReadCapacityUnits when latency spikes. Compare the key the app writes with what you see in the DynamoDB console — mismatches are almost always prefix or hashing issues.
  • Clearing the cache. cache:pool:clear only works if the pool is wired into Symfony's cache framework; otherwise expiry and prefixes are your only tools. Ship a small console command that scans and batch-deletes by prefix for the environments where you need it.
  • Upgrade notes for v4.2.0. No breaking changes or deprecations are announced — the release is Symfony 8 CI/enablement, a .gitattributes chore, and a base-library floor of >= 3.2. Practically that means: composer update will likely move rikudou/psr6-dynamo-db forward too, Symfony 8 is now supported alongside the older majors, and there is no migration step from 4.0.1. Still run your test suite against a real (or Local) table after bumping, because the base-library jump is where behavioural drift would hide. Also confirm the bundle's own symfony/* constraints in composer.json match your framework version before assuming the CI matrix makes it safe.
  • Extension points: decorate the pool service for key prefixing, metrics or logging; override the client service to change region/credentials; use container tags to swap implementations per environment. Because the package is niche and docs are thin, pin the version (^4.2) and skim the changelog and src/ before each upgrade — the surface area is small enough that reading it is a ten-minute job.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin