Search in sources :

Example 1 with Client

use of org.zalando.nakadi.security.Client in project nakadi by zalando.

the class StreamingStateTest method prepareMocks.

@Before
public void prepareMocks() throws Exception {
    state = new StreamingState();
    final StreamingContext contextMock = mock(StreamingContext.class);
    when(contextMock.getCursorComparator()).thenReturn(Comparator.comparing(NakadiCursor::getOffset));
    when(contextMock.getSessionId()).thenReturn(SESSION_ID);
    when(contextMock.isInState(Mockito.same(state))).thenReturn(true);
    subscription = mock(Subscription.class);
    when(contextMock.getSubscription()).thenReturn(subscription);
    timelineService = mock(TimelineService.class);
    when(contextMock.getTimelineService()).thenReturn(timelineService);
    final MetricRegistry metricRegistry = mock(MetricRegistry.class);
    when(metricRegistry.register(any(), any())).thenReturn(null);
    when(contextMock.getMetricRegistry()).thenReturn(metricRegistry);
    zkMock = mock(ZkSubscriptionClient.class);
    when(contextMock.getZkClient()).thenReturn(zkMock);
    cursorConverter = mock(CursorConverter.class);
    when(contextMock.getCursorConverter()).thenReturn(cursorConverter);
    final Client client = mock(Client.class);
    when(client.getClientId()).thenReturn("consumingAppId");
    final StreamParameters spMock = createStreamParameters(1000, 100L, 100, 100L, 100, 100, 100, client);
    when(contextMock.getParameters()).thenReturn(spMock);
    state.setContext(contextMock, "test");
}
Also used : StreamingContext(org.zalando.nakadi.service.subscription.StreamingContext) ZkSubscriptionClient(org.zalando.nakadi.service.subscription.zk.ZkSubscriptionClient) MetricRegistry(com.codahale.metrics.MetricRegistry) TimelineService(org.zalando.nakadi.service.timeline.TimelineService) Subscription(org.zalando.nakadi.domain.Subscription) ZkSubscription(org.zalando.nakadi.service.subscription.zk.ZkSubscription) ZkSubscriptionClient(org.zalando.nakadi.service.subscription.zk.ZkSubscriptionClient) Client(org.zalando.nakadi.security.Client) CursorConverter(org.zalando.nakadi.service.CursorConverter) StreamParametersTest.createStreamParameters(org.zalando.nakadi.service.subscription.StreamParametersTest.createStreamParameters) StreamParameters(org.zalando.nakadi.service.subscription.StreamParameters) Before(org.junit.Before)

Example 2 with Client

use of org.zalando.nakadi.security.Client in project nakadi by zalando.

the class StreamingState method publishKpi.

private void publishKpi(final String eventTypeName) {
    final Client client = getContext().getParameters().getConsumingClient();
    final NakadiKpiPublisher kpiPublisher = getContext().getKpiPublisher();
    final long bytes = kpiDataPerEventType.get(eventTypeName).getAndResetBytesSent();
    final long count = kpiDataPerEventType.get(eventTypeName).getAndResetNumberOfEventsSent();
    final String appNameHashed = kpiPublisher.hash(client.getClientId());
    getLog().info("[SLO] [streamed-data] api={} eventTypeName={} app={} appHashed={} " + "numberOfEvents={} bytesStreamed={} subscription={}", "hila", eventTypeName, client.getClientId(), appNameHashed, count, bytes, getContext().getSubscription().getId());
    kpiPublisher.publish(getContext().getKpiDataStreamedEventType(), () -> new JSONObject().put("api", "hila").put("subscription", getContext().getSubscription().getId()).put("event_type", eventTypeName).put("app", client.getClientId()).put("app_hashed", appNameHashed).put("token_realm", client.getRealm()).put("number_of_events", count).put("bytes_streamed", bytes));
}
Also used : JSONObject(org.json.JSONObject) NakadiKpiPublisher(org.zalando.nakadi.service.NakadiKpiPublisher) ZkSubscriptionClient(org.zalando.nakadi.service.subscription.zk.ZkSubscriptionClient) Client(org.zalando.nakadi.security.Client)

Example 3 with Client

use of org.zalando.nakadi.security.Client in project nakadi by zalando.

the class EventStreamController method streamEvents.

@RequestMapping(value = "/event-types/{name}/events", method = RequestMethod.GET)
public StreamingResponseBody streamEvents(@PathVariable("name") final String eventTypeName, @Nullable @RequestParam(value = "batch_limit", required = false) final Integer batchLimit, @Nullable @RequestParam(value = "stream_limit", required = false) final Integer streamLimit, @Nullable @RequestParam(value = "batch_flush_timeout", required = false) final Integer batchTimeout, @Nullable @RequestParam(value = "stream_timeout", required = false) final Integer streamTimeout, @Nullable @RequestParam(value = "stream_keep_alive_limit", required = false) final Integer streamKeepAliveLimit, @Nullable @RequestHeader(name = "X-nakadi-cursors", required = false) final String cursorsStr, final HttpServletRequest request, final HttpServletResponse response, final Client client) {
    final String flowId = FlowIdUtils.peek();
    return outputStream -> {
        FlowIdUtils.push(flowId);
        if (blacklistService.isConsumptionBlocked(eventTypeName, client.getClientId())) {
            writeProblemResponse(response, outputStream, Problem.valueOf(Response.Status.FORBIDDEN, "Application or event type is blocked"));
            return;
        }
        final AtomicBoolean connectionReady = closedConnectionsCrutch.listenForConnectionClose(request);
        Counter consumerCounter = null;
        EventStream eventStream = null;
        List<ConnectionSlot> connectionSlots = ImmutableList.of();
        final AtomicBoolean needCheckAuthorization = new AtomicBoolean(false);
        LOG.info("[X-NAKADI-CURSORS] \"{}\" {}", eventTypeName, Optional.ofNullable(cursorsStr).orElse("-"));
        try (Closeable ignore = eventTypeChangeListener.registerListener(et -> needCheckAuthorization.set(true), Collections.singletonList(eventTypeName))) {
            final EventType eventType = eventTypeRepository.findByName(eventTypeName);
            authorizeStreamRead(eventTypeName);
            // validate parameters
            final EventStreamConfig streamConfig = EventStreamConfig.builder().withBatchLimit(batchLimit).withStreamLimit(streamLimit).withBatchTimeout(batchTimeout).withStreamTimeout(streamTimeout).withStreamKeepAliveLimit(streamKeepAliveLimit).withEtName(eventTypeName).withConsumingClient(client).withCursors(getStreamingStart(eventType, cursorsStr)).withMaxMemoryUsageBytes(maxMemoryUsageBytes).build();
            // acquire connection slots to limit the number of simultaneous connections from one client
            if (featureToggleService.isFeatureEnabled(LIMIT_CONSUMERS_NUMBER)) {
                final List<String> partitions = streamConfig.getCursors().stream().map(NakadiCursor::getPartition).collect(Collectors.toList());
                connectionSlots = consumerLimitingService.acquireConnectionSlots(client.getClientId(), eventTypeName, partitions);
            }
            consumerCounter = metricRegistry.counter(metricNameFor(eventTypeName, CONSUMERS_COUNT_METRIC_NAME));
            consumerCounter.inc();
            final String kafkaQuotaClientId = getKafkaQuotaClientId(eventTypeName, client);
            response.setStatus(HttpStatus.OK.value());
            response.setHeader("Warning", "299 - nakadi - the Low-level API is deprecated and will " + "be removed from a future release. Please consider migrating to the Subscriptions API.");
            response.setContentType("application/x-json-stream");
            final EventConsumer eventConsumer = timelineService.createEventConsumer(kafkaQuotaClientId, streamConfig.getCursors());
            final String bytesFlushedMetricName = MetricUtils.metricNameForLoLAStream(client.getClientId(), eventTypeName);
            final Meter bytesFlushedMeter = this.streamMetrics.meter(bytesFlushedMetricName);
            eventStream = eventStreamFactory.createEventStream(outputStream, eventConsumer, streamConfig, bytesFlushedMeter);
            // Flush status code to client
            outputStream.flush();
            eventStream.streamEvents(connectionReady, () -> {
                if (needCheckAuthorization.getAndSet(false)) {
                    authorizeStreamRead(eventTypeName);
                }
            });
        } catch (final UnparseableCursorException e) {
            LOG.debug("Incorrect syntax of X-nakadi-cursors header: {}. Respond with BAD_REQUEST.", e.getCursors(), e);
            writeProblemResponse(response, outputStream, BAD_REQUEST, e.getMessage());
        } catch (final NoSuchEventTypeException e) {
            writeProblemResponse(response, outputStream, NOT_FOUND, "topic not found");
        } catch (final NoConnectionSlotsException e) {
            LOG.debug("Connection creation failed due to exceeding max connection count");
            writeProblemResponse(response, outputStream, e.asProblem());
        } catch (final NakadiException e) {
            LOG.error("Error while trying to stream events.", e);
            writeProblemResponse(response, outputStream, e.asProblem());
        } catch (final InvalidCursorException e) {
            writeProblemResponse(response, outputStream, PRECONDITION_FAILED, e.getMessage());
        } catch (final AccessDeniedException e) {
            writeProblemResponse(response, outputStream, FORBIDDEN, e.explain());
        } catch (final Exception e) {
            LOG.error("Error while trying to stream events. Respond with INTERNAL_SERVER_ERROR.", e);
            writeProblemResponse(response, outputStream, INTERNAL_SERVER_ERROR, e.getMessage());
        } finally {
            connectionReady.set(false);
            consumerLimitingService.releaseConnectionSlots(connectionSlots);
            if (consumerCounter != null) {
                consumerCounter.dec();
            }
            if (eventStream != null) {
                eventStream.close();
            }
            try {
                outputStream.flush();
            } finally {
                outputStream.close();
            }
        }
    };
}
Also used : PathVariable(org.springframework.web.bind.annotation.PathVariable) RequestParam(org.springframework.web.bind.annotation.RequestParam) FlowIdUtils(org.zalando.nakadi.util.FlowIdUtils) LoggerFactory(org.slf4j.LoggerFactory) Autowired(org.springframework.beans.factory.annotation.Autowired) NakadiException(org.zalando.nakadi.exceptions.NakadiException) NoSuchEventTypeException(org.zalando.nakadi.exceptions.NoSuchEventTypeException) ServiceUnavailableException(org.zalando.nakadi.exceptions.ServiceUnavailableException) Problem(org.zalando.problem.Problem) Map(java.util.Map) Counter(com.codahale.metrics.Counter) TimelineService(org.zalando.nakadi.service.timeline.TimelineService) TypeReference(com.fasterxml.jackson.core.type.TypeReference) FeatureToggleService(org.zalando.nakadi.service.FeatureToggleService) EventTypeChangeListener(org.zalando.nakadi.service.EventTypeChangeListener) BAD_REQUEST(javax.ws.rs.core.Response.Status.BAD_REQUEST) ConsumerLimitingService(org.zalando.nakadi.service.ConsumerLimitingService) TopicRepository(org.zalando.nakadi.repository.TopicRepository) ConnectionSlot(org.zalando.nakadi.service.ConnectionSlot) NOT_FOUND(javax.ws.rs.core.Response.Status.NOT_FOUND) PartitionStatistics(org.zalando.nakadi.domain.PartitionStatistics) RequestMethod(org.springframework.web.bind.annotation.RequestMethod) StreamingResponseBody(org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody) InvalidCursorException(org.zalando.nakadi.exceptions.InvalidCursorException) BlacklistService(org.zalando.nakadi.service.BlacklistService) RestController(org.springframework.web.bind.annotation.RestController) Collectors(java.util.stream.Collectors) List(java.util.List) Response(javax.ws.rs.core.Response) Timeline(org.zalando.nakadi.domain.Timeline) ServiceTemporarilyUnavailableException(org.zalando.nakadi.exceptions.runtime.ServiceTemporarilyUnavailableException) Optional(java.util.Optional) RequestHeader(org.springframework.web.bind.annotation.RequestHeader) UnparseableCursorException(org.zalando.nakadi.exceptions.UnparseableCursorException) Client(org.zalando.nakadi.security.Client) Storage(org.zalando.nakadi.domain.Storage) ClosedConnectionsCrutch(org.zalando.nakadi.service.ClosedConnectionsCrutch) NakadiCursor(org.zalando.nakadi.domain.NakadiCursor) RequestMapping(org.springframework.web.bind.annotation.RequestMapping) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Cursor(org.zalando.nakadi.view.Cursor) ArrayList(java.util.ArrayList) Value(org.springframework.beans.factory.annotation.Value) Meter(com.codahale.metrics.Meter) HttpServletRequest(javax.servlet.http.HttpServletRequest) ImmutableList(com.google.common.collect.ImmutableList) EventStreamFactory(org.zalando.nakadi.service.EventStreamFactory) Qualifier(org.springframework.beans.factory.annotation.Qualifier) PRECONDITION_FAILED(javax.ws.rs.core.Response.Status.PRECONDITION_FAILED) Nullable(javax.annotation.Nullable) OutputStream(java.io.OutputStream) AccessDeniedException(org.zalando.nakadi.exceptions.runtime.AccessDeniedException) EventType(org.zalando.nakadi.domain.EventType) MetricRegistry(com.codahale.metrics.MetricRegistry) Logger(org.slf4j.Logger) INTERNAL_SERVER_ERROR(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) HttpServletResponse(javax.servlet.http.HttpServletResponse) MetricUtils.metricNameFor(org.zalando.nakadi.metrics.MetricUtils.metricNameFor) IOException(java.io.IOException) FORBIDDEN(javax.ws.rs.core.Response.Status.FORBIDDEN) EventStream(org.zalando.nakadi.service.EventStream) AuthorizationValidator(org.zalando.nakadi.service.AuthorizationValidator) HttpStatus(org.springframework.http.HttpStatus) EventConsumer(org.zalando.nakadi.repository.EventConsumer) EventTypeRepository(org.zalando.nakadi.repository.EventTypeRepository) Closeable(java.io.Closeable) NoConnectionSlotsException(org.zalando.nakadi.exceptions.NoConnectionSlotsException) LIMIT_CONSUMERS_NUMBER(org.zalando.nakadi.service.FeatureToggleService.Feature.LIMIT_CONSUMERS_NUMBER) CursorError(org.zalando.nakadi.domain.CursorError) VisibleForTesting(com.google.common.annotations.VisibleForTesting) MetricUtils(org.zalando.nakadi.metrics.MetricUtils) EventStreamConfig(org.zalando.nakadi.service.EventStreamConfig) InternalNakadiException(org.zalando.nakadi.exceptions.InternalNakadiException) Collections(java.util.Collections) CursorConverter(org.zalando.nakadi.service.CursorConverter) AccessDeniedException(org.zalando.nakadi.exceptions.runtime.AccessDeniedException) NakadiCursor(org.zalando.nakadi.domain.NakadiCursor) EventType(org.zalando.nakadi.domain.EventType) Meter(com.codahale.metrics.Meter) Closeable(java.io.Closeable) InvalidCursorException(org.zalando.nakadi.exceptions.InvalidCursorException) UnparseableCursorException(org.zalando.nakadi.exceptions.UnparseableCursorException) NakadiException(org.zalando.nakadi.exceptions.NakadiException) NoSuchEventTypeException(org.zalando.nakadi.exceptions.NoSuchEventTypeException) ServiceUnavailableException(org.zalando.nakadi.exceptions.ServiceUnavailableException) InvalidCursorException(org.zalando.nakadi.exceptions.InvalidCursorException) ServiceTemporarilyUnavailableException(org.zalando.nakadi.exceptions.runtime.ServiceTemporarilyUnavailableException) UnparseableCursorException(org.zalando.nakadi.exceptions.UnparseableCursorException) AccessDeniedException(org.zalando.nakadi.exceptions.runtime.AccessDeniedException) IOException(java.io.IOException) NoConnectionSlotsException(org.zalando.nakadi.exceptions.NoConnectionSlotsException) InternalNakadiException(org.zalando.nakadi.exceptions.InternalNakadiException) NakadiException(org.zalando.nakadi.exceptions.NakadiException) InternalNakadiException(org.zalando.nakadi.exceptions.InternalNakadiException) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) EventStreamConfig(org.zalando.nakadi.service.EventStreamConfig) EventConsumer(org.zalando.nakadi.repository.EventConsumer) Counter(com.codahale.metrics.Counter) EventStream(org.zalando.nakadi.service.EventStream) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) NoConnectionSlotsException(org.zalando.nakadi.exceptions.NoConnectionSlotsException) NoSuchEventTypeException(org.zalando.nakadi.exceptions.NoSuchEventTypeException) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 4 with Client

use of org.zalando.nakadi.security.Client in project nakadi by zalando.

the class EventStream method streamEvents.

public void streamEvents(final AtomicBoolean connectionReady, final Runnable checkAuthorization) {
    try {
        int messagesRead = 0;
        final Map<String, Integer> keepAliveInARow = createMapWithPartitionKeys(partition -> 0);
        final Map<String, List<byte[]>> currentBatches = createMapWithPartitionKeys(partition -> Lists.newArrayList());
        // Partition to NakadiCursor.
        final Map<String, NakadiCursor> latestOffsets = config.getCursors().stream().collect(Collectors.toMap(NakadiCursor::getPartition, c -> c));
        final long start = currentTimeMillis();
        final Map<String, Long> batchStartTimes = createMapWithPartitionKeys(partition -> start);
        final List<ConsumedEvent> consumedEvents = new LinkedList<>();
        long lastKpiEventSent = System.currentTimeMillis();
        long bytesInMemory = 0;
        while (connectionReady.get() && !blacklistService.isConsumptionBlocked(config.getEtName(), config.getConsumingClient().getClientId())) {
            checkAuthorization.run();
            if (consumedEvents.isEmpty()) {
                // TODO: There are a lot of optimizations here, one can significantly improve code by processing
                // all events at the same time, instead of processing one by one.
                consumedEvents.addAll(eventConsumer.readEvents());
            }
            final Optional<ConsumedEvent> eventOrEmpty = consumedEvents.isEmpty() ? Optional.empty() : Optional.of(consumedEvents.remove(0));
            if (eventOrEmpty.isPresent()) {
                final ConsumedEvent event = eventOrEmpty.get();
                // update offset for the partition of event that was read
                latestOffsets.put(event.getPosition().getPartition(), event.getPosition());
                // put message to batch
                currentBatches.get(event.getPosition().getPartition()).add(event.getEvent());
                messagesRead++;
                bytesInMemory += event.getEvent().length;
                // if we read the message - reset keep alive counter for this partition
                keepAliveInARow.put(event.getPosition().getPartition(), 0);
            }
            // for each partition check if it's time to send the batch
            for (final String partition : latestOffsets.keySet()) {
                final long timeSinceBatchStart = currentTimeMillis() - batchStartTimes.get(partition);
                if (config.getBatchTimeout() * 1000 <= timeSinceBatchStart || currentBatches.get(partition).size() >= config.getBatchLimit()) {
                    final List<byte[]> eventsToSend = currentBatches.get(partition);
                    sendBatch(latestOffsets.get(partition), eventsToSend);
                    if (!eventsToSend.isEmpty()) {
                        bytesInMemory -= eventsToSend.stream().mapToLong(v -> v.length).sum();
                        eventsToSend.clear();
                    } else {
                        // if we hit keep alive count limit - close the stream
                        keepAliveInARow.put(partition, keepAliveInARow.get(partition) + 1);
                    }
                    batchStartTimes.put(partition, currentTimeMillis());
                }
            }
            // Dump some data that is exceeding memory limits
            while (isMemoryLimitReached(bytesInMemory)) {
                final Map.Entry<String, List<byte[]>> heaviestPartition = currentBatches.entrySet().stream().max(Comparator.comparing(entry -> entry.getValue().stream().mapToLong(event -> event.length).sum())).get();
                sendBatch(latestOffsets.get(heaviestPartition.getKey()), heaviestPartition.getValue());
                final long freed = heaviestPartition.getValue().stream().mapToLong(v -> v.length).sum();
                LOG.warn("Memory limit reached for event type {}: {} bytes. Freed: {} bytes, {} messages", config.getEtName(), bytesInMemory, freed, heaviestPartition.getValue().size());
                bytesInMemory -= freed;
                // Init new batch for subscription
                heaviestPartition.getValue().clear();
                batchStartTimes.put(heaviestPartition.getKey(), currentTimeMillis());
            }
            if (lastKpiEventSent + kpiFrequencyMs < System.currentTimeMillis()) {
                final long count = kpiData.getAndResetNumberOfEventsSent();
                final long bytes = kpiData.getAndResetBytesSent();
                publishKpi(config.getConsumingClient(), count, bytes);
                lastKpiEventSent = System.currentTimeMillis();
            }
            // check if we reached keepAliveInARow for all the partitions; if yes - then close stream
            if (config.getStreamKeepAliveLimit() != 0) {
                final boolean keepAliveLimitReachedForAllPartitions = keepAliveInARow.values().stream().allMatch(keepAlives -> keepAlives >= config.getStreamKeepAliveLimit());
                if (keepAliveLimitReachedForAllPartitions) {
                    break;
                }
            }
            // check if we reached the stream timeout or message count limit
            final long timeSinceStart = currentTimeMillis() - start;
            if (config.getStreamTimeout() != 0 && timeSinceStart >= config.getStreamTimeout() * 1000 || config.getStreamLimit() != 0 && messagesRead >= config.getStreamLimit()) {
                for (final String partition : latestOffsets.keySet()) {
                    if (currentBatches.get(partition).size() > 0) {
                        sendBatch(latestOffsets.get(partition), currentBatches.get(partition));
                    }
                }
                break;
            }
        }
    } catch (final IOException e) {
        LOG.info("I/O error occurred when streaming events (possibly client closed connection)", e);
    } catch (final IllegalStateException e) {
        LOG.info("Error occurred when streaming events (possibly server closed connection)", e);
    } catch (final KafkaException e) {
        LOG.error("Error occurred when polling events from kafka; consumer: {}, event-type: {}", config.getConsumingClient().getClientId(), config.getEtName(), e);
    } finally {
        publishKpi(config.getConsumingClient(), kpiData.getAndResetNumberOfEventsSent(), kpiData.getAndResetBytesSent());
    }
}
Also used : OutputStream(java.io.OutputStream) ConsumedEvent(org.zalando.nakadi.domain.ConsumedEvent) Logger(org.slf4j.Logger) System.currentTimeMillis(java.lang.System.currentTimeMillis) NakadiCursor(org.zalando.nakadi.domain.NakadiCursor) LoggerFactory(org.slf4j.LoggerFactory) KafkaException(org.apache.kafka.common.KafkaException) StreamKpiData(org.zalando.nakadi.metrics.StreamKpiData) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) IOException(java.io.IOException) Function(java.util.function.Function) Collectors(java.util.stream.Collectors) Meter(com.codahale.metrics.Meter) EventConsumer(org.zalando.nakadi.repository.EventConsumer) List(java.util.List) Lists(com.google.common.collect.Lists) JSONObject(org.json.JSONObject) Map(java.util.Map) Function.identity(java.util.function.Function.identity) Optional(java.util.Optional) Comparator(java.util.Comparator) LinkedList(java.util.LinkedList) Client(org.zalando.nakadi.security.Client) NakadiCursor(org.zalando.nakadi.domain.NakadiCursor) ConsumedEvent(org.zalando.nakadi.domain.ConsumedEvent) IOException(java.io.IOException) LinkedList(java.util.LinkedList) List(java.util.List) LinkedList(java.util.LinkedList) KafkaException(org.apache.kafka.common.KafkaException) Map(java.util.Map)

Aggregations

Client (org.zalando.nakadi.security.Client)4 Meter (com.codahale.metrics.Meter)2 MetricRegistry (com.codahale.metrics.MetricRegistry)2 IOException (java.io.IOException)2 OutputStream (java.io.OutputStream)2 List (java.util.List)2 Map (java.util.Map)2 Optional (java.util.Optional)2 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)2 Collectors (java.util.stream.Collectors)2 JSONObject (org.json.JSONObject)2 Logger (org.slf4j.Logger)2 LoggerFactory (org.slf4j.LoggerFactory)2 CursorConverter (org.zalando.nakadi.service.CursorConverter)2 ZkSubscriptionClient (org.zalando.nakadi.service.subscription.zk.ZkSubscriptionClient)2 TimelineService (org.zalando.nakadi.service.timeline.TimelineService)2 Counter (com.codahale.metrics.Counter)1 TypeReference (com.fasterxml.jackson.core.type.TypeReference)1 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)1 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1