@Before public void setUp() { MockitoAnnotations.initMocks(this); listenerSupport = new ListenerSupport(); listenerSupport.addListener(listener); listenerSupport.addListener(validationStatusListener); listenerSupport.addListener(producerAndValidationStatusListener); }
protected HollowIncrementalProducer(HollowProducer producer, double threadsPerCpu, HollowConsumer.AnnouncementWatcher announcementWatcher, HollowConsumer.BlobRetriever blobRetriever, List<IncrementalCycleListener> listeners, Class<?>... classes) { this.producer = producer; this.mutations = new ConcurrentHashMap<RecordPrimaryKey, Object>(); this.populator = new HollowIncrementalCyclePopulator(mutations, threadsPerCpu); this.dataModel = classes; this.announcementWatcher = announcementWatcher; this.blobRetriever = blobRetriever; this.listeners = new ListenerSupport(); this.cycleMetadata = new HashMap<String, Object>(); this.threadsPerCpu = threadsPerCpu; for (IncrementalCycleListener listener : listeners) this.listeners.add(listener); }
private <T> void fire(Collection<T> ls, Consumer<? super T> r) { fire(ls.stream(), r); }
@Test public void testDuplicates() { ListenerSupport ls = new ListenerSupport(); CycleListener l = Mockito.mock(CycleListener.class); ls.addListener(l); ls.addListener(l); ListenerSupport.Listeners s = ls.listeners(); s.fireCycleStart(1); Mockito.verify(l, Mockito.times(1)).onCycleStart(1); }
/** * Initializes the data model for the given classes. * <p> * Data model initialization is required prior to {@link #restore(long, HollowConsumer.BlobRetriever) restoring} * the producer. * This ensures that restoration can correctly compare the producer's current data model * with the data model of the restored data state and manage any differences in those models * (such as not restoring state for any types in the restoring data model not present in the * producer's current data model). * <p> * After initialization a data model initialization event will be emitted * to all registered data model initialization * {@link com.netflix.hollow.api.producer.listener.DataModelInitializationListener listeners}. * * @param classes the data model classes * @throws IllegalArgumentException if {@code classes} is empty * @see #restore(long, HollowConsumer.BlobRetriever) */ public void initializeDataModel(Class<?>... classes) { Objects.requireNonNull(classes); if (classes.length == 0) { throw new IllegalArgumentException("classes is empty"); } long start = currentTimeMillis(); for (Class<?> c : classes) { objectMapper.initializeTypeState(c); } listeners.listeners().fireProducerInit(currentTimeMillis() - start); isInitialized = true; }
/** * Adds a listener to this producer. * <p> * If the listener was previously added to this consumer, as determined by reference equality or {@code Object} * equality, then this method does nothing. * <p> * If a listener is added, concurrently, during the occurrence of a cycle or restore then the listener will not * receive events until the next cycle or restore. The listener may also be removed concurrently. * * @param listener the listener to add */ public void addListener(HollowProducerEventListener listener) { listeners.addListener(listener); }
/** * Runs a Hollow Cycle, if successful, cleans the mutations map. * * @return the version of the cycle if successful, otherwise the {@link #FAILED_VERSION} * @since 2.9.9 */ public long runCycle() { long recordsRemoved = countRecordsToRemove(); long recordsAddedOrModified = this.mutations.values().size() - recordsRemoved; try { long version = producer.runCycle(populator); if(version == lastSucessfulCycle) { return version; } listeners.fireIncrementalCycleComplete(version, recordsAddedOrModified, recordsRemoved, new HashMap<String, Object>(cycleMetadata)); //Only clean changes when the version is new. clearChanges(); lastSucessfulCycle = version; return version; } catch (Exception e) { listeners.fireIncrementalCycleFail(e, recordsAddedOrModified, recordsRemoved, new HashMap<String, Object>(cycleMetadata)); return FAILED_VERSION; } finally { clearCycleMetadata(); } }
private HollowProducer( BlobStager blobStager, Publisher publisher, Announcer announcer, List<? extends HollowProducerEventListener> eventListeners, VersionMinter versionMinter, Executor snapshotPublishExecutor, int numStatesBetweenSnapshots, long targetMaxTypeShardSize, HollowMetricsCollector<HollowProducerMetrics> metricsCollector, BlobStorageCleaner blobStorageCleaner, SingleProducerEnforcer singleProducerEnforcer) { this.publisher = publisher; this.announcer = announcer; this.versionMinter = versionMinter; this.blobStager = blobStager; this.singleProducerEnforcer = singleProducerEnforcer; this.snapshotPublishExecutor = snapshotPublishExecutor == null ? Runnable::run : snapshotPublishExecutor; this.numStatesBetweenSnapshots = numStatesBetweenSnapshots; HollowWriteStateEngine writeEngine = new HollowWriteStateEngine(); writeEngine.setTargetMaxTypeShardSize(targetMaxTypeShardSize); this.objectMapper = new HollowObjectMapper(writeEngine); this.readStates = ReadStateHelper.newDeltaChain(); this.blobStorageCleaner = blobStorageCleaner; this.listeners = new ListenerSupport(eventListeners.stream().distinct().collect(toList())); this.metrics = new HollowProducerMetrics(); this.metricsCollector = metricsCollector; }
/** * Removes a listener to this producer. * <p> * If the listener was not previously added to this producer, as determined by reference equality or {@code Object} * equality, then this method does nothing. * <p> * If a listener is removed, concurrently, during the occurrence of a cycle or restore then the listener will * receive all events for that cycle or restore but not receive events for a subsequent cycle or restore. * * @param listener the listener to remove */ public void removeListener(HollowProducerEventListener listener) { listeners.removeListener(listener); }
/** * Registers an event listener that will receive events in accordance to the event listener * types that are implemented. * * @param listener the event listener * @return this builder * @throws IllegalArgumentException if the listener does not implement a recognized event listener type */ public B withListener(HollowProducerEventListener listener) { if (!ListenerSupport.isValidListener(listener)) { throw new IllegalArgumentException( "Listener does not implement a recognized event listener type: " + listener); } this.eventListeners.add(listener); return (B) this; }
public void removeListener(IncrementalCycleListener listener) { this.listeners.remove(listener); }
public void addListener(IncrementalCycleListener listener) { this.listeners.add(listener); }
@Test public void testRemoveDuringCycle() { ListenerSupport ls = new ListenerSupport(); ls.addListener(fcl); ls.addListener(scl); ListenerSupport.Listeners s = ls.listeners(); s.fireCycleStart(1); s.fireCycleComplete(new Status.StageWithStateBuilder()); Assert.assertEquals(1, fcl.scl.cycleComplete); s = ls.listeners(); s.fireCycleStart(1); s.fireCycleComplete(new Status.StageWithStateBuilder());
/** * Initializes the producer data model for the given schemas. * <p> * Data model initialization is required prior to {@link #restore(long, HollowConsumer.BlobRetriever) restoring} * the producer. * This ensures that restoration can correctly compare the producer's current data model * with the data model of the restored data state and manage any differences in those models * (such as not restoring state for any types in the restoring data model not present in the * producer's current data model). * <p> * After initialization a data model initialization event will be emitted * to all registered data model initialization * {@link com.netflix.hollow.api.producer.listener.DataModelInitializationListener listeners}. * * @param schemas the data model classes * @throws IllegalArgumentException if {@code schemas} is empty * @see #restore(long, HollowConsumer.BlobRetriever) */ public void initializeDataModel(HollowSchema... schemas) { Objects.requireNonNull(schemas); if (schemas.length == 0) { throw new IllegalArgumentException("classes is empty"); } long start = currentTimeMillis(); HollowWriteStateCreator.populateStateEngineWithTypeWriteStates(getWriteEngine(), Arrays.asList(schemas)); listeners.listeners().fireProducerInit(currentTimeMillis() - start); isInitialized = true; }
/** * Adds a listener to this producer. * <p> * If the listener was previously added to this consumer, as determined by reference equality or {@code Object} * equality, then this method does nothing. * <p> * If a listener is added, concurrently, during the occurrence of a cycle or restore then the listener will not * receive events until the next cycle or restore. The listener may also be removed concurrently. * * @param listener the listener to add */ public void addListener(HollowProducerListener listener) { listeners.addListener(listener); }
/** * Removes a listener to this producer. * <p> * If the listener was not previously added to this producer, as determined by reference equality or {@code Object} * equality, then this method does nothing. * <p> * If a listener is removed, concurrently, during the occurrence of a cycle or restore then the listener will * receive all events for that cycle or restore but not receive events for a subsequent cycle or restore. * * @param listener the listener to remove */ public void removeListener(HollowProducerListener listener) { listeners.removeListener(listener); }
/** * Registers one or more event listeners each of which will receive events in accordance to the * event listener types that are implemented. * * @param listeners one or more event listeners * @return this builder * @throws IllegalArgumentException if the listener does not implement a recognized event listener type */ public B withListeners(HollowProducerEventListener... listeners) { for (HollowProducerEventListener listener : listeners) { if (!ListenerSupport.isValidListener(listener)) { throw new IllegalArgumentException( "Listener does not implement a recognized event listener type: " + listener); } this.eventListeners.add(listener); } return (B) this; }
@Test public void testAddDuringCycle() { ListenerSupport ls = new ListenerSupport(); ls.addListener(fcl); ListenerSupport.Listeners s = ls.listeners(); s.fireCycleStart(1); s.fireCycleComplete(new Status.StageWithStateBuilder()); Assert.assertEquals(0, fcl.scl.cycleComplete); s = ls.listeners(); s.fireCycleStart(1); s.fireCycleComplete(new Status.StageWithStateBuilder());
@Test public void fireProducerInitDontStopWhenOneFails() { long version = 31337; HollowProducer.ReadState readState = Mockito.mock(HollowProducer.ReadState.class); Mockito.when(readState.getVersion()).thenReturn(version); Mockito.doThrow(RuntimeException.class).when(listener).onProducerInit(1L, MILLISECONDS); listenerSupport.listeners().fireProducerInit(1L); Mockito.verify(listener).onProducerInit(Duration.ofMillis(1L)); }
void fireIncrementalCycleComplete( long version, long recordsAddedOrModified, long recordsRemoved, Map<String, Object> cycleMetadata) { // @@@ This behaviour appears incomplete, the build is created and built // for each listener. The start time (builder creation) and end time (builder built) // results in an effectively meaningless elasped time. IncrementalCycleStatus.Builder icsb = new IncrementalCycleStatus.Builder() .success(version, recordsAddedOrModified, recordsRemoved, cycleMetadata); fire(incrementalCycleListeners, l -> l.onCycleComplete(icsb.build(), icsb.elapsed(), MILLISECONDS)); }