17
17
package com.couchbase.client.performer.kotlin
18
18
19
19
import com.couchbase.client.core.error.CouchbaseException
20
+ import com.couchbase.client.core.error.InvalidArgumentException
20
21
import com.couchbase.client.kotlin.CommonOptions
21
22
import com.couchbase.client.kotlin.codec.JacksonJsonSerializer
22
23
import com.couchbase.client.kotlin.codec.JsonTranscoder
23
24
import com.couchbase.client.kotlin.codec.RawBinaryTranscoder
24
25
import com.couchbase.client.kotlin.codec.RawJsonTranscoder
25
26
import com.couchbase.client.kotlin.codec.RawStringTranscoder
27
+ import com.couchbase.client.kotlin.kv.DEFAULT_SCAN_BATCH_ITEM_LIMIT
28
+ import com.couchbase.client.kotlin.kv.DEFAULT_SCAN_BATCH_SIZE_LIMIT
26
29
import com.couchbase.client.kotlin.kv.Durability
27
30
import com.couchbase.client.kotlin.kv.Expiry
28
31
import com.couchbase.client.kotlin.kv.GetResult
32
+ import com.couchbase.client.kotlin.kv.KvScanConsistency
29
33
import com.couchbase.client.kotlin.kv.MutationResult
30
34
import com.couchbase.client.kotlin.kv.PersistTo
31
35
import com.couchbase.client.kotlin.kv.ReplicateTo
36
+ import com.couchbase.client.kotlin.kv.ScanSort
37
+ import com.couchbase.client.kotlin.util.StorageSize.Companion.bytes
32
38
import com.couchbase.client.performer.core.commands.SdkCommandExecutor
33
39
import com.couchbase.client.performer.core.perf.Counters
34
40
import com.couchbase.client.performer.core.perf.PerRun
35
41
import com.couchbase.client.performer.core.util.ErrorUtil
36
42
import com.couchbase.client.performer.core.util.TimeUtil
37
43
import com.couchbase.client.performer.kotlin.util.ClusterConnection
44
+ import com.couchbase.client.protocol.sdk.kv.rangescan.ScanOptions
45
+ import com.couchbase.client.protocol.sdk.kv.rangescan.ScanSort.KV_RANGE_SCAN_SORT_ASCENDING
46
+ import com.couchbase.client.protocol.sdk.kv.rangescan.ScanSort.KV_RANGE_SCAN_SORT_NONE
38
47
import com.couchbase.client.protocol.shared.CouchbaseExceptionEx
39
48
import com.couchbase.client.protocol.shared.Exception
40
49
import com.couchbase.client.protocol.shared.ExceptionOther
41
50
import com.couchbase.client.protocol.shared.MutationToken
42
51
import com.couchbase.client.protocol.shared.Transcoder
52
+ import com.couchbase.client.protocol.streams.Created
53
+ import com.couchbase.client.protocol.streams.Type.STREAM_KV_RANGE_SCAN
54
+ import com.couchbase.stream.FluxStreamer
43
55
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
44
56
import com.fasterxml.jackson.module.kotlin.KotlinModule
45
57
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
46
58
import com.fasterxml.jackson.module.kotlin.jsonMapper
47
59
import com.google.protobuf.ByteString
60
+ import kotlinx.coroutines.reactive.asPublisher
48
61
import kotlinx.coroutines.runBlocking
62
+ import reactor.core.publisher.Flux
49
63
import java.time.Instant
50
64
import kotlin.time.Duration.Companion.milliseconds
51
65
import kotlin.time.Duration.Companion.seconds
@@ -58,6 +72,7 @@ import com.couchbase.client.protocol.shared.Durability as FitDurability
58
72
import com.couchbase.client.protocol.shared.Expiry as FitExpiry
59
73
import com.couchbase.client.protocol.shared.PersistTo as FitPersistTo
60
74
import com.couchbase.client.protocol.shared.ReplicateTo as FitReplicateTo
75
+ import com.couchbase.client.protocol.streams.Signal as FitSignal
61
76
62
77
/* *
63
78
* Performs each requested SDK operation
@@ -223,6 +238,73 @@ class KotlinSdkCommandExecutor(
223
238
result.elapsedNanos = System .nanoTime() - start
224
239
if (op.returnResult) populateResult(result, r)
225
240
else setSuccess(result)
241
+ } else if (op.hasRangeScan()) {
242
+ val request = op.rangeScan
243
+ val collection = connection.collection(request.collection)
244
+ result.initiated = TimeUtil .getTimeNow()
245
+
246
+ val options = request.options
247
+ val idsOnly = options.hasIdsOnly() && options.idsOnly
248
+
249
+ fun ScanOptions.ktCommon () = createCommon(hasTimeoutMsecs(), timeoutMsecs)
250
+
251
+ fun ScanOptions.ktSort () =
252
+ if (! hasSort()) ScanSort .NONE
253
+ else when (sort) {
254
+ KV_RANGE_SCAN_SORT_NONE -> ScanSort .NONE
255
+ KV_RANGE_SCAN_SORT_ASCENDING -> ScanSort .ASCENDING
256
+ else -> throw UnsupportedOperationException (" Unsupported scan sort: $sort " )
257
+ }
258
+
259
+ fun ScanOptions.ktConsistency () =
260
+ if (! hasConsistentWith()) KvScanConsistency .notBounded()
261
+ else options.consistentWith.toKotlin()
262
+
263
+ fun ScanOptions.ktBatchItemLimit () = if (hasBatchItemLimit()) batchItemLimit else DEFAULT_SCAN_BATCH_ITEM_LIMIT
264
+
265
+ fun ScanOptions.ktBatchSizeLimit () = if (hasBatchByteLimit()) batchByteLimit.bytes else DEFAULT_SCAN_BATCH_SIZE_LIMIT
266
+
267
+ val start = System .nanoTime()
268
+ val flow =
269
+ if (idsOnly) collection.scanIds(
270
+ type = request.scanType.toKotlin(),
271
+ common = options.ktCommon(),
272
+ sort = options.ktSort(),
273
+ consistency = options.ktConsistency(),
274
+ batchItemLimit = options.ktBatchItemLimit(),
275
+ batchSizeLimit = options.ktBatchSizeLimit(),
276
+ )
277
+ else collection.scanDocuments(
278
+ type = request.scanType.toKotlin(),
279
+ common = options.ktCommon(),
280
+ sort = options.ktSort(),
281
+ consistency = options.ktConsistency(),
282
+ batchItemLimit = options.ktBatchItemLimit(),
283
+ batchSizeLimit = options.ktBatchSizeLimit(),
284
+ )
285
+ result.elapsedNanos = System .nanoTime() - start
286
+
287
+ val results = Flux .from(flow.asPublisher())
288
+
289
+ val streamer: FluxStreamer <Any > = // "Any" is GetResult or String (document ID)
290
+ FluxStreamer (
291
+ results,
292
+ perRun,
293
+ request.streamConfig.streamId,
294
+ request.streamConfig,
295
+ ) { documentOrId ->
296
+ processScanResult(request, documentOrId)
297
+ }
298
+
299
+ perRun.streamerOwner().addAndStart(streamer)
300
+ result.setStream(
301
+ FitSignal .newBuilder()
302
+ .setCreated(
303
+ Created .newBuilder()
304
+ .setType(STREAM_KV_RANGE_SCAN )
305
+ .setStreamId(streamer.streamId())
306
+ )
307
+ )
226
308
} else {
227
309
throw UnsupportedOperationException (IllegalArgumentException (" Unknown operation" ))
228
310
}
@@ -314,6 +396,14 @@ class KotlinSdkCommandExecutor(
314
396
}
315
397
316
398
fun convertExceptionKt (raw : Throwable ): Exception {
399
+ if (raw is IllegalArgumentException ) {
400
+ // When there's no meaningful error context, the Kotlin SDK sometimes throws
401
+ // a standard IllegalArgumentException. "Promote" these to the
402
+ // InvalidArgumentException expected by the FIT driver.
403
+ // TODO: Reconsider throwing standard IllegalArgumentException
404
+ return convertExceptionKt(InvalidArgumentException (raw.message, raw.cause, null ))
405
+ }
406
+
317
407
if (raw is CouchbaseException || raw is UnsupportedOperationException ) {
318
408
val out = CouchbaseExceptionEx .newBuilder()
319
409
.setName(raw.javaClass.simpleName)
0 commit comments