18
18
package org .apache .kyuubi .engine
19
19
20
20
import java .util .UUID
21
+ import java .util .concurrent .Executors
21
22
22
23
import org .apache .hadoop .security .UserGroupInformation
23
24
import org .scalatest .time .SpanSugar .convertIntToGrainOfTime
@@ -28,6 +29,8 @@ import org.apache.kyuubi.config.KyuubiConf._
28
29
import org .apache .kyuubi .ha .HighAvailabilityConf
29
30
import org .apache .kyuubi .ha .client .DiscoveryClientProvider
30
31
import org .apache .kyuubi .ha .client .DiscoveryPaths
32
+ import org .apache .kyuubi .metrics .MetricsConstants .ENGINE_TOTAL
33
+ import org .apache .kyuubi .metrics .MetricsSystem
31
34
import org .apache .kyuubi .util .NamedThreadFactory
32
35
import org .apache .kyuubi .zookeeper .{EmbeddedZookeeper , ZookeeperConf }
33
36
@@ -37,6 +40,7 @@ class EngineRefSuite extends KyuubiFunSuite {
37
40
private val zkServer = new EmbeddedZookeeper
38
41
private val conf = KyuubiConf ()
39
42
private val user = Utils .currentUser
43
+ private val metricsSystem = new MetricsSystem
40
44
41
45
override def beforeAll (): Unit = {
42
46
val zkData = Utils .createTempDir()
@@ -45,17 +49,22 @@ class EngineRefSuite extends KyuubiFunSuite {
45
49
.set(" spark.sql.catalogImplementation" , " in-memory" )
46
50
zkServer.initialize(conf)
47
51
zkServer.start()
52
+ metricsSystem.initialize(conf)
53
+ metricsSystem.start()
48
54
super .beforeAll()
49
55
}
50
56
51
57
override def afterAll (): Unit = {
58
+ metricsSystem.stop()
52
59
zkServer.stop()
53
60
super .afterAll()
54
61
}
55
62
56
63
override def beforeEach (): Unit = {
57
64
conf.unset(KyuubiConf .ENGINE_SHARE_LEVEL_SUBDOMAIN )
58
65
conf.unset(KyuubiConf .ENGINE_SHARE_LEVEL_SUB_DOMAIN )
66
+ conf.unset(KyuubiConf .ENGINE_POOL_SIZE )
67
+ conf.unset(KyuubiConf .ENGINE_POOL_NAME )
59
68
super .beforeEach()
60
69
}
61
70
@@ -247,4 +256,90 @@ class EngineRefSuite extends KyuubiFunSuite {
247
256
assert(port2 == port1, " engine shared" )
248
257
}
249
258
}
259
+
260
+ test(" different engine type should use its own lock" ) {
261
+ conf.set(KyuubiConf .ENGINE_SHARE_LEVEL , USER .toString)
262
+ conf.set(KyuubiConf .FRONTEND_THRIFT_BINARY_BIND_PORT , 0 )
263
+ conf.set(KyuubiConf .ENGINE_INIT_TIMEOUT , 3000L )
264
+ conf.set(HighAvailabilityConf .HA_ZK_NAMESPACE , " engine_test1" )
265
+ conf.set(HighAvailabilityConf .HA_ZK_QUORUM , zkServer.getConnectString)
266
+ val conf1 = conf.clone
267
+ conf1.set(KyuubiConf .ENGINE_TYPE , SPARK_SQL .toString)
268
+ val conf2 = conf.clone
269
+ conf2.set(KyuubiConf .ENGINE_TYPE , HIVE_SQL .toString)
270
+
271
+ val start = System .currentTimeMillis()
272
+ val times = new Array [Long ](2 )
273
+ val executor = Executors .newFixedThreadPool(2 )
274
+ try {
275
+ executor.execute(() => {
276
+ DiscoveryClientProvider .withDiscoveryClient(conf1) { client =>
277
+ try {
278
+ new EngineRef (conf1, user, UUID .randomUUID().toString, null )
279
+ .getOrCreate(client)
280
+ } finally {
281
+ times(0 ) = System .currentTimeMillis()
282
+ }
283
+ }
284
+ })
285
+ executor.execute(() => {
286
+ DiscoveryClientProvider .withDiscoveryClient(conf2) { client =>
287
+ try {
288
+ new EngineRef (conf2, user, UUID .randomUUID().toString, null )
289
+ .getOrCreate(client)
290
+ } finally {
291
+ times(1 ) = System .currentTimeMillis()
292
+ }
293
+ }
294
+ })
295
+
296
+ eventually(timeout(10 .seconds), interval(200 .milliseconds)) {
297
+ assert(times.forall(_ > start))
298
+ // ENGINE_INIT_TIMEOUT is 3000ms
299
+ assert(times.max - times.min < 2500 )
300
+ }
301
+ } finally {
302
+ executor.shutdown()
303
+ }
304
+ }
305
+
306
+ test(" three same lock request with initialization timeout" ) {
307
+ val id = UUID .randomUUID().toString
308
+ conf.set(KyuubiConf .ENGINE_SHARE_LEVEL , USER .toString)
309
+ conf.set(KyuubiConf .ENGINE_TYPE , SPARK_SQL .toString)
310
+ conf.set(KyuubiConf .FRONTEND_THRIFT_BINARY_BIND_PORT , 0 )
311
+ conf.set(KyuubiConf .ENGINE_INIT_TIMEOUT , 3000L )
312
+ conf.set(HighAvailabilityConf .HA_ZK_NAMESPACE , " engine_test2" )
313
+ conf.set(HighAvailabilityConf .HA_ZK_QUORUM , zkServer.getConnectString)
314
+
315
+ val beforeEngines = MetricsSystem .counterValue(ENGINE_TOTAL ).getOrElse(0L )
316
+ val start = System .currentTimeMillis()
317
+ val times = new Array [Long ](3 )
318
+ val executor = Executors .newFixedThreadPool(3 )
319
+ try {
320
+ (0 until (3 )).foreach { i =>
321
+ val cloned = conf.clone
322
+ executor.execute(() => {
323
+ DiscoveryClientProvider .withDiscoveryClient(cloned) { client =>
324
+ try {
325
+ new EngineRef (cloned, user, id, null ).getOrCreate(client)
326
+ } finally {
327
+ times(i) = System .currentTimeMillis()
328
+ }
329
+ }
330
+ })
331
+ }
332
+
333
+ eventually(timeout(20 .seconds), interval(200 .milliseconds)) {
334
+ assert(times.forall(_ > start))
335
+ // ENGINE_INIT_TIMEOUT is 3000ms
336
+ assert(times.max - times.min > 2800 )
337
+ }
338
+
339
+ // we should only submit two engines, the last request should timeout and fail
340
+ assert(MetricsSystem .counterValue(ENGINE_TOTAL ).get - beforeEngines == 2 )
341
+ } finally {
342
+ executor.shutdown()
343
+ }
344
+ }
250
345
}
0 commit comments