Best approach to create a background task - ruby-on-rails

I'm currently developing a Ruby on Rails application that on certain moment has to import a (at least for me) medium-large dataset using a third-party API. It has to do an average of 6000 API calls. One after another. It lasts about 20 minutes.
Right now I have made a rails task that does everything as I want (calls, write to db, etc). But now I want this task/code to be ALSO called from a button on the web. I know it's not a good approach to let the controller call the task so that's why I'm asking.
I want this import code to be available to be called from a controller and a task, because later I want to be able to call this task from a cronjob, and even if it's possible to have callbacks on the progress of the task on the controller, i.e. know how many calls are left.

I know it's not a good approach to let the controller call the task
There's nothing wrong with having a button trigger a background task like this, but of course you need to do so with care. For example, perhaps:
If the task is already running, don't let a second instance overlap.
If the task runs for too long, automatically kill it.
Carefully restrict who can trigger this.
There are many libraries available for implementing a progress bar, or you could even write a custom implementation. For example, see this blog post - which works by polling the current progress:
// app/views/exports/export_users.js.haml
:plain
var interval;
$('.export .well').show();
interval = setInterval(function(){
$.ajax({
url: '/progress-job/' + #{#job.id},
success: function(job){
var stage, progress;
// If there are errors
if (job.last_error != null) {
$('.progress-status').addClass('text-danger').text(job.progress_stage);
$('.progress-bar').addClass('progress-bar-danger');
$('.progress').removeClass('active');
clearInterval(interval);
}
progress = job.progress_current / job.progress_max * 100;
// In job stage
if (progress.toString() !== 'NaN'){
$('.progress-status').text(job.progress_current + '/' + job.progress_max);
$('.progress-bar').css('width', progress + '%').text(progress + '%');
}
},
error: function(){
// Job is no loger in database which means it finished successfuly
$('.progress').removeClass('active');
$('.progress-bar').css('width', '100%').text('100%');
$('.progress-status').text('Successfully exported!');
$('.export-link').show();
clearInterval(interval);
}
})
},100);
An variant approach you could consider is to use a websocket to see progress, rather than polling.

Convert the specific tasks into background jobs, i.e. (active job, sideqik), so your system can continue working while it's doing the tasks. Create classes for each task and call those classes within your background jobs or cronjobs.
One design pattern that could fit here is the "command" pattern, I gave you a list of things you can Google :).

Just move most of the code from the task to a module or method in a model. You can call this code from the task (as your do it now) or from a background job that would start through a controller when you press a button on a view.

Related

Cloud functions in Firebase trigger are not part of the transaction update

I am using the realtime database and I am using transactions to ensure the integrity of my data set. In my example below I am updating currentTime on every update.
export const updateTime = functions.database.ref("/users/{userId}/projects/{projectId}")
.onUpdate((snapshot) => {
const beforeData = snapshot.before.val();
const afterData = snapshot.after.val();
if (beforeData.currentTime !== afterData.currentTime) {
return Promise.resolve();
} else {
return snapshot.after.ref.update( {currentTime: new Date().getTime()})
.catch((err) =>{
console.error(err);
});
}
});
It seems the cloud function is not part of the transaction, but triggers multiple updates in my clients, which I try to avoid.
For example, I watched this starter tutorial which replaces :pizza: with a pizza emoji. In my client I would see :pizza: for one frame before it gets replaced with the emoji. I know, the pizza tutorial is just an example, but I am running into a similar issue. Any advice is highly appreciated!
Cloud Functions don't run as part of the database transaction indeed. They run after the database has been updated, and receive "before" and "after" snapshots of the affected data.
If you want a Cloud Function to serve as an approval process, the idiomatic approach is to have the clients write to a different location (typically called a pending queue) that the function listens to. The function then performs whatever operation it wants, and writes the result to the final location.

Is there any way to run perticular scenario in karate more than one time if specific condition met true?

I want to run my perticular scenario or feature file more than one time.
Let's say if user enter 5 then i want my url to be hit 5 times.
is it possible in karate? Any help would be appreciated
Yes, read the docs: https://github.com/intuit/karate#loops
But also see example below using dynamic scenario outlines:
EDIT: using a Background will not work in Karate 1.3.0 onwards, please refer to this example: https://stackoverflow.com/a/75155712/143475
Background:
* def fun = function(i){ return { name: 'User ' + (i + 1) } }
* def data = karate.repeat(5, fun)
Scenario Outline:
* url 'http://httpbin.org/anything'
* request __row
* method post
Examples:
| data |
So run this, see how it works and study how it works as well.
Note that data driven features is an alternate approach where you can call a second feature file in a loop. So for example after using karate.repeat() 5 times like in the above Background, you use data as the argument to a second feature file that hits your url.

Why do progress events stop firing when I add 'false' to createjs.LoadQueue()?

OK, the docs are messy at best. I have huge issues fading in and out preloaded assets if I do not add 'false' to the instance of PreloadJS. But when I add it I completely lose the progress event ... what is it that's so deeply hidden in the docs, that I cannot find anything about this?
And has anyone got a complete example of HOW to actually and properly load an array (actually an object) of images without losing the progress event AND still have an asset that behaves as expected when adding it to the DOM and fade it in?
This was also posted in a question on GitHub.
The short answer is that loading with tags (setting the first param useXHR to false) doesn't support granular progress events because downloading images with tags doesn't give progress events in the browser.
You can still get progress events from the LoadQueue any time an image loads, but each image will just provide a single "complete" event.
#Lanny True for that part, but in my case I was also missing the 'true' in .getResult(), and the createObjectURL() for the image data:
…
var preloader = new createjs.LoadQueue();
…
…
function handleFileLoad ( e ) {
var item = e.item,
result = preloader.getResult(item.id, true),
blob_url = URL.createObjectURL( result );
…
So, that I was actually able to handle the image data as a blob … I couldn't find anything close to 'createObjectURL' in the docs. I guess that renders the docs 'not complete' at best …

Block script execution in Firefox extension?

Probably looking for an answer to an age-old question, but I would like to block script execution. In my use-case blocking the browser is acceptable.
Also, in my use-case I am trying to do this from a Firefox extension, which means my code is "Chrome code", running in the browser environment.
This can easily be done by using a modal window, then programmatically closing the window. So this demonstrates that there is a blocking mechanism that exists.
Is there any way to achieve modal blocking without actually creating or opening the modal window? Some way to tap into the blocking mechanism used for modal windows?
I've done a lot of searching on this subject, but to no avail.
Using nsIProcess you can block the thread.
You can create an executable which has a sleep or usleep method or equivalent. Then run the process synchronously (nsIProcess.run) and set blocking argument to true.
Of course for portability you will need to create an executable appropriate for each platform you wish to support, and supply code for discrimination.
Basic code is something like the following. I have verified on 'nix (Mac OS X) this code to work, using a bash script with only the line sleep .03:
let testex = Components.classes["#mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsIFile);
testex.initWithPath("/Users/allasso/Desktop/pause.sh");
let process = Components.classes["#mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(testex);
let delay = 30; // convert this to milliseconds in the executable
process.run(true,[delay],1); // `run` method runs synchronously, first arg says to block thread
In an extension you probably would want to make your nsIFile file object more portable:
Components.utils.import("resource://gre/modules/FileUtils.jsm");
let testex = FileUtils.getFile("ProfD",["extension#moz.org","resources","pause.sh"]);
Of course keep in mind that Javascript is basically single-threaded, so unless you are blocking a thread spawned using Web Workers you will be freezing the entire UI during the sleep period (just like you would if you opened a modal window).
References:
https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcess
https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFile
https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Getting_special_files
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/basic_usage
OPTION 1
There is enterModalState and leaveModalState in nsIDOMWindowUtils here: MDN :: nsIDOMWindowUtils Reference
However they don't seem to work for me. This topic might explain why: nsIDOMWindowUtils.isInModalState() not working they topic says isInModalState is marked [noscript] which I see, but enterModalState and leaveModalState are not marked [noscript] I have no idea why it's not working.
What does work for me though is suppressEventHandling:
var utils = Services.wm.getMostRecentWindow('navigator:browser').
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.suppressEventHandling(true); //set arg to false to unsupress
OPTION 2
You can open a tiny window with the source window as the window you want to make modal and as dialog but open it off screen. Its dialog so it wont show a new window the OS tab bars. However hitting alt+f4 will close that win, but you can attach event listeners (or maybe use the utils.suppressEventHandling so keyboard doesnt work in it) to avoid the closing till you want it closed. Here's the code:
var sDOMWin = Services.wm.getMostRecentWindow(null);
var sa = Cc["#mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
var wuri = Cc["#mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
wuri.data = 'about:blank';
sa.AppendElement(wuri);
let features = "chrome,modal,width=1,height=1,left=-100";
if (PrivateBrowsingUtils.permanentPrivateBrowsing || PrivateBrowsingUtils.isWindowPrivate(sDOMWin)) {
features += ",private";
} else {
features += ",non-private";
}
var XULWindow = Services.ww.openWindow(sDOMWin, 'chrome://browser/content/browser.xul', null, features, sa);
/*
XULWindow.addEventListener('load', function() {
var DOMWindow = XULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
DOMWindow.gBrowser.selectedTab.linkedBrowser.webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL);
DOMWindow.gBrowser.swapBrowsersAndCloseOther(DOMWindow.gBrowser.selectedTab, aTab);
//DOMWindow.gBrowser.selectedTab = newTab;
}, false);
*/

Quartz.Net job doesn't fire continuously

I have a job scheduled in Application_start event using quartz.net, the trigger is fired every 1 min given by the variable repeatDurationTestData = "0 0/1 * * * ?";
The triggering starts when I first open the site, But stops after some random time when I close the browser and starts again when I open the site. Following is the code
IMyJob testData = new SynchronizeTestData();
IJobDetail jobTestData = new JobDetailImpl("Job", "Group", testData.GetType());
ICronTrigger triggerTestData = new CronTriggerImpl("Trigger", "Group", repeatDurationTestData);
_scheduler.ScheduleJob(jobTestData, triggerTestData);
DateTimeOffset? nextFireTime = triggerTestData.GetNextFireTimeUtc();
What Am i doing wrong here, Is this because of some misfire. Please suggest.
Thanks
At First I would use a simple trigger in this case as it takes a repeat interval and seems to fit better than the cron trigger would (from lesson 5 quartz.net website) :
SimpleTrigger trigger2 = new SimpleTrigger("myTrigger",
null,
DateTime.UtcNow,
null,
SimpleTrigger.RepeatIndefinitely,
TimeSpan.FromSeconds(60));
I would also recommend you don't put the quartz scheduler within the website. the main purpose of a job system is to work independently of anyother system so it generally fits naturally into a windows service. By putting it as part of the website you arn't guaranteed its going to keep going. If you loose the app pool or it restarts, you wont get a reliable result.
There is an example with the quartz.net download.
hope that helps.

Resources