@Override public void onCompleted() { notificationsSubject.onCompleted(); }
@Override public void call(Subscriber<? super BufferState> subscriber) { updatesSubject.pause(); try { BufferState current = shouldBatch(interest); subscriber.onNext(current); updatesSubject.map(new Func1<Boolean, BufferState>() { @Override public BufferState call(Boolean tick) { return shouldBatch(interest); } }).subscribe(subscriber); } finally { updatesSubject.resume(); } } }).distinctUntilChanged();
public static <T> PauseableSubject<T> create() { final Subject<T, T> notificationSubject = PublishSubject.create(); return new PauseableSubject<T>(new OnSubscribe<T>() { @Override public void call(Subscriber<? super T> subscriber) { notificationSubject.subscribe(subscriber); } }, notificationSubject); }
public ResumeResult resume() { if (isPaused()) { if (resumeState.compareAndSet(ResumeState.NotPaused.ordinal(), ResumeState.Resuming.ordinal())) { try { drainBuffer(); // We have here a race condition, as onNext could be called just before paused is set to false, // but after the while loop above. That is why we need to drain the buffer, before each onNext. paused.set(false); if (completedWhenPaused) { onCompleted(); } else if (null != errorWhenPaused) { onError(errorWhenPaused); } return ResumeResult.Resumed; } catch (Exception e) { logger.error("Error while resuming notifications subject.", e); resumeState.compareAndSet(ResumeState.Resuming.ordinal(), ResumeState.Error.ordinal()); onError(e); return ResumeResult.Resumed; } finally { resumeState.compareAndSet(ResumeState.Resuming.ordinal(), ResumeState.NotPaused.ordinal()); } } else { return ResumeResult.DuplicateResume; } } else { return ResumeResult.NotPaused; } }
@Override public void call(Subscriber<? super ChangeNotification<T>> subscriber) { // We need to buffer all the changes while the init state is replayed. // Because new instance holder updates will be added while we replay them, they will be // partially visible by the subscriber. When we replay buffered real time updates, // they may overlap with what was already sent from the init holder. // TODO can we make this buffering cheaper? final PauseableSubject<ChangeNotification<T>> realTimeSubject = PauseableSubject.create(); realTimeSubject.pause(); realTimeSource.subscribe(realTimeSubject); realTimeSubject.mergeWith(Observable.from(initStateHolder).doOnCompleted(new Action0() { @Override public void call() { realTimeSubject.resume(); } })).subscribe(subscriber); } });
public InstanceInfoInitStateHolder(Iterator<ChangeNotification<InstanceInfo>> initialRegistry, Interest<InstanceInfo> interest) { super(PauseableSubject.<ChangeNotification<InstanceInfo>>create()); this.bufferStartNotification = new StreamStateNotification<>(BufferState.BufferStart, interest); this.bufferEndNotification = new StreamStateNotification<>(BufferState.BufferEnd, interest); notificationMap = new ConcurrentHashMap<>(); while (initialRegistry.hasNext()) { ChangeNotification<InstanceInfo> next = initialRegistry.next(); notificationMap.put(next.getData().getId(), next); // Always Kind.Add } }
@Override public boolean hasObservers() { return notificationsSubject.hasObservers(); }
/** * Return an observable of all matching InstanceInfo for the current registry snapshot, * as {@link ChangeNotification}s * @param interest * @return an observable of all matching InstanceInfo for the current registry snapshot, * as {@link ChangeNotification}s */ @Override public Observable<ChangeNotification<InstanceInfo>> forInterest(Interest<InstanceInfo> interest) { try { // TODO: this method can be run concurrently from different channels, unless we run everything on single server event loop. // It is possible that the same instanceinfo will be both in snapshot and paused notification queue. pauseableSubject.pause(); // Pause notifications till we get a snapshot of current registry (registry.values()) if (interest instanceof MultipleInterests) { return indexRegistry.forCompositeInterest((MultipleInterests) interest, this); } else { return indexRegistry.forInterest(interest, pauseableSubject, new InstanceInfoInitStateHolder(getSnapshotForInterest(interest), interest)); } } finally { pauseableSubject.resume(); } }
public SourcedEurekaRegistryImpl(IndexRegistry<InstanceInfo> indexRegistry, EurekaRegistryMetricFactory metricsFactory, Scheduler scheduler) { this.indexRegistry = indexRegistry; this.metrics = metricsFactory.getEurekaServerRegistryMetrics(); invoker = new NotifyingInstanceInfoHolder.NotificationTaskInvoker( metricsFactory.getRegistryTaskInvokerMetrics(), scheduler); internalStore = new ConcurrentHashMap<>(); pauseableSubject = PauseableSubject.create(); internalStoreAccessor = new MultiSourcedDataHolder.HolderStoreAccessor<NotifyingInstanceInfoHolder>() { @Override public void add(NotifyingInstanceInfoHolder holder) { internalStore.put(holder.getId(), holder); metrics.setRegistrySize(internalStore.size()); } @Override public NotifyingInstanceInfoHolder get(String id) { return internalStore.get(id); } @Override public void remove(String id) { internalStore.remove(id); metrics.setRegistrySize(internalStore.size()); } @Override public boolean contains(String id) { return internalStore.containsKey(id); } }; }
@Override public void shutdown() { updatesSubject.onCompleted(); notificationSources.onCompleted(); }
@Override public Observable<Void> shutdown() { logger.info("Shutting down the eureka registry"); invoker.shutdown(); pauseableSubject.onCompleted(); internalStore.clear(); return indexRegistry.shutdown(); }
@Override public Observable<Void> shutdown(Throwable cause) { invoker.shutdown(); pauseableSubject.onCompleted(); return indexRegistry.shutdown(cause); }