From 868b9ff38bfcb29bc4202f2bf4df2b20d509d97b Mon Sep 17 00:00:00 2001 From: Pau Date: Wed, 17 Jun 2020 00:08:33 +0200 Subject: [PATCH 01/12] Created docs --- .gitignore | 8 + README.md | 251 +--------------------------- build.sbt | 88 +++++++++- docs/configuration.md | 53 ++++++ docs/consumer.md | 53 ++++++ docs/getting-started.md | 145 ++++++++++++++++ docs/producer.md | 61 +++++++ project/plugins.sbt | 5 +- website/core/Footer.js | 29 ++++ website/i18n/en.json | 35 ++++ website/package.json | 15 ++ website/pages/en/index.js | 138 +++++++++++++++ website/sidebars.json | 5 + website/siteConfig.js | 47 ++++++ website/static/css/custom.css | 66 ++++++++ website/static/img/apache_kafka.png | Bin 0 -> 11401 bytes website/static/img/kafka.png | Bin 0 -> 3434 bytes website/static/img/monix-logo.png | Bin 0 -> 66471 bytes website/static/img/monix-logo.svg | 190 +++++++++++++++++++++ 19 files changed, 937 insertions(+), 252 deletions(-) create mode 100644 docs/configuration.md create mode 100644 docs/consumer.md create mode 100644 docs/getting-started.md create mode 100644 docs/producer.md create mode 100644 website/core/Footer.js create mode 100644 website/i18n/en.json create mode 100644 website/package.json create mode 100644 website/pages/en/index.js create mode 100644 website/sidebars.json create mode 100644 website/siteConfig.js create mode 100644 website/static/css/custom.css create mode 100644 website/static/img/apache_kafka.png create mode 100644 website/static/img/kafka.png create mode 100644 website/static/img/monix-logo.png create mode 100644 website/static/img/monix-logo.svg diff --git a/.gitignore b/.gitignore index 1310ac33..d2272ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,11 @@ project/plugins/project/ .scala_dependencies .worksheet .idea + +#website +/website/blog/ +/website/build/ +/website/node_modules/ +/website/static/api/ +/website/variables.js +/website/yarn.lock \ No newline at end of file diff --git a/README.md b/README.md index 28ff1acc..d12aad98 100644 --- a/README.md +++ b/README.md @@ -3,258 +3,9 @@ [![Build Status](https://travis-ci.org/monix/monix-kafka.svg?branch=master)](https://travis-ci.org/monix/monix-kafka) [![Maven Central](https://img.shields.io/maven-central/v/io.monix/monix-kafka-1x_2.12.svg)](https://search.maven.org/search?q=g:io.monix%20AND%20a:monix-kafka-1x_2.12) [![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org) - [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/monix/monix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Monix integration with Kafka - -Work in progress! - -## Table of Contents -1. [Getting Started with Kafka 1.0.x or above](#getting-started-with-kafka-10x-or-above) -2. [Getting Started with Kafka 0.11.x](#getting-started-with-kafka-011x) -3. [Getting Started with Kafka 0.10.x](#getting-started-with-kafka-010x) -4. [Getting Started with Kafka 0.9.x](#getting-started-with-kafka-09x) -5. [Getting Started with Kafka 0.8.x (no longer supported)](#getting-started-with-kafka-08x) -6. [Usage](#usage) -7. [How can I contribute to Monix-Kafka?](#how-can-i-contribute-to-monix-kafka?) -8. [Maintainers](#maintainers) -9. [License](#license) - -## Getting Started with Kafka 1.0.x or above - -In SBT: - -```scala -libraryDependencies += "io.monix" %% "monix-kafka-1x" % "1.0.0-RC5" -``` - -For `kafka` versions higher than `1.0.x` also add a dependency override: - -```scala -dependencyOverrides += "org.apache.kafka" % "kafka" % "2.1.0" -``` - -Or in case you're interested in running the tests of this project, it -now supports embedded kafka for integration testing. You can simply run: - -```bash -sbt kafka1x/test -``` - -## Getting Started with Kafka 0.11.x - -In SBT: - -```scala -libraryDependencies += "io.monix" %% "monix-kafka-11" % "1.0.0-RC5" -``` - -Or in case you're interested in running the tests of this project, it -now supports embedded kafka for integration testing. You can simply run: - -```bash -sbt kafka11/test -``` - -## Getting Started with Kafka 0.10.x - -In SBT: - -```scala -libraryDependencies += "io.monix" %% "monix-kafka-10" % "1.0.0-RC5" -``` - -Or in case you're interested in running the tests of this project, it -now supports embedded kafka for integration testing. You can simply run: - -```bash -sbt kafka10/test -``` - -## Getting Started with Kafka 0.9.x - -Please note that `EmbeddedKafka` is not supported for Kafka `0.9.x` - -In SBT: - -```scala -libraryDependencies += "io.monix" %% "monix-kafka-9" % "1.0.0-RC5" -``` - -Or in case you're interested in running the tests of this project, -first download the Kafka server, version `0.9.x` from their -[download page](https://kafka.apache.org/downloads.html) (note that -`0.10.x` or higher do not work with `0.9`), then as the -[quick start](https://kafka.apache.org/090/documentation.html#quickstart) -section says, open a terminal window and first start Zookeeper: - -```bash -bin/zookeeper-server-start.sh config/zookeeper.properties -``` - -Then start Kafka: - -```bash -bin/kafka-server-start.sh config/server.properties -``` - -Create the topic we need for our tests: - -```bash -bin/kafka-topics.sh --create --zookeeper localhost:2181 \ - --replication-factor 1 --partitions 1 \ - --topic monix-kafka-tests -``` - -And run the tests: - -```bash -sbt kafka9/test -``` - -## Getting Started with Kafka 0.8.x - -Please note that support for Kafka `0.8.x` is dropped and the last available version with this dependency is `0.14`. - -In SBT: - -```scala -libraryDependencies += "io.monix" %% "monix-kafka-8" % "0.14" -``` - -Or in case you're interested in running the tests of this project, -first download the Kafka server, version `0.8.x` from their -[download page](https://kafka.apache.org/downloads.html) (note that -`0.9.x` or higher do not work with `0.8`), then as the -[quick start](https://kafka.apache.org/082/documentation.html#quickstart) -section says, open a terminal window and first start Zookeeper: - -```bash -bin/zookeeper-server-start.sh config/zookeeper.properties -``` - -Then start Kafka: - -```bash -bin/kafka-server-start.sh config/server.properties -``` - -Create the topics we need for our tests: - -```bash -bin/kafka-topics.sh --create --zookeeper localhost:2181 \ - --replication-factor 1 --partitions 1 \ - --topic monix-kafka-tests -bin/kafka-topics.sh --create --zookeeper localhost:2181 \ - --replication-factor 1 --partitions 1 \ - --topic monix-kafka-manual-commit-tests -``` - -And run the tests: - -```bash -sbt kafka8/test -``` - -## Usage - -### Producer - -```scala -import monix.kafka._ -import monix.execution.Scheduler - -implicit val scheduler: Scheduler = monix.execution.Scheduler.global - -// Init -val producerCfg = KafkaProducerConfig.default.copy( - bootstrapServers = List("127.0.0.1:9092") -) - -val producer = KafkaProducer[String,String](producerCfg, scheduler) - -// For sending one message -val recordMetadataF = producer.send("my-topic", "my-message").runToFuture - -// For closing the producer connection -val closeF = producer.close().runToFuture -``` - -Calling `producer.send` returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` which can then be run and transformed into a `Future`. - -If the `Task` completes with `None` it means that `producer.send` method was called after the producer was closed and that the message wasn't successfully acknowledged by the Kafka broker. In case of the failure of the underlying Kafka client the producer will bubble up the exception and fail the `Task`. All successfully delivered messages will complete with `Some[RecordMetadata]`. - -For pushing an entire `Observable` to Apache Kafka: - -```scala -import monix.kafka._ -import monix.execution.Scheduler -import monix.reactive.Observable -import org.apache.kafka.clients.producer.ProducerRecord - -implicit val scheduler: Scheduler = monix.execution.Scheduler.global - -// Initializing the producer -val producerCfg = KafkaProducerConfig.default.copy( - bootstrapServers = List("127.0.0.1:9092") -) - -val producer = KafkaProducerSink[String,String](producerCfg, scheduler) - -// Lets pretend we have this observable of records -val observable: Observable[ProducerRecord[String,String]] = ??? - -observable - // on overflow, start dropping incoming events - .whileBusyDrop - // buffers into batches if the consumer is busy, up to a max size - .bufferIntrospective(1024) - // consume everything by pushing into Apache Kafka - .consumeWith(producer) - // ready, set, go! - .runToFuture -``` - -### Consumer - -There are several ways for consuming from Apache Kafka (Version 0.11.x and above): - -Consumer which commits offsets itself: -```scala -import monix.kafka._ - -val consumerCfg = KafkaConsumerConfig.default.copy( - bootstrapServers = List("127.0.0.1:9092"), - groupId = "kafka-tests" - // you can use this settings for At Most Once semantics: - // observableCommitOrder = ObservableCommitOrder.BeforeAck -) - -val observable = - KafkaConsumerObservable[String,String](consumerCfg, List("my-topic")) - .take(10000) - .map(_.value()) -``` - -Consumer which allows you to commit offsets manually: -```scala -import monix.kafka._ - -val consumerCfg = KafkaConsumerConfig.default.copy( - bootstrapServers = List("127.0.0.1:9092"), - groupId = "kafka-tests" -) - -val observable = - KafkaConsumerObservable.manualCommit[String,String](consumerCfg, List("my-topic")) - .map(message => message.record.value() -> message.committableOffset) - .mapEval { case (value, offset) => performBusinessLogic(value).map(_ => offset) } - .bufferTimedAndCounted(1.second, 1000) - .mapEval(offsets => CommittableOffsetBatch(offsets).commitSync()) -``` - -Enjoy! +See the [documentation web site](https://monix.github.io/monix-kafka) to get started. ### Caveats diff --git a/build.sbt b/build.sbt index df7a2ab6..b9076c9f 100644 --- a/build.sbt +++ b/build.sbt @@ -136,7 +136,7 @@ lazy val sharedSettings = warnUnusedImport ++ Seq( licenses := Seq("APL2" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")), homepage := Some(url("https://github.com/monix/monix-kafka")), headerLicense := Some(HeaderLicense.Custom( - """|Copyright (c) 2014-2019 by The Monix Project Developers. + """|Copyright (c) 2014-2020 by The Monix Project Developers. | |Licensed under the Apache License, Version 2.0 (the "License"); |you may not use this file except in compliance with the License. @@ -283,4 +283,90 @@ git.formattedShaVersion := { git.gitHeadCommit.value map { _.substring(0, 7) } map { sha => git.baseVersion.value + "-" + sha + suffix } +} + +lazy val docs = project + .in(file("monix-kafka-docs")) + .settings( + moduleName := "monix-kafka-docs", + name := moduleName.value, + sharedSettings, + skipOnPublishSettings, + mdocSettings + ) + .enablePlugins(DocusaurusPlugin, MdocPlugin, ScalaUnidocPlugin) + +lazy val docsMappingsAPIDir = + settingKey[String]("Name of subdirectory in site target directory for api docs") + +lazy val skipOnPublishSettings = Seq( + skip in publish := true, + publish := (()), + publishLocal := (()), + publishArtifact := false, + publishTo := None +) + +lazy val mdocSettings = Seq( + scalacOptions --= Seq("-Xfatal-warnings", "-Ywarn-unused"), + crossScalaVersions := Seq(scalaVersion.value), + unidocProjectFilter in (ScalaUnidoc, unidoc) := inProjects(monixKafka), + target in (ScalaUnidoc, unidoc) := (baseDirectory in LocalRootProject).value / "website" / "p" / "api", + cleanFiles += (target in (ScalaUnidoc, unidoc)).value, + docusaurusCreateSite := docusaurusCreateSite + .dependsOn(unidoc in Compile) + .dependsOn(updateSiteVariables in ThisBuild) + .value, + docusaurusPublishGhpages := + docusaurusPublishGhpages + .dependsOn(unidoc in Compile) + .dependsOn(updateSiteVariables in ThisBuild) + .value, + scalacOptions in (ScalaUnidoc, unidoc) ++= Seq( + "-doc-source-url", s"https://github.com/monix/monix-kafka/tree/v${version.value}€{FILE_PATH}.scala", + "-sourcepath", baseDirectory.in(LocalRootProject).value.getAbsolutePath, + "-doc-title", "Monix Kafka", + "-doc-version", s"v${version.value}", + "-groups" + ), + // Exclude monix.*.internal from ScalaDoc + sources in (ScalaUnidoc, unidoc) ~= (_ filterNot { file => + // Exclude all internal Java files from documentation + file.getCanonicalPath matches "^.*monix.+?internal.*?\\.java$" + }), +) + +def minorVersion(version: String): String = { + val (major, minor) = + CrossVersion.partialVersion(version).get + s"$major.$minor" +} + +val updateSiteVariables = taskKey[Unit]("Update site variables") +updateSiteVariables in ThisBuild := { + val file = + (baseDirectory in LocalRootProject).value / "website" / "variables.js" + + val variables = + Map[String, String]( + "organization" -> (organization in LocalRootProject).value, + "coreModuleName" -> (moduleName in monixKafka).value, + "latestVersion" -> version.value, + "scalaPublishVersions" -> { + val minorVersions = (crossScalaVersions in monixKafka).value.map(minorVersion) + if (minorVersions.size <= 2) minorVersions.mkString(" and ") + else minorVersions.init.mkString(", ") ++ " and " ++ minorVersions.last + } + ) + + val fileHeader = + "// Generated by sbt. Do not edit directly." + + val fileContents = + variables.toList + .sortBy { case (key, _) => key } + .map { case (key, value) => s" $key: '$value'" } + .mkString(s"$fileHeader\nmodule.exports = {\n", ",\n", "\n};\n") + + IO.write(file, fileContents) } \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 00000000..ad08181e --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,53 @@ +--- +id: consumer +title: Consumer +--- + +Apache Kafka does provide a wide range of parameters to be configured, it allows to cover the most specific business cases and also highly recommendable to fine tune them for reaching out the best +possible performance. + + +Monix Kafka designed to provide file driven configuration to the user's application, so the values set on [default.conf](/ka) would represents the default configuration + + + + + +### Auto commit consumer + + +```scala +import monix.kafka._ + +val consumerConf = KafkaConsumerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092"), + groupId = "kafka-tests" + // you can use this settings for At Most Once semantics: + // observableCommitOrder = ObservableCommitOrder.BeforeAck +) + +val observable = + KafkaConsumerObservable[String,String](consumerConf, List("my-topic")) + .take(10000) + .map(_.value()) +``` + +Consumer which allows you to commit offsets manually: +```scala +import monix.kafka._ + +val consumerCfg = KafkaConsumerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092"), + groupId = "kafka-tests" +) + +val observable = + KafkaConsumerObservable.manualCommit[String,String](consumerCfg, List("my-topic")) + .map(message => message.record.value() -> message.committableOffset) + .mapEval { case (value, offset) => performBusinessLogic(value).map(_ => offset) } + .bufferTimedAndCounted(1.second, 1000) + .mapEval(offsets => CommittableOffsetBatch(offsets).commitSync()) +``` + + + diff --git a/docs/consumer.md b/docs/consumer.md new file mode 100644 index 00000000..20726216 --- /dev/null +++ b/docs/consumer.md @@ -0,0 +1,53 @@ +--- +id: consumer +title: Consumer +--- + + +In order to understand how to read data from Kafka, you first need to understand the concept of a Kafka consumer and consumer groups. + +Monix Kafka implements the Consumer API in form of monix `Observable` type, which would represent an unbounded stream of events consumed from the specified kafka topics. + + +There are several ways for consuming from Apache Kafka (Version 0.11.x and above): + + + +### Auto commit consumer + + +```scala +import monix.kafka._ + +val consumerConf = KafkaConsumerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092"), + groupId = "kafka-tests" + // you can use this settings for At Most Once semantics: + // observableCommitOrder = ObservableCommitOrder.BeforeAck +) + +val observable = + KafkaConsumerObservable[String,String](consumerConf, List("my-topic")) + .take(10000) + .map(_.value()) +``` + +Consumer which allows you to commit offsets manually: +```scala +import monix.kafka._ + +val consumerCfg = KafkaConsumerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092"), + groupId = "kafka-tests" +) + +val observable = + KafkaConsumerObservable.manualCommit[String,String](consumerCfg, List("my-topic")) + .map(message => message.record.value() -> message.committableOffset) + .mapEval { case (value, offset) => performBusinessLogic(value).map(_ => offset) } + .bufferTimedAndCounted(1.second, 1000) + .mapEval(offsets => CommittableOffsetBatch(offsets).commitSync()) +``` + + + diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..17996416 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,145 @@ +--- +id: getting-started +title: Getting Started +--- + +This project supports different versions of Apache Kafka, +see in below sections how to get started with each of them: + +## Kafka 1.0.x or above + +In SBT: + +```scala +libraryDependencies += "io.monix" %% "monix-kafka-1x" % "1.0.0-RC5" +``` + +For `kafka` versions higher than `1.0.x` also add a dependency override: + +```scala +dependencyOverrides += "org.apache.kafka" % "kafka" % "2.1.0" +``` + +Or in case you're interested in running the tests of this project, it +now supports embedded kafka for integration testing. You can simply run: + +```bash +sbt kafka1x/test +``` + +## Kafka 0.11.x + +In SBT: + +```scala +libraryDependencies += "io.monix" %% "monix-kafka-11" % "1.0.0-RC5" +``` + +Or in case you're interested in running the tests of this project, it +now supports embedded kafka for integration testing. You can simply run: + +```bash +sbt kafka11/test +``` + +## Kafka 0.10.x + +In SBT: + +```scala +libraryDependencies += "io.monix" %% "monix-kafka-10" % "1.0.0-RC5" +``` + +Or in case you're interested in running the tests of this project, it +now supports embedded kafka for integration testing. You can simply run: + +```bash +sbt kafka10/test +``` + +## Kafka 0.9.x + +Please note that `EmbeddedKafka` is not supported for Kafka `0.9.x` + +In SBT: + +```scala +libraryDependencies += "io.monix" %% "monix-kafka-9" % "1.0.0-RC5" +``` + +Or in case you're interested in running the tests of this project, +first download the Kafka server, version `0.9.x` from their +[download page](https://kafka.apache.org/downloads.html) (note that +`0.10.x` or higher do not work with `0.9`), then as the +[quick start](https://kafka.apache.org/090/documentation.html#quickstart) +section says, open a terminal window and first start Zookeeper: + +```bash +bin/zookeeper-server-start.sh config/zookeeper.properties +``` + +Then start Kafka: + +```bash +bin/kafka-server-start.sh config/server.properties +``` + +Create the topic we need for our tests: + +```bash +bin/kafka-topics.sh --create --zookeeper localhost:2181 \ + --replication-factor 1 --partitions 1 \ + --topic monix-kafka-tests +``` + +And run the tests: + +```bash +sbt kafka9/test +``` + +## Kafka 0.8.x + +Please note that support for Kafka `0.8.x` is dropped and the last available version with this dependency is `0.14`. + +In SBT: + +```scala +libraryDependencies += "io.monix" %% "monix-kafka-8" % "0.14" +``` + +Or in case you're interested in running the tests of this project, +first download the Kafka server, version `0.8.x` from their +[download page](https://kafka.apache.org/downloads.html) (note that +`0.9.x` or higher do not work with `0.8`), then as the +[quick start](https://kafka.apache.org/082/documentation.html#quickstart) +section says, open a terminal window and first start Zookeeper: + +```bash +bin/zookeeper-server-start.sh config/zookeeper.properties +``` + +Then start Kafka: + +```bash +bin/kafka-server-start.sh config/server.properties +``` + +Create the topics we need for our tests: + +```bash +bin/kafka-topics.sh --create --zookeeper localhost:2181 \ + --replication-factor 1 --partitions 1 \ + --topic monix-kafka-tests +bin/kafka-topics.sh --create --zookeeper localhost:2181 \ + --replication-factor 1 --partitions 1 \ + --topic monix-kafka-manual-commit-tests +``` + +And run the tests: + +```bash +sbt kafka8/test +``` + + diff --git a/docs/producer.md b/docs/producer.md new file mode 100644 index 00000000..10c2a734 --- /dev/null +++ b/docs/producer.md @@ -0,0 +1,61 @@ +--- +id: producer +title: Producer +--- + + ```scala + import monix.kafka._ + import monix.execution.Scheduler + + implicit val scheduler: Scheduler = monix.execution.Scheduler.global + + // Init + val producerCfg = KafkaProducerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092") + ) + + val producer = KafkaProducer[String,String](producerCfg, scheduler) + + // For sending one message + val recordMetadataF = producer.send("my-topic", "my-message").runToFuture + + // For closing the producer connection + val closeF = producer.close().runToFuture + ``` + + Calling `producer.send` returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` which can then be run and transformed into a `Future`. + + If the `Task` completes with `None` it means that `producer.send` method was called after the producer was closed and that the message wasn't successfully acknowledged by the Kafka broker. In case of the failure of the underlying Kafka client the producer will bubble up the exception and fail the `Task`. All successfully delivered messages will complete with `Some[RecordMetadata]`. + + For pushing an entire `Observable` to Apache Kafka: + + ```scala + import monix.kafka._ + import monix.execution.Scheduler + import monix.reactive.Observable + import org.apache.kafka.clients.producer.ProducerRecord + + implicit val scheduler: Scheduler = monix.execution.Scheduler.global + + // Initializing the producer + val producerCfg = KafkaProducerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092") + ) + + val producer = KafkaProducerSink[String,String](producerCfg, scheduler) + + // Lets pretend we have this observable of records + val observable: Observable[ProducerRecord[String,String]] = ??? + + observable + // on overflow, start dropping incoming events + .whileBusyDrop + // buffers into batches if the consumer is busy, up to a max size + .bufferIntrospective(1024) + // consume everything by pushing into Apache Kafka + .consumeWith(producer) + // ready, set, go! + .runToFuture + ``` + + diff --git a/project/plugins.sbt b/project/plugins.sbt index a9c701be..31cdd709 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,4 +4,7 @@ addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.8.1") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.4") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.0") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.0") \ No newline at end of file +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.0") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.1") +addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.3") + diff --git a/website/core/Footer.js b/website/core/Footer.js new file mode 100644 index 00000000..ad889f11 --- /dev/null +++ b/website/core/Footer.js @@ -0,0 +1,29 @@ +const React = require("react"); + +class Footer extends React.Component { + docUrl(doc, language) { + const baseUrl = this.props.config.baseUrl; + const docsUrl = this.props.config.docsUrl; + const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`; + const langPart = `${language ? `${language}/` : ""}`; + return `${baseUrl}${docsPart}${langPart}${doc}`; + } + + pageUrl(doc, language) { + const baseUrl = this.props.config.baseUrl; + return baseUrl + (language ? `${language}/` : "") + doc; + } + + render() { + return ( + + ); + } +} + +module.exports = Footer; \ No newline at end of file diff --git a/website/i18n/en.json b/website/i18n/en.json new file mode 100644 index 00000000..f0f98090 --- /dev/null +++ b/website/i18n/en.json @@ -0,0 +1,35 @@ +{ + "_comment": "This file is auto-generated by write-translations.js", + "localized-strings": { + "next": "Next", + "previous": "Previous", + "tagline": "A Monix integration with Kafka.", + "docs": { + "consumer": { + "title": "Consumer" + }, + "getting-started": { + "title": "Getting Started" + }, + "overview": { + "title": "Overview" + }, + "producer": { + "title": "Producer" + } + }, + "links": { + "API Docs": "API Docs", + "Documentation": "Documentation", + "GitHub": "GitHub" + }, + "categories": { + "Documentation": "Documentation" + } + }, + "pages-strings": { + "Help Translate|recruit community translators for your project": "Help Translate", + "Edit this Doc|recruitment message asking to edit the doc source": "Edit", + "Translate this Doc|recruitment message asking to translate the docs": "Translate" + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 00000000..54174ec0 --- /dev/null +++ b/website/package.json @@ -0,0 +1,15 @@ +{ + "license": "Apache-2.0", + "scripts": { + "examples": "docusaurus-examples", + "start": "docusaurus-start", + "build": "docusaurus-build", + "publish-gh-pages": "docusaurus-publish", + "write-translations": "docusaurus-write-translations", + "version": "docusaurus-version", + "rename-version": "docusaurus-rename-version" + }, + "devDependencies": { + "docusaurus": "^1.6.2" + } +} \ No newline at end of file diff --git a/website/pages/en/index.js b/website/pages/en/index.js new file mode 100644 index 00000000..2ab84142 --- /dev/null +++ b/website/pages/en/index.js @@ -0,0 +1,138 @@ +const React = require("react"); + +const CompLibrary = require("../../core/CompLibrary.js"); + +const variables = require(process.cwd() + "/variables.js"); + +const MarkdownBlock = CompLibrary.MarkdownBlock; +const Container = CompLibrary.Container; +const GridBlock = CompLibrary.GridBlock; + +class HomeSplash extends React.Component { + render() { + const { siteConfig, language = "" } = this.props; + const { baseUrl, docsUrl } = siteConfig; + const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`; + const langPart = `${language ? `${language}/` : ""}`; + const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`; + + const SplashContainer = props => ( +
+
+
{props.children}
+
+
+ ); + + const ProjectTitle = () => ( +

+ + + + {siteConfig.title} + + + + + {siteConfig.tagline} +

+ ); + + const PromoSection = props => ( +
+
+
{props.children}
+
+
+ ); + + const Button = props => ( +
+ + {props.children} + +
+ ); + + return ( + +
+ + + + + + +
+
+ ); + } +} + +class Index extends React.Component { + render() { + const { config: siteConfig, language = "" } = this.props; + const { baseUrl } = siteConfig; + +const index = ` + +[![Build Status](https://travis-ci.org/monix/monix-kafka.svg?branch=master)](https://travis-ci.org/monix/monix-kafka) +[![Maven Central](https://img.shields.io/maven-central/v/io.monix/monix-kafka-1x_2.12.svg)](https://search.maven.org/search?q=g:io.monix%20AND%20a:monix-kafka-1x_2.12) +[![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/monix/monix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + + +This project provides a [Monix](https://monix.io) stream based interface for [Apache Kafka](https://kafka.apache.org/documentation.html#producerapi), +which means it offers full advantage on performance that a the Monix reactive application mixed with the main Kafka based capabilities such like publish and subscribe to streams in a fault-tolerant durable way. + +### Getting Started +The latest stable version, compatible with _Monix 3.x_ and it does support _Kafka 1.0.x_, _0.11.x_, _0.10.x_, _0.9.x_: + +\`\`\`scala +libraryDependencies += "${organization}" %% "${coreModuleName}" % "${latestVersion}" +\`\`\` + +Published for Scala ${scalaPublishVersions}. +`.trim(); + + const { + organization, + coreModuleName, + latestVersion, + scalaPublishVersions + } = variables; + + const latestVersionBadge = latestVersion + .replace("-", "--") + .replace("_", "__"); + + const Block = props => ( + + + + ); + + + + return ( +
+ +
+
+ {index} +
+
+
+ ); + } +} + +module.exports = Index; \ No newline at end of file diff --git a/website/sidebars.json b/website/sidebars.json new file mode 100644 index 00000000..70da22ba --- /dev/null +++ b/website/sidebars.json @@ -0,0 +1,5 @@ +{ + "docs": { + "Documentation": ["getting-started", "producer", "consumer"] + } +} \ No newline at end of file diff --git a/website/siteConfig.js b/website/siteConfig.js new file mode 100644 index 00000000..645ad5e9 --- /dev/null +++ b/website/siteConfig.js @@ -0,0 +1,47 @@ +const repoUrl = "https://github.com/monix/monix-kafka"; + +const apiUrl = "api/monix/kafka/index.html" + +const siteConfig = { + title: "Monix Kafka", + tagline: "A Monix integration with Kafka.", + url: "https://monix.github.io/monix-kafka", + baseUrl: "/monix-kafka/", + cname: "monix.github.io/monix-kafka", + + customDocsPath: "monix-kafka-docs/target/mdoc", + + projectName: "monix-kafka", + organizationName: "monix", + + headerLinks: [ + { href: apiUrl, label: "API Docs" }, + { doc: "overview", label: "Documentation" }, + { href: repoUrl, label: "GitHub" } + ], + + headerIcon: "img/monix-logo.svg", + titleIcon: "img/monix-logo.svg", + favicon: "img/monix-logo.png", + + colors: { + primaryColor: "#122932", + secondaryColor: "#153243" + }, + + copyright: `Copyright © 2014-${new Date().getFullYear()} The Monix Developers.`, + + highlight: { theme: "github" }, + + onPageNav: "separate", + + separateCss: ["api"], + + cleanUrl: true, + + repoUrl, + + apiUrl +}; + +module.exports = siteConfig; diff --git a/website/static/css/custom.css b/website/static/css/custom.css new file mode 100644 index 00000000..1ba46366 --- /dev/null +++ b/website/static/css/custom.css @@ -0,0 +1,66 @@ +@media only screen and (min-device-width: 360px) and (max-device-width: 736px) { +} + +@media only screen and (min-width: 1024px) { +} + +@media only screen and (max-width: 1023px) { +} + +@media only screen and (min-width: 1400px) { +} + +@media only screen and (min-width: 1500px) { +} + +.nav-footer { + background: unset; +} + +.nav-footer .copyright { + color: #7c7c7c; +} + +.nav-footer .separator { + width: 350px; + margin: 0 auto; + margin-bottom: 10px; + border-top: 0; +} + +.mainContainer { + padding-bottom: 0; +} + +.mainContainer .index { + margin: 0 auto; + max-width: 650px; + padding: 0 10px; +} + +.homeSplashFade .wrapper.homeWrapper { + padding-bottom: 0; + padding-top: 25px; +} + +.projectTitleLogo { + width: 55px; + vertical-align: bottom; + margin-right: 3px; +} + +.fixedHeaderContainer header img { + margin-right: 3px; +} + +.buttonWrapper { + margin-bottom: 5px; +} + +a { + color: #19647e; +} + +.onPageNav a:hover { + color: #122932; +} \ No newline at end of file diff --git a/website/static/img/apache_kafka.png b/website/static/img/apache_kafka.png new file mode 100644 index 0000000000000000000000000000000000000000..83b2acd29bdd469bfe5ba5912f8ae13a4fbb9bd5 GIT binary patch literal 11401 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 M07*qoM6N<$f?8RIvH$=8 literal 0 HcmV?d00001 diff --git a/website/static/img/monix-logo.png b/website/static/img/monix-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4705f411c613c9a6352077b30e8aa08c6710d24c GIT binary patch literal 66471 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 + + + + + image/svg+xml + + Monix - Blue + + + + + + + + Monix - Blue + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ddee637f660f042c5402f09c61dc38236a2fd1cb Mon Sep 17 00:00:00 2001 From: Pau Date: Wed, 17 Jun 2020 09:22:55 +0200 Subject: [PATCH 02/12] Added configuration section --- build.sbt | 13 +++++ docs/configuration.md | 56 ++++++++++-------- examples/src/it/resources/default.conf | 80 ++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 23 deletions(-) create mode 100644 examples/src/it/resources/default.conf diff --git a/build.sbt b/build.sbt index b9076c9f..d2805c65 100644 --- a/build.sbt +++ b/build.sbt @@ -262,6 +262,8 @@ lazy val kafka9 = project.in(file("kafka-0.9.x")) ) ) + + //------------- For Release enablePlugins(GitVersioning) @@ -285,6 +287,17 @@ git.formattedShaVersion := { } } +lazy val examples = project.in(file("examples")) + .settings(sharedSettings) + .settings(commonDependencies) + .settings( + name := "examples", + organization := "io.monix", + scalaVersion := "2.12.10" + ) + .aggregate(kafka1x) + .dependsOn(kafka1x) + lazy val docs = project .in(file("monix-kafka-docs")) .settings( diff --git a/docs/configuration.md b/docs/configuration.md index ad08181e..448623e8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -3,51 +3,61 @@ id: consumer title: Consumer --- +### Introduction + Apache Kafka does provide a wide range of parameters to be configured, it allows to cover the most specific business cases and also highly recommendable to fine tune them for reaching out the best possible performance. +Monix Kafka designed to provide file driven configuration to the user's application, so the values set on [default.conf](/examples/src/it/resources/default.conf) represents the default configuration used +by either the Consumer or the Producer. But it will be necessary to override these config parameters either from the same conf file, from environment variables or directly from the code. -Monix Kafka designed to provide file driven configuration to the user's application, so the values set on [default.conf](/ka) would represents the default configuration +Let's see how to do so in the following section. +### Getting started with configuration +As mentioned before, you can specify configuration parameters from `.conf`. This has to be `HOCON` file, +an awesome file format that supports many features to fit your use case, it does support java format, substitutions, comments, properties-like notation, and more importantly, allow to substitute from environment variables. +For more info see: [typesafe config](https://github.com/lightbend/config). +As a quick go through the configuration, let's highlight some of the most important configuration fields: +The first one and more important one is the kafka broker you are pointing to, being as default `localhost:9092` would probably don't need to be modified to work locally, but you would definetly +have to update this one with the required bootstrap servers of your kafka cluster: -### Auto commit consumer +```hocon +kafka { + bootstrap.servers = ["localhost:9092", "localhost:9093"] + client.id = "" + ... +} +``` +You could for example overwrite the client id from an env var like: -```scala -import monix.kafka._ +```hocon + client.id = "" + client.id = ${KAFKA_CLIENT_ID} +``` -val consumerConf = KafkaConsumerConfig.default.copy( - bootstrapServers = List("127.0.0.1:9092"), - groupId = "kafka-tests" - // you can use this settings for At Most Once semantics: - // observableCommitOrder = ObservableCommitOrder.BeforeAck -) +Or if you put an interrogant before the env var name, it would just use it in case the variable exists, otherwise would just fallback to the default value: -val observable = - KafkaConsumerObservable[String,String](consumerConf, List("my-topic")) - .take(10000) - .map(_.value()) +```hocon + client.id = "default-client-id" + client.id = ${?KAFKA_CLIENT_ID} ``` -Consumer which allows you to commit offsets manually: +Finally, you can just set all these values from your code in a very nice way: + ```scala import monix.kafka._ -val consumerCfg = KafkaConsumerConfig.default.copy( +val consumerConf = KafkaConsumerConfig.default.copy( bootstrapServers = List("127.0.0.1:9092"), groupId = "kafka-tests" ) - -val observable = - KafkaConsumerObservable.manualCommit[String,String](consumerCfg, List("my-topic")) - .map(message => message.record.value() -> message.committableOffset) - .mapEval { case (value, offset) => performBusinessLogic(value).map(_ => offset) } - .bufferTimedAndCounted(1.second, 1000) - .mapEval(offsets => CommittableOffsetBatch(offsets).commitSync()) ``` +There are roughly 70 fields to be configured, mostly they are related with security, broker, topic, communication, producer and consumer. +For more information about those, you would better consult either the kafka or confluent [configuration documentation](https://docs.confluent.io/current/installation/configuration/index.html). diff --git a/examples/src/it/resources/default.conf b/examples/src/it/resources/default.conf new file mode 100644 index 00000000..0a6009af --- /dev/null +++ b/examples/src/it/resources/default.conf @@ -0,0 +1,80 @@ +kafka { + bootstrap.servers = "localhost:9092" + client.id = "" + + # E.g. "org.apache.kafka.clients.producer.internals.DefaultPartitioner" + partitioner.class = null + + acks = "1" + buffer.memory = 33554432 + compression.type = "none" + retries = 0 + max.in.flight.requests.per.connection = 5 + + ssl.key.password = null + ssl.keystore.password = null + ssl.keystore.location = null + ssl.truststore.password = null + ssl.truststore.location = null + + batch.size = 16384 + connections.max.idle.ms = 540000 + linger.ms = 0 + max.block.ms = 60000 + max.request.size = 1048576 + + receive.buffer.bytes = 32768 + request.timeout.ms = 40000 + + sasl.kerberos.service.name = null + sasl.mechanism = "GSSAPI" + + security.protocol = "PLAINTEXT" + send.buffer.bytes = 131072 + ssl.enabled.protocols = "TLSv1.2,TLSv1.1,TLSv1" + ssl.keystore.type = "JKS" + ssl.protocol = "TLS" + ssl.provider = null + ssl.truststore.type = "JKS" + + reconnect.backoff.ms = 50 + retry.backoff.ms = 100 + + metadata.max.age.ms = 300000 + + metric.reporters = "" + metrics.num.samples = 2 + metrics.sample.window.ms = 30000 + + # Consumer specific settings + + fetch.min.bytes = 1 + fetch.max.bytes = 52428800 + group.id = "" + heartbeat.interval.ms = 3000 + max.partition.fetch.bytes = 1048576 + auto.offset.reset = "latest" + # Disabled to use back-pressure or manual commits instead + enable.auto.commit = false + exclude.internal.topics = true + receive.buffer.bytes = 65536 + check.crcs = true + fetch.max.wait.ms = 500 + # Default values for polling + # See https://cwiki.apache.org/confluence/display/KAFKA/KIP-62%3A+Allow+consumer+to+send+heartbeats+from+a+background+thread + session.timeout.ms = 10000 + max.poll.records = 500 + max.poll.interval.ms = 300000 + + # Monix specific settings + + # Number of requests that KafkaProducerSink + # can push in parallel + monix.producer.sink.parallelism = 100 + # Triggers a seekToEnd when the observable starts + monix.observable.seekEnd.onStart = false + # Possible values: sync, async + monix.observable.commit.type = "sync" + # Possible values: before-ack, after-ack or no-ack + monix.observable.commit.order = "after-ack" +} \ No newline at end of file From 4ab448da0fa8dead263f528fc47bd8d7d652b6ed Mon Sep 17 00:00:00 2001 From: Pau Date: Thu, 18 Jun 2020 08:32:24 +0200 Subject: [PATCH 03/12] Consumer documentation --- docs/configuration.md | 20 ++++++----- docs/consumer.md | 81 ++++++++++++++++++++++++++++++++++++------- website/i18n/en.json | 3 ++ website/sidebars.json | 2 +- 4 files changed, 84 insertions(+), 22 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 448623e8..0ed4c304 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,6 +1,6 @@ --- -id: consumer -title: Consumer +id: configuration +title: Configuration --- ### Introduction @@ -8,18 +8,19 @@ title: Consumer Apache Kafka does provide a wide range of parameters to be configured, it allows to cover the most specific business cases and also highly recommendable to fine tune them for reaching out the best possible performance. -Monix Kafka designed to provide file driven configuration to the user's application, so the values set on [default.conf](/examples/src/it/resources/default.conf) represents the default configuration used -by either the Consumer or the Producer. But it will be necessary to override these config parameters either from the same conf file, from environment variables or directly from the code. +Monix Kafka provides file driven configuration to the user's application, so it means that the values set on [default.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf) represents the kafka configuration used by default. + +If a file with format `.conf` is in the folder `src/resource` of your project, will be used as a default one and from there on you can overwrite using environment variables or directly from the code. Let's see how to do so in the following section. -### Getting started with configuration +### Getting started with the configuration -As mentioned before, you can specify configuration parameters from `.conf`. This has to be `HOCON` file, -an awesome file format that supports many features to fit your use case, it does support java format, substitutions, comments, properties-like notation, and more importantly, allow to substitute from environment variables. +As mentioned before, you can specify configuration parameters from `.conf`. It represents a `HOCON` file, +an awesome file format that supports many features to fit your use case, with support for java format, substitutions, comments, properties-like notation, and more importantly, it allows substitution from environment variables and from your code. For more info see: [typesafe config](https://github.com/lightbend/config). -As a quick go through the configuration, let's highlight some of the most important configuration fields: +As a quick go through, let's highlight some of the most important configuration fields: The first one and more important one is the kafka broker you are pointing to, being as default `localhost:9092` would probably don't need to be modified to work locally, but you would definetly have to update this one with the required bootstrap servers of your kafka cluster: @@ -57,7 +58,8 @@ val consumerConf = KafkaConsumerConfig.default.copy( ) ``` -There are roughly 70 fields to be configured, mostly they are related with security, broker, topic, communication, producer and consumer. +There are roughly 70 fields to be configured, mostly they are related with security, broker, topic, communication, consumer and producer. For more information about those, you would better consult either the kafka or confluent [configuration documentation](https://docs.confluent.io/current/installation/configuration/index.html). +Consumer and Producer specific configurations would be explained in detail on their respective sections. diff --git a/docs/consumer.md b/docs/consumer.md index 20726216..a594bcb6 100644 --- a/docs/consumer.md +++ b/docs/consumer.md @@ -3,28 +3,75 @@ id: consumer title: Consumer --- +_Monix Kafka_ abstracts the _Kafka Consumer API_ in form of `Observable` type, which would represent an unbounded stream of events consumed from the specified kafka topics. + +Below table shows the two different ways of consuming from Kafka topics are available (Version 0.11.x and above): + +| __Offsets handling__ | __Signature__ | __Stream element type__ | + | :---: | :---: | :---: | + | No (auto commit can be enabled)_| KafkaConsumerObservable_.apply_ | _ConsumerRecord[K, V]_ | + | Manual commit | KafkaConsumerObservable_.manualCommit_ | _CommittableMessage[K, V]_ | + +These two will be further explained in code on next sections, but first let's review the _Consumer configuration_. + +### Consumer configuration + +As it was mentioned on the [previous]() sections, configuration can be specified either from [default.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf#L49) or +overwriting default values from the same code. Such file aggregates all Kafka related config, but you would better get used to the following ones before using the kafka consumer api: + +```hocon +kafka { + # these only represents consumer related configurable parameters + client.rack = "" + fetch.min.bytes = 1 + fetch.max.bytes = 52428800 + group.id = "" + heartbeat.interval.ms = 3000 + max.partition.fetch.bytes = 1048576 + auto.offset.reset = "latest" + enable.auto.commit = false + exclude.internal.topics = true + receive.buffer.bytes = 65536 + check.crcs = true + fetch.max.wait.ms = 500 + session.timeout.ms = 10000 + max.poll.records = 500 + max.poll.interval.ms = 300000 + # sync, async + monix.observable.commit.type = "sync" + # before-ack, after-ack or no-ack + monix.observable.commit.order = "after-ack" +} +``` -In order to understand how to read data from Kafka, you first need to understand the concept of a Kafka consumer and consumer groups. - -Monix Kafka implements the Consumer API in form of monix `Observable` type, which would represent an unbounded stream of events consumed from the specified kafka topics. - - -There are several ways for consuming from Apache Kafka (Version 0.11.x and above): - +For more details about what each of these configurable parameters mean, please directly review the [official confluent documentation](https://docs.confluent.io/current/installation/configuration/consumer-configs.html#cp-config-consumer) +for _Kafka Consumer Configuration_. +### Plain consumer -### Auto commit consumer +The `plainSource` emits `ConsumerRecord` elements, a record represents the received key/value pair that also contains information about the topic, partition, offset and timestamp). +But more importantly, it __does not support offsets commitment__ to Kafka, then it can be used when the offset is stored externally or with auto-commit. +Note that auto-commit is disabled by default, you can fine tune the auto commitment by setting the monix observable specific configuration `ObservableCommitOrder`, +which will allow you to decide whether to commit the records before receiving an acknowledgement from downstream, after that, or to just don't acknowledge (as a default one). +If _At Most Once_ semantics is seek, auto commit must be enabled and observable commit order done before ack: + ```scala import monix.kafka._ val consumerConf = KafkaConsumerConfig.default.copy( bootstrapServers = List("127.0.0.1:9092"), - groupId = "kafka-tests" - // you can use this settings for At Most Once semantics: - // observableCommitOrder = ObservableCommitOrder.BeforeAck + groupId = "kafka-tests", + enableAutoCommit = true, + observableCommitOrder = ObservableCommitOrder.BeforeAck ) +``` + +If the concept of _auto-commit_ and _observableCommitOrder_ was well understood, the implementation will be straight forward for you: + +```scala +import monix.kafka._ val observable = KafkaConsumerObservable[String,String](consumerConf, List("my-topic")) @@ -32,7 +79,15 @@ val observable = .map(_.value()) ``` -Consumer which allows you to commit offsets manually: +### Manual commit consumer: + +The `manualCommit` makes it possible to commit offset positions to Kafka. In this case the emitted record would `CommitableMessage`, +this is just a wrapper for `ConsumerRecord` with `CommittableOffset`. + +Committable offset represents the offset for specified topic and partition that can be committed synchronously by `commitSync` method call or asynchronously by one of commitAsync methods. + To achieve good performance it is recommended to use batched commit with `CommittableOffsetBatch` class. + +Let's see an example on how to use the batch committable offset. ```scala import monix.kafka._ @@ -49,5 +104,7 @@ val observable = .mapEval(offsets => CommittableOffsetBatch(offsets).commitSync()) ``` +In summary, this consumer is useful when _At Least Once_ delivery is desired, as each message will be delivered at least once but in failure cases could be duplicated. +And compared with _auto commit_, it gives fine granted control over when a message is considered consumed or not. diff --git a/website/i18n/en.json b/website/i18n/en.json index f0f98090..0b773c19 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -5,6 +5,9 @@ "previous": "Previous", "tagline": "A Monix integration with Kafka.", "docs": { + "configuration": { + "title": "Configuration" + }, "consumer": { "title": "Consumer" }, diff --git a/website/sidebars.json b/website/sidebars.json index 70da22ba..6b467391 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -1,5 +1,5 @@ { "docs": { - "Documentation": ["getting-started", "producer", "consumer"] + "Documentation": ["getting-started", "configuration", "producer", "consumer"] } } \ No newline at end of file From 3b9ee9d939893bed4b483d6d3cde48edc2d9431a Mon Sep 17 00:00:00 2001 From: Pau Date: Thu, 18 Jun 2020 10:01:04 +0200 Subject: [PATCH 04/12] More consumer doc --- docs/consumer.md | 21 ++++++++++++++------- docs/producer.md | 9 +++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/consumer.md b/docs/consumer.md index a594bcb6..1f6a8c6e 100644 --- a/docs/consumer.md +++ b/docs/consumer.md @@ -9,25 +9,28 @@ Below table shows the two different ways of consuming from Kafka topics are avai | __Offsets handling__ | __Signature__ | __Stream element type__ | | :---: | :---: | :---: | - | No (auto commit can be enabled)_| KafkaConsumerObservable_.apply_ | _ConsumerRecord[K, V]_ | - | Manual commit | KafkaConsumerObservable_.manualCommit_ | _CommittableMessage[K, V]_ | + | No _(auto commit can be enabled)_| _KafkaConsumerObservable.apply_ | _ConsumerRecord[K, V]_ | + | Manual commit | _KafkaConsumerObservable.manualCommit_ | _CommittableMessage[K, V]_ | These two will be further explained in code on next sections, but first let's review the _Consumer configuration_. ### Consumer configuration As it was mentioned on the [previous]() sections, configuration can be specified either from [default.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf#L49) or -overwriting default values from the same code. Such file aggregates all Kafka related config, but you would better get used to the following ones before using the kafka consumer api: +overwriting default values from the same code. Below list of properties represents the parameters +of the _HOCON_ file that are __only__ consumer related properties which could give you a first glance of what to look at when configuring the consumer, but of course there +are more configurable parameters related to Kafka: ```hocon kafka { - # these only represents consumer related configurable parameters - client.rack = "" + # these represents ony consumer related configurable parameters + # but you might want to set other kafka configurations that affect can consumer too fetch.min.bytes = 1 fetch.max.bytes = 52428800 group.id = "" heartbeat.interval.ms = 3000 max.partition.fetch.bytes = 1048576 + session.timeout.ms = 10000 auto.offset.reset = "latest" enable.auto.commit = false exclude.internal.topics = true @@ -46,6 +49,9 @@ kafka { For more details about what each of these configurable parameters mean, please directly review the [official confluent documentation](https://docs.confluent.io/current/installation/configuration/consumer-configs.html#cp-config-consumer) for _Kafka Consumer Configuration_. +You would also better check `monix.kafka.KafkaConsumerConfig` in order to know exactly what are the properties the consumer takes care of. +Note that `monix.observable.commit.type` and `monix.observable.commit.order` are not passed to Kafka, since they are monix self configurations that would be taken into account only for the +`plain consumer` but not for `manualCommit`. See next section for more info about these. ### Plain consumer @@ -81,13 +87,14 @@ val observable = ### Manual commit consumer: -The `manualCommit` makes it possible to commit offset positions to Kafka. In this case the emitted record would `CommitableMessage`, +The `manualCommit` makes it possible to commit offset positions to Kafka. In this case the emitted record would be `CommitableMessage`, this is just a wrapper for `ConsumerRecord` with `CommittableOffset`. Committable offset represents the offset for specified topic and partition that can be committed synchronously by `commitSync` method call or asynchronously by one of commitAsync methods. To achieve good performance it is recommended to use batched commit with `CommittableOffsetBatch` class. -Let's see an example on how to use the batch committable offset. +Let's now see an example on how to use the batch committable offset: + ```scala import monix.kafka._ diff --git a/docs/producer.md b/docs/producer.md index 10c2a734..c7aa17d9 100644 --- a/docs/producer.md +++ b/docs/producer.md @@ -3,6 +3,15 @@ id: producer title: Producer --- +The _Kafka Producer API_ allows an application to publish a stream of records to one or more Kafka topics. + +Using Monix Kafka you could either produce a single event or to define a producer that will push an unbounded incoming stream of events, they complement very well to accomplish different possible use cases. + + +## Producer Configurations + +https://docs.confluent.io/current/installation/configuration/producer-configs.html + ```scala import monix.kafka._ import monix.execution.Scheduler From e4038a696262ffc7861e22b8c7394ba1bba5c6e7 Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 21 Jun 2020 10:40:48 +0200 Subject: [PATCH 05/12] Producer docs --- docs/consumer.md | 16 +++++---- docs/getting-started.md | 2 +- docs/producer.md | 74 +++++++++++++++++++++++++++------------ website/pages/en/index.js | 2 +- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/docs/consumer.md b/docs/consumer.md index 1f6a8c6e..452fdc2c 100644 --- a/docs/consumer.md +++ b/docs/consumer.md @@ -7,10 +7,10 @@ _Monix Kafka_ abstracts the _Kafka Consumer API_ in form of `Observable` type, w Below table shows the two different ways of consuming from Kafka topics are available (Version 0.11.x and above): -| __Offsets handling__ | __Signature__ | __Stream element type__ | +| __Signature__ | __Expected elements__ | __Stream element type__ | | :---: | :---: | :---: | - | No _(auto commit can be enabled)_| _KafkaConsumerObservable.apply_ | _ConsumerRecord[K, V]_ | - | Manual commit | _KafkaConsumerObservable.manualCommit_ | _CommittableMessage[K, V]_ | + | _KafkaConsumerObservable.apply_ | No _(auto commit can be enabled)_ | `ConsumerRecord[K, V]` | + | _KafkaConsumerObservable.manualCommit_ | Manual commit | `CommittableMessage[K, V]` | These two will be further explained in code on next sections, but first let's review the _Consumer configuration_. @@ -40,6 +40,8 @@ kafka { session.timeout.ms = 10000 max.poll.records = 500 max.poll.interval.ms = 300000 + # triggers a seekToEnd when the observable starts + monix.observable.seekEnd.onStart = false # sync, async monix.observable.commit.type = "sync" # before-ack, after-ack or no-ack @@ -49,7 +51,7 @@ kafka { For more details about what each of these configurable parameters mean, please directly review the [official confluent documentation](https://docs.confluent.io/current/installation/configuration/consumer-configs.html#cp-config-consumer) for _Kafka Consumer Configuration_. -You would also better check `monix.kafka.KafkaConsumerConfig` in order to know exactly what are the properties the consumer takes care of. +You could also refer to `monix.kafka.KafkaConsumerConfig` in order to know exactly what are the properties the consumer takes care of. Note that `monix.observable.commit.type` and `monix.observable.commit.order` are not passed to Kafka, since they are monix self configurations that would be taken into account only for the `plain consumer` but not for `manualCommit`. See next section for more info about these. @@ -88,10 +90,10 @@ val observable = ### Manual commit consumer: The `manualCommit` makes it possible to commit offset positions to Kafka. In this case the emitted record would be `CommitableMessage`, -this is just a wrapper for `ConsumerRecord` with `CommittableOffset`. + being just a wrapper for `ConsumerRecord` with `CommittableOffset`. -Committable offset represents the offset for specified topic and partition that can be committed synchronously by `commitSync` method call or asynchronously by one of commitAsync methods. - To achieve good performance it is recommended to use batched commit with `CommittableOffsetBatch` class. +The committable offset represents a offset for specified topic and partition that can be committed synchronously by `commitSync` method call or asynchronously by one of `commitAsync` methods. + In order to achieve a better performance it is recommended to use batched commit with `CommittableOffsetBatch` class. Let's now see an example on how to use the batch committable offset: diff --git a/docs/getting-started.md b/docs/getting-started.md index 17996416..2c430574 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -3,7 +3,7 @@ id: getting-started title: Getting Started --- -This project supports different versions of Apache Kafka, +This project supports different versions of _Apache Kafka_, see in below sections how to get started with each of them: ## Kafka 1.0.x or above diff --git a/docs/producer.md b/docs/producer.md index c7aa17d9..a6f8c528 100644 --- a/docs/producer.md +++ b/docs/producer.md @@ -3,57 +3,87 @@ id: producer title: Producer --- -The _Kafka Producer API_ allows an application to publish a stream of records to one or more Kafka topics. +The _Monix Kafka_ producer module uses the underlying _Kafka Producer API_ that would allow the application to asynchronously publish to one or more Kafka topics. +You could either produce a single event or to define a producer that will push an unbounded stream of events, they complement very well to accomplish different possible use cases. -Using Monix Kafka you could either produce a single event or to define a producer that will push an unbounded incoming stream of events, they complement very well to accomplish different possible use cases. +Below table describes the available different ways of publishing events to Kafka as mentioned before, although further details and implementation can be found in the next sections: + + | __Signature__ | __Expects__ | __Input__ | __Described by__ | + | :---: | :---: | :---: | :---: | + | _KafkaProducer.send_ | Single record | `ProducerRecord[K, V]`, (`K`, `V`) or just `V` | `Task` | + | _KafkaProdcuerSink.apply_ | Multiple records | `Observable[Seq[ProducerRecord[K, V]]]` | `Consumer[Seq[ProducerRecord[K, V]], Unit]` | - ## Producer Configurations -https://docs.confluent.io/current/installation/configuration/producer-configs.html +This section only mentions the producer related configurable parameters, but you might need to set other kafka configurations that can affect producer behaviour too such like buffer memory and size, security protocols and others. +As you might notice, there are not as much configurable parameters for the _Kafka Producer_ than there is for _Consumer_, but still are quite important in regards to get the best possible performance. + +```hocon +kafka { + # N. of times for the client to resend any record whose send fails with a potentially transient error. + retries = 0 + # N. of requests that KafkaProducerSink can push in parallel + monix.producer.sink.parallelism = 100 +} +``` + +For more details about all the configurable parameters, please directly review the [official confluent documentation](https://docs.confluent.io/current/installation/configuration/producer-configs.html) +for _Kafka Producer Configuration_. +You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exactly what are the properties the producer would expect. +## Single record producer + + The best way of asynchronously producing single records to _Kafka_ is using the `monix.kafka.KafkaProducer`, it exposes the `.send` signature which accepts different inputs, being a `ProducerRecord[K, V]`, (`K`, `V`) or just the `V` + and it returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` that can later be run and transformed into a `Future`. + + On the one hand, if task `Task` completes with `None` it means that `producer.send` method was called after the producer was closed and therefore the message wasn't successfully acknowledged by the Kafka broker. + + On the other hand, in case of the failure of the underlying Kafka client the producer will bubble up the exception and fail the `Task`. + + Finally, all successfully delivered messages will complete with `Some[RecordMetadata]`. + + ```scala import monix.kafka._ - import monix.execution.Scheduler - + implicit val scheduler: Scheduler = monix.execution.Scheduler.global - - // Init - val producerCfg = KafkaProducerConfig.default.copy( + // init producer configuration + val producerConf = KafkaProducerConfig.default.copy( bootstrapServers = List("127.0.0.1:9092") ) - val producer = KafkaProducer[String,String](producerCfg, scheduler) + // builds monix kafka producer + val producer = KafkaProducer[String,String](producerConf, scheduler) - // For sending one message - val recordMetadataF = producer.send("my-topic", "my-message").runToFuture + // sends a single message + val recordMetadataF: Future[Option[RecordMetadata]] = producer.send("my-topic", "my-message").runToFuture - // For closing the producer connection + // closes the connection val closeF = producer.close().runToFuture ``` - Calling `producer.send` returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` which can then be run and transformed into a `Future`. + ## Sink producer - If the `Task` completes with `None` it means that `producer.send` method was called after the producer was closed and that the message wasn't successfully acknowledged by the Kafka broker. In case of the failure of the underlying Kafka client the producer will bubble up the exception and fail the `Task`. All successfully delivered messages will complete with `Some[RecordMetadata]`. + On the other hand, if you want to produce an unbounded number of records you would better use `monix.kafka.KafkaProducerSink`, which would push an `Observable[ProducerRecord[K, V]]` to the specified _Apache Kafka_ topics. - For pushing an entire `Observable` to Apache Kafka: + As it was shown in the previous section, `monix.producer.sink.parallelism` exposes a monix's producer sink parameter that specifies the parallelism on producing requests. ```scala import monix.kafka._ - import monix.execution.Scheduler import monix.reactive.Observable import org.apache.kafka.clients.producer.ProducerRecord implicit val scheduler: Scheduler = monix.execution.Scheduler.global - // Initializing the producer - val producerCfg = KafkaProducerConfig.default.copy( - bootstrapServers = List("127.0.0.1:9092") + // init producer configuration + val producerConf = KafkaProducerConfig.default.copy( + bootstrapServers = List("127.0.0.1:9092"), + monixSinkParallelism = 3 ) - val producer = KafkaProducerSink[String,String](producerCfg, scheduler) + val producer = KafkaProducerSink[String,String](producerConf, scheduler) - // Lets pretend we have this observable of records + // lets pretend we have this observable of records val observable: Observable[ProducerRecord[String,String]] = ??? observable diff --git a/website/pages/en/index.js b/website/pages/en/index.js index 2ab84142..172b3ea2 100644 --- a/website/pages/en/index.js +++ b/website/pages/en/index.js @@ -83,7 +83,7 @@ const index = ` This project provides a [Monix](https://monix.io) stream based interface for [Apache Kafka](https://kafka.apache.org/documentation.html#producerapi), -which means it offers full advantage on performance that a the Monix reactive application mixed with the main Kafka based capabilities such like publish and subscribe to streams in a fault-tolerant durable way. +which offers the full advantage on mixing the performance and asynchronously that Monix provides with the Kafka based capabilities such like publish and subscribe to streams in a fault-tolerant durable way. ### Getting Started The latest stable version, compatible with _Monix 3.x_ and it does support _Kafka 1.0.x_, _0.11.x_, _0.10.x_, _0.9.x_: From 561e51fe2b8ae33bc2e538ba00f93a65ce03c45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Alarc=C3=B3n?= <33580722+paualarco@users.noreply.github.com> Date: Sun, 21 Jun 2020 11:44:26 +0200 Subject: [PATCH 06/12] Added Github action --- .github/workflows/build.yml | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..8e6d391b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,62 @@ +name: build + +on: [push, pull_request] + +jobs: + + tests: + name: scala-${{ matrix.scala }} jdk-${{ matrix.java }} tests + runs-on: ubuntu-latest + + strategy: + fail-fast: true + matrix: + java: [8] + scala: [2.11.12, 2.12.10] + + steps: + - uses: actions/checkout@v2 + - uses: olafurpg/setup-scala@v7 + with: + java-version: "adopt@1.${{ matrix.java }}" + + - name: Cache SBT Coursier directory + uses: actions/cache@v1 + with: + path: ~/.cache/coursier/v1 + key: ${{ runner.os }}-coursier-${{ hashFiles('**/*.sbt') }} + restore-keys: | + ${{ runner.os }}-coursier- + - name: Cache SBT directory + uses: actions/cache@v1 + with: + path: ~/.sbt + key: | + ${{ runner.os }}-sbt-${{ hashFiles('project/build.properties') }}-${{ hashFiles('project/plugins.sbt') }} + restore-keys: ${{ runner.os }}-sbt- + + - name: Run Tests for Java ${{ matrix.java }}, Scala ${{ matrix.scala }} + run: sbt ci + + publish: + name: Publish + needs: [ tests ] + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: olafurpg/setup-scala@v2 + - uses: olafurpg/setup-gpg@v2 + - name: Publish release ${{ github.ref }} + run: sbt ci-release + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + - name: Publis documentation web page + run: | + ./.github/scripts/create-web-site.sh + env: + GIT_DEPLOY_KEY: ${{ secrets.GIT_DEPLOY_KEY }} + From 934eae549ff98ce896340e021b29a7c95b36e202 Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 21 Jun 2020 11:57:00 +0200 Subject: [PATCH 07/12] Create website script --- .github/scripts/create-web-site.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 .github/scripts/create-web-site.sh diff --git a/.github/scripts/create-web-site.sh b/.github/scripts/create-web-site.sh new file mode 100755 index 00000000..aa8aa6f9 --- /dev/null +++ b/.github/scripts/create-web-site.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +echo "GIT_DEPLOY_KEY: $GIT_DEPLOY_KEY" + +sbt docs/docusaurusPublishGhpages \ No newline at end of file From 1858313f6ab5086f58047c68e1b46d8490a20bdb Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 21 Jun 2020 13:00:53 +0200 Subject: [PATCH 08/12] Refined docs --- AUTHORS | 3 +++ README.md | 7 +++++-- docs/consumer.md | 14 +++++++------- docs/producer.md | 16 ++++++++-------- website/pages/en/index.js | 11 +++++++---- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2a6df353..00b7adbe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,3 +21,6 @@ https://github.com/poslegm Vasiliy Efimov https://github.com/voidconductor + +Pau Alarcón +https://github.com/paualarco \ No newline at end of file diff --git a/README.md b/README.md index d12aad98..724e71ff 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ -# Monix-Kafka +# Monix Kafka + + + [![Build Status](https://travis-ci.org/monix/monix-kafka.svg?branch=master)](https://travis-ci.org/monix/monix-kafka) [![Maven Central](https://img.shields.io/maven-central/v/io.monix/monix-kafka-1x_2.12.svg)](https://search.maven.org/search?q=g:io.monix%20AND%20a:monix-kafka-1x_2.12) [![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/monix/monix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -See the [documentation web site](https://monix.github.io/monix-kafka) to get started. +See the [__documentation web site__](https://monix.github.io/monix-kafka) to get started. ### Caveats diff --git a/docs/consumer.md b/docs/consumer.md index 452fdc2c..22e51d48 100644 --- a/docs/consumer.md +++ b/docs/consumer.md @@ -3,16 +3,16 @@ id: consumer title: Consumer --- -_Monix Kafka_ abstracts the _Kafka Consumer API_ in form of `Observable` type, which would represent an unbounded stream of events consumed from the specified kafka topics. +The _Monix Kafka_ consumer implementation relies in the underlying _Kafka Consumer API_, which would abstract an unbounded stream of events consumed from the specified kafka topics in form of `Observable`. -Below table shows the two different ways of consuming from Kafka topics are available (Version 0.11.x and above): +Below table shows the two available ways of consuming from _Kafka_ topics. (Version 0.11.x and above): | __Signature__ | __Expected elements__ | __Stream element type__ | | :---: | :---: | :---: | | _KafkaConsumerObservable.apply_ | No _(auto commit can be enabled)_ | `ConsumerRecord[K, V]` | | _KafkaConsumerObservable.manualCommit_ | Manual commit | `CommittableMessage[K, V]` | -These two will be further explained in code on next sections, but first let's review the _Consumer configuration_. +These will be further explained in code on next sections, but first let's review the _Consumer configuration_. ### Consumer configuration @@ -57,13 +57,13 @@ Note that `monix.observable.commit.type` and `monix.observable.commit.order` are ### Plain consumer -The `plainSource` emits `ConsumerRecord` elements, a record represents the received key/value pair that also contains information about the topic, partition, offset and timestamp). -But more importantly, it __does not support offsets commitment__ to Kafka, then it can be used when the offset is stored externally or with auto-commit. +The `plainSource` emits `ConsumerRecord` elements, a record represents the received _key/value_ pair that also contains information about the topic, partition, offset and timestamp. +But more importantly, it __does not support offsets commitment__ to Kafka, you can then store offsets externally or with the _auto-commit_ flag. -Note that auto-commit is disabled by default, you can fine tune the auto commitment by setting the monix observable specific configuration `ObservableCommitOrder`, +Note that _auto-commit_ is disabled by default, you can fine tune the auto commitment by setting the monix's observable specific configuration `ObservableCommitOrder`, which will allow you to decide whether to commit the records before receiving an acknowledgement from downstream, after that, or to just don't acknowledge (as a default one). -If _At Most Once_ semantics is seek, auto commit must be enabled and observable commit order done before ack: +If _At Most Once_ semantics is seek, _auto-commit_ must be enabled and _observable commit order_ done before ack: ```scala import monix.kafka._ diff --git a/docs/producer.md b/docs/producer.md index a6f8c528..d67b22b1 100644 --- a/docs/producer.md +++ b/docs/producer.md @@ -3,7 +3,7 @@ id: producer title: Producer --- -The _Monix Kafka_ producer module uses the underlying _Kafka Producer API_ that would allow the application to asynchronously publish to one or more Kafka topics. +The _Monix Kafka_ producer module relies in the underlying _Kafka Producer API_ that would allow the application to asynchronously publish to one or more Kafka topics. You could either produce a single event or to define a producer that will push an unbounded stream of events, they complement very well to accomplish different possible use cases. Below table describes the available different ways of publishing events to Kafka as mentioned before, although further details and implementation can be found in the next sections: @@ -33,14 +33,14 @@ You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exact ## Single record producer - The best way of asynchronously producing single records to _Kafka_ is using the `monix.kafka.KafkaProducer`, it exposes the `.send` signature which accepts different inputs, being a `ProducerRecord[K, V]`, (`K`, `V`) or just the `V` - and it returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` that can later be run and transformed into a `Future`. + The best way of asynchronously producing single records to _Kafka_ is using the `monix.kafka.KafkaProducer` which exposes the `.send` signature. + It accepts different inputs, being a `ProducerRecord[K, V]`, (`K`, `V`) or just the `V`, and returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` can later be run and transformed into a `Future`, that: - On the one hand, if task `Task` completes with `None` it means that `producer.send` method was called after the producer was closed and therefore the message wasn't successfully acknowledged by the Kafka broker. - - On the other hand, in case of the failure of the underlying Kafka client the producer will bubble up the exception and fail the `Task`. + - If it completes with `None` it means that `producer.send` method was called after the producer was closed and therefore the message wasn't successfully acknowledged by the Kafka broker. + + - In case of failure reported by the underlying _Kafka client_, the producer will bubble up the exception and fail the `Task`. - Finally, all successfully delivered messages will complete with `Some[RecordMetadata]`. + - Finally, all successfully delivered messages will complete with `Some[RecordMetadata]`. ```scala @@ -64,7 +64,7 @@ You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exact ## Sink producer - On the other hand, if you want to produce an unbounded number of records you would better use `monix.kafka.KafkaProducerSink`, which would push an `Observable[ProducerRecord[K, V]]` to the specified _Apache Kafka_ topics. + On the other hand, if an unbounded number of records needs to be produced, better to use `monix.kafka.KafkaProducerSink`, which provides the logic for pushing an `Observable[ProducerRecord[K, V]]` to the specified _Apache Kafka_ topics. As it was shown in the previous section, `monix.producer.sink.parallelism` exposes a monix's producer sink parameter that specifies the parallelism on producing requests. diff --git a/website/pages/en/index.js b/website/pages/en/index.js index 172b3ea2..df4483c9 100644 --- a/website/pages/en/index.js +++ b/website/pages/en/index.js @@ -83,16 +83,19 @@ const index = ` This project provides a [Monix](https://monix.io) stream based interface for [Apache Kafka](https://kafka.apache.org/documentation.html#producerapi), -which offers the full advantage on mixing the performance and asynchronously that Monix provides with the Kafka based capabilities such like publish and subscribe to streams in a fault-tolerant durable way. +which offers the full advantage on mixing the performance and asynchronously that _Monix_ provides with the _Kafka_ based capabilities such like publish and subscribe to streams in a fault-tolerant durable way. ### Getting Started -The latest stable version, compatible with _Monix 3.x_ and it does support _Kafka 1.0.x_, _0.11.x_, _0.10.x_, _0.9.x_: +The latest stable version is compatible with __Monix__ _3.x_ with support __Kafka__ _2.x_, _1.x_, _0.11.x_, _0.10.x_, _0.9.x_. And published for __Scala__ _2.11_ and _2.12_. + +Add the following dependency on _sbt_ to get started with any Kafka version higher than _1.0.0_. \`\`\`scala -libraryDependencies += "${organization}" %% "${coreModuleName}" % "${latestVersion}" +libraryDependencies += "io.monix" %% "monix-kafka-1x" % "1.0.0-RC6" \`\`\` -Published for Scala ${scalaPublishVersions}. +Or if you still use an older version check out how to [_get started with it_](http://localhost:3014/monix-kafka/docs/getting-started). + `.trim(); const { From 0ab516df9fb4b3a2fdfbfa544797093a09a7d716 Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 21 Jun 2020 13:24:09 +0200 Subject: [PATCH 09/12] Removed examples sub-module --- README.md | 1 - build.sbt | 11 ---- examples/src/it/resources/default.conf | 80 -------------------------- 3 files changed, 92 deletions(-) delete mode 100644 examples/src/it/resources/default.conf diff --git a/README.md b/README.md index 724e71ff..55ea2621 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ - [![Build Status](https://travis-ci.org/monix/monix-kafka.svg?branch=master)](https://travis-ci.org/monix/monix-kafka) [![Maven Central](https://img.shields.io/maven-central/v/io.monix/monix-kafka-1x_2.12.svg)](https://search.maven.org/search?q=g:io.monix%20AND%20a:monix-kafka-1x_2.12) [![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org) diff --git a/build.sbt b/build.sbt index d2805c65..639d8b0b 100644 --- a/build.sbt +++ b/build.sbt @@ -287,17 +287,6 @@ git.formattedShaVersion := { } } -lazy val examples = project.in(file("examples")) - .settings(sharedSettings) - .settings(commonDependencies) - .settings( - name := "examples", - organization := "io.monix", - scalaVersion := "2.12.10" - ) - .aggregate(kafka1x) - .dependsOn(kafka1x) - lazy val docs = project .in(file("monix-kafka-docs")) .settings( diff --git a/examples/src/it/resources/default.conf b/examples/src/it/resources/default.conf deleted file mode 100644 index 0a6009af..00000000 --- a/examples/src/it/resources/default.conf +++ /dev/null @@ -1,80 +0,0 @@ -kafka { - bootstrap.servers = "localhost:9092" - client.id = "" - - # E.g. "org.apache.kafka.clients.producer.internals.DefaultPartitioner" - partitioner.class = null - - acks = "1" - buffer.memory = 33554432 - compression.type = "none" - retries = 0 - max.in.flight.requests.per.connection = 5 - - ssl.key.password = null - ssl.keystore.password = null - ssl.keystore.location = null - ssl.truststore.password = null - ssl.truststore.location = null - - batch.size = 16384 - connections.max.idle.ms = 540000 - linger.ms = 0 - max.block.ms = 60000 - max.request.size = 1048576 - - receive.buffer.bytes = 32768 - request.timeout.ms = 40000 - - sasl.kerberos.service.name = null - sasl.mechanism = "GSSAPI" - - security.protocol = "PLAINTEXT" - send.buffer.bytes = 131072 - ssl.enabled.protocols = "TLSv1.2,TLSv1.1,TLSv1" - ssl.keystore.type = "JKS" - ssl.protocol = "TLS" - ssl.provider = null - ssl.truststore.type = "JKS" - - reconnect.backoff.ms = 50 - retry.backoff.ms = 100 - - metadata.max.age.ms = 300000 - - metric.reporters = "" - metrics.num.samples = 2 - metrics.sample.window.ms = 30000 - - # Consumer specific settings - - fetch.min.bytes = 1 - fetch.max.bytes = 52428800 - group.id = "" - heartbeat.interval.ms = 3000 - max.partition.fetch.bytes = 1048576 - auto.offset.reset = "latest" - # Disabled to use back-pressure or manual commits instead - enable.auto.commit = false - exclude.internal.topics = true - receive.buffer.bytes = 65536 - check.crcs = true - fetch.max.wait.ms = 500 - # Default values for polling - # See https://cwiki.apache.org/confluence/display/KAFKA/KIP-62%3A+Allow+consumer+to+send+heartbeats+from+a+background+thread - session.timeout.ms = 10000 - max.poll.records = 500 - max.poll.interval.ms = 300000 - - # Monix specific settings - - # Number of requests that KafkaProducerSink - # can push in parallel - monix.producer.sink.parallelism = 100 - # Triggers a seekToEnd when the observable starts - monix.observable.seekEnd.onStart = false - # Possible values: sync, async - monix.observable.commit.type = "sync" - # Possible values: before-ack, after-ack or no-ack - monix.observable.commit.order = "after-ack" -} \ No newline at end of file From c20305a2cf2c5745a49d2ba65c9df5c236093b83 Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 21 Jun 2020 18:22:04 +0200 Subject: [PATCH 10/12] Configuration section refined --- build.sbt | 2 -- docs/configuration.md | 22 +++++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/build.sbt b/build.sbt index 639d8b0b..b9076c9f 100644 --- a/build.sbt +++ b/build.sbt @@ -262,8 +262,6 @@ lazy val kafka9 = project.in(file("kafka-0.9.x")) ) ) - - //------------- For Release enablePlugins(GitVersioning) diff --git a/docs/configuration.md b/docs/configuration.md index 0ed4c304..a837f1f3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -5,25 +5,25 @@ title: Configuration ### Introduction -Apache Kafka does provide a wide range of parameters to be configured, it allows to cover the most specific business cases and also highly recommendable to fine tune them for reaching out the best +_Apache Kafka_ does provide a wide range of parameters to be configured, it allows to cover the most specific business cases and also highly recommendable to fine tune them for reaching out the best possible performance. -Monix Kafka provides file driven configuration to the user's application, so it means that the values set on [default.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf) represents the kafka configuration used by default. +_Monix Kafka_ provides file driven configuration to the application, which makes it very intuitive to define and set up all these kafka default parametrization from the [default.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf) file. -If a file with format `.conf` is in the folder `src/resource` of your project, will be used as a default one and from there on you can overwrite using environment variables or directly from the code. +Indeed, any file with format `.conf` that is in `resources` folder of your project will be used as a default one and from there on you can overwrite using environment variables or directly from the code. Let's see how to do so in the following section. ### Getting started with the configuration -As mentioned before, you can specify configuration parameters from `.conf`. It represents a `HOCON` file, -an awesome file format that supports many features to fit your use case, with support for java format, substitutions, comments, properties-like notation, and more importantly, it allows substitution from environment variables and from your code. -For more info see: [typesafe config](https://github.com/lightbend/config). +As mentioned before, you can specify configuration parameters as a `HOCON` file identified with `.conf`. +An awesome file format that supports many features different use cases, with support for java format, substitutions, comments, properties-like notation and more importantly it allows substitution from environment variables and from your code. +For more info on how to use it see refer to the [typesafe config documentation](https://github.com/lightbend/config). As a quick go through, let's highlight some of the most important configuration fields: -The first one and more important one is the kafka broker you are pointing to, being as default `localhost:9092` would probably don't need to be modified to work locally, but you would definetly -have to update this one with the required bootstrap servers of your kafka cluster: +The first and more important one is the specification of the kafka brokers, being as default `localhost:9092` would probably don't need to be modified to work locally but you would definetly +have to update it with the required bootstrap servers of your kafka cluster: ```hocon kafka { @@ -33,7 +33,7 @@ kafka { } ``` -You could for example overwrite the client id from an env var like: +You could for example overwrite the client id from an _environment variable_ like: ```hocon client.id = "" @@ -47,7 +47,7 @@ Or if you put an interrogant before the env var name, it would just use it in ca client.id = ${?KAFKA_CLIENT_ID} ``` -Finally, you can just set all these values from your code in a very nice way: +Finally, you can just set all these values from your code in a very neat way like: ```scala import monix.kafka._ @@ -62,4 +62,4 @@ There are roughly 70 fields to be configured, mostly they are related with secur For more information about those, you would better consult either the kafka or confluent [configuration documentation](https://docs.confluent.io/current/installation/configuration/index.html). -Consumer and Producer specific configurations would be explained in detail on their respective sections. +Consumer and Producer specific configurations would be explained in more detail on their respective sections. From f281969cad8c3ad633506d59576b83ba000da59f Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 21 Jun 2020 23:26:48 +0200 Subject: [PATCH 11/12] Removed travis and more documentation refinement --- docs/consumer.md | 11 ++++++----- docs/producer.md | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/consumer.md b/docs/consumer.md index 22e51d48..43eed287 100644 --- a/docs/consumer.md +++ b/docs/consumer.md @@ -3,7 +3,7 @@ id: consumer title: Consumer --- -The _Monix Kafka_ consumer implementation relies in the underlying _Kafka Consumer API_, which would abstract an unbounded stream of events consumed from the specified kafka topics in form of `Observable`. +The _Monix Kafka_ consumer implementation relies on the underlying _Kafka Consumer API_, which would abstract the an unbounded stream of events consumed from the specified kafka topics in form of `Observable`. Below table shows the two available ways of consuming from _Kafka_ topics. (Version 0.11.x and above): @@ -16,10 +16,11 @@ These will be further explained in code on next sections, but first let's review ### Consumer configuration -As it was mentioned on the [previous]() sections, configuration can be specified either from [default.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf#L49) or -overwriting default values from the same code. Below list of properties represents the parameters -of the _HOCON_ file that are __only__ consumer related properties which could give you a first glance of what to look at when configuring the consumer, but of course there -are more configurable parameters related to Kafka: +As mentioned on previous sections, configuration can be specified either from a `HOCON` file [.conf](https://github.com/monix/monix-kafka/blob/master/kafka-1.0.x/src/main/resources/monix/kafka/default.conf#L49) or +from the very same code. + + Below list of properties represents __only__ those parameters that are kafka consumer related properties which could give you a first glance of what to look at when configuring the consumer, but of course there +are more configurable parameters related to _Kafka_ in general. ```hocon kafka { diff --git a/docs/producer.md b/docs/producer.md index d67b22b1..04f40b82 100644 --- a/docs/producer.md +++ b/docs/producer.md @@ -3,16 +3,18 @@ id: producer title: Producer --- -The _Monix Kafka_ producer module relies in the underlying _Kafka Producer API_ that would allow the application to asynchronously publish to one or more Kafka topics. -You could either produce a single event or to define a producer that will push an unbounded stream of events, they complement very well to accomplish different possible use cases. +The _Monix Kafka_ producer module relies in the underlying _Kafka Producer API_ that would allow the application to asynchronously publish on one or more Kafka topics. + +Below table describes the two available ways of publishing events to Kafka, which both complements very well to accomplish different possible use cases, by either producing a single event or instead using the producer sink that will push an unbounded stream of events. -Below table describes the available different ways of publishing events to Kafka as mentioned before, although further details and implementation can be found in the next sections: | __Signature__ | __Expects__ | __Input__ | __Described by__ | | :---: | :---: | :---: | :---: | | _KafkaProducer.send_ | Single record | `ProducerRecord[K, V]`, (`K`, `V`) or just `V` | `Task` | | _KafkaProdcuerSink.apply_ | Multiple records | `Observable[Seq[ProducerRecord[K, V]]]` | `Consumer[Seq[ProducerRecord[K, V]], Unit]` | +More details and examples can be found in the next sections: + ## Producer Configurations This section only mentions the producer related configurable parameters, but you might need to set other kafka configurations that can affect producer behaviour too such like buffer memory and size, security protocols and others. @@ -29,11 +31,11 @@ kafka { For more details about all the configurable parameters, please directly review the [official confluent documentation](https://docs.confluent.io/current/installation/configuration/producer-configs.html) for _Kafka Producer Configuration_. -You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exactly what are the properties the producer would expect. +You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exactly what are the properties that the producer expects. ## Single record producer - The best way of asynchronously producing single records to _Kafka_ is using the `monix.kafka.KafkaProducer` which exposes the `.send` signature. + The best way of asynchronously producing a single record to _Kafka_ is by using the `.send` method from `monix.kafka.KafkaProducer`. It accepts different inputs, being a `ProducerRecord[K, V]`, (`K`, `V`) or just the `V`, and returns a [Task](https://monix.io/docs/3x/eval/task.html) of `Option[RecordMetadata]` can later be run and transformed into a `Future`, that: - If it completes with `None` it means that `producer.send` method was called after the producer was closed and therefore the message wasn't successfully acknowledged by the Kafka broker. @@ -42,7 +44,6 @@ You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exact - Finally, all successfully delivered messages will complete with `Some[RecordMetadata]`. - ```scala import monix.kafka._ @@ -64,9 +65,11 @@ You could also refer to `monix.kafka.KafkaProducerConfig` in order to know exact ## Sink producer - On the other hand, if an unbounded number of records needs to be produced, better to use `monix.kafka.KafkaProducerSink`, which provides the logic for pushing an `Observable[ProducerRecord[K, V]]` to the specified _Apache Kafka_ topics. + On the other hand, if an unbounded number of records needs to be produced, it is better to use `monix.kafka.KafkaProducerSink`, which provides the logic for pushing an `Observable[ProducerRecord[K, V]]` to the specified _Kafka_ topics. + + As it was mentioned in the producer configuration section, `monix.producer.sink.parallelism` allows to specify the parallelism on producing requests from `KafkaProducerSink`. - As it was shown in the previous section, `monix.producer.sink.parallelism` exposes a monix's producer sink parameter that specifies the parallelism on producing requests. + See below an example on how to use the kafka producer sink: ```scala import monix.kafka._ From e738e84feebfdc443ea376d627be73bda8372fda Mon Sep 17 00:00:00 2001 From: Pau Date: Mon, 22 Jun 2020 23:52:16 +0200 Subject: [PATCH 12/12] Updated project's header --- kafka-0.10.x/src/main/scala/monix/kafka/Commit.scala | 2 +- .../src/main/scala/monix/kafka/CommittableMessage.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffset.scala | 2 +- .../src/main/scala/monix/kafka/CommittableOffsetBatch.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/Deserializer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerConfig.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerObservable.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableManualCommit.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaProducerConfig.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerSink.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/Serializer.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/config/Acks.scala | 2 +- .../src/main/scala/monix/kafka/config/AutoOffsetReset.scala | 2 +- kafka-0.10.x/src/main/scala/monix/kafka/config/ClassName.scala | 2 +- .../src/main/scala/monix/kafka/config/CompressionType.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitOrder.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitType.scala | 2 +- .../src/main/scala/monix/kafka/config/PartitionerName.scala | 2 +- .../src/main/scala/monix/kafka/config/SSLProtocol.scala | 2 +- .../src/main/scala/monix/kafka/config/SecurityProtocol.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/Commit.scala | 2 +- .../src/main/scala/monix/kafka/CommittableMessage.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffset.scala | 2 +- .../src/main/scala/monix/kafka/CommittableOffsetBatch.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/Deserializer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerConfig.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerObservable.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableManualCommit.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaProducerConfig.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerSink.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/Serializer.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/config/Acks.scala | 2 +- .../src/main/scala/monix/kafka/config/AutoOffsetReset.scala | 2 +- kafka-0.11.x/src/main/scala/monix/kafka/config/ClassName.scala | 2 +- .../src/main/scala/monix/kafka/config/CompressionType.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitOrder.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitType.scala | 2 +- .../src/main/scala/monix/kafka/config/PartitionerName.scala | 2 +- .../src/main/scala/monix/kafka/config/SSLProtocol.scala | 2 +- .../src/main/scala/monix/kafka/config/SecurityProtocol.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/Commit.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/CommittableMessage.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffset.scala | 2 +- .../src/main/scala/monix/kafka/CommittableOffsetBatch.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/Deserializer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerConfig.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerObservable.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableManualCommit.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaProducerConfig.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerSink.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/Serializer.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/config/Acks.scala | 2 +- .../src/main/scala/monix/kafka/config/AutoOffsetReset.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/config/ClassName.scala | 2 +- .../src/main/scala/monix/kafka/config/CompressionType.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitOrder.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitType.scala | 2 +- .../src/main/scala/monix/kafka/config/PartitionerName.scala | 2 +- kafka-0.9.x/src/main/scala/monix/kafka/config/SSLProtocol.scala | 2 +- .../src/main/scala/monix/kafka/config/SecurityProtocol.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/Commit.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/CommittableMessage.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffset.scala | 2 +- .../src/main/scala/monix/kafka/CommittableOffsetBatch.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/Deserializer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerConfig.scala | 2 +- .../src/main/scala/monix/kafka/KafkaConsumerObservable.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala | 2 +- .../scala/monix/kafka/KafkaConsumerObservableManualCommit.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducer.scala | 2 +- .../src/main/scala/monix/kafka/KafkaProducerConfig.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerSink.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/Serializer.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/config/Acks.scala | 2 +- .../src/main/scala/monix/kafka/config/AutoOffsetReset.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/config/ClassName.scala | 2 +- .../src/main/scala/monix/kafka/config/CompressionType.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitOrder.scala | 2 +- .../main/scala/monix/kafka/config/ObservableCommitType.scala | 2 +- .../src/main/scala/monix/kafka/config/PartitionerName.scala | 2 +- kafka-1.0.x/src/main/scala/monix/kafka/config/SSLProtocol.scala | 2 +- .../src/main/scala/monix/kafka/config/SecurityProtocol.scala | 2 +- 88 files changed, 88 insertions(+), 88 deletions(-) diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/Commit.scala b/kafka-0.10.x/src/main/scala/monix/kafka/Commit.scala index 74354265..400d2d0a 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/Commit.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/Commit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/CommittableMessage.scala b/kafka-0.10.x/src/main/scala/monix/kafka/CommittableMessage.scala index 19a8ddc6..318f2ed8 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/CommittableMessage.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/CommittableMessage.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffset.scala b/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffset.scala index f0620885..7d28177c 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffset.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala b/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala index 8b2b098e..df389b1b 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/Deserializer.scala b/kafka-0.10.x/src/main/scala/monix/kafka/Deserializer.scala index 04194f9e..6a9fea21 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/Deserializer.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/Deserializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala index e4d94c4e..3d618d7b 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala index 26fd9cb9..34098ef9 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala index 72cf39f4..31388846 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala index 648a0cfb..87bfca09 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducer.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducer.scala index 0eb776f5..405df29f 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducer.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala index 1a490eaf..9affa6cb 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerSink.scala b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerSink.scala index 640cba0f..9c00eca5 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerSink.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/KafkaProducerSink.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/Serializer.scala b/kafka-0.10.x/src/main/scala/monix/kafka/Serializer.scala index 964ebdf9..51e33a2a 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/Serializer.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/Serializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/Acks.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/Acks.scala index 7e7ca2a7..86ef8831 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/Acks.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/Acks.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala index 832b9c5a..fbbc78e0 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/ClassName.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/ClassName.scala index cdc04187..797b11ee 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/ClassName.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/ClassName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/CompressionType.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/CompressionType.scala index 9427ac1e..d1b6c203 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/CompressionType.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/CompressionType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala index 0eb2f118..370d459f 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala index 65db39c1..59f91622 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/PartitionerName.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/PartitionerName.scala index 61291fa3..7d5c65c5 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/PartitionerName.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/PartitionerName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/SSLProtocol.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/SSLProtocol.scala index 19b85a13..185acb51 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/SSLProtocol.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/SSLProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.10.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala b/kafka-0.10.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala index 492dc2d0..db00c8f2 100644 --- a/kafka-0.10.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala +++ b/kafka-0.10.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/Commit.scala b/kafka-0.11.x/src/main/scala/monix/kafka/Commit.scala index 74354265..400d2d0a 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/Commit.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/Commit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/CommittableMessage.scala b/kafka-0.11.x/src/main/scala/monix/kafka/CommittableMessage.scala index 19a8ddc6..318f2ed8 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/CommittableMessage.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/CommittableMessage.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffset.scala b/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffset.scala index f0620885..7d28177c 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffset.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala b/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala index a7ed6f7a..c9829959 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/Deserializer.scala b/kafka-0.11.x/src/main/scala/monix/kafka/Deserializer.scala index 04194f9e..6a9fea21 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/Deserializer.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/Deserializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala index 3faede6b..8f6f30bc 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala index 26fd9cb9..34098ef9 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala index 72cf39f4..31388846 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala index 648a0cfb..87bfca09 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducer.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducer.scala index 0eb776f5..405df29f 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducer.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala index f56dd573..1b823a9b 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerSink.scala b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerSink.scala index 640cba0f..9c00eca5 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerSink.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/KafkaProducerSink.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/Serializer.scala b/kafka-0.11.x/src/main/scala/monix/kafka/Serializer.scala index 964ebdf9..51e33a2a 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/Serializer.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/Serializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/Acks.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/Acks.scala index 7e7ca2a7..86ef8831 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/Acks.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/Acks.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala index 832b9c5a..fbbc78e0 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/ClassName.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/ClassName.scala index cdc04187..797b11ee 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/ClassName.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/ClassName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/CompressionType.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/CompressionType.scala index 9427ac1e..d1b6c203 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/CompressionType.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/CompressionType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala index 0eb2f118..370d459f 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala index 65db39c1..59f91622 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/PartitionerName.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/PartitionerName.scala index 61291fa3..7d5c65c5 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/PartitionerName.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/PartitionerName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/SSLProtocol.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/SSLProtocol.scala index 19b85a13..185acb51 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/SSLProtocol.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/SSLProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.11.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala b/kafka-0.11.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala index 492dc2d0..db00c8f2 100644 --- a/kafka-0.11.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala +++ b/kafka-0.11.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/Commit.scala b/kafka-0.9.x/src/main/scala/monix/kafka/Commit.scala index 74354265..400d2d0a 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/Commit.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/Commit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/CommittableMessage.scala b/kafka-0.9.x/src/main/scala/monix/kafka/CommittableMessage.scala index 19a8ddc6..318f2ed8 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/CommittableMessage.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/CommittableMessage.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffset.scala b/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffset.scala index f0620885..7d28177c 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffset.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala b/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala index 8b2b098e..df389b1b 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/Deserializer.scala b/kafka-0.9.x/src/main/scala/monix/kafka/Deserializer.scala index 32a202fb..8f16393f 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/Deserializer.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/Deserializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala index ff080101..22e37b95 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala index c65d38a3..8d3bb62a 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala index 72cf39f4..31388846 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala index 648a0cfb..87bfca09 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducer.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducer.scala index 0eb776f5..405df29f 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducer.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala index 8e6fa914..48c89b52 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerSink.scala b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerSink.scala index 640cba0f..9c00eca5 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerSink.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/KafkaProducerSink.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/Serializer.scala b/kafka-0.9.x/src/main/scala/monix/kafka/Serializer.scala index 21bc4892..e8ea15f7 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/Serializer.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/Serializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/Acks.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/Acks.scala index 7e7ca2a7..86ef8831 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/Acks.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/Acks.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala index 832b9c5a..fbbc78e0 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/ClassName.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/ClassName.scala index cdc04187..797b11ee 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/ClassName.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/ClassName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/CompressionType.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/CompressionType.scala index 9427ac1e..d1b6c203 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/CompressionType.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/CompressionType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala index 0eb2f118..370d459f 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala index 65db39c1..59f91622 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/PartitionerName.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/PartitionerName.scala index 61291fa3..7d5c65c5 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/PartitionerName.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/PartitionerName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/SSLProtocol.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/SSLProtocol.scala index 19b85a13..185acb51 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/SSLProtocol.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/SSLProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-0.9.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala b/kafka-0.9.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala index 492dc2d0..db00c8f2 100644 --- a/kafka-0.9.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala +++ b/kafka-0.9.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/Commit.scala b/kafka-1.0.x/src/main/scala/monix/kafka/Commit.scala index 74354265..400d2d0a 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/Commit.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/Commit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/CommittableMessage.scala b/kafka-1.0.x/src/main/scala/monix/kafka/CommittableMessage.scala index 19a8ddc6..318f2ed8 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/CommittableMessage.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/CommittableMessage.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffset.scala b/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffset.scala index f0620885..7d28177c 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffset.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala b/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala index 8b2b098e..df389b1b 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/CommittableOffsetBatch.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/Deserializer.scala b/kafka-1.0.x/src/main/scala/monix/kafka/Deserializer.scala index 04194f9e..6a9fea21 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/Deserializer.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/Deserializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala index 3faede6b..8f6f30bc 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala index 6429f384..05479c91 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservable.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala index 72cf39f4..31388846 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableAutoCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala index 648a0cfb..87bfca09 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaConsumerObservableManualCommit.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducer.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducer.scala index 0eb776f5..405df29f 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducer.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala index 6b01e6b1..6b74468a 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerConfig.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerSink.scala b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerSink.scala index 640cba0f..9c00eca5 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerSink.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/KafkaProducerSink.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/Serializer.scala b/kafka-1.0.x/src/main/scala/monix/kafka/Serializer.scala index 964ebdf9..51e33a2a 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/Serializer.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/Serializer.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/Acks.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/Acks.scala index 7e7ca2a7..86ef8831 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/Acks.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/Acks.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala index 832b9c5a..fbbc78e0 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/AutoOffsetReset.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/ClassName.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/ClassName.scala index cdc04187..797b11ee 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/ClassName.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/ClassName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/CompressionType.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/CompressionType.scala index 9427ac1e..d1b6c203 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/CompressionType.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/CompressionType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala index 0eb2f118..370d459f 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitOrder.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala index 65db39c1..59f91622 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/ObservableCommitType.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/PartitionerName.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/PartitionerName.scala index 61291fa3..7d5c65c5 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/PartitionerName.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/PartitionerName.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/SSLProtocol.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/SSLProtocol.scala index 19b85a13..185acb51 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/SSLProtocol.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/SSLProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/kafka-1.0.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala b/kafka-1.0.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala index 492dc2d0..db00c8f2 100644 --- a/kafka-1.0.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala +++ b/kafka-1.0.x/src/main/scala/monix/kafka/config/SecurityProtocol.scala @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019 by The Monix Project Developers. + * Copyright (c) 2014-2020 by The Monix Project Developers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.