I am trying to build quizup like app and want to send broadcast every 10 second with a random question for 2 minutes. How do I do that using rails ? I am using action cable for sending broadcast. I can use rufus-scheduler for running an action every few seconds but I am not sure if it make sense to use it for my use case .
Simplest solution would be to fork a new thread:
Thread.new do
duration = 2.minutes
interval = 10.seconds
number_of_questions_left = duration.seconds / interval.seconds
while(number_of_questions_left > 0) do
ActionCable.server.broadcast(
"some_broadcast_id", { random_question: 'How are you doing?' }
)
number_of_questions_left -= 1
sleep(interval)
end
end
Notes:
This is only a simple solution of which you are actually ending up more than 2.minutes of total run time, because each loop actually ends up sleeping very slightly more than 10 seconds. If this discrepancy is not important, then the solution above would be already sufficient.
Also, this kind-of-scheduler only persists in memory, as opposed to a dedicated background worker like sidekiq. So, if the rails process gets terminated, then all currently running "looping" code will also be terminated as well, which you might intentionally want or not want.
If using rufus-scheduler:
number_of_questions_left = 12
scheduler = Rufus::Scheduler.new
# `first_in` is set so that first-time job runs immediately
scheduler.every '10s', first_in: 0.1 do |job|
ActionCable.server.broadcast(
"some_broadcast_id", { random_question: 'How are you doing?' }
)
number_of_questions_left -= 1
job.unschedule if number_of_questions_left == 0
end
I wanted to group the total checkin in a day using the groupdate gem with this code.
from = Time.zone.now.beginning_of_day
to = Time.zone.now.end_of_day
customer_checkins = CustomerCheckin.where(created_at: from..to, account_id: 139)
hours = customer_checkins.group_by_hour_of_day(:created_at).count
The result is weird because all counts are tagged into hour 0 instead of being tagged to its specific hour of day. The result is shown below.
{0=>677, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0, 8=>0,
9=>4, 10=>0, 11=>0, 12=>0, 13=>0, 14=>0, 15=>0, 16=>0, 17=>0,
18=>0, 19=>0, 20=>0, 21=>0, 22=>0, 23=>0}
What could be the problem with this one?
You can run your code in rails c.The console will pop up your sql query,so it may help you to find out the problem.
I guess group_by_hour_of_day is your CustomerCheckin model function.I don't now whether this function has trim your :created_at format. By default the :created_at will contain the second like "%Y-%m-%d %H:%M:%S",so the sql will group by base on the second not the hour. You may try to trim the :created_at format. Like
DateTime.parse("2015-05-08 02:30:01 +0800").strftime("%Y-%m-%d %H")
=> "2015-05-08 02"
Hope it will help you.
Per docs I thought it would be (for everyday at 3pm)
daily.hour_of_day(15)
What I'm getting is a random mess. First, it's executing whenever I push to Heroku regardless of time, and then beyond that, seemingly randomly. So the latest push to Heroku was 1:30pm. It executed: twice at 1:30pm, once at 2pm, once at 4pm, once at 5pm.
Thoughts on what's wrong?
Full code (note this is for the Fist of Fury gem, but FoF is heavily influenced by Sidetiq so help from Sidetiq users would be great as well).
class Outstanding
include SuckerPunch::Job
include FistOfFury::Recurrent
recurs { daily.hour_of_day(15) }
def perform
ActiveRecord::Base.connection_pool.with_connection do
# Auto email lenders every other day if they have outstanding requests
lender_array = Array.new
Inventory.where(id: (Borrow.where(status1:1).all.pluck("inventory_id"))).each { |i| lender_array << i.signup.id }
lender_array.uniq!
lender_array.each { |l| InventoryMailer.outstanding_request(l).deliver }
end
end
end
Maybe you should use:
recurrence { daily.hour_of_day(15) }
instead of recurs?
I cant figure out how to get lua to do any common timing tricks, such as
sleep - stop all action on thread
pause/wait - don't go on to the next
command, but allow other code in the
application to continue
block - don't go on to next command until the
current one returns
And I've read that a
while os.clock()<time_point do
--nothing
end
eats up CPU time.
Any suggestions? Is there an API call I'm missing?
UPDATE: I wrote this question a long time ago trying to get WOW Lua to replay actions on a schedule (i.e. stand, wait 1 sec, dance, wait 2 sec, sit. Without pauses, these happen almost all in the same quarter second.) As it turned out WOW had purposely disabled pretty much everything that allows doing action on a clock because it could break the game or enable bots. I figured to re-create a clock once it had been taken away, I'd have to do something crazy like create a work array (with an action and execution time) and then register an event handler on a bunch of common events, like mouse move, then in the even handler, process any action whose time had come. The event handler wouldn't actually happen every X milliseconds, but if it was happening every 2-100 ms, it would be close enough. Sadly I never tried it.
[I was going to post this as a comment on John Cromartie's post, but didn't realize you couldn't use formatting in a comment.]
I agree. Dropping it to a shell with os.execute() will definitely work but in general making shell calls is expensive. Wrapping some C code will be much quicker at run-time. In C/C++ on a Linux system, you could use:
static int lua_sleep(lua_State *L)
{
int m = static_cast<int> (luaL_checknumber(L,1));
usleep(m * 1000);
// usleep takes microseconds. This converts the parameter to milliseconds.
// Change this as necessary.
// Alternatively, use 'sleep()' to treat the parameter as whole seconds.
return 0;
}
Then, in main, do:
lua_pushcfunction(L, lua_sleep);
lua_setglobal(L, "sleep");
where "L" is your lua_State. Then, in your Lua script called from C/C++, you can use your function by calling:
sleep(1000) -- Sleeps for one second
If you happen to use LuaSocket in your project, or just have it installed and don't mind to use it, you can use the socket.sleep(time) function which sleeps for a given amount of time (in seconds).
This works both on Windows and Unix, and you do not have to compile additional modules.
I should add that the function supports fractional seconds as a parameter, i.e. socket.sleep(0.5) will sleep half a second. It uses Sleep() on Windows and nanosleep() elsewhere, so you may have issues with Windows accuracy when time gets too low.
You can't do it in pure Lua without eating CPU, but there's a simple, non-portable way:
os.execute("sleep 1")
(it will block)
Obviously, this only works on operating systems for which "sleep 1" is a valid command, for instance Unix, but not Windows.
Sleep Function - Usage : sleep(1) -- sleeps for 1 second
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do
end
end
Pause Function - Usage : pause() -- pause and waits for the Return key
function pause()
io.stdin:read'*l'
end
hope, this is what you needed! :D - Joe DF
for windows you can do this:
os.execute("CHOICE /n /d:y /c:yn /t:5")
It doesn't get easier than this. Sleep might be implemented in your FLTK or whatever, but this covers all the best ways to do standard sort of system sleeps without special event interrupts. Behold:
-- we "pcall" (try/catch) the "ex", which had better include os.sleep
-- it may be a part of the standard library in future Lua versions (past 5.2)
local ok,ex = pcall(require,"ex")
if ok then
-- print("Ex")
-- we need a hack now too? ex.install(), you say? okay
pcall(ex.install)
-- let's try something else. why not?
if ex.sleep and not os.sleep then os.sleep = ex.sleep end
end
if not os.sleep then
-- we make os.sleep
-- first by trying ffi, which is part of LuaJIT, which lets us write C code
local ok,ffi = pcall(require,"ffi")
if ok then
-- print("FFI")
-- we can use FFI
-- let's just check one more time to make sure we still don't have os.sleep
if not os.sleep then
-- okay, here is our custom C sleep code:
ffi.cdef[[
void Sleep(int ms);
int poll(struct pollfd *fds,unsigned long nfds,int timeout);
]]
if ffi.os == "Windows" then
os.sleep = function(sec)
ffi.C.Sleep(sec*1000)
end
else
os.sleep = function(sec)
ffi.C.poll(nil,0,sec*1000)
end
end
end
else
-- if we can't use FFI, we try LuaSocket, which is just called "socket"
-- I'm 99.99999999% sure of that
local ok,socket = pcall(require,"socket")
-- ...but I'm not 100% sure of that
if not ok then local ok,socket = pcall(require,"luasocket") end
-- so if we're really using socket...
if ok then
-- print("Socket")
-- we might as well confirm there still is no os.sleep
if not os.sleep then
-- our custom socket.select to os.sleep code:
os.sleep = function(sec)
socket.select(nil,nil,sec)
end
end
else
-- now we're going to test "alien"
local ok,alien = pcall(require,"alien")
if ok then
-- print("Alien")
-- beam me up...
if not os.sleep then
-- if we still don't have os.sleep, that is
-- now, I don't know what the hell the following code does
if alien.platform == "windows" then
kernel32 = alien.load("kernel32.dll")
local slep = kernel32.Sleep
slep:types{ret="void",abi="stdcall","uint"}
os.sleep = function(sec)
slep(sec*1000)
end
else
local pol = alien.default.poll
pol:types('struct', 'unsigned long', 'int')
os.sleep = function(sec)
pol(nil,0,sec*1000)
end
end
end
elseif package.config:match("^\\") then
-- print("busywait")
-- if the computer is politically opposed to NIXon, we do the busywait
-- and shake it all about
os.sleep = function(sec)
local timr = os.time()
repeat until os.time() > timr + sec
end
else
-- print("NIX")
-- or we get NIXed
os.sleep = function(sec)
os.execute("sleep " .. sec)
end
end
end
end
end
For the second request, pause/wait, where you stop processing in Lua and continue to run your application, you need coroutines. You end up with some C code like this following:
Lthread=lua_newthread(L);
luaL_loadfile(Lthread, file);
while ((status=lua_resume(Lthread, 0) == LUA_YIELD) {
/* do some C code here */
}
and in Lua, you have the following:
function try_pause (func, param)
local rc=func(param)
while rc == false do
coroutine.yield()
rc=func(param)
end
end
function is_data_ready (data)
local rc=true
-- check if data is ready, update rc to false if not ready
return rc
end
try_pause(is_data_ready, data)
I would implement a simple function to wrap the host system's sleep function in C.
Pure Lua uses only what is in ANSI standard C. Luiz Figuereido's lposix module contains much of what you need to do more systemsy things.
require 'alien'
if alien.platform == "windows" then
kernel32 = alien.load("kernel32.dll")
sleep = kernel32.Sleep
sleep:types{ret="void",abi="stdcall","uint"}
else
-- untested !!!
libc = alien.default
local usleep = libc.usleep
usleep:types('int', 'uint')
sleep = function(ms)
while ms > 1000 do
usleep(1000)
ms = ms - 1000
end
usleep(1000 * ms)
end
end
print('hello')
sleep(500) -- sleep 500 ms
print('world')
I agree with John on wrapping the sleep function.
You could also use this wrapped sleep function to implement a pause function in lua (which would simply sleep then check to see if a certain condition has changed every so often). An alternative is to use hooks.
I'm not exactly sure what you mean with your third bulletpoint (don't commands usually complete before the next is executed?) but hooks may be able to help with this also.
See:
Question: How can I end a Lua thread cleanly?
for an example of using hooks.
You can use:
os.execute("sleep 1") -- I think you can do every command of CMD using os.execute("command")
or you can use:
function wait(waitTime)
timer = os.time()
repeat until os.time() > timer + waitTime
end
wait(YourNumberHere)
You want win.Sleep(milliseconds), methinks.
Yeah, you definitely don't want to do a busy-wait like you describe.
It's also easy to use Alien as a libc/msvcrt wrapper:
> luarocks install alien
Then from lua:
require 'alien'
if alien.platform == "windows" then
-- untested!!
libc = alien.load("msvcrt.dll")
else
libc = alien.default
end
usleep = libc.usleep
usleep:types('int', 'uint')
function sleep(ms)
while ms > 1000 do
usleep(1000)
ms = ms - 1000
end
usleep(1000 * ms)
end
print('hello')
sleep(500) -- sleep 500 ms
print('world')
Caveat lector: I haven't tried this on MSWindows; I don't even know if msvcrt has a usleep()
I started with Lua but, then I found that I wanted to see the results instead of just the good old command line flash. So i just added the following line to my file and hey presto, the standard:
please press any key to continue...
os.execute("PAUSE")
My example file is only a print and then a pause statment so I am sure you don't need that posted here.
I am not sure of the CPU implications of a running a process for a full script. However stopping the code mid flow in debugging could be useful.
I believe for windows you may use: os.execute("ping 1.1.1.1 /n 1 /w <time in milliseconds> >nul as a simple timer.
(remove the "<>" when inserting the time in milliseconds) (there is a space between the rest of the code and >nul)
cy = function()
local T = os.time()
coroutine.yield(coroutine.resume(coroutine.create(function()
end)))
return os.time()-T
end
sleep = function(time)
if not time or time == 0 then
time = cy()
end
local t = 0
repeat
local T = os.time()
coroutine.yield(coroutine.resume(coroutine.create(function() end)))
t = t + (os.time()-T)
until t >= time
end
You can do this:
function Sleep(seconds)
local endTime = os.time() + seconds
while os.time() < endTime do
end
end
print("This is printed first!")
Sleep(5)
print("This is printed 5 seconds later!")
You could try this:
function wait(seconds)
local start = os.time()
repeat until os.time() > start + seconds
end
wait(5) print("cargo. Cargo what? Cargo, storage.") -- waits 5 seconds and then prints
It worked for me in Lua CLI.
This should work:
os.execute("PAUSE")