 a sponsor talk by Muvit, the company we work at. And we want to talk about SideKick Scheduler, which is an extension for SideKick. There we go. So my name is Andreas, and this is Gian. We work for Muvit, we're based out of South America and Uruguay. And we've been growing, we've got people in Argentina and Colombia, and we also have an office in Austin. If you're in Austin, we're also always hiring, so go check us out. Yeah, so SideKick Scheduler, that's where it's hosted at. And I'll leave you with Gian now for a while, he'll explain a little more about what it is. Okay, so let's start with a couple of use cases that we have. In 2012, we were working on a project that, among other things, needed to daily fetch info related to credit level scores based on financial data, and apply interest to unpaid credit card debts. Also on Friday nights, we needed to send week activity summaries reports to a couple of email addresses. The first approach, which we thought of, was to set up current entries, which will execute rate tasks, which will execute rate tasks, performing the processes that I mentioned before. We thought it was a reasonable approach, but with some drawbacks. So why not run? The answer to that question is composed of, because it needs to start the RU interpreter in each run. We were using JRuby and take some time to start up and consumes a little bit of memory. So if we have a current entry that says, run this at 3 p.m., for example, it will need to start another JRuby process and consume some memory. There's no easy way to programmatically add, remove, enable, and disable current entries. Currents configuration will need to live outside of the apps configuration. It's not a portable solution because it relies on the operating system. And you can start and stop the scaler without affecting other current entries. There are also some other drawbacks, like current minimum resolution is at minute level, not at seconds level, and deploying the app in a cluster will turn to trigger duplicated tasks when in fact you need to run only once. Despite those drawbacks, we didn't discard current, but we saw if there was some other alternative solution. We found a gem in 2012, named a psychic scaler, whose purpose was to schedule psychic jobs at a specific time in the future. This wasn't what we were looking for, but we asked ourselves how hard can it be to add some current support to psychic scaler. Researching options, we stumbled upon Rufus scaler, which is a gem where you can use current syntax to schedule a call to a block of free calls. So we came up with idea of integrating Rufus into psychic scaler, adding that way, current support. That was in August 2012, and after doing that, we started using psychic scaler in the project we were working on. In 2013, Morton was fighting hard to actively maintain the gem, so he transferred the ownership to us. And in 2016, we added support for real jobs when psychic is acting as activity as a backend. So how do we use psychic scaler? A psychic scaler is just an extension like applying it to psychic. Using it is not really different than using psychic. First of all, we need to declare a regular psychic worker, which is a Ruby class, including psychic worker module, and that also responds to the performance of... The schedule configuration is placed inside psychic configuration file at the schedule key. So in that example, we have a hello world job that runs every minute when second is equal to zero. And when that specific point in time occurs, psychic scaler will push the hello world job into psychic. Okay, then you install the gem, of course, with gem install, or if you want to use bundler, you just hide into the gem file as usual. Then we run psychic as usual also. As we are here in the example, we are outside of race. We are telling psychic to require our Ruby file that contains the worker class. And that's it. Every minute, psychic scaler will enqueue the job and then psychic will perform the job. Okay, there are different scaling types and as we rely on Rufus, which is a separate gem, as we rely on Rufus, different scale types are supported. The common one, the most common one, most popular one is Krone, which runs every minute and it's the same Krone-like syntax that you are used to, the Krone tool, the standard Unix tool, you can use it in psychic scaler. Add type, which pushes a job once at a specific point in time. Every, that's it, pushes jobs in a current Y way following a given frequency. And in type, which pushes a job once after some time duration has elapsed. Okay, the main purpose of psychic scaler is to push jobs into psychic. This is the main idea. Letting psychic then random shows as usual. So how does psychic scaler works? When psychic scaler is required, when you require psychic scaler, it hooks into the startup and shut down psychic-like cycle method. We will explain then what those life-cycle events or phases are, okay? Then on the startup phase, we fetch the configuration, do the configuration and it starts Rufus scaler, which is Rufus scaler is just a thread that iterates over all the scales and invokes each Ruby blog when needed. We start Rufus scaler and set up a scale job for every one of the configuration jobs. Each handler in Rufus is responsible for store execution info into RADIS, verify that in case of crone and add shops, that show instance was not previously pushed and finally push the show into psychic random show as usual. While implementing this approach, we stumbled upon the challenge of managing multiple instances, multiple running instances at the same time. At first, the current jobs were meant to be run on one psychic instance, only one. And if multiple nodes were needed, only one node will run psychic scaler. While the others will host regular psychic instances, running shows pushed by a single one. Support for multiple nodes was added later for crone and add scale types. So right now it's possible to run multiple psychic scalers, scaler instances, having crone and add shops. Those crone and add shops will not run duplicate. All right, well, here. Otherwise I'll just shout and you'll hear me. Sorry about that. All right, let's talk a little bit about the future, which, yeah, honestly, the one thing that we want to do is we want to stop colluding the global psychic configuration, because right now we kind of load the configuration into the psychic configuration and kind of, you know, if psychic itself would try to do some other stuff, we could possibly have a collision there. So we want to take that away. We want to get every and end schedule types to work with multiple instances, you know. So you don't have to worry about that. And well, so one of the things we don't want to do is we don't want to work on psychic scheduler with stuff that is outside of the scope of just scheduling, right? So we want to say that psychic scheduler does that job and nothing more. Also, there's some test refactor that needs to be done and a little code-based refactor. All right, demo time. Now, I did make a video out of this, so I don't, you know, so stuff works. Hopefully, all right. So really one idea that we had in mind for the demo was for our repository or open source repository. If you have, let's not worry, let's try to ignore it. If you have some issues, and let's say, you know, the last comment and an issue was from you and you're not really trying to implement that issue and maybe it falls into an activity and the original poster maybe lost interest or really doesn't care about it anymore. We thought that maybe after a month or two you might want to automatically close it. So that was the general idea we had, but since we didn't have a few months to generate all that test data, what we did is to have the invalid and duplicate marked issues. So we created a little code and psychic scheduler thing that runs every minute and checks those marked issues and just closes them. So right now you go to the code base and sorry if that's a little small. You run psychic scheduler and you'll see we have a cron job there in Qt which is called issue clean up there. Schedule to run every minute and then not in a minute but in a few seconds we'll see here that we'll start running. We'll find the issues, just close them and so that you also believe me. We're gonna go over to the browser now and refresh it. You'll see that they were closed. And obviously if nothing is open the job will run and it will do nothing so it will not find any issue. And so if we go back and now let's say a few months past and no activity was on that issue it will simulate that by marking it as a duplicate or a move and the next time the job runs it will just clean it up. Okay, so as a bonus to this talk we wanted to show you what it takes or what tools you have available to write a psychic extension. So you got these four events in the psychic lifecycle that you can hook into. Like John mentioned earlier we hook into startup and shutdown events. So that's where we, so in the startup event we load up the configuration, start the roof of scheduler and then in the shutdown I don't think it does a lot it just dies, clean up the thread and die. Yeah, and then one thing that I think we didn't display here but when you run the web extension psychic scheduler would actually create another tab for you and it will show you the jobs that are currently enqueued and it will also let you disable them. So you have without logging into the server you can control it via the web. And so to do that you just got to create like a module extension where you got to hook into that method called self.registered and then you can with that app.get and put, you can put the path in there and it's a way for you to define actions and return HTML or whatever data you want to render there on that tab. And once you do that you have to in the initializer I think you have to register the extension on psychic so on the psychic web you got to register it and you can add the tab and there you put the name and the path that it points to and the path really is back here when you do app.get myex, that's the path you got to use there. And yeah, you also have the option of adding locales in case you support various languages. With psychic scheduler it's just those four I think. A little more, okay, so we're just showing four. So you can support more than one language. And yeah, so this example is up on the move it in that URL, although if you wanna check out the psychic extension, maybe wanna check out psychic scheduler. All right, now about feedback and collaboration since this is obviously an open source project. It has had since, I mean we didn't create it, we just took over and maintenance a few years ago. So over the course of the years it has had 43 contributors. And right now we have like six hours a week allocated for four issues. So if you find anything wrong with psychic scheduler you'll get some time for us to fix it. And as always, help and feedback is very appreciated. And as a last thing, we have some other open source libraries, we got Rusen, which is a simple exception notification gem that you can use for logging and sending errors in any Ruby app that you have. We have Rui, which is a lightweight rules evaluator for context-driven conditional expressions. You can do some weird stuff with that. Then we have Angus, so the name for that was a real story. In Uruguay, we're a meat-loving country, we like to eat a lot of meat, so Angus obviously is a famous cow, a good one. But it really doesn't have to do anything with that. We just thought it was cool. What it is, it's a REST-like API framework and it will run on RAC, so if you're already using a RAC app you can use that to serve your API. And it already generates, so while you're writing it kind of generates documentation on the way. And yeah, so you can use that for RESTful APIs. And then we have Fakeit, which is not really something for Ruby, it's an Android fake data, or fake, but realistic data generator, so you can generate names, emails, dates, and country names, which it will be fake, but it will be kind of real, so you don't want to generate just a few letters for an email, you need a certain format. So, but that is for Android tests, basically. Yeah, and that's it for us, pretty short. So the question is, how do we manage the second resolution within the scheduler? Well, in fact, we use Rufus scale for that. Rufus just starts up a thread and every, between some milliseconds period, like, I don't know, 500, I think, it checks its loops through all the schedules and asks if the scheduler, the schedule has to run. And in that case, it calls the Ruby block. So it's not just like hooks with some events from the operative system, it just makes a loop, it slips, loops again, it slips, loops again. So it's, that means that your Ruby block will not run at 2 p.m., 0 minutes, 0 seconds, 0 milliseconds, it will run at, I don't know, 119 milliseconds, okay? It's not, it's not really precise, okay? But yeah, yeah, what assures is that it will run in that second, in that second. Thank you very much, everyone for joining.