OpenAPITools / OpenAPITools/openapi-generator
[BUG] [JAVA] High memory consumption when uploading a file
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
I am uploading a 700 MB zip file with the generated client of the following api definition.
The memory, according to the taks manager, skyrockets to about 4 GB.
When i limit the memory via -Xmx, then i get an OutOfMemoryError.
Exception:
java.lang.OutOfMemoryError: Java heap space
at java.base/java.lang.StringCoding.decodeUTF8_0(StringCoding.java:733)
at java.base/java.lang.StringCoding.decodeUTF8(StringCoding.java:725)
at java.base/java.lang.StringCoding.decode(StringCoding.java:257)
at java.base/java.lang.String.<init>(String.java:507)
at java.base/java.lang.String.<init>(String.java:561)
at okio.Buffer.readString(Buffer.kt:313)
at okio.Buffer.readUtf8(Buffer.kt:297)
at client.communication.rest.gen.invoker.ApiClient.requestBodyToString(ApiClient.java:1569)
at client.communication.rest.gen.invoker.ApiClient.buildRequest(ApiClient.java:1220)
at client.communication.rest.gen.invoker.ApiClient.buildCall(ApiClient.java:1168)
at client.communication.rest.gen.api.FileApi.postFileCall(FileApi.java:542)
at client.communication.rest.gen.api.FileApi.postFileValidateBeforeCall(FileApi.java:557)
at client.communication.rest.gen.api.FileApi.postFileAsync(FileApi.java:617)
openapi-generator version
OpenAPI declaration file content or url
openapi: 3.0.0
info:
description: Server REST API
title: Server
version: 1.0.1
servers:
- description: Server url definition
url: '{protocol}://{domain}:{port}/{path}'
variables:
protocol:
default: https
enum:
- http
- https
domain:
default: localhost
description: Domain assigned by the service provider
port:
default: '33003'
description: Port assigned by the service provider
path:
default: api
description: The path for the API resources
security:
- apiKeyAuth: []
tags:
- name: File
paths:
/file/{type}/{id}:
delete:
description: Deletes a File
operationId: deleteFile
parameters:
- description: Id for the file path
in: path
name: id
required: true
schema:
format: integer
type: integer
- description: File Type for the file path.
in: path
name: type
required: true
schema:
$ref: '#/components/schemas/fileType'
responses:
'200':
description: File successfully deleted
'401':
description: API key is missing or invalid
headers:
X-API-KEY:
explode: false
schema:
type: string
style: simple
tags:
- File
get:
description: Provides a file, if available
operationId: getFile
parameters:
- description: Id for the file path.
in: path
name: id
required: true
schema:
format: integer
type: integer
- description: File Type for the file path.
in: path
name: type
required: true
schema:
$ref: '#/components/schemas/fileType'
responses:
'200':
content:
application/octet-stream:
schema:
format: binary
type: string
description: Success
'401':
description: API key is missing or invalid
headers:
X-API-KEY:
explode: false
schema:
type: string
style: simple
tags:
- File
post:
description: Uploads a File
operationId: postFile
parameters:
- description: Id for the file path.
in: path
name: id
required: true
schema:
format: integer
type: integer
- description: File Type for the file path.
in: path
name: type
required: true
schema:
$ref: '#/components/schemas/fileType'
requestBody:
content:
application/octet-stream:
schema:
format: binary
type: string
responses:
'200':
description: File successfully uploaded
'401':
description: API key is missing or invalid
headers:
X-API-KEY:
explode: false
schema:
type: string
style: simple
tags:
- File
components:
parameters:
filePathId:
description: Id for the file path.
in: path
name: id
required: true
schema:
format: integer
type: integer
filePathType:
description: File Type for the file path.
in: path
name: type
required: true
schema:
$ref: '#/components/schemas/fileType'
responses:
Unauthorized:
description: API key is missing or invalid
headers:
X-API-KEY:
explode: false
schema:
type: string
style: simple
fileAvailableResponse:
content:
application/json:
schema:
$ref: '#/components/schemas/fileAvailableResponse'
description: File Availability Response
schemas:
fileType:
description: Type of a File
enum:
- TEXT
- PDF
title: File Type
type: string
fileAvailableResponse:
properties:
available:
description: If the requested file is available or not
type: boolean
required:
- available
title: File Availability Response
type: object
securitySchemes:
apiKeyAuth:
in: header
name: X-API-KEY
type: apiKey
Generation Details
Gradle Plugin: id 'org.openapi.generator' version '6.6.0'
Task Config:
tasks.register('buildOpenApiClientSource', GenerateTask) {
generatorName = "java"
inputSpec = "$buildDir/generated/server_openapi.json".toString()
outputDir = "$buildDir/generated".toString()
apiPackage = "client.communication.rest.gen.api"
modelPackage = "client.communication.rest.gen.model"
invokerPackage = "client.communication.rest.gen.invoker"
generateModelTests = false
generateModelDocumentation = false
generateApiTests = false
generateApiDocumentation = false
cleanupOutput = false
configOptions.put("sourceFolder", "src/gen/java")
configOptions.put("booleanGetterPrefix", "is")
configOptions.put("enumUnknownDefaultCase", "true")
configOptions.put("dateLibrary", "java8")
configOptions.put("useGzipFeature", "false")
configOptions.put("serializableModel", "true")
configOptions.put("serializationLibrary", "gson")
configOptions.put("hideGenerationTimestamp", "true")
}
Steps to reproduce
Upload a file with the generated client.
Related issues/PRs
Suggest a fix
As a temporary fix i created a sub class, overrode the method, copied the code from the generated code and passed null as a payload for the method updateParamsForAuth.
The payload was added with https://github.com/OpenAPITools/openapi-generator/commit/c06a21e63806546562d82d9e5e15f0060d0d3c1c.
I found the Pull Request https://github.com/OpenAPITools/openapi-generator/pull/10858 for the library jersey.
In the library jersey the ApiClient.mustache the payload is only filled if a signature method is present:
{{#hasHttpSignatureMethods}}
serializeToString(body, formParams, contentType, isBodyNullable),
{{/hasHttpSignatureMethods}}
{{^hasHttpSignatureMethods}}
null,
{{/hasHttpSignatureMethods}}
Maybe this can be added for the okhttp-gson.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the generated ApiClient.java stack-trace locations, especially requestBodyToString and buildRequest, then inspect the okhttp-gson ApiClient.mustache template. Compare its payload handling with the jersey template shown from PR #10858 and reproduce through the Gradle buildOpenApiClientSource task using the supplied specification. Done means a large binary upload no longer creates the reported heap exhaustion.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100