Rails 使用 fetch 进行缓存
Posted
技术标签:
【中文标题】Rails 使用 fetch 进行缓存【英文标题】:Rails caching using fetch 【发布时间】:2021-11-27 06:20:43 【问题描述】:Rails.cache.fetch(plan_list_cache_key(project), expires_in: EXPIRES_TIME) do
plans = Plan.all.where(is_active: true)
project_plan = project.project_plan
hash_array = []
plans.each do |plan|
plan = plan.attributes
expired = if (plan["id"] == project.plan.id)
(project_plan.end_date.to_date - Date.current).to_i.positive? ? false : true
else
false
end
subscribed = plan["id"] == project.plan.id
hash_array << plan.merge!(is_expired: expired , subscribed: subscribed)
end
hash_array
end
型号:
class Plan < mysqlBase
has_many :project_plans
end
class ProjectPlan < MysqlBase
belongs_to :plan
belongs_to :project
end
class Project < MysqlBase
has_one :project_plan
has_one :plan, :through => :project_plan
end
架构:
create_table "plans", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "name"
t.text "description"
t.integer "email_limit"
t.integer "validity"
t.float "unit_price", limit: 24
t.string "currency"
t.integer "base_user_count"
t.boolean "is_renewable"
t.boolean "is_active"
t.boolean "is_free"
t.string "terms"
t.integer "trial_days"
t.boolean "default"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "project_plans", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.integer "plan_id"
t.integer "project_id"
t.datetime "start_date"
t.datetime "end_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
回应
"status": "OK",
"status_code": 200,
"message": "ok",
"data":
"plans": [
"id": 1,
"name": "Free",
"description": "30 days free trial",
"email_limit": 10000,
"validity": 30,
"unit_price": 0.0,
"currency": "USD",
"base_user_count": 0,
"trial_days": 30,
"is_free": true,
"subscribed": false,
"is_expired": false
,
"id": 2,
"name": "Standard",
"description": "$3 for 100 users",
"email_limit": 0,
"validity": 30,
"unit_price": 0.03,
"currency": "USD",
"base_user_count": 100,
"trial_days": 0,
"is_free": false,
"subscribed": true,
"is_expired": false
]
每个项目都有一个单独的 project_plan。所有用户的计划都是相同的。但我要做的是在 API 响应中添加两个附加字段 is_expired 和 subscribed
我在这里使用 fetch 缓存我的响应。但是由于有一些数据库和逻辑操作,这是进行缓存的正确方法吗
如果这不是正确的方法,这里会发生什么类型的错误?
进行这种缓存的最佳方法是什么?
【问题讨论】:
这里最好的方法是使用关联和预先加载。你在 Ruby 中做了一堆效率极低的工作,这些工作应该在数据库中处理。我怀疑你真的需要缓存。 guides.rubyonrails.org/…guides.rubyonrails.org/… 为了给你一个真正解决问题的答案,我们需要一个模型、模式、数据和预期输出的例子。 我已经用架构和模型更新了描述。请检查@max 谢谢,但您仍然缺少预期结果的示例。虽然我们可以从您的代码中对其进行逆向工程,但这只会导致误解。 抱歉,已添加回复 【参考方案1】:这里的一个解决方案是加入项目计划表并选择聚合:
SELECT
plans.*,
-- you will get integers since MySQL is a peasant database and does not have real boolean types
COUNT(pp.id) > 0 AS subscribed,
COALESCE(MAX(pp.ends_at) < NOW(), FALSE) AS is_expired
FROM plans
LEFT OUTER JOIN project_plans pp
ON pp.plan_id = plans.id
AND pp.project_id = ?
GROUP BY plans.id
class Plan
def with_statuses_for_project(project)
pp = ProjectPlan.arel_table
j = pp.join(arel_table).on(
pp[:plan_id].eq(arel_table[:id])
.and(
pp[:project_id].eq(project.id)
)
)
.select(
arel_table[Arel.star],
"COUNT(pp.id) > 0 AS subscribed",
"COALESCE(MAX(pp.ends_at) < NOW(), FALSE) AS is_expired"
)
.joins(j.join_sources)
.group(:id)
end
end
【讨论】:
以上是关于Rails 使用 fetch 进行缓存的主要内容,如果未能解决你的问题,请参考以下文章
由于缓存目录将所有权更改为 root,Rails 6 应用程序失败
Rails:ENV.fetch() 和 ENV[] 之间的区别
Rails 5可操作的未定义方法'fetch'用于nil:NilClass