/** * {@link Scheduler} that dynamically creates Workers resources and caches * eventually, reusing them once the Workers have been shut down. * <p> * The maximum number of created workers is unbounded. * * @param ttlSeconds Time-to-live for an idle {@link reactor.core.scheduler.Scheduler.Worker} * @param threadFactory a {@link ThreadFactory} to use * * @return a new {@link Scheduler} that dynamically creates Workers resources and * caches eventually, reusing them once the Workers have been shut down. */ default Scheduler newElastic(int ttlSeconds, ThreadFactory threadFactory) { return new ElasticScheduler(threadFactory, ttlSeconds); }
CachedService(@Nullable ElasticScheduler parent) { this.parent = parent; if (parent != null) { this.exec = Schedulers.decorateExecutorService(parent, parent.get()); } else { this.exec = Executors.newSingleThreadScheduledExecutor(); this.exec.shutdownNow(); } }
@Override public Object scanUnsafe(Attr key) { if (key == Attr.TERMINATED || key == Attr.CANCELLED) return isDisposed(); if (key == Attr.CAPACITY) return Integer.MAX_VALUE; if (key == Attr.BUFFERED) return cache.size(); //BUFFERED: number of workers alive if (key == Attr.NAME) return this.toString(); return null; }
@Override public Worker createWorker() { return new ElasticWorker(pick()); }
@Override public Object scanUnsafe(Attr key) { if (key == Attr.NAME) return parent.scanUnsafe(key); if (key == Attr.PARENT) return parent; if (key == Attr.TERMINATED || key == Attr.CANCELLED) return isDisposed(); if (key == Attr.CAPACITY) { //assume 1 if unknown, otherwise use the one from underlying executor Integer capacity = (Integer) Schedulers.scanExecutor(exec, key); if (capacity == null || capacity == -1) return 1; } return Schedulers.scanExecutor(exec, key); } }
@Test(timeout = 10000) public void eviction() throws Exception { Scheduler s = Schedulers.newElastic("test-recycle", 2); ((ElasticScheduler)s).evictor.shutdownNow(); try{ Disposable d = s.schedule(() -> { try { Thread.sleep(10000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); d.dispose(); while(((ElasticScheduler)s).cache.peek() != null){ ((ElasticScheduler)s).eviction(); Thread.sleep(100); } } finally { s.dispose(); s.dispose();//noop } assertThat(((ElasticScheduler)s).cache).isEmpty(); assertThat(s.isDisposed()).isTrue(); }
@Override public Disposable schedule(Runnable task) { CachedService cached = pick(); return Schedulers.directSchedule(cached.exec, new DirectScheduleTask(task, cached), 0L, TimeUnit.MILLISECONDS); }
@Override public Object scanUnsafe(Attr key) { if (key == Attr.NAME) return parent.scanUnsafe(key); if (key == Attr.PARENT) return parent; if (key == Attr.TERMINATED || key == Attr.CANCELLED) return isDisposed(); if (key == Attr.CAPACITY) { //assume 1 if unknown, otherwise use the one from underlying executor Integer capacity = (Integer) Schedulers.scanExecutor(exec, key); if (capacity == null || capacity == -1) return 1; } return Schedulers.scanExecutor(exec, key); } }
@Override public Disposable schedule(Runnable task, long delay, TimeUnit unit) { CachedService cached = pick(); return Schedulers.directSchedule(cached.exec, new DirectScheduleTask(task, cached), delay, unit); }
@Override public Object scanUnsafe(Attr key) { if (key == Attr.TERMINATED || key == Attr.CANCELLED) return isDisposed(); if (key == Attr.CAPACITY) return Integer.MAX_VALUE; if (key == Attr.BUFFERED) return cache.size(); //BUFFERED: number of workers alive if (key == Attr.NAME) return this.toString(); return null; }
CachedService(@Nullable ElasticScheduler parent) { this.parent = parent; if (parent != null) { this.exec = Schedulers.decorateExecutorService(parent, parent.get()); } else { this.exec = Executors.newSingleThreadScheduledExecutor(); this.exec.shutdownNow(); } }
/** * {@link Scheduler} that dynamically creates Workers resources and caches * eventually, reusing them once the Workers have been shut down. * <p> * The maximum number of created workers is unbounded. * * @param ttlSeconds Time-to-live for an idle {@link reactor.core.scheduler.Scheduler.Worker} * @param threadFactory a {@link ThreadFactory} to use * * @return a new {@link Scheduler} that dynamically creates Workers resources and * caches eventually, reusing them once the Workers have been shut down. */ default Scheduler newElastic(int ttlSeconds, ThreadFactory threadFactory) { return new ElasticScheduler(threadFactory, ttlSeconds); }
@Override public Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { CachedService cached = pick(); return Disposables.composite(Schedulers.directSchedulePeriodically(cached.exec, task, initialDelay, period, unit), cached); }
@Override public Worker createWorker() { return new ElasticWorker(pick()); }
@Override public Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { CachedService cached = pick(); return Disposables.composite(Schedulers.directSchedulePeriodically(cached.exec, task, initialDelay, period, unit), cached); }
@Override public Disposable schedule(Runnable task) { CachedService cached = pick(); return Schedulers.directSchedule(cached.exec, new DirectScheduleTask(task, cached), 0L, TimeUnit.MILLISECONDS); }
@Override public Disposable schedule(Runnable task, long delay, TimeUnit unit) { CachedService cached = pick(); return Schedulers.directSchedule(cached.exec, new DirectScheduleTask(task, cached), delay, unit); }