ExportXMLWordPrintableJSON

    • Type: Improvement
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Kotlin, Reactive Streams
    • None
    • None
    • Java Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      When the user does not call batchSize(), the Reactive Streams and Kotlin coroutine drivers derive the wire-level batchSize of find/aggregate and every getMore from Reactive Streams demand (Subscription.request(n)) in BatchCursorFlux.calculateBatchSize:

      int calculateBatchSize\(final long demand\) {
          Integer setBatchSize = batchCursorPublisher.getBatchSize\(\);
          if \(setBatchSize \!= null\) {
              return setBatchSize;
          } else if \(demand > Integer.MAX\_VALUE\) {
              return Integer.MAX\_VALUE;
          }
          return Math.max\(2, \(int\) demand\);
      }
      

      The result depends on the consumer, not on anything the user configured:
      * Kotlin coroutine collect/toList/first() (asFlow() with Channel.BUFFERED): batchSize: 64 on find and every getMore.
      * Reactor blockLast()/collectList(): batchSize: 2147483647.
      * Anything requesting one element at a time (Mono.from, next(), take(1), limitRate(1), awaitFirst(), Spring Data reactive findOne, RxJava firstElement(), hand-written request(1) subscribers): batchSize: 2 on find and every getMore, so a 10k-document result takes ~5k round trips.

      The server default (101 for the first batch, then up to 16 MiB) is never used. The CRUD specification says batchSize "is sent only if the caller explicitly provides a value"; the Java/Kotlin sync drivers and all other drivers comply.

      History* JAVA-5700 (Nov 2024): reported the 64 case; resolved by deciding to document the demand-driven behaviour. Package-level javadoc was never added.

      • HELP-89623 (Feb 2026): another customer hit batchSize: 2 and could not tell where it came from.
      • DOCSP-58157 (Mar 2026): docs now state a default of Long.MAX_VALUE (Java RS) and 64 (Kotlin). Neither is correct in general because there is no fixed default.
      • CUSTOMER-I-10533 (Mar 2026, https://feedback.mongodb.com/ideas/CUSTOMER-I-10533): asks to use the server default; one commenter reports the batchSize: 2 behaviour caused a revenue-impacting outage.
      • JAVA-5838: Kotlin findOne sends batchSize: 64; same root cause.

      Proposed change

      Decouple fetching from emission:
      * When batchSize() was not called, leave batchSize unset on the initial command and on getMore so the server default applies. Model "unset" as null, not 0 (batchSize: 0 on find means an empty first batch).
      * Keep using demand purely for emission/backpressure. BatchCursorFlux already buffers via FluxSink.OverflowStrategy.BUFFER, so a 101-document server batch with smaller downstream demand is drained from the buffer and getMore is only issued when the buffer is empty and demand is outstanding.
      * When batchSize(n) was called, keep sending n as today.
      * Ensure Kotlin first()/firstOrNull() no longer sends a batch size (covers JAVA-5838).

      Behaviour change / risk

      Users who relied on limitRate(n) to tune server batches will now get the server default; batchSize(n) is the explicit replacement. This reverses the position taken on JAVA-5700 and needs team sign-off. BatchCursorFlux has had regressions before (#652), so BatchCursorFluxTest dynamic-batch-size cases must be rewritten rather than removed.

      Follow-ups

      • Correct the Java RS and Kotlin coroutine docs (DOCSP-58157 wording) to say the server default applies unless batchSize() is set.
      • Update CUSTOMER-I-10533 and JAVA-5700 with a link to this ticket.

            Assignee:
            Unassigned
            Reporter:
            Nabil Hachicha
            None
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: