Skip to content

fix: cast cached post count to int (Redis serializer crash)#21

Merged
paulocastellano merged 1 commit into
mainfrom
hotfix/has-usage-int-cast
May 7, 2026
Merged

fix: cast cached post count to int (Redis serializer crash)#21
paulocastellano merged 1 commit into
mainfrom
hotfix/has-usage-int-cast

Conversation

@paulocastellano
Copy link
Copy Markdown
Contributor

Summary

  • Adds an (int) cast to Account::cachedPostCount() so the strict : int return type survives Redis's is_numeric raw-store optimisation, which returns ints as strings on read.
  • Changes the local CACHE_STORE default from database to redis so dev matches production and driver-specific bugs surface before deploy.
  • Regression test seeds the cache with a literal string (what prod's Redis returns) and asserts the function still returns an int.

Why

Production crashed on every Inertia request right after the PostHog branch (#20) merged:

TypeError: App\Models\Account::cachedPostCount(): Return value must be
of type int, string returned at app/Models/Traits/HasUsage.php:80

Laravel's RedisStore stores is_numeric values raw (not serialised) so they stay INCR/DECR-able atomically — the side effect is that an int written via Cache::put comes back as a string on read. Local/CI used array/database/file drivers that serialise blindly and preserve the type, so the bug never showed up until production hit Redis.

Test plan

  • php artisan test --compact tests/Feature/Models/HasUsageTraitTest.php — 6/6 pass, including the new postCount survives a string-typed cache value (Redis serializer quirk) regression.
  • Full suite: 1446 passed / 2 skipped / 0 failed.
  • Manually verify in a Redis-backed environment that requests no longer 500 on account.usage().
  • After merge: php artisan cache:clear on the affected production deploy to evict the stale string values that were poisoning warm caches.

…edis

Production crashed on every Inertia request after the PostHog branch
landed:

  TypeError: App\Models\Account::cachedPostCount(): Return value must
  be of type int, string returned at app/Models/Traits/HasUsage.php:80

Root cause: Laravel's RedisStore optimises is_numeric values by storing
them raw (not serialised) so they remain INCR/DECR-able atomically.
The side effect is that an int written via Cache::put comes back as a
string on read. The strict ': int' return type on cachedPostCount then
threw a TypeError.

Local dev and CI used the file/array/database drivers respectively,
which serialise everything blindly and preserve the int type, so the
bug never surfaced before deploy.

Fixes:
- Cast the Cache::remember result to (int) — defensive, survives any
  driver-specific behaviour. Documented inline so the cast is not
  later removed as redundant.
- Change config/cache.php default from 'database' to 'redis' so local
  dev matches prod by default and similar driver-specific bugs surface
  before merge instead of after deploy.
- Regression test that seeds the cache with a literal string (mimics
  the production Redis read) and asserts cachedPostCount still returns
  an int.
@paulocastellano paulocastellano merged commit e653c90 into main May 7, 2026
2 checks passed
@paulocastellano paulocastellano deleted the hotfix/has-usage-int-cast branch May 7, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant