-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jackson encoder releases resources in wrong order #30493
Comments
Thanks for the analysis, but you're way ahead of us and this description doesn't help us to understand the problem nor if the proposed solution would break other use cases. Can you describe what's required to reproduce this problem or share a minimal sample application that throws this error? We've had this optimization in place since 5.2.x and we would like to ensure that any change there doesn't cause regressions. |
The fundamental issue is with When the It then tries to flush this buffer during Basically there are 2 separate problems:
|
Minimal repro based on public class JacksonCsvEncoder extends AbstractJackson2Encoder {
public static final MediaType TEXT_CSV = new MediaType("text", "csv");
public JacksonCsvEncoder() {
this(CsvMapper.builder().build(), TEXT_CSV);
}
@Override
protected byte[] getStreamingMediaTypeSeparator(MimeType mimeType) {
// CsvMapper emits newlines
return new byte[0];
}
public JacksonCsvEncoder(ObjectMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
Assert.isInstanceOf(CsvMapper.class, mapper);
setStreamingMediaTypes(List.of(TEXT_CSV));
}
@Override
protected ObjectWriter customizeWriter(ObjectWriter writer, MimeType mimeType, ResolvableType elementType, Map<String, Object> hints) {
var mapper = (CsvMapper) getObjectMapper();
return writer.with(mapper.schemaFor(elementType.toClass()).withHeader());
}
} If you try to use this encoder to encode a Personally I've made the following change to fix the problem: var emitTrailers = Mono.defer(() -> {
try {
// allow generator to emit trailers
generator.close();
byte[] buffer = byteBuilder.toByteArray();
if (buffer.length != 0) {
DataBuffer dataBuffer = bufferFactory.wrap(buffer);
Hints.touchDataBuffer(dataBuffer, hints, logger);
return Mono.just(dataBuffer);
}
} catch (IOException ex) {
logger.error("Could not flush generator", ex);
}
return Mono.empty();
});
return Flux.from(inputStream)
.map(value -> encodeStreamingValue(value, bufferFactory, hints, sequenceWriter, byteBuilder,
separator))
.concatWith(emitTrailers.flux())
.doAfterTerminate(() -> {
try {
// emitTrailers doesn't get executed on exception
if (!generator.isClosed()) {
generator.close();
}
byteBuilder.release();
}
catch (IOException ex) {
logger.error("Could not close Encoder resources", ex);
}
}); |
Sorry for the delayed response. I've tried to reproduce this issue with a dedicated test but failed to get the The change (switching the order of closing resources) would still make sense, but I would like to ensure that we're fixing the issue you're having. Thanks! |
You need to encode |
This is now fixed on the main branch, ready to be released with 6.1.1. I have scheduled a backport for 6.0.15 and 5.3.32 (no release dates scheduled for now). Sorry again about the delay and hopefully the fix will find its way into your applications. Thanks for the help @Kiskae ! |
Affects: 5.2.10+, 5.3.0+
Ran into the following issue while using
AbstractJackson2Encoder
and the jackson csv serializer:The problem is the optimization in commit 7bee3d1 after calling
byteBuilder.release()
it should not be used, but ifgenerator
has its own buffers thengenerator.close()
can trigger a flush to thebyteBuilder
.It should be fixable by reordering the operations so
byteBuilder
is released last.This probably also means the generator needs to be flushed and checked whether there is a trailing
DataBuffer
that still needs to be sent before terminating the stream. (concatWith(if generator.flush() != [], DataBuffer)
)The text was updated successfully, but these errors were encountered: