Skip to content

Commit 3f2e904

Browse files
authored
Adding PulsarAdmin configs to PulsarHelper (#158)
* comment * adding adminopts * removing exception * using moduleparams * scalastyle * readme * retrigger * adding test * adding deleted tests back
1 parent cd93f8d commit 3f2e904

5 files changed

Lines changed: 80 additions & 10 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,17 @@ You can use `org.apache.spark.sql.pulsar.JsonUtils.topicOffsets(Map[String, Mess
424424
Please check [Pulsar Client Configuration](https://pulsar.apache.org/docs/2.11.x/client-libraries-java/#client) for more details </td>
425425
</tr>
426426

427+
<tr>
428+
<td>`pulsar.admin.*`</td>
429+
<td>Pulsar Admin configurations</td>
430+
<td>No</td>
431+
<td>None</td>
432+
<td>Streaming and Batch</td>
433+
<td>Admin configurations. Example: "pulsar.admin.tlsAllowInsecureConnection".
434+
435+
Please check [Pulsar Admin Configuration](https://pulsar.apache.org/docs/2.10.x/admin-api-overview/) for more details </td>
436+
</tr>
437+
427438
<tr>
428439
<td>`pulsar.reader.*`</td>
429440
<td>Pulsar Reader configurations</td>

src/main/scala/org/apache/spark/sql/pulsar/PulsarHelper.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import org.apache.spark.sql.types.StructType
4747
private[pulsar] case class PulsarHelper(
4848
serviceUrl: String,
4949
adminUrl: Option[String],
50+
adminConf: ju.Map[String, Object],
5051
clientConf: ju.Map[String, Object],
5152
driverGroupIdPrefix: String,
5253
caseInsensitiveParameters: Map[String, String],
@@ -66,7 +67,8 @@ private[pulsar] case class PulsarHelper(
6667
// will only be called if latestOffset is called and there should
6768
// be an exception thrown in PulsarProvider if maxBytes is set,
6869
// and adminUrl is not set
69-
private lazy val admissionControlHelper = new PulsarAdmissionControlHelper(adminUrl.get)
70+
private lazy val admissionControlHelper = new
71+
PulsarAdmissionControlHelper(adminUrl.get, adminConf)
7072

7173
override def close(): Unit = {
7274
// do nothing
@@ -136,6 +138,9 @@ private[pulsar] case class PulsarHelper(
136138
try {
137139
val (subscription, _) = extractSubscription(predefinedSubscription, tp)
138140
val consumer = CachedConsumer.getOrCreate(tp, subscription, client)
141+
// We need to do this because the consumer does not attempt to
142+
// reconnect after calling .seek().
143+
// TODO: Remove this once we have upgraded to a version so that this is no longer needed
139144
if (!consumer.isConnected) consumer.getLastMessageId
140145
consumer.seek(mid)
141146
} catch {
@@ -517,10 +522,11 @@ private[pulsar] case class PulsarHelper(
517522
}
518523
}
519524

520-
class PulsarAdmissionControlHelper(adminUrl: String)
525+
class PulsarAdmissionControlHelper(adminUrl: String, conf: ju.Map[String, Object])
521526
extends Logging {
522527

523-
private lazy val pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(adminUrl).build()
528+
private lazy val pulsarAdmin =
529+
PulsarAdmin.builder().serviceHttpUrl(adminUrl).loadConf(conf).build()
524530

525531
import scala.collection.JavaConverters._
526532

src/main/scala/org/apache/spark/sql/pulsar/PulsarOptions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ private[pulsar] object PulsarOptions {
2222

2323
// option key prefix for different modules
2424
val PulsarClientOptionKeyPrefix: String = "pulsar.client."
25+
val PulsarAdminOptionKeyPrefix: String = "pulsar.admin."
2526
val PulsarProducerOptionKeyPrefix: String = "pulsar.producer."
2627
val PulsarReaderOptionKeyPrefix: String = "pulsar.reader."
2728

src/main/scala/org/apache/spark/sql/pulsar/PulsarProvider.scala

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@ private[pulsar] class PulsarProvider
5656
parameters: Map[String, String]): (String, StructType) = {
5757

5858
val caseInsensitiveParams = validateStreamOptions(parameters)
59-
val (clientConfig, _, serviceUrlConfig, adminUrl) = prepareConfForReader(parameters)
59+
val (clientConfig, _, adminConfig,
60+
serviceUrlConfig, adminUrl) = prepareConfForReader(parameters)
6061

6162
val subscriptionNamePrefix = s"spark-pulsar-${UUID.randomUUID}"
6263
val inferredSchema = Utils.tryWithResource(
6364
PulsarHelper(
6465
serviceUrlConfig,
6566
adminUrl,
67+
adminConfig,
6668
clientConfig,
6769
subscriptionNamePrefix,
6870
caseInsensitiveParams,
@@ -85,14 +87,17 @@ private[pulsar] class PulsarProvider
8587
logDebug(s"Creating Pulsar source: $parameters")
8688

8789
val caseInsensitiveParams = validateStreamOptions(parameters)
88-
val (clientConfig, readerConfig, serviceUrl, adminUrl) = prepareConfForReader(parameters)
90+
val (clientConfig, readerConfig,
91+
adminConfig, serviceUrl, adminUrl) = prepareConfForReader(parameters)
92+
8993
logDebug(
9094
s"Client config: $clientConfig; Reader config: $readerConfig; Service URL: $serviceUrl")
9195

9296
val subscriptionNamePrefix = getSubscriptionPrefix(parameters)
9397
val pulsarHelper = PulsarHelper(
9498
serviceUrl,
9599
adminUrl,
100+
adminConfig,
96101
clientConfig,
97102
subscriptionNamePrefix,
98103
caseInsensitiveParams,
@@ -134,11 +139,14 @@ private[pulsar] class PulsarProvider
134139

135140
val subscriptionNamePrefix = getSubscriptionPrefix(parameters, isBatch = true)
136141

137-
val (clientConfig, readerConfig, serviceUrl, adminUrl) = prepareConfForReader(parameters)
142+
val (clientConfig, readerConfig,
143+
adminConfig, serviceUrl, adminUrl) = prepareConfForReader(parameters)
144+
138145
val (start, end, schema, pSchema) = Utils.tryWithResource(
139146
PulsarHelper(
140147
serviceUrl,
141148
adminUrl,
149+
adminConfig,
142150
clientConfig,
143151
subscriptionNamePrefix,
144152
caseInsensitiveParams,
@@ -258,6 +266,10 @@ private[pulsar] object PulsarProvider extends Logging {
258266
}
259267
}
260268

269+
private def getAdminParams(parameters: Map[String, String]): Map[String, String] = {
270+
getModuleParams(parameters, PulsarAdminOptionKeyPrefix, clientConfKeys)
271+
}
272+
261273
private def getProducerParams(parameters: Map[String, String]): Map[String, String] = {
262274
getModuleParams(parameters, PulsarProducerOptionKeyPrefix, producerConfKeys)
263275
}
@@ -507,17 +519,20 @@ private[pulsar] object PulsarProvider extends Logging {
507519
}
508520

509521
private def prepareConfForReader(parameters: Map[String, String])
510-
: (ju.Map[String, Object], ju.Map[String, Object], String, Option[String]) = {
522+
: (ju.Map[String, Object], ju.Map[String, Object],
523+
ju.Map[String, Object], String, Option[String]) = {
511524

512525
val serviceUrl = getServiceUrl(parameters)
513526
val adminUrl = getAdminUrl(parameters)
514527
var clientParams = getClientParams(parameters)
515528
clientParams += (ServiceUrlOptionKey -> serviceUrl)
516529
val readerParams = getReaderParams(parameters)
530+
val adminParams = getAdminParams(parameters)
517531

518532
(
519533
paramsToPulsarConf("pulsar.client", clientParams),
520534
paramsToPulsarConf("pulsar.reader", readerParams),
535+
paramsToPulsarConf("pulsar.admin", adminParams),
521536
serviceUrl, adminUrl)
522537
}
523538

src/test/scala/org/apache/spark/sql/pulsar/PulsarAdmissionControlSuite.scala

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.apache.spark.sql.pulsar
22

3+
import java.{util => ju}
4+
35
import org.apache.pulsar.client.admin.PulsarAdmin
46
import org.apache.pulsar.client.api.MessageId
57
import org.apache.pulsar.client.internal.DefaultImplementation
@@ -33,18 +35,32 @@ class PulsarAdmissionControlSuite extends PulsarSourceTest {
3335
val firstLedger = getLedgerId(firstMid)
3436
val firstEntry = getEntryId(firstMid)
3537
require(getLatestOffsets(Set(topic)).size === 1)
36-
val admissionControlHelper = new PulsarAdmissionControlHelper(adminUrl)
38+
val admissionControlHelper = new PulsarAdmissionControlHelper(adminUrl, new ju.HashMap[String, Object]())
3739
val offset = admissionControlHelper.latestOffsetForTopicPartition(topic, MessageId.earliest, approxSizeOfInt)
3840
assert(getLedgerId(offset) == firstLedger && getEntryId(offset) == firstEntry)
3941
}
4042

43+
test("setting invalid config PulsarAdmin") {
44+
val conf = new ju.HashMap[String, Object]()
45+
conf.put("INVALID", "INVALID")
46+
47+
val topic = newTopic()
48+
// Need to call latestOffsetForTopicPartition so the helper instantiates
49+
// the admin
50+
val admissionControlHelper = new PulsarAdmissionControlHelper(adminUrl, conf)
51+
val e = intercept[RuntimeException] {
52+
admissionControlHelper.latestOffsetForTopicPartition(topic, MessageId.earliest, approxSizeOfInt)
53+
}
54+
assert(e.getMessage.contains("Failed to load config into existing configuration data"))
55+
}
56+
4157
test("Admit entry in the middle of the ledger") {
4258
val topic = newTopic()
4359
val messageIds = sendMessages(topic, Array("1", "2", "3"))
4460
val firstMid = messageIds.head._2
4561
val secondMid = messageIds.apply(1)._2
4662
require(getLatestOffsets(Set(topic)).size === 1)
47-
val admissionControlHelper = new PulsarAdmissionControlHelper(adminUrl)
63+
val admissionControlHelper = new PulsarAdmissionControlHelper(adminUrl, new ju.HashMap[String, Object]())
4864
val offset = admissionControlHelper.latestOffsetForTopicPartition(topic, firstMid, approxSizeOfInt)
4965
assert(getLedgerId(offset) == getLedgerId(secondMid) && getEntryId(offset) == getEntryId(secondMid))
5066

@@ -75,7 +91,7 @@ class PulsarAdmissionControlSuite extends PulsarSourceTest {
7591
AddPulsarData(Set(topic), 4, 5, 6, 7, 8, 9),
7692
AssertOnQuery { query =>
7793
query.recentProgress.map(microBatch =>
78-
microBatch.numInputRows <= 4
94+
microBatch.numInputRows <= 4
7995
).forall(_ == true)
8096
}
8197
)
@@ -141,6 +157,27 @@ class PulsarAdmissionControlSuite extends PulsarSourceTest {
141157
)
142158
}
143159

160+
test("Set invalid configuration for PulsarAdmin in stream options") {
161+
val topic = newTopic()
162+
sendMessages(topic, Array("-1"))
163+
require(getLatestOffsets(Set(topic)).size === 1)
164+
165+
val e = intercept[IllegalArgumentException] {
166+
spark.readStream
167+
.format("pulsar")
168+
.option(TopicSingle, topic)
169+
.option(ServiceUrlOptionKey, serviceUrl)
170+
.option(AdminUrlOptionKey, adminUrl)
171+
.option("pulsar.admin.INVALID", "INVALID")
172+
.option(FailOnDataLossOptionKey, "true")
173+
.option(MaxBytesPerTrigger, approxSizeOfInt * 3)
174+
.load()
175+
.selectExpr("CAST(__key AS STRING)", "CAST(value AS STRING)")
176+
.as[(String, String)]
177+
}
178+
assert(e.getMessage.contains("invalid not supported by pulsar"))
179+
}
180+
144181
test("Admission Control with one topic-partition") {
145182
val topic = newTopic()
146183

0 commit comments

Comments
 (0)