Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit 6fe5371

Browse files
markglhagaro1121
authored andcommitted
Feature/issue28 test kpl properties (#30)
* Upgrading KPL/KCL libraries, replacing deprecated shutdown calls * auto format from compile * restructure the ProducerConf into its own file and spec * further refactoring of Producer to use ProducerConf * tests pass - ish * improve test exception for manager * Fix Intermittent failing test - #10 * refactored producer to remove pointless trait, updated readme and specs * removed unused config * fix scalafmt * address potters comments * address formatting issues * added ConsumerConfig test plus missing consumer fields * improved thread reference docs * removed comment
1 parent fc7fee2 commit 6fe5371

File tree

11 files changed

+776
-326
lines changed

11 files changed

+776
-326
lines changed

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,22 +410,21 @@ kpa ! Send(producerEvent) //Send without a callback confirmation
410410

411411
<a name="usage-usage-producer-pure-scala-based-implementation-simple-wrapper-around-kpl"></a>
412412
### Pure Scala based implementation (simple wrapper around KPL)
413-
*Note that throttling will be unavailable using this method.*
413+
*Note that future throttling will be unavailable using this method.*
414414

415415
```scala
416416
import java.util.UUID
417417
import com.amazonaws.services.kinesis.producer.{UserRecordFailedException, UserRecordResult}
418+
import com.weightwatchers.reactive.kinesis.producer.KinesisProducer
419+
import com.weightwatchers.reactive.kinesis.producer.ProducerConf
418420
import com.typesafe.config._
419421
import com.weightwatchers.reactive.kinesis.models._
420-
import com.weightwatchers.reactive.kinesis.producer.KinesisProducerKPL
421422
import scala.concurrent.Future
422423
import scala.concurrent.ExecutionContext.Implicits.global //Not for production
423424

424425
val kinesisConfig: Config = ConfigFactory.load().getConfig("kinesis")
425-
val producerConfig: Config = kinesisConfig.getConfig("some-producer")
426-
val streamName: String = producerConfig.getString("stream-name")
427426

428-
val kpl = KinesisProducerKPL(kinesisConfig.getConfig("kpl"), streamName)
427+
val kpl = KinesisProducer(ProducerConf(kinesisConfig, "some-producer"))
429428

430429
val producerEvent = ProducerEvent(UUID.randomUUID.toString, "{Some Payload}")
431430

src/it/scala/com/weightwatchers/reactive.kinesis/SimpleKinesisProducer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import com.weightwatchers.reactive.kinesis.producer.KinesisProducerActor.{
1212
SendSuccessful,
1313
SendWithCallback
1414
}
15-
import com.weightwatchers.reactive.kinesis.producer.{KinesisProducerActor, KinesisProducerKPL}
15+
import com.weightwatchers.reactive.kinesis.producer.{KinesisProducerActor, KinesisProducer}
1616

1717
import scala.collection.mutable
1818
import scala.concurrent.ExecutionContext.Implicits.global
@@ -62,7 +62,7 @@ class SimpleKinesisProducer(kConfig: Config) extends Actor with LazyLogging {
6262
//We're creating the producer the hard way to get access to the underlying KPL
6363
val kpaProps = KinesisProducerActor.props(kinesisConfig, "testProducer")
6464
val kpa = context.actorOf(kpaProps)
65-
val kinesisProducerKPL = kpaProps.args.head.asInstanceOf[KinesisProducerKPL]
65+
val kinesisProducerKPL = kpaProps.args.head.asInstanceOf[KinesisProducer]
6666

6767
/* producer without actor:
6868
val producerConfig = kinesisConfig.getConfig("testProducer")

src/main/resources/reference.conf

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ kinesis {
166166
# Default: 443
167167
# Minimum: 1
168168
# Maximum (inclusive): 65535
169-
#KinesisPort = 443
169+
# KinesisPort = 443
170170

171171
# If true, throttled puts are not retried. The records that got throttled
172172
# will be failed immediately upon receiving the throttling error. This is
@@ -379,12 +379,18 @@ kinesis {
379379

380380
# Sets the threading model that the native process will use.
381381
# Enum:
382-
# ThreadingModel.PER_REQUEST: Tells the native process to create a thread for each request.
383-
# ThreadingModel.POOLED: Tells the native process to use a thread pool. The size of the pool can be controlled by ThreadPoolSize
384-
# Default = ThreadingModel.PER_REQUEST
382+
# PER_REQUEST: Tells the native process to create a thread for each request.
383+
# Under heavy load this can create a very large number of threads, which may cause resource exhaustion.
384+
# POOLED: Tells the native process to use a thread pool. The size of the pool can be controlled by ThreadPoolSize
385+
# This uses a queue, and thread pool to execute requests to Kinesis.
386+
# This limits the number of threads that the native process may use.
387+
# Under extremely heavy load this can increase latency significantly more than the per request model.
388+
#
389+
# Default = PER_REQUEST
385390
# ThreadingModel =
386391

387392
# Sets the maximum number of threads that the native process' thread pool will be configured with.
393+
#
388394
# Default: 0
389395
# ThreadPoolSize =
390396
}
@@ -527,11 +533,11 @@ kinesis {
527533
# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3912
528534
#
529535
# Default: null
530-
#kinesisEndpoint = https://kinesis
536+
#kinesisEndpoint = "https://kinesis"
531537

532538
# DynamoDB endpoint
533539
# Default: null
534-
#DynamoDBEndpoint =
540+
#dynamoDBEndpoint = "https://dynamo"
535541

536542
# Don't call processRecords() on the record processor for empty record lists.
537543
# Enables applications flush/checkpoint (if they have some data "in progress but don't get new data for while)
@@ -582,6 +588,13 @@ kinesis {
582588
# Default: Operation, ShardId
583589
#metricsEnabledDimensions = Operation, ShardId
584590

591+
# Sets the max size of the thread pool that will be used to renew leases.
592+
# Setting this to low may starve the lease renewal process, and cause the worker to lose leases at a higher rate.
593+
#
594+
# Min: 2
595+
# Default: 20
596+
#maxLeaseRenewalThreads=20
597+
585598

586599
# The max number of leases (shards) this worker should process.
587600
# This can be useful to avoid overloading (and thrashing) a worker when a host has resource constraints
@@ -628,7 +641,17 @@ kinesis {
628641

629642
# TableName name of the lease table in DynamoDB
630643
# Default = <applicationName>
631-
#TableName =
644+
#tableName =
645+
646+
# A timeout when dispatching records to the client MultiLang record processor.
647+
# If the record processor doesn't respond within the timeout the parent Java process will be terminated.
648+
# This is a temporary fix to handle cases where the KCL becomes blocked while waiting for a client record processor.
649+
# Setting this can cause the KCL to exit suddenly,
650+
# before using this ensure that you have an automated restart for your application
651+
#
652+
# Default: no timeout
653+
#timeoutInSeconds =
654+
632655
}
633656
}
634657
}

src/main/scala/com/weightwatchers/reactive/kinesis/producer/KinesisProducerKPL.scala renamed to src/main/scala/com/weightwatchers/reactive/kinesis/producer/KinesisProducer.scala

Lines changed: 68 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -29,64 +29,7 @@ import com.weightwatchers.reactive.kinesis.utils.{FutureUtils, TypesafeConfigExt
2929

3030
import scala.concurrent.{ExecutionContextExecutor, Future}
3131

32-
trait KinesisProducer {
33-
34-
/**
35-
* Adds a message to the next batch to be sent to the configured stream.
36-
*
37-
* @return On success: Future{UserRecordResult}
38-
* On failure: Future.failed(...): Any Throwable related to put.
39-
* @see Callee `com.amazonaws.services.kinesis.producer.KinesisProducer.addUserRecord`
40-
* @see UserRecordResult
41-
* @see KinesisProducerConfiguration#setRecordTtl(long)
42-
* @see UserRecordFailedException
43-
*/
44-
def addUserRecord(event: ProducerEvent)(
45-
implicit ec: ExecutionContextExecutor
46-
): Future[UserRecordResult]
47-
48-
/**
49-
* Get the number of unfinished records currently being processed. The
50-
* records could either be waiting to be sent to the child process, or have
51-
* reached the child process and are being worked on.
52-
*
53-
* <p>
54-
* This is equal to the number of futures returned from [[addUserRecord]]
55-
* that have not finished.
56-
*
57-
* This is useful for applying backpressure and throttling the number of concurrent Futures.
58-
*
59-
* @return The number of unfinished records currently being processed.
60-
*/
61-
def outstandingRecordsCount(): Int
62-
63-
/**
64-
* Firstly, blocks whilst all all records are complete (either succeeding or failing).
65-
*
66-
* <p>
67-
*
68-
* The includes whilst any retries are performed. Depending on
69-
* your configuration of record TTL and request timeout, this can
70-
* potentially take a long time if the library is having trouble delivering
71-
* records to the backend, for example due to network problems.
72-
*
73-
* <p>
74-
*
75-
* Finally the [[KinesisProducer]] is destroyed, preventing further use.
76-
*
77-
* @throws com.amazonaws.services.kinesis.producer.DaemonException if the child process is dead //TODO - handle this better?
78-
* @see [[AWSKinesisProducer]]
79-
*/
80-
def stop(): Unit
81-
82-
/**
83-
* @return true if the [[KinesisProducer]] has been stopped & destroyed.
84-
*/
85-
def destroyed(): Boolean
86-
87-
}
88-
89-
object KinesisProducerKPL extends LazyLogging {
32+
object KinesisProducer extends LazyLogging {
9033

9134
/**
9235
* The config passed is expected to contain the AWS KPL properties at the top level.
@@ -100,9 +43,11 @@ object KinesisProducerKPL extends LazyLogging {
10043
* @param credentialsProvider A specific CredentialsProvider. The KCL defaults to DefaultAWSCredentialsProviderChain.
10144
* @return an instantiated [[KinesisProducer]]
10245
*/
46+
@deprecated("Use KinesisProducer(producerConf: ProducerConf) instead", "v0.5.7")
10347
def apply(kplConfig: Config,
10448
streamName: String,
10549
credentialsProvider: Option[AWSCredentialsProvider] = None): KinesisProducer = {
50+
10651
import TypesafeConfigExtensions._
10752

10853
// We directly load our properties into the KPL as a Java `Properties` object
@@ -117,7 +62,32 @@ object KinesisProducerKPL extends LazyLogging {
11762
KinesisProducerConfiguration.fromProperties(kplProps)
11863
credentialsProvider.foreach(kplLibConfiguration.setCredentialsProvider)
11964

120-
new KinesisProducerKPL(new AWSKinesisProducer(kplLibConfiguration), streamName)
65+
new KinesisProducer(new AWSKinesisProducer(kplLibConfiguration), streamName)
66+
}
67+
68+
/**
69+
* The config passed is expected to contain the AWS KPL properties at the top level.
70+
*
71+
* @param producerConf An instance of [[ProducerConf]] which contains all required configuration for the KPL.
72+
* @return an instantiated [[KinesisProducer]]
73+
*/
74+
def apply(producerConf: ProducerConf): KinesisProducer = {
75+
apply(producerConf.kplLibConfiguration, producerConf.streamName)
76+
}
77+
78+
/**
79+
* The [[KinesisProducerConfiguration]] argument is passed directly to the KPL library.
80+
* This constructor makes no use of the Typesafe config.
81+
*
82+
* @see `src/it/resources/reference.conf` for a more detailed example.
83+
* @param kplConfig An instance of the underlying [[KinesisProducerConfiguration]] to be passed
84+
* directly to the library.
85+
* @param streamName Th name of the Kinesis stream, which must exist.
86+
* @return an instantiated [[KinesisProducer]]
87+
*/
88+
def apply(kplConfig: KinesisProducerConfiguration, streamName: String): KinesisProducer = {
89+
//TODO add logging
90+
new KinesisProducer(new AWSKinesisProducer(kplConfig), streamName)
12191
}
12292
}
12393

@@ -126,9 +96,7 @@ object KinesisProducerKPL extends LazyLogging {
12696
*
12797
* To create an instance of this class, we recommend using the apply method to instantiate from config.
12898
*/
129-
class KinesisProducerKPL(kinesis: AWSKinesisProducer, streamName: String)
130-
extends LazyLogging
131-
with KinesisProducer {
99+
class KinesisProducer(kinesis: AWSKinesisProducer, streamName: String) extends LazyLogging {
132100

133101
val underlying = kinesis
134102
private var _destroyed = false
@@ -137,9 +105,16 @@ class KinesisProducerKPL(kinesis: AWSKinesisProducer, streamName: String)
137105
//TODO seems difficult to get access to stream specific operations from producer
138106

139107
/**
140-
* @see [[KinesisProducer]].addUserRecord
108+
* Adds a message to the next batch to be sent to the configured stream.
109+
*
110+
* @return On success: Future{UserRecordResult}
111+
* On failure: Future.failed(...): Any Throwable related to put.
112+
* @see Callee `com.amazonaws.services.kinesis.producer.KinesisProducer.addUserRecord`
113+
* @see UserRecordResult
114+
* @see KinesisProducerConfiguration#setRecordTtl(long)
115+
* @see UserRecordFailedException
141116
*/
142-
override def addUserRecord(
117+
def addUserRecord(
143118
event: ProducerEvent
144119
)(implicit ec: ExecutionContextExecutor): Future[UserRecordResult] = {
145120
assert(!_destroyed, "Kinesis has been destroyed, no longer accepting messages") //TODO specific exception?
@@ -148,24 +123,47 @@ class KinesisProducerKPL(kinesis: AWSKinesisProducer, streamName: String)
148123
}
149124

150125
/**
151-
* @see [[KinesisProducer]].outstandingRecordsCount()
126+
* Get the number of unfinished records currently being processed. The
127+
* records could either be waiting to be sent to the child process, or have
128+
* reached the child process and are being worked on.
129+
*
130+
* <p>
131+
* This is equal to the number of futures returned from [[addUserRecord]]
132+
* that have not finished.
133+
*
134+
* This is useful for applying backpressure and throttling the number of concurrent Futures.
135+
*
136+
* @return The number of unfinished records currently being processed.
152137
*/
153-
override def outstandingRecordsCount(): Int = {
138+
def outstandingRecordsCount(): Int = {
154139
kinesis.getOutstandingRecordsCount
155140
}
156141

157142
/**
158-
* @see [[KinesisProducer]].stop()sbt publish
143+
* Firstly, blocks whilst all all records are complete (either succeeding or failing).
144+
*
145+
* <p>
159146
*
147+
* The includes whilst any retries are performed. Depending on
148+
* your configuration of record TTL and request timeout, this can
149+
* potentially take a long time if the library is having trouble delivering
150+
* records to the backend, for example due to network problems.
151+
*
152+
* <p>
153+
*
154+
* Finally the [[KinesisProducer]] is destroyed, preventing further use.
155+
*
156+
* @throws com.amazonaws.services.kinesis.producer.DaemonException if the child process is dead //TODO - handle this better?
157+
* @see [[AWSKinesisProducer]]
160158
*/
161-
override def stop(): Unit = {
159+
def stop(): Unit = {
162160
kinesis.flushSync() //This blocks until all records are flushed
163161
kinesis.destroy()
164162
_destroyed = true
165163
}
166164

167165
/**
168-
* @see [[KinesisProducer]]destroyed()
166+
* @return true if the [[KinesisProducer]] has been stopped & destroyed.
169167
*/
170-
override def destroyed(): Boolean = _destroyed
168+
def destroyed(): Boolean = _destroyed
171169
}

0 commit comments

Comments
 (0)