Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ file is added to it, and with every status it lists it anyway. It also writes th
phase, such as when it has started, and after it answers actions (at most every 200 millis), so the status shows what
an action did.

While tracing is on, the connector appends the new traces to `~/.camel/<pid>-trace.json`, one JSON object per line.
When the file would grow past 10 MB, it starts a new file: a tool that follows the file sees it become smaller, and
reads the new file from its start.

Write the action file atomically (write a temporary file, then move it), so the connector never reads half an action.
An action file that cannot be read yet is retried for two seconds. Deleting the action file before the answer is
written cancels the action.
Expand Down
4 changes: 4 additions & 0 deletions dsl/camel-cli-connector/src/main/docs/cli-connector.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ file is added to it, and with every status it lists it anyway. It also writes th
phase, such as when it has started, and after it answers actions (at most every 200 millis), so the status shows what
an action did.

While tracing is on, the connector appends the new traces to `~/.camel/<pid>-trace.json`, one JSON object per line.
When the file would grow past 10 MB, it starts a new file: a tool that follows the file sees it become smaller, and
reads the new file from its start.

Write the action file atomically (write a temporary file, then move it), so the connector never reads half an action.
An action file that cannot be read yet is retried for two seconds. Deleting the action file before the answer is
written cancels the action.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public class FileCliConnectorTransport extends ServiceSupport implements CliConn
private static final long CLOCK_TICK_MS = 20;
// how often at most the status is written for the actions answered between two snapshots, as collecting it costs
private static final long ANSWERED_STATUS_MS = 200;
// the trace file starts again when it would grow past this: it grew without limit while tracing is on, about 15 MB a
// minute at 100 exchanges a second, and camel trace reads it whole (CAMEL-26929)
static final long MAX_TRACE_FILE_SIZE = 10 * 1024 * 1024;

private record PendingAction(File output, boolean replyAlways, CompletableFuture<CliActionOutcome> outcome) {
}
Expand Down Expand Up @@ -446,6 +449,20 @@ protected void statusTask() {
}
}

/**
* Deletes the file when the data to append would make it larger than the maximum, so the data starts a new file. A
* reader that follows the file sees it become smaller, and reads the new file from its start.
*
* @return whether the file was deleted
*/
static boolean startNewFileIfFull(File file, long append, long max) {
long length = file.length();
if (length > 0 && length + append > max) {
return FileUtil.deleteFile(file);
}
return false;
}

protected void traceTask() {
try {
JsonObject json = snapshots.trace();
Expand All @@ -462,6 +479,7 @@ protected void traceTask() {
// store traces in a special file
LOG.trace("Updating trace file: {}", traceFile);
String data = json.toJson() + System.lineSeparator();
startNewFileIfFull(traceFile, data.length(), MAX_TRACE_FILE_SIZE);
IOHelper.appendText(data, traceFile);
json = arr.getMap(arr.size() - 1);
traceFilePos = json.getLong("uid");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.cli.connector;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.assertj.core.api.Assertions.assertThat;

/**
* The trace file starts again when it is full, instead of growing while tracing is on (CAMEL-26929).
*/
class TraceFileSizeTest {

@TempDir
Path dir;

@Test
void aFileThatWouldGrowPastTheMaximumStartsAgain() throws Exception {
File file = Files.writeString(dir.resolve("1-trace.json"), "x".repeat(90)).toFile();

assertThat(FileCliConnectorTransport.startNewFileIfFull(file, 20, 100)).isTrue();
assertThat(file).doesNotExist();
}

@Test
void aFileWithRoomIsAppendedTo() throws Exception {
File file = Files.writeString(dir.resolve("1-trace.json"), "x".repeat(50)).toFile();

assertThat(FileCliConnectorTransport.startNewFileIfFull(file, 20, 100)).isFalse();
assertThat(file).hasContent("x".repeat(50));
}

@Test
void anEmptyFileTakesDataLargerThanTheMaximum() throws Exception {
// a single snapshot larger than the maximum is still written
File file = dir.resolve("1-trace.json").toFile();

assertThat(FileCliConnectorTransport.startNewFileIfFull(file, 200, 100)).isFalse();
}

@Test
void theMaximumIsTenMegabytes() {
assertThat(FileCliConnectorTransport.MAX_TRACE_FILE_SIZE).isEqualTo(10L * 1024 * 1024);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -514,13 +514,19 @@ private int readTraceFiles(Map<Long, Pid> pids) throws Exception {
int lines = 0;

for (Pid pid : pids.values()) {
Path file = getTraceFile(pid.pid);
long size = Files.exists(file) ? Files.size(file) : 0;
if (pid.reader != null && isNewFile(pid.size, size)) {
// the integration started a new trace file, as the old one was full: read the new one from its start
IOHelper.close(pid.reader);
pid.reader = new LineNumberReader(Files.newBufferedReader(file));
}
pid.size = size;
if (pid.reader == null) {
Path file = getTraceFile(pid.pid);
if (Files.exists(file)) {
pid.reader = new LineNumberReader(Files.newBufferedReader(file));
if (tail == 0) {
// only read new lines so forward to end of reader
long size = Files.size(file);
pid.reader.skip(size);
}
}
Expand Down Expand Up @@ -549,6 +555,13 @@ private int readTraceFiles(Map<Long, Pid> pids) throws Exception {
return lines;
}

/**
* Whether a file that a reader follows was started again: it is smaller than when it was last read (CAMEL-26929).
*/
static boolean isNewFile(long lastSize, long size) {
return size < lastSize;
}

private List<Row> parseTraceLine(Pid pid, String line) {
JsonObject root = null;
try {
Expand Down Expand Up @@ -942,6 +955,8 @@ private static class Pid {
Queue<String> fifo;
int depth;
LineNumberReader reader;
// the size of the trace file when it was last read
long size;
}

private static class Row {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,13 @@ private static JsonObject status(boolean enabled, boolean standby) {
root.put("trace", trace);
return root;
}

@Test
void aTraceFileThatGotSmallerIsANewFile() {
// CAMEL-26929: the integration starts a new trace file when it is full, and camel trace reads it from its start
assertThat(CamelTraceAction.isNewFile(10_000, 120)).isTrue();
assertThat(CamelTraceAction.isNewFile(10_000, 10_000)).isFalse();
assertThat(CamelTraceAction.isNewFile(10_000, 12_000)).isFalse();
assertThat(CamelTraceAction.isNewFile(0, 120)).isFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,14 @@ void refreshTraceData(List<Long> pids) {
traces.set(allTraces);
}

/**
* Where to read a file that grows on from: the position read up to, or the start when the file is smaller than
* that, as the integration started a new trace file when it was full (CAMEL-26929).
*/
static long readFrom(long lastPos, long length) {
return length < lastPos ? 0 : lastPos;
}

private void readTraceFile(String pid, List<TraceEntry> allTraces) {
Path traceFile = CommandLineHelper.getCamelDir().resolve(pid + "-trace.json");
if (!Files.exists(traceFile)) {
Expand All @@ -678,6 +686,7 @@ private void readTraceFile(String pid, List<TraceEntry> allTraces) {

try (RandomAccessFile raf = new RandomAccessFile(traceFile.toFile(), "r")) {
long length = raf.length();
lastPos = readFrom(lastPos, length);
if (length <= lastPos) {
return; // no new data
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,12 @@ public boolean isInfraSelected() {
},
Path::of, Path::of);
}

@Test
void aTraceFileThatGotSmallerIsReadFromItsStart() {
// CAMEL-26929: the integration starts a new trace file when it is full; the panel read nothing from it again
assertThat(DataRefreshService.readFrom(10_000, 120)).isZero();
assertThat(DataRefreshService.readFrom(10_000, 12_000)).isEqualTo(10_000);
assertThat(DataRefreshService.readFrom(0, 120)).isZero();
}
}
Loading