Concurrent moderating on a CMS - ruby-on-rails

I'm building a custom CMS which allow user to post message to it. Messages are short and like tweets.
The problem is that these message are moderated by real moderators. And there are multiple moderators working on the messages stream at the same time. And my concern is that what if these moderator are processing a same message. This is both inefficient and inconsistent. Since one message can be rejected by one moderator and then passed by another.
Therefore I want to build some kind of mechanism so that the CMS can distribute these messages to different moderator and avoiding duplication. The CMS is expecting to deal with large volume of message in a short time. Therefore this problem become more serious.
Any Idea is appreciated. Cheers.

I would do it like this:
Each logged-in moderator gets his own queue of messages to moderate
There is a central queue which will be used as a buffert
Posted messages go into the central queue
Each moderator queue fetches, say, 10 messages at a time.
When there's only 5 left in a moderator queue that queue will automatically fetch 10 new messages.
The downside is that you will need a central queue with a locking mechanism. If you want to avoid even that locking I propose one of two solutions:
Remove the central queue entirely and post messages on-the-fly into one of the moderator queues (maybe a randmoly chosen one), or,
Have a central queue and let each moderator have a randmoly chosen message from the top part of the queue (e.g., let them have one from the top-20). If there is "double moderation" due to absense of locking, just ignore the second moderation and accept the time-waste.

You could have the moderators pull the message off a queue before moderating. Sort-of like a check-out? So the moderator clicks something that assigns them a number of messages to process. They deal with those, then grab another batch off the queue.

Have your update action for Messages do this
def update
# perform regular update stuff ;)
rescue ActiveRecord::StaleObjectError
flash[:message] = "Someone else has updated this message"
redirect_to message_path(#message)
end
Check out http://railscasts.com/episodes/59-optimistic-locking or other pages on 'locking' (optimistic or otherwise) in Rails.

Related

Rails ApplicationCable and Channels best practice?

I am currently using Rails to prepare backed for reacting and react-native applications. The token is used for authentication.
I've got two features in my application, which require WebSockets:
Chat system(one chat open at a time)
Notification system
I see two ways to implement WebSockets:
Two separate channels
First channel: NotificationChannel to which I subscribe when client opens application and then I broadcast sth if it is required
Second channel: ChatChannel to which I subscribe when I open a chat, the code would look as follows:
class ChatChannel < ApplicationCable::Channel
def subscribed
authorize_chat_room_id(params[:chat_room_id]) #
stream_from "chat_channel_#{params[:chat_room_id]}"
end
# here methods for receiving data from client
# maybe some other methods for tracking if sb is typing and so on
# private authorize_chat_room_id method
end
In this scenario user is subscribed maximally to 2 channels and authorization is performed only once when subscribing to chat.
In this approach, I authorize only once the user, when he subscribes to channel, however, I don't know how having 2 channels opened instead of 1, affects performance on the server.
One channel
The single channel would be called: PersonalChannel, and would take care of all information flow, the code would look sth like this:
def subscribed
stream_from "personal_channel_#{current_user.id}"
end
def send_message(data)
if authorize_chat_room_id(data['chat_room_id'])
#create message
end
end
def start_typing(data)
if authorize_chat_room_id(data['chat_room_id'])
# some broadcast
end
end
And when I would broadcast, then I would send in the payload for example type(ex. 'MESSAGE_SEND'), and based on that, react client would print the message if chat would be open.
The advantage of this approach is that when sending the message I can broadcast only to one user and I won't get back message from the channel. But in this case, I have to authorize each time performed an action.
My question is which solution would be better in case of performance and what is generally accepted approach(to make Channels very general or to make each Channel take care of single task)?
On this issue, I would suggest that you consider prioritizing code maintenance concerns over performance.
The concept of channels allows us to decouple publishers from consumers. This simplifies a lot of code and helps with separation of concerns.
For example, a user posting in a chat room wouldn't need to know (nor manage) how many people are subscribed to that chat room, nor who they might be. These concerns are now handled by the pub/sub system rather than the user.
This separation of concerns makes adding or removing chat members much easier (you don't need to updated every user, you only need to update the pub/sub system).
This separation also prevents multiple copies of the data from being stored (the pub/sub system holds the only valid copy).
In the long run, by consolidating all the data into a single PersonalChannel, you'll be reintroducing the coupling between publishers and consumers - this will increase complexity and make maintenance mode difficult over time.
In the chatroom example, each user will need to grab a copy of all the room's members and send each message to all users. This increases the number of copies (of the member's list) as well as introduces synchronization concerns.
The advantage of this approach is that when sending the message I can broadcast only to one user and I won't get back message from the channel. But in this case, I have to authorize each time performed an action.
This advantage is actually something you can easily achieve using the first approach by subscribing each user to a personal channel as well as the other channels, subscribing to three channels instead of two.

Subscription Model in asp.net mvc

I am looking to design a notification service which on certain conditions when met would send status report to users.I am not able to understand how to save those conditions in database like model wise ( for example user is interested in seeing report where job is 100% or user is interested in report which has failed status).The approach I was thinking was to have either columns for each of those conditions or serialize those conditions and save it as json in db.
Please provide some insights to it?
Depending on your requirements you could look into a message queue based system where you would put a message on a queue to indicate that you want to send a report to someone.
A message handler like NServicebus can then monitor the queue and handle the actual sending of reports via email or other means.

Rails - updating flash message after asynchronous job

I am using beanstalkd plus backburner to run asynchronous jobs and I want to automatically update the flash message with some information after each job finishes executing.
I can get the flash message on the controller after a job finishes executing. After a page refresh, the flash message appears. But what I want, is the message to appear by itself. How can I do that?
I've been toying with this idea for a while,
something that avoids polling or setting up of websockets to do pushing,
one approach is to create a table of pending flash messages, index it by created_at, make it a has_many relationship of the user model and set a before_filter on application controller to render the flash messages that happened after last_request_at.
You set up a nice user method called User#message for example and say some_user.message "blah blah" and it'll render the message to the user.
Downside is a lot of possible db spam but since the flash message table is just timestamps, foreign key and a string column this should actually be pretty performant. However, considering your choice of beanstalkd this might not be an optimal solution for you.
Plan B (assuming you don't need the flashes to be persistent) you can keep the messages in Rails.cache and just clear the cache when you render.
A/B hybrid solution is to put these messages into something like mongo, redis etc
Just make sure it's all wrapped up in a nice, abstract API like some_user.message and you can switch between different approaches later on with ease

Scheduling events in Ruby on Rails

SO sorry if this is a duplicate, I tried searching for this but wasnt sure what search terms to use and didnt really find anything.
I have a Ruby on Rails app that will be used to send text messages out to users that contain a link to a multiple choice question probably using clickatell. The questions have a date and time associated with them. I want to make the ruby on rails app automatically send those SMS messages to the users' phones on those specified dates.
I don't really know how one would go about doing this. Can anyone point me in the general direction of a a way to schedule events like this in ruby on rails. I don't need an exact solution, maybe if someone could just clarify what exactly this is called so I can find some resources on line.
thanks
It seems the sending out your questions is not reoccuring? In this case I would not do this via a cronjob. I would do this via: https://github.com/bvandenbos/resque-scheduler
So whenever a question is getting scheduled you just add it to the delayed queue and resque-scheduler handles moving them on the correct working queue when its time has come.
This way you don't have to worry about some sort of polling cronjob, this will be done by resque-scheduler automatically. You also get asynchronous handling of sending out the SMSes via resque for free. So if you have to send lots and lots of SMS you can run them in parallel.
So it would go like this:
when a question is saved, you queue a message on the delayed queue in the future for sending out the question
when the date comes up, the message is moved onto 'ready to send'-queue, which is in charge of gathering all the users the question needs to be sent to.
for each of those users you create another message on the 'ready to send'-queue
the 'ready to send'-queue will then send out the actual SMSes
You then can run many workers on the 'ready to send'-queue and have the SMSes be sent out in parallel. You also get error handling for free with resque, it gahers all messages that resulted in an exception in a 'failure' queue, which you can then debug or reschedule again
You could use whenever to schedule events.

How to go about sending email x hours after a user signs up in Ruby on Rails?

How would I go about sending an email to a user, say, 48 hours after they sign up, in Ruby on Rails? Thanks!
As Joseph Daigle mentioned, you need to obviously record the exact date and time the user registered. After that, you need a cron running every certain number of minutes (every hour, for example) checking to see if there's any new users whose registration time is greater than 48 hours, send a mail to said user and mark that user as already emailed, so you don't email them again.
As per the actual mail sending, check out the following documentation page:
http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer
It has all you need to know to send mails with RoR.
I recommend that you use the latest version of BackgrounDRb to handle this. You can read about BackgrounDRb here: http://backgroundrb.rubyforge.org/
In order to queue a message for later delivery, the BackgrounDRb client code (in your application model's after_create callback, maybe) could look something like this:
MiddleMan(:email_worker).enq_send_email_task(:message => #message,
:job_key => "notify1",
:scheduled_at => Time.now + 48.hours)
You'd have to build a BackgrounDRb worker to handle sending the email:
# RAILS_ROOT/lib/workers/email_worker.rb
class EmailWorker < BackgrounDRb::MetaWorker
set_worker_name :email_worker
def send_email_task(message)
# ... Code to send the email message
end
end
Note that in order to use BackgrounDRb in this way, you have to use persistent job queues, so make sure you run the migration included with BackgrounDRb to set up the persistence table in your application.
BackgrounDRb is started separately from Rails (mongrel, apache, etc) using 'script/backgroundrb start', so make sure that you add the daemon to whatever process monitoring you're using (god, monit, etc) or that you create an /etc/init.d script for it.
First you're going to need a running daemon or background service which can poll your queue (probably from in a database) every few minutes.
The algorithm is pretty simple. Record the time of the user event in the queue. When the daemon checks that item in the queue, and the time difference is greater than 48 hours, prepare the e-mail to send.
You can queue jobs with a delay using async observer. Ideally, anything you have that isn't known to be instant (or very close to it) all the time should pass through something like that.
I wrote a plugin called acts_as_scheduled that may help you out.
acts_as_scheduled allows you to manage
scheduled events for your models.
A good example of this is scheduling
the update of RSS Feeds in a
background process using Cron or
BackgroundRB.
With acts_as_scheduled your schedule
manager can simply call
"Model.find_next_scheduled()" to grab
the next item from the database.
How I would approach this is by creating a scheduling controller, that will query the database for the next_scheduled and then use a mailer to send the message. The you set up a Cron Job to call the controller periodically using WGET or CURL. The advantage of the Cron/Controller approach is that no further infrastructure or configuration is required on the server and you avoid complicated threading code.
I think I'd be inclined to store the need for the email and the earliest time after which it should be sent, somewhere separate, then have my things-to-do task look at that. That way I only have to process as many records as there are emails to be sent, rather than examine every user every time, which would either get tedious or require an otherwise probably unnecessary index. As a bonus, if I had other tasks to be performed on some sort of a diarised basis, the same construct would be useful with little modification.

Resources