I just straced my Rails-App and it produces a lot of
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2309, ...}) = 0
Calls (really a lot!).
In other contexts I've read that this is because the Timezone is not set.
Is there a way to "fix" this?
Best,
Tobias
It's not a ruby issue but rather a C / Linux issue:
Setting "TZ" ENV-Var will lead to no more stat calls on etc/localtime.
It wont have significant performance impacts though:
# irb
require 'benchmark'
Benchmark.measure { 10_000_000.times { Time.now } }
=> 17.880000 0.540000 18.420000 ( 21.535307)
# same with TZ=CET irb
=> 18.040000 0.550000 18.590000 ( 20.892542)
As #fabio said, you should report this to the Rails forums or mailinglist, because its probably a bug.
However, to set the time zone, in your config/environment.rb:
Rails::Initializer.run do |config|
config.time_zone = "Central Time (US & Canada)"
end
you can get available time zones with rake time:zones:us, rake time:zones:local, or rake time:zones:all (depending on where you are in the world.)
Related
in the rails console, on one server i get
ActiveSupport::TimeZone.all.map(&:name).select{|x| x.downcase.include? "east"}
=> ["Indiana (East)"]
But in another I get
ActiveSupport::TimeZone.all.map(&:name).select{|x| x.downcase.include? "east"}
=> ["Eastern Time (US & Canada)", "Indiana (East)"]
What is the determinant of the available time zones in a Rails app?
Thanks
Kevin
I have a JavaScript component (e.g. a date picker) that heavily depends on -
The current system time
The current system time zone
In Ruby and Capybara it's possible to stub any time with the help of libraries such as Timecop.
Is it also possible to stub these values in the headless browser that Capybara controls?
Thanks!
Edit: Here's an example of how Ruby is stubbed but Capybara's browser still uses the system time
before do
now = Time.zone.parse("Apr 15, 2018 12:00PM")
Timecop.freeze(now)
visit root_path
binding.pry
end
> Time.zone.now
=> Sun, 15 Apr 2018 12:00:00 UTC +00:00
> page.evaluate_script("new Date();")
=> "2018-03-27T04:15:44Z"
As you've discovered, Timecop only affects the time in the tests and application under test. The browser is run as a separate process and completely unaffected by Timecop. Because of that you need to stub/mock the time in the browser as well using one of many JS libraries designed to do that. The one I generally use is sinon - http://sinonjs.org/ - , which I conditionally install in the pages head using something like
- if defined?(Timecop) && Timecop.top_stack_item
= javascript_include_tag "sinon.js" # adjust based on where you're loading sinon from
- unix_millis = (Time.now.to_f * 1000.0).to_i
:javascript
sinon.useFakeTimers(#{unix_millis});
That should work in a haml template (adjust if using erb) and would install and mock the browsers time whenever a page is visited while Timecop is being used to mock the apps time.
I know that the question is a bit old, but we had the same request and found the following solution to work with rails 6:
context 'different timezones between back-end and front-end' do
it 'shows the event timestamp according to front-end timezone' do
# Arrange
previous_timezone = Time.zone
previous_timezone_env = ENV['TZ']
server_timezone = "Europe/Copenhagen"
browser_timezone = "America/Godthab"
Time.zone = server_timezone
ENV['TZ'] = browser_timezone
Capybara.using_session(browser_timezone) do
freeze_time do
# Act
# ... Code here
# Assert
server_clock = Time.zone.now.strftime('%H:%M')
client_clock = Time.zone.now.in_time_zone(browser_timezone).strftime('%H:%M')
expect(page).not_to have_content(server_clock)
expect(page).to have_content(client_clock)
end
end
# (restore)
Time.zone = previous_timezone
ENV['TZ'] = previous_timezone_env
end
end
This helps me for usage in pair with zonebie gem
RSpec.configure do |config|
config.before(:suite) do
ENV['TZ'] = Time.zone.tzinfo.name
# ...
end
end
I have an assertion in a test that looks like this:
assert_equals object.sent_at, Time.now
When I run this test, I keep getting an error that looks like this
--- expected
+++ actual
## -1 +1 ##
-Fri, 04 Mar 2016 18:57:47 UTC +00:00
+Fri, 04 Mar 2016
I've tried a few combinations to make this test pass.
My actual code updates the sent_at value with a Time.now but its not quite in the perfect format. It is close but not enough to pass. How can I make this test pass.
Here are some combinations I've tried in my assertions:
Time.now.utc
Date.today
Time.now
and a lot of to_time , to_datetime etc. How can I make the test pass?
Old but still valid... The output shows that the comparison is against UTC which would be Time.current
At this time you would probably use:
assert_in_delta object.sent_at, Time.current, 1
To tolerate <1 second difference
Using Time#to_i isn't the best solution. If the task you are running takes more than a second the comparison would fail. Even if your task is fast enough, this comparison would fail:
time = Time.now # 2018-04-18 3:00:00.990
# after 20ms
assert_equal Time.now.to_i, time.to_i # Fails
Time.now would be 2018-04-18 3:00:01.010 and to_i would give you 2018-04-18 3:00:01 and time was 2018-04-18 3:00:00.990 and to_i: 2018-04-18 3:00:00. So the assert fails.
So, sometimes the test would pass and others would fail, depending on when (in miliseconds) it starts.
The better solution is to freeze the Time. You could use a gem like Timecop or write your own code, like (using MiniTest):
current_time = Time.now
# You need Mocha gem to use #stubs
Time.stubs(:now).returns(current_time)
You can also use a block, so that after the block the clock is back to normal
# For this you don't need Mocha
Time.stub :now, current_time do # stub goes away once the block is done
assert your_task
end
I think it is easiest to use Time#to_i to compare the time in seconds.
assert_equals object.sent_at.to_i, Time.now.to_i # seconds
You can use Timecop gem: https://github.com/travisjeffery/timecop
def test
Timecop.freeze do # Freeze current time
Time.now # some value
...
Time.now # The same value as previous
end
end
I currently queue my DelayedJob like so:
Delayed::Job.enqueue MyJob.new, 5, 1.day.from_now
I'm looking for a way to set a different execution time:
Tomorrow morning at 9:30am PST
Does Rails have a helper that can take care of this? Thanks
If your rails server is not running in PST/PDT:
Time.use_zone("Pacific Time (US & Canada)") { 1.day.from_now.beginning_of_day + 9.5.hours }
If it is already running in PST/PDT, you can shorten it to:
1.day.from_now.beginning_of_day + 9.5.hours
Have a look at Time and TimeZone for more info.
When I run
rails server
or
rake -T
or some other rails script, it takes a lot of time, approx 1 minute.
What is the best way to determine what exactly is so slow ?
How can the speed be improved ?
Rails v is 3.0.3 run trough ruby 1.9.2 (RVM) - Linux
That is bothering me also, since I have switched to Rails 3.
To your second question: I found by digging through the framework that the initializers take about half the time of a simple rake or rails call before it actually starts doing its task.
If you put these simple timing lines into the loop of initializer calls in $GEM_PATH/gems/railties-3.0.3/lib/rails/initializable.rb (or piggy-back it if you like):
def run_initializers(*args)
return if instance_variable_defined?(:#ran)
t0 = Time.now
initializers.tsort.each do |initializer|
t = Time.now
initializer.run(*args)
puts("%60s: %.3f sec" % [initializer.name, Time.now - t])
end
puts "%60s: %.3f sec" % ["for all", Time.now - t0]
#ran = true
end
EDIT: Or, for railties 4.2.1:
def run_initializers(group=:default, *args)
return if instance_variable_defined?(:#ran)
t0 = Time.now
initializers.tsort.each do |initializer|
t = Time.now
initializer.run(*args) if initializer.belongs_to?(group)
puts("%60s: %.3f sec" % [initializer.name, Time.now - t])
end
puts "%60s: %.3f sec" % ["for all", Time.now - t0]
#ran = true
end
... you can follow up what happens. On my system, which is a 2.4 Core 2 Duo MacBook the initializers take about 7 seconds.
There are a few that are especially slow on my system. When I filter all out below a second, I get this result on my system:
load_active_support: 1.123 sec
active_support.initialize_time_zone: 1.579 sec
load_init_rb: 1.118 sec
set_routes_reloader: 1.291 sec
I am sure somebody (is it me?) will take some time to start there and optimize.
Our Rails 3.1 startup time was almost 1 minute (having a lot of gems)
Then we found out about some Ruby 1.9.3 tuning options on reddit:
http://www.reddit.com/r/ruby/comments/wgtqj/how_i_spend_my_time_building_rails_apps/c5daer4
export RUBY_HEAP_MIN_SLOTS=800000
export RUBY_HEAP_FREE_MIN=100000
export RUBY_HEAP_SLOTS_INCREMENT=300000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
export RUBY_GC_MALLOC_LIMIT=79000000
put this in your shell environment/profile/bashrc, and you are done.
We boosted our startup from 1 minute to 9 seconds
One workaround I use for this is to preload the rails environment with rails-sh. That way only the first rails/rake command is slow and the rest are pretty fast. Wrote a fuller answer to it in this question.
Another way I tried more recently and is compatible with the first is installing a patched ruby (with rvm or rubyenv or from source) and adjusting environment variables (see #stwienert's answer). The falcon patch and railsexpress patches both seem to pick up significant performance in ruby 1.9. Check out rvm/rubyenv on how to install patched rubies with them.
I used robokopp's tip here to discover that most of the time was being used in the build_middleware_stack and load_config_initializers steps for me. This is because I am using the omniauth gem that adds middlewares and perhaps has heavy initialization steps. I am on Rails 3.1.rc1, and my initialization takes almost 13 seconds (I am on ruby 1.9.2p180).
Even for brand new rails 3.1.rc1 app, the initialization takes ~3.6 seconds, with max time taken by load_config_initializers.
So I suggest you look for gems/your own code that have heavy initializers or add too many middlewares.