Rails cached value lost/nil despite expires_in 24.hours - ruby-on-rails

I am using ruby 2.3.3 and Rails 4.2.8 with Puma (1 worker, 5 threads) and on my admin (i.e. not critical) page I want to show some stats (integer values) from my database. Some requests take quite a long time to perform so I decided to cache these values and use a rake task to re-write them every day.
Admin#index controller
require 'timeout'
begin
timeout(8) do
#products_a = Rails.cache.fetch('products_a', :expires_in => 24.hours) { Product.where(heavy_condition_a).size }
#products_b = Rails.cache.fetch('products_b', :expires_in => 24.hours) { Product.where(heavy_condition_b).size }
#products_c = Rails.cache.fetch('products_c', :expires_in => 24.hours) { Product.where(heavy_condition_c).size }
#products_d = Rails.cache.fetch('products_d', :expires_in => 24.hours) { Product.where(heavy_condition_d).size }
end
rescue Timeout::Error
#products_a = 999
#products_b = 999
#products_c = 999
#products_d = 999
end
Admin#index view
<li>Products A: <%= #products_a %></li>
<li>Products B: <%= #products_b %></li>
<li>Products C: <%= #products_c %></li>
<li>Products D: <%= #products_d %></li>
Rake task
task :set_admin_index_stats => :environment do
Rails.cache.write('products_a', Product.where(heavy_condition_a).size, :expires_in => 24.hours)
Rails.cache.write('products_b', Product.where(heavy_condition_b).size, :expires_in => 24.hours)
Rails.cache.write('products_c', Product.where(heavy_condition_c).size, :expires_in => 24.hours)
Rails.cache.write('products_d', Product.where(heavy_condition_d).size, :expires_in => 24.hours)
end
I am using this in production and use Memcachier (on Heroku) as a cache store. I also use it for page caching on the website and it works fine there. I have:
production.rb
config.cache_store = :dalli_store
The problem I am experiencing is that the cached values disappear almost instantly, and quite intermittently, from the cache. In the console I have tried:
I Rails.cache.write one value (e.g. product_a) and check it a minute later, it is still there. Although crude, I can see the "Set cmds" increments by one in Memcachier admin tool.
However, when I add the next value (e.g. product_b) the first one disappears (becomes nil)! Sometimes if I add all 4 values, 2 seems to stick. These are not always the same values. It is like whack-a-mole!
If I run the rake to write the values and then try to read the values typically only two values are left, whereas the others are lost.
I have seen a similar question related to this where the reason explained was the use of a multithread server. The cached value was saved in one thread and could not be reached in another, the solution was to use a memcache, which I do.
It is not only the console. If I just reload admin#index view to store the values or run the rake task, I experience the same problem. The values do not stick.
My suspicion is that I am either not using the Rails.cache-commands properly or that these commands do not in fact use Memcachier. I have not been able to determine whether my values are in fact stored in Memcachier but when I use my first command in the console I do get the following:
Rails.cache.read('products_a')
Dalli::Server#connect mc1.dev.eu.ec2.memcachier.com:11211
Dalli/SASL authenticating as abc123
Dalli/SASL authenticating as abc123
Dalli/SASL: abc123
Dalli/SASL: abc123
=> 0
but I do not get that for subsequent writes (which I assume is a matter of readability in the console and not a proof of Memcachier not being used.
What am I missing here? Why won't the values stick in my cache?

Heroku DevCenter states a little different cache config and gives some advice about threaded Rails app servers like Puma using connection_pool gem:
config.cache_store = :mem_cache_store,
(ENV["MEMCACHIER_SERVERS"] || "").split(","),
{ :username => ENV["MEMCACHIER_USERNAME"],
:password => ENV["MEMCACHIER_PASSWORD"],
:failover => true,
:socket_timeout => 1.5,
:socket_failure_delay => 0.2,
:down_retry_delay => 60,
:pool_size => 5
}

Related

Defining time parameters in rails environment files

I have lots of different timeouts for various cookies through out my app.
If I move these settings to be centralized in the Rails configuration files will they work properly since the environment variables is initialised only once when the application loads or am I mistaken?
In other words: will the timeout be set to when the cookie was created or will the timeout be set according to when the app loaded?
TODAY
cookies['locale'] = {
:value => current_user && current_user.locale || 'en',
:expires => 1.week.from_now
}
TOMORROW
cookies['locale'] = {
:value => current_user && current_user.locale || 'en',
:expires => Application.config.locale_timeout
}
config/environments/qa.rb
config.locale_timeout = 1.week.from_now
In other words: will the timeout be set to when the cookie was created
or will the timeout be set according to when the app loaded
the timeout be set according to when the app loaded
So,
In my opinion, The correct way is to define Time offset in config:
Application.config.locale_timeout = 1.week
Then you will use it like this
:expires => Time.current + Application.config.locale_timeout
OR
:expires => Application.config.locale_timeout.from_now

Warning! ActionDispatch::Session::CacheStore failed to save session. Content dropped. in logfile

I'm running a Rails 4 application and I'm seeing this in my production logs on Heroku. I see it frequently and some users are reported getting logged out and I don't see anything in the logs that would indicate why it's happening. I don't see any information from dalli that it cannot save. For most users, it works just fine. Any tips on how to hunt this down?
Error Message in Production Logs
Warning! ActionDispatch::Session::CacheStore failed to save session. Content dropped.
My configuration in production
config.cache_store = :dalli_store,
(ENV["MEMCACHIER_SERVERS"] || "").split(","),
{:username => ENV["MEMCACHIER_USERNAME"],
:password => ENV["MEMCACHIER_PASSWORD"],
:failover => true,
:socket_timeout => 1.5,
:socket_failure_delay => 0.2
}
config.session_store = :mem_cache_store
I found the problem that was creating this error, I'm using a custom header variable to set the session ID and the client was sending nil which was causing rack to not be able to store to a nil session ID. I wrapped the call in a blank? check to ensure it's only set if there is a valid value and that fixed my issue.
if(!request.headers["Session"].blank?)
request.session_options[:id] = request.headers["Session"]
end

A/B Test with Rails resets data after version change

I'm using split gem on Rails and my experiments keeps incrementing it's version, and after each increment all the data are lost. I don't know the pattern for it to change thus don't know how to prevent it. For example:
Experiment: landing v19 Goal:view-wheels
ALTERNATIVE NAME PARTICIPANTS NON-FINISHED COMPLETED CONVERSION RATE
A 70 40 30 42.86%
B 70 27 43 61.43% +43.33%
After some event that I don't know, the version will change to v20 and all data will be lost. I want to prevent it, I need to keep counting until I actively say to stop.
My redis configuration:
On redis.rb:
uri = URI.parse(ENV['REDISTOGO_URL'])
REDIS = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password, :username => uri.user)
On split.rb:
require "split/dashboard"
include Split::Helper
Split.redis = REDIS
Split.configure do |config|
config.experiments = {
"landing" => {
:alternatives => ["A", "B"],
:resettable => false,
:goals => ["opt-in", "view-wheels"]
}
}
config.db_failover = true # handle redis errors gracefully
config.db_failover_on_db_error = proc{|error| Rails.logger.error(error.message) }
end

How to "crawl" only the root URL with Anemone?

In the example below I would like anemone to only execute on the root URL (example.com). I am unsure if I should apply the on_page_like method and if so what pattern I would need.
require 'anemone'
Anemone.crawl("http://www.example.com/") do |anemone|
anemone.on_pages_like(???) do |page|
# some code to execute
end
end
require 'anemone'
Anemone.crawl("http://www.example.com/", :depth_limit => 1) do |anemone|
# some code to execute
end
You can also specify the following in the options hash, below are the defaults:
# run 4 Tentacle threads to fetch pages
:threads => 4,
# disable verbose output
:verbose => false,
# don't throw away the page response body after scanning it for links
:discard_page_bodies => false,
# identify self as Anemone/VERSION
:user_agent => "Anemone/#{Anemone::VERSION}",
# no delay between requests
:delay => 0,
# don't obey the robots exclusion protocol
:obey_robots_txt => false,
# by default, don't limit the depth of the crawl
:depth_limit => false,
# number of times HTTP redirects will be followed
:redirect_limit => 5,
# storage engine defaults to Hash in +process_options+ if none specified
:storage => nil,
# Hash of cookie name => value to send with HTTP requests
:cookies => nil,
# accept cookies from the server and send them back?
:accept_cookies => false,
# skip any link with a query string? e.g. http://foo.com/?u=user
:skip_query_strings => false,
# proxy server hostname
:proxy_host => nil,
# proxy server port number
:proxy_port => false,
# HTTP read timeout in seconds
:read_timeout => nil
My personal experience is that Anemone was not very fast and had a lot of corner cases. The docs are lacking (as you have experienced) and the author doesn't seem to be maintaining the project. YMMV. I tried Nutch shortly but didn't play aroud as much but it seemed faster. No benchmarks, sorry.

ArgumentError on application requests

I've written a basic Rails 3 application that shows a form and an upload form on specific URLs. It was all working fine yesterday, but now I'm running into several problems that require fixing. I'll try to describe each problem as best as I can. The reason i'm combining them, is because I feel they're all related and preventing me from finishing my task.
1. Cannot run the application in development mode
For some unknown reason, I cannot get the application to run in development mode. Currently i've overwritten the production.rb file from the environment with the settings from the development environment to get actuall stacktraces.
I've added the RailsEnv production setting to my VirtualHost setting in apache2, but it seems to make no difference. Nor does settings ENV variable to production.
2. ArgumentError on all calls
Whatever call I seem to make, results in this error message. The logfile tells me the following:
Started GET "/" for 192.168.33.82 at
Thu Apr 07 00:54:48 -0700 2011
ArgumentError (wrong number of
arguments (1 for 0)):
Rendered
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.erb
(1.0ms) Rendered
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(4.1ms) Rendered
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
within rescues/layout (8.4ms)
This means nothing to me really. I have no clue what's going wrong. I currently have only one controller which looks like this:
class SearchEngineController < ApplicationController
def upload
end
def search
#rows = nil
end
# This function will receive the query string from the search form and perform a search on the
# F.I.S.E index to find any matching results
def query
index = Ferret::Index::Index.new :path => "/public/F.I.S.E", :default_field => 'content'
#rows = Array.New
index.search_each "content|title:#{params[:query]}" do |id,score, title|
#rows << {:id => id, :score => score, :title => title}
end
render :search
end
# This function will receive the file uploaded by the user and process it into the
# F.I.S.E for searching on keywords and synonims
def process
index = Ferret::Index::Index.new :path => "public/F.I.S.E", :default_field => 'content'
file = File.open params[:file], "r"
xml = REXML::Document.new file
filename = params[:file]
title = xml.root.elements['//body/title/text()']
content = xml.root.elements['normalize-space(//body)']
index << { :filename => filename, :title => title, :content => content}
file.close
FileUtils.rm file
end
end
The routing of my application has the following setup: Again this is all pretty basic and probably can be done better.
Roularta::Application.routes.draw do
# define all the url paths we support
match '/upload' => 'search_engine#upload', :via => :get
match '/process' => 'search_engine#process', :via => :post
# redirect the root of the application to the search page
root :to => 'search_engine#search'
# redirect all incoming requests to the query view of the search engine
match '/:controller(/:action(/:id))' => 'search_engine#search'
end
If anyone can spot what's wrong and why this application is failing, please let me know. If needed I can edit this awnser and include additional files that might be required to solve this problem.
EDIT
i've managed to get further by renaming one of the functions on the controller. I renamed search into create and now I'm getting back HAML errors. Perhaps I used a keyword...?
woot, finally found the solutions....
Seems I used keywords to define my actions, and Rails didn't like this. This solved issue 2.
Issue 1 got solved by adding Rails.env= 'development' to the environment.rb file

Resources