hi juri, i haven't done a full review, however, your approach via JobRunnableAdapter looks fine. with an optional lookup, you would even get rid of the default implementation. (+ imo it's a spi and not an api)
for different requirements (in >different< projects) we did all 3 approaches mentioned in this thread (and even some "hybrid" approaches e.g. based on apache ignite). that was one of the reasons for adding SchedulerBaseConfig.JobCustomization.RUNNABLE_ADAPTER_CLASS_NAME. quite soon i'll summarize and share an approach i (and several others) did over and over the past few years. in 2017 i suggested to add a simple abstraction for it (to ds), however, maybe we can reach an agreement this time... regards, gerhard Am Sa., 9. Feb. 2019 um 11:10 Uhr schrieb Juri Berlanda < [email protected]>: > Hi all, > > I created an issue at > https://issues.apache.org/jira/browse/DELTASPIKE-1369. I already have a > working prototype, though code polishing is still needed. > > @gerhard: I didn't know about the Listeners in Quartz, but I agree: what > I propose is pretty much another way of doing > TriggerListener.vetoJobExecution(Trigger trigger, JobExecutionContext > context). But I guess (please correct me if I'm wrong) these Listeners > cannot use Injection (except via BeanProvider, BeanManager and stuff > like that). My approach - being a CDI Bean - would provide support for > @Inject out of the box for any customizing bean. > > In addition, the Listeners seem to not provide a mechanism to prevent > the scheduler from starting all together (see my initial use case in the > first mail of this thread). > > I propose we move the discussion to the Jira issue. I also have listed > some minor points I'd like your feedback on. > > Cheers, > > Juri > |||| > > On 2/4/19 9:29 PM, Gerhard Petracek wrote: > > hi @ all, > > > > limiting it to the scheduler-module is possible - but if it is the only > > use-case, it wouldn't be needed, because it's easier to use > > Scheduler#unwrap to register your own TriggerListener via > > #getListenerManager. in the end such use-cases are the reason for > > Scheduler#unwrap. > > > > regards, > > gerhard > > > > > > > > Am Mo., 4. Feb. 2019 um 17:26 Uhr schrieb Mark Struberg > > <[email protected]>: > > > >> doesn't sound wrong - actually sounds really fine ;) > >> > >> Do you probably want to provide a ticket and patch? > >> > >> LieGrue, > >> strub > >> > >>> Am 04.02.2019 um 14:19 schrieb Juri Berlanda < > [email protected] > >>> : > >>> > >>> Hi, > >>> > >>> I still think it would be nice to just have a simple mechanism telling > >> "Just don't start here". > >>> I'm sceptic on a.) and b.) because they would introduce a database > >> binding to DeltaSpike, which I think may make it hard to use in some > stacks > >> (think projects running on NoSQL databases). In addition, I think > something > >> similar as you proposed in a.) can already be achieved by running > Quartz in > >> ClusteredMode, though I never tried that. > >>> What I would propose is some pluggable Bean (via Alternative, > >> Specializes or stuff like that) with 2 functions: > >>> boolean isSchedulerEnabled(); > >>> > >>> boolean shouldJobBeStarted(Class<T>); > >>> > >>> The default implementation would return true on both. Any Alternative > >> could then return false on isSchedulerEnabled() to fully disable it > >> (lowering overall overhead in a scenario as mine), or do something > smart in > >> shouldJobBeStarted() to determine at Runtime whether a specific Job > should > >> be ran on the current node (should accomodate for your usecase). > >>> What do you think? > >>> > >>> Cheers, > >>> > >>> Juri > >>> > >>> On 1/30/19 9:13 AM, Mark Struberg wrote: > >>>> Hi folks! > >>>> > >>>> Yes, that solution works. > >>>> > >>>> I had the same problem (multiple nodes, code should only run once). > >>>> Pinning to one cluster node is a possible solution, but if that one > >> node is down then the processing stops. > >>>> I went for another solution. I wrote some Interceptor which basically > >> guards against a central DB. > >>>> There are 2 different strategies: > >>>> a.) Keep an pesimistic lock on a row in a DB. One row per Class or > >> locking key. (DB must support row locking). > >>>> Pro: easy to implement. Most work is done by the database > >>>> Con: If the whole thread gets stuck then you're in back luck. One is > >> also bound to the maximum transaction timeout. > >>>> So if you want to have a task running for one hour (doing e.g. > >> multiple transaction batches) you cannot use this strategy. > >>>> b.) Have a 'watchdog' table which remembers the node and whether > >> active. 2 Minutes not updated means that the task blew up and another > node > >> might take up. > >>>> Pro: sufficiently easy to implement. A background thread which is a > >> watchdog and uipdates the 'lastActive' timestamp in the DB in the > >> background. > >>>> Con: It takes a while for another node to pick up the work. Minimum > 2 > >> minutes. We also need a clear 'nodeId'. That might be the IP, but if > >> multiple JVMs run on the same box then you need some custom identifier. > The > >> JVM id would be a candidate as it is unique. Otoh a restart would mean 2 > >> minutes pause. > >>>> c.) no database at all but network based locking. Might be easier or > >> harder to setup depending on your infrastructure and security measures. > >>>> Should we implement any of these in DeltaSpike? > >>>> Which one makes more sense? > >>>> (I personally went for option b for my use cases) > >>>> > >>>> LieGrue, > >>>> strub > >>>> > >>>> > >>>>> Am 28.01.2019 um 13:28 schrieb Juri Berlanda < > >> [email protected]>: > >>>>> Hi, > >>>>> > >>>>> just for completeness sake: I was able to achieve what I wanted using > >> a custom config source. I presume this is not how it is supposed to be, > but > >> it works: > >>>>> public class SchedulerConfigSource implements ConfigSource { > >>>>> private static final String CLASS_DEACTIVATOR_KEY = > >> "org.apache.deltaspike.core.spi.activation.ClassDeactivator"; > >>>>> private static final String CLASS_DEACTIVATOR_VALUE = > >> "org.apache.deltaspike.core.impl.activation.DefaultClassDeactivator"; > >>>>> private static final String SCHEDULER_DISABLED_KEY = > >> "deactivate.org.apache.deltaspike.scheduler.impl.SchedulerExtension"; > >>>>> private final int ordinal; > >>>>> > >>>>> SchedulerConfigSource(int ordinal) { > >>>>> this.ordinal = ordinal; > >>>>> } > >>>>> > >>>>> @Override > >>>>> public int getOrdinal() { > >>>>> return ordinal; > >>>>> } > >>>>> > >>>>> @Override > >>>>> public Map<String, String> getProperties() { > >>>>> return Stream.of(CLASS_DEACTIVATOR_KEY, > SCHEDULER_DISABLED_KEY) > >>>>> .collect(Collectors.toMap(Function.identity(), > >> this::getPropertyValue)); > >>>>> } > >>>>> > >>>>> @Override > >>>>> public String getPropertyValue(String key) { > >>>>> if (CLASS_DEACTIVATOR_KEY.equals(key)) > >>>>> return CLASS_DEACTIVATOR_VALUE; > >>>>> if (SCHEDULER_DISABLED_KEY.equals(key)) > >>>>> return Boolean.toString(!isSchedulerNode()); > >>>>> return null; > >>>>> } > >>>>> > >>>>> private boolean isSchedulerNode() { > >>>>> // Evaluate the condition here > >>>>> } > >>>>> > >>>>> @Override > >>>>> public String getConfigName() { > >>>>> return "SchedulerConfigSource"; > >>>>> } > >>>>> > >>>>> @Override > >>>>> public boolean isScannable() { > >>>>> return false; > >>>>> } > >>>>> } > >>>>> > >>>>> Thought I may as well add it if somebody else. And don't forget to > >> register the ConfigSource. > >>>>> I presume there is a better way to achieve this. If you know of one, > >> please let me know. > >>>>> Cheers, > >>>>> > >>>>> Juri > >>>>> > >>>>> On 1/25/19 3:56 PM, Juri Berlanda wrote: > >>>>>> I was able to achieve similar with Deltaspike's own Deactivable. It > >> does work, i.e. I can set: > >>>>>> > >> > org.apache.deltaspike.core.spi.activation.ClassDeactivator=org.apache.deltaspike.core.impl.activation.DefaultClassDeactivator > >> deactivate.org.apache.deltaspike.scheduler.impl.SchedulerExtension=true > >>>>>> in my configs, and Scheduler stays off. But - as mentioned - I need > >> this to be evaluated on system startup, not at Config level. So I tried > >> implementing my own SchedulerExtension like: > >>>>>> public class MySchedulerExtension extends SchedulerExtension { > >>>>>> @Override > >>>>>> protected void init(@Observes BeforeBeanDiscovery > >> beforeBeanDiscovery) { > >>>>>> if (isSchedulerNode()) > >>>>>> super.init(beforeBeanDiscovery); > >>>>>> } > >>>>>> } > >>>>>> > >>>>>> I can register it via SPI and from the logs I see it is indeed > >> initialized, while SchedulerExtension is not. Victory, right? Not > quite... > >>>>>> I am testing this with OpenWebbeans 2.0.6., and I face the problem, > >> that CDI now complains about ambiguous Bean for SchedulerExtension in > >> SchedulerProducer (which I can see where that comes from), but I am just > >> not able to exclude SchedulerProducer - I wouldn't even need it. I tried > >> various combinations of @Specialize, @Alternative and <scan><exclude > >> .../></scan>, but none of them seem to work. I guess the reason for it > is > >> the CDI 1.0 beans.xml in scheduler-impl? Can anybody confirm? Would it > be > >> possible to move higher - 1.1 at least for getting support for > >> <scan><exclude .../></scan>? > >>>>>> This leads me to the assumption, that scheduler-impl's > >> SchedulerExtension is just not extensible at the moment. Or did anybody > >> succeed in such an endeavor? > >>>>>> Since I do not want to patch the implementation, my next guess is to > >> implement a custom ConfigSource, which evaluates isSchedulerNode() and > sets > >> deactivate.org.apache.deltaspike.scheduler.impl.SchedulerExtension > >> accordingly. Does that make sense? > >>>>>> Kind regards, > >>>>>> > >>>>>> Juri > >>>>>> > >>>>>> On 1/24/19 9:04 PM, Alex Roytman wrote: > >>>>>>> in my case i need to be able to turn it on/off on demand and I only > >> have > >>>>>>> couple of daily tasks so for me it was good enough > >>>>>>> If if you just need to do it on startup by node type you could bind > >> it to a > >>>>>>> property > >>>>>>> @Scheduled(cronExpression = "{reindex.schedule}") > >>>>>>> public class ReindexTask implements org.quartz.Job { > >>>>>>> ... > >>>>>>> and that property could probably be a cron expression which never > >> fire on > >>>>>>> all of your nodes but the scheduler > >>>>>>> not nice but the whole thing is rather static - admittedly i did > not > >> dig > >>>>>>> very deep > >>>>>>> > >>>>>>> > >>>>>>> On Thu, Jan 24, 2019 at 2:44 PM Juri Berlanda < > >> [email protected]> > >>>>>>> wrote: > >>>>>>> > >>>>>>>> Thanks for the quick reply. I thought about that, but I don't like > >> this > >>>>>>>> solution, since it involves to much boilerplate for my taste. In > >>>>>>>> addition, I find it cumbersome having to add these 2 lines in > every > >>>>>>>> single task. I also thought about having an abstract base class > for > >> this > >>>>>>>> purpose, but I'm not happy with the solution... > >>>>>>>> > >>>>>>>> In short: I hoped for a cleaner solution. > >>>>>>>> > >>>>>>>> On 1/24/19 7:03 PM, Alex Roytman wrote: > >>>>>>>>> Let the scheduler run and execute your task but inside of the > task > >>>>>>>> itself > >>>>>>>>> check if you want to execute your logic or short circuit it to > >> noop. > >>>>>>>> Since > >>>>>>>>> you do not run it often should not be an overhead and it will let > >> you > >>>>>>>> fail > >>>>>>>>> over for any mode to execute it as long as you have a mechanism > to > >> lock > >>>>>>>> on > >>>>>>>>> something and record execution result to avoid simultaneous > >> execution or > >>>>>>>>> double exexution > >>>>>>>>> > >>>>>>>>> On Thu, Jan 24, 2019, 12:37 PM Juri Berlanda < > >> [email protected] > >>>>>>>>> wrote: > >>>>>>>>> > >>>>>>>>>> Hi, > >>>>>>>>>> > >>>>>>>>>> I am currently trying to implement scheduled jobs using > >> DeltaSpike's > >>>>>>>>>> Scheduler module, and I really like how little boilerplate I > need > >> for > >>>>>>>>>> getting it up and running. > >>>>>>>>>> > >>>>>>>>>> Our application runs on multiple nodes, but the tasks are very > >>>>>>>>>> inexpensive, run only once a day, and I don't need failover - if > >> they > >>>>>>>>>> fail once, and succeed the day after its totally fine. Therefore > >> I'd > >>>>>>>>>> like to avoid setting up Quartz in clustered mode. But I still > >> want the > >>>>>>>>>> Jobs to only run once. So my idea was to restrict the execution > >> of the > >>>>>>>>>> jobs to a single scheduler node. > >>>>>>>>>> > >>>>>>>>>> So my question is: Is it possible to somehow hook into the > >> Scheduler > >>>>>>>>>> module to say something like: > >>>>>>>>>> > >>>>>>>>>> if (isSchedulerNode()) > >>>>>>>>>> startScheduler(); > >>>>>>>>>> else > >>>>>>>>>> doNothing(); > >>>>>>>>>> > >>>>>>>>>> It would be perfect if said isSchedulerNode() could be evaluated > >> on > >>>>>>>>>> system startup (e.g. acquire a distributed lock) and would not > >> rely on > >>>>>>>>>> static values (e.g. config files, environment variables, etc.). > >>>>>>>>>> > >>>>>>>>>> I can see how this is a bad idea in general (no load-balancing, > no > >>>>>>>>>> failover) and I do have some ideas on how I would implement > that. > >> But > >>>>>>>>>> for these jobs I just don't care about any of this, so I'd like > >> to avoid > >>>>>>>>>> having to set up a whole lot of infrastructure around my > >> application > >>>>>>>>>> just to see this working. > >>>>>>>>>> > >>>>>>>>>> Is there a possibility to achieve this without patching > >>>>>>>>>> deltaspike-scheduler-module-impl? > >>>>>>>>>> > >>>>>>>>>> Kind regards, > >>>>>>>>>> > >>>>>>>>>> Juri > >>>>>>>>>> > >>>>>>>>>> > >> >
