Federico Mariani created CAMEL-26653:
----------------------------------------
Summary: camel-minio - maxMessagesPerPoll only sets the listing
page size: one poll lists, stats and routes the whole bucket
Key: CAMEL-26653
URL: https://issues.apache.org/jira/browse/CAMEL-26653
Project: Camel
Issue Type: Bug
Components: camel-minio
Affects Versions: 4.14.9, 4.18.5, 4.23.0, 4.22.2
Reporter: Federico Mariani
h3. Problem
The {{maxMessagesPerPoll}} option (default 10) is documented as "the maximum
number of messages as a limit to poll at each polling ... Use 0 or a negative
number to set it as unlimited". The minio consumer uses it only as the page
size of the listing, so one poll lists every page of the bucket, gets the
metadata of every object, and routes all of them.
Code on main (commit 7978db3d42dd), in
{{components/camel-minio/src/main/java/org/apache/camel/component/minio/MinioConsumer.java}}:
* Lines 156-158: {{listObjectRequest.maxKeys(maxMessagesPerPoll)}}.
* Lines 226-242: {{createExchanges}} loops with {{do ... while
(minioObjectSummaries.hasNext())}} and has no count check.
* In minio-java 9.0.3, the iterator returned by {{listObjects}} is lazy: when
a page is truncated, {{hasNext()}} and {{next()}} fetch the next one
({{MinioAsyncClient.ObjectIterator}} calls {{populate()}} again).
* So the loop reads every page.
* Line 567: each listed object gets a {{statObject}} before anything is routed
({{MinioEndpoint.getObjectStat}}, {{MinioEndpoint.java}} line 246).
* Line 346: {{processBatch}} routes every exchange and has no limit either.
With the defaults ({{includeBody=true}}, {{deleteAfterRead=true}}), each object
is downloaded and then deleted.
* Lines 191-195: the continuation check compares the number of objects listed
by the whole poll to {{maxMessagesPerPoll}}. For any bucket with more objects
than that, the next poll starts after the last object of the bucket and gets an
empty listing.
The same behaviour is in camel-4.14.9, 4.18.5, 4.22.2 and 4.23.0 (checked in
the source, which is structured slightly differently there).
>From the code, for 10k objects with the default of 10: about 1000 list
>requests and 10k stat requests happen before the first exchange is routed, and
>that one poll then routes all 10k objects. This figure is arithmetic, not
>measured at that size.
h3. How to reproduce
Measured on main with a JUnit test:
* A real {{MinioClient}} against an in-process fake S3 endpoint: the module's
{{FakeS3Server}} test helper, extended with request counters. It pages
ListObjectsV2 by max-keys and continuation-token.
* 30 objects, {{maxMessagesPerPoll=10}}, {{startScheduler=false}}, polls called
by the test.
||deleteAfterRead||poll||list requests||stat (HEAD)||get||routed||
|true|1|3|30|30|30|
|true|2|1, start-after=o030.txt, empty|0|0|0|
|false|1|3|30|30|30|
|false|2|1, empty|0|0|0|
|false|3|3|30|30|30|
With the documented limit, a poll would make 1 list request and route at most
10 objects.
h3. Proposed change
* When {{maxMessagesPerPoll}} is above 0, stop reading the listing once that
many objects were listed. That means one page per poll, as camel-aws2-s3 does:
{{AWS2S3Consumer}} makes one ListObjectsV2 call with {{maxKeys}} and keeps the
continuation token for the next poll.
* Base the continuation on what this poll listed: continue after the last
listed object when the page was full, and start over when the listing has
ended. This also removes the empty listing every other poll.
* Keep {{maxMessagesPerPoll}} 0 or below as today (all pages).
* Risk: some listed objects are not routed. These are folders (when
{{includeFolders=false}}), objects still in flight (CAMEL-25512) and objects
that cannot be got (CAMEL-25770). So a poll can route fewer objects than the
limit, and the next poll continues after them, as with aws2-s3.
h3. Impact / upgrade
* Filed as a Bug: the documented option is not applied to routing, which goes
beyond a performance issue.
* This is a behaviour change. With the default {{maxMessagesPerPoll=10}}, a
poll routes at most 10 objects instead of the whole bucket, so draining a large
bucket takes more polls. Setting {{maxMessagesPerPoll=0}} keeps the old
behaviour.
* It needs an entry in the 4.24 upgrade guide.
* An endpoint with {{objectName}} set gets one object per poll and is not
affected.
* The component has been deprecated since 4.23 (CAMEL-24750). The change is
small and the option is documented, so it still seems worth fixing.
h3. Related
* CAMEL-17100: "Minio Consumer is really slow on listing initially if you have
a lot of files". Its fix (7de327d91eb6) moved the download of each object from
the listing into {{processBatch}}. The listing over all pages and the stat per
object stayed.
* Commit 62fd4017653d introduced the continuation check that compares the count
of the whole poll.
* The loop without a count check has been there since the component was added
(67bad5a5978a, CAMEL-13934).
* CAMEL-25512 and CAMEL-25770: recent fixes in the same loop.
_Claude Code on behalf of Federico Mariani_
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)