Skip to content

Add endpoint parameter and fix resource leaks - #1

Draft
Watson1978 wants to merge 6 commits into
masterfrom
add-endpoint-parameter
Draft

Add endpoint parameter and fix resource leaks#1
Watson1978 wants to merge 6 commits into
masterfrom
add-endpoint-parameter

Conversation

@Watson1978

Copy link
Copy Markdown

No description provided.

Running embulk.run() in a loop inside a single JVM accumulated resources on
every run and eventually led to an OutOfMemoryError. The S3Client built by
S3ClientManager was never closed anywhere in the repository.

Propagate close() from S3ClientManager through AbstractStrategy down to each
strategy, so the SDK resources are released on Embulk's task completion
lifecycle. embulk-core calls close() after commit() / abort(), so the client
is no longer in use at that point. In FileOutputStrategy the super.close()
call sits in a finally block so the client is released even when closeBuffer()
throws.

Also build the S3ClientManager after validate() rather than before it. When
validate() throws, the instance never reaches FileOutputRunner#open, so Embulk
never gets a chance to call close() and the client was leaked. The exception
types and messages are unchanged.

Also shut down the ExecutorService that multiPartUpload() creates on every
call. It was never shut down, leaking non-daemon threads per upload.

Move the getRegion() stub in BufferedStrategyTests from setUp() to
testValidateTrue(): the validation-failure path no longer builds a client, so
Mockito's strict stubs flagged the stub as unnecessary.
./gradlew build was green while running zero tests. The dynamic version
'org.junit.jupiter:junit-jupiter:5.+' resolved to 5.14.4 (junit-platform
1.14.4), while Gradle 5.2.1 bundles junit-platform-launcher 1.3.1. The
generation gap made test discovery fail, and because that failure is only a
warning, the build stayed green:

    TestEngine with ID 'junit-jupiter' failed to discover tests
    org.junit.platform.commons.JUnitException: OutputDirectoryCreator not
    available; probably due to unaligned versions of the junit-platform-engine
    and junit-platform-launcher jars

A recent change broke an existing test and the build did not notice.

Pin junit-jupiter to 5.14.4 and put a matching junit-platform-launcher 1.14.4
on the test runtime classpath, which Gradle 5.2.1 prefers over its bundled
one. All 10 tests now run, and a deliberately broken assertion does fail the
build.

Pin mockito to 3.12.4 (the version '3.+' resolved to) for the same reason: a
dynamic version can break the build without anything changing here.

Drop jcenter(), which shut down in 2022. Every dependency resolves from
mavenCentral() alone, verified with --refresh-dependencies.
An application that embeds the plugin via EmbulkEmbed and loops embulk.run()
over many tables grows its heap on every run until it dies with an
OutOfMemoryError.

The caller hands the ownership of the buffer over to add(). embulk-core's
FileOutputOutputStream#doFlush() calls out.add(buffer) and then drops its own
reference with buffer = BufferImpl.EMPTY -- it never releases it. The canonical
implementation, OutputStreamFileOutput#add() in embulk-core, therefore releases
the buffer in a finally block. This plugin did not, so every pooled buffer it
was handed stayed allocated in the Netty heap arena, and the arena never
reclaims it: EmbulkEmbed builds one BufferAllocator at construction time and
shares it across every run. Each run leaked as many bytes as it wrote.

Measured with the real FileOutputStrategy driven through the real
FileOutputOutputStream, one shared allocator, 32MB per run (heap after GC):

                      before     after     OutputStreamFileOutput (control)
    after run 1        45 MB     13 MB      7 MB
    after run 2        77 MB     13 MB      7 MB
    after run 4       141 MB     13 MB      7 MB
    after run 8       269 MB     13 MB      7 MB

Releasing after the synchronous write to the local file is safe: nothing keeps
a reference to the buffer's array past add().

Known and NOT fixed here: BufferedStrategy#add() has the same missing release,
but it retains the pooled array via ByteBuffer.wrap() until commit(), so simply
releasing there would corrupt data once the array is recycled. Fixing it needs
a copy-and-accumulate change, which belongs with the separate bug where that
strategy silently drops everything but the last buffer. It is a non-default path
(enable_temp_file_output: false) and does not match the reported symptom.
@Watson1978 Watson1978 changed the title Add endpoint parameter Add endpoint parameter and fix resource leaks Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant