class Blog
def self.articles
@@articles ||= Dir.glob(Rails.root.join('app', 'views', 'articles', '*.html.erb')).map do |file|
parse_file(file).front_matter
end
end
end
Is the above code thread safe / safe?
(i.e. I am asking about the use of @@articles ||= to cache the expensive operation)
Since ||= is in fact two operations, your code would not prevent multiple executions of Dir.glob... at the same time. In that sense it is not “thread safe”.
Whether this creates a real problem for your application depends on your context (performance constraints, side effects of Dir.glob..., etc.)
For my understanding the worst case scenario is that two threads (or more) start reading the files together and then they both assign (the same computed value) to the variable.
I guess that the assignment operation (=) is atomic, so it should not create a corrupted state for the class variable in my case (the files are static and are only read).
That’s right. However, for a slightly more elegant approach, you could load these cached values on initialization stage. For example, if you add a config/initializers/load_articles.rb:
Rails.application.config.to_prepare do
Blog.articles = Dir.glob … # Assuming you have Blog.articles=.
end
This will not only load them once in a thread-safe manner, but would also (if memory serves right) reload these files in development, if you update/add/remove articles, without having to restart the Rails server.