Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kai-init's avatar

kai-init wrote a reply+100 XP

2w ago

I built a automatic normalized caching layer for Eloquent — massive Redis memory savings

Very insightful qustions again. There's a few optimisations I've done to keep the package performant for hydration. New Model initlisation for each type of model is only done once, and cloned to setRawAttributes for bypassing laravel magics like boot, observer registration etc. It is still slightly more cpu run time than manually caching it with Cache::remeber() but not by much.

I've done some quick benchmark on CPU run time for direct db access, manual cache::remember and using normcache if you are interested in the detail:

benchmark

For standalone count and exists they would bypass the package competely for now. Currently the package only handles caching instance where model is returned. There are other model related counts that's handled by the package. The count from a standard paginate is cached via count:{model}:v{version}:{hash} key and withCount, withSum (eloquent aggregates) are cached via agg:{table}:{parentId}:{column}:{function}:{relation}:{constraintHash}:v{relatedVersion}

kai-init's avatar

kai-init wrote a reply+100 XP

2w ago

I built a automatic normalized caching layer for Eloquent — massive Redis memory savings

Hi @imrandevbd That’s a really solid question. It's fully normalized. I made a conscious choice to never embed child models.

The eloquent builder override somewhat handles normlization by default. It intercepts the standard eager-loading flow and routes it through a few different strategies depending on the relation.

Basically anything but a BelongsTo relation gets a seperate query cache for the list of models to load.

  • BelongsTo: Since we already has the child IDs, It skip the query cache entirely and just do a batch MGET against the entity store.
  • HasMany: It intercept that whereIn query and cache just the child IDs in their own query cache (query:{child}:v{v}:{hash}).
  • Many-to-Many: These get a specialized "pivot cache" so It can map the IDs while keeping the unique join-table data (the pivot attributes) normalized (query:{child}:v{v}:{hash})
  • Through: Similar logic for Through relations, it bridge the distant models with a "through cache" that's version sensitive to the target and the bridge model.
kai-init's avatar

kai-init started a new conversation+100 XP

2w ago

I built a automatic normalized caching layer for Eloquent — massive Redis memory savings

Hey everyone 👋

Over the past few months, I’ve been working on a small open-source package called laravel-normcache.

It actually started from a problem I kept running into across a few different projects. I found myself repeatedly building the same kind of caching layer to avoid redundant Eloquent data sitting everywhere in Redis, so eventually I thought: why not turn it into a reusable package.

A lot of the ideas are inspired by patterns used in things like Redux/Apollo normalized stores, and I’ve been experimenting and refining things as I use it across real projects.

The main idea behind laravel-normcache is normalizing cached Eloquent results instead of storing entire query collections repeatedly. It stores individual models and query ID maps separately, so when one model changes, every cached query containing that model automatically reflects the update.

Query cache  →  "posts where active=1, page 2"  →  [4, 7, 12]
Model cache  →  post:4  →  { id:4, title:..., body:... }
             →  post:7  →  { id:7, title:..., body:... }
             →  post:12 →  { id:12, title:..., body:... }

Some things it currently does:

  • Automatic caching + invalidation with just a single trait
  • Avoids storing duplicate model data across query caches
  • Instant consistency across cached query results
  • O(1) invalidation using versioned cache keys
  • Uses dramatically less Redis memory compared to traditional query caching approaches

There’s still plenty I want to improve and optimize over time, but I felt it was finally in a good enough place to share with the community.

Here’s a quick benchmark comparing direct DB queries vs laravel-model-cache for reference:

benchmark

I’d genuinely appreciate any feedback, criticism, ideas, or contributions from people more experienced than me. I’m mainly here to learn and improve as an open-source developer.

Repository: https://github.com/kai-init/laravel-normcache

Thanks for reading 🙏

kai-init's avatar

kai-init wrote a reply+100 XP

2w ago

Increment/Decrement on High Traffic

Honestly I think that's what I would do as well. Use the cached data for view and check DB again for any operations.