詢問促銷活動快取問題


#1

大家好,

由於編目和商品頁有用快取,
這樣會導致每次促銷活動時間到/結束,
會需要清除快取,
讓編目與商品頁重新讀取資料,
才能顯示出促銷資訊,

活動開始 -> 清除快取(導致促銷活動延後N分鐘)
活動結束 -> 清除快取(導致促銷活動多跑N分鐘)

想請教該怎麼處理或設計比較好?

謝謝大家 :dizzy_face:


#2

快取設計其實有非常非常多方式 … 而你說的應該是 Rails 內建的『 fragment cache 』?

與法會類似

<% cache do %>
<% end %>

這東西很有趣,剛剛看一下 doc 會類似

<% p = Proc.new do ... end %><!-- 宣告 do...end 的程式-->
<% x = Rails.cache.read(MD5(p.to_s))%><!-- 嘗試取得之前的內容-->
<% if x %>
  <%= x %><!-- 有的話直接顯示 -->
<% else %>
  <%= x = p.exec %><!-- 沒有的話取得內容,顯示,並快取 -->
  <% Rails.cache.write(MD5(p.to_s) , x )%><!-- 寫入來讓下一個 run 使用-->
<% end%>

實作大概就這樣的流程而已,而你也可以增加快取時間,類似

<% cache(key, expires_in: 1.hour) do %>
   ...
<% end %>

當然你的時間需要逆算,因為 memcache 是用剩餘時間表示的就是了,然而如果你要最精確的時間的話 … 給你有趣而奇怪的大絕招唄?

<%= render partial: "link_area", layout: "graybar" %>

這是 render partial 一般結構式,而前面有 <%= 所以印出來的後面有 99% 機會都是 string,不然保險自己 to_s 也可以,所以你可以自幹 cache 類似

<%
  now_int = Time.now.to_i
  is_show = false
  # 從 memcache 內讀出,看目前是否有快取(取得 > 檢查 + 顯示)
  if source = Rails.cache.read("EVENT_NAME") #假定 source = Marshal([end_at_timestamp , body])
    source = Marshal.load(source)
    if source && source[0] > now_int
      is_show = true
%><%= 
      source[1]
%><%
    else #過期了,要清理 # dalli 有移除指令可以用,換用 dalli 應該會好點,否則就是寫入本身並瞬間過期
      Rails.cache.write("EVENT_NAME" , Marshal.dump(false) , expires_in: 0.001)
    end
  end
%>
<% unless is_show
  # 這邊做初始化,檢查是否有新活動,並將 view 都抽成 partial 會好點
  # 當然這段通常檢查量會很大,建議放在 Redis 內之類的?這邊單純從 Model 取出
  if project = Project.where("status = 2 AND end_at > ?" , Time.now).first
    content = render(partial: "show_project", locals: {project: project}).to_s
    Rails.cache.write("EVENT_NAME" , Marshal.dump([
      project.end_at.to_i ,
      content
    ])) #檢查 + 寫入,並顯示
    %><%= content %><%
  end
%>

大概是這樣唄,其實簡易的 cache 大都是『 使用 > 檢查 + 寫入 > (LOOP) 』而已,而你可以用 Marshal 來做物件的 serialize to string 後來放到 cache 內來做重新存取與檢查即可,上面的 code 全部都尚未測試,單純提個點子出來給你就是了

這個應該算是簡易版本的,複雜點的我可能會 memcache + redis + ssdb 甚至 RDBMS 一起配合來做資料整理唄 … 甚至是整個 job 切去別的 process 來定時完成之類的,這邊就先不複雜化了 … ((某史詩等級的專案進行中