I am encountering an issue with my Spring Boot web service when sending multiple requests at once. Some requests fail, but when debugging, they go through successfully.
Stack:
- Spring Boot version: 2.7.4
- Embedded server: Tomcat 9.0.65
- IDE: IntelliJ IDEA
- Client: Postman
When sending a request, I receive the following errors in my logs:
2025-03-31 16:22:29.271 DEBUG 16768 --- [nio-8082-exec-1] o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header
2025-03-31 16:22:29.272 DEBUG 16768 --- [nio-8082-exec-1] o.apache.coyote.http11.Http11Processor : Error state [CLOSE_CONNECTION_NOW] reported while processing request
Stack trace:
java.io.EOFException: null
at .apache.tomcat.util.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1340) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at .apache.tomcat.util.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at .apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:805) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at .apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:360) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at .apache.coyote.http11.Http11Processor.service(Http11Processor.java:271) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
...
When I send multiple requests at the same time, some of them fail, but they pass when debugging.
Code:
Controller endpoint:
@PostMapping("api/synchro/process")
public ResponseEntity<String> processEntries(@RequestBody List<Synchro> entries) {
try {
for (Synchro entry : entries) {
System.out.println(entry.getId());
synchroService.processSynchroEntry(entry);
}
return ResponseEntity.ok("PROCESSED");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error processing entries: " + e.getMessage());
}
}
Service method:
@Transactional
public void processSynchroEntry(Synchro entry) {
String eventType = entry.getEventType().toUpperCase();
String data = entry.getData();
String tableName = entry.getTableName();
switch (eventType) {
case "INSERT" -> this.handleInsert(tableName, data);
case "UPDATE" -> this.handleUpdate(tableName, data);
case "DELETE" -> this.handleDelete(tableName, data);
default -> throw new IllegalArgumentException("Unsupported event type: " + eventType);
}
}
Dynamic Insert Query Execution:
protected void handleInsert(String tableName, String data) {
try {
JsonNode dataNode = this.objectMapper.readTree(data);
StringBuilder query = new StringBuilder("INSERT INTO ");
query.append(tableName).append(" (");
MapSqlParameterSource params = new MapSqlParameterSource();
StringBuilder columns = new StringBuilder();
StringBuilder values = new StringBuilder();
Iterator<Map.Entry<String, JsonNode>> it = dataNode.fields();
while(it.hasNext()) {
Map.Entry<String, JsonNode> field = it.next();
if (columns.length() > 0) {
columns.append(", ");
values.append(", ");
}
columns.append(field.getKey());
values.append(":" + field.getKey());
params.addValue(field.getKey(), field.getValue().asText());
}
query.append(columns).append(") VALUES (").append(values).append(")");
this.namedParameterJdbcTemplate.update(query.toString(), params);
} catch (Exception e) {
throw new RuntimeException("Error processing insert", e);
}
}
What I've tried:
- Checked the request format: Requests are sent correctly via Postman.
- Enabled Spring Boot debug logs: Logs confirm that some requests fail with EOFException.
- Tested different concurrency levels: When sending requests one by one, they succeed. When sending them in bulk, some fail.
- Validated JSON payloads: All JSON data sent is properly formatted.
- Tried increasing Tomcat connection settings:
server.tomcat.max-threads=200
server.tomcat.accept-count=100
server.connection-timeout=60000
Questions:
- What could be causing these EOFExceptions when sending multiple requests?
- Could this be related to transaction handling or how Tomcat manages connections?
- Are there any known Spring Boot/Tomcat issues that might cause this behavior?
- Any insights or suggestions would be greatly appreciated!
public class Synchro {
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY
)
@Column(
name = "id",
unique = true,
nullable = false
)
private @NotNull(
message = "Id ne peut pas être null"
) Integer id;
@Column(
name = "eventType",
length = 100,
nullable = false
)
private @NotNull(
message = "EventType ne peut pas être null"
) String eventType;
@Column(
name = "data",
nullable = false
)
private @NotNull(
message = "Data ne peut pas être null"
) String data;
@Temporal(TemporalType.TIMESTAMP)
@Column(
name = "createdAt"
)
private @NotNull(
message = "CreatedAt ne peut pas être null"
) Date createdAt;
@Temporal(TemporalType.TIMESTAMP)
@Column(
name = "updatedAt"
)
private Date updatedAt;
@Column(
name = "status",
length = 20,
nullable = false
)
private @NotNull(
message = "Status ne peut pas être null"
) String status;
@Column(
name = "tableName",
length = 100,
nullable = false
)
private @NotNull(
message = "TableName ne peut pas être null"
) String tableName;
@Transient
private String source;