Skip to content

Instantly share code, notes, and snippets.

@maicher
Last active December 13, 2017 12:08
Show Gist options
  • Save maicher/8701cdcbac3640329c0226e99f43a4cf to your computer and use it in GitHub Desktop.
Save maicher/8701cdcbac3640329c0226e99f43a4cf to your computer and use it in GitHub Desktop.
My proposal for implementing caching in OfferProvider componenet

Caching in OfferProvider

  • Why? - To improve performance (that is not call db an each provider request).
  • Where? - At the point of fething data from OfferProvider component (repositories).
  • How? - By using Rails.cache#fetch

Introduction

Why we don't like caching?

  • cache keys? (we don't know how to name the keys)
  • cache invalidaiton? (we are fraid that after updating record, app will still serve old data)
  • cache size? (ActiveRecord models when serialized can grow of of controll)

That 3 points above we makes caching not a trivial task and IMHO that's why developers tend to avoid it.

Hovewer, because in OfferProvider:

  • we don't write do db in any other way than throught repository
  • we don't read in any other way than throught repository
  • and we use dry-structs as data containers (not AR models)

In other words, because we encapsulate data access by Repository - cache becomes simple to manage.

In next sections I'd like to present my proposal.

Repositories encapsulates data access (reads and writes to db)

Read:

def find(id, provider_id)
  build_from_record(find_record(id, provider_id))
end

# with cache:
def find(id, provider_id)
  Rails.cache.fetch('cache_key_will_be_described_later') do
    build_from_record(find_record(id, provider_id))
  end
end

Write:

def update(provider_id:, id:, changeset:)
  record = find_record(id, provider_id)
  record.update!(update_query_params(changeset.to_h))

  build_from_record(record)
end

# with cache
def update(provider_id:, id:, changeset:)
  record = find_record(id, provider_id)
  record.update!(update_query_params(changeset.to_h))
  
  invalidate_cache(provider_id)
  build_from_record(record)
end

Repositories methods and their parameters define the cache key.

Cache key will depend on methods parameters.

eg. for:

find(id, provider_id)

cache key: "providers_#{provider_id}_#{id}_find"

Repositories modify data

Currently we have 3 methods in OfferProvider repository (create, update, delete). That simplifies cache invalidation. Cache will be invalidated only in these methods.

We control cache size thanks to dry-stucts

How data are stored in cache? Data is serialized on write to cache and deserialized on reading from cache. In our cache we will cache our dry-structs, so it's lightweight and we completely controll it.

Example

@startuml
"Controller/Service" -> Repository : find_by(a, b)
Repository -> Record : find_by(a, b)
Repository <-- Record : record
Repository -> Factory : build_from_record(record)
Repository <-- Factory : entity
"Controller/Service" <-- Repository : entity

box "Persistence layer" #LightBlue
participant Repository
participant Record
participant Factory
end box
@enduml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment