Skip to content

Commit f1f009b

Browse files
authored
Merge branch 'pinterest:master' into bump_version_08092
2 parents 5da44a9 + eb7d908 commit f1f009b

File tree

2 files changed

+62
-110
lines changed

2 files changed

+62
-110
lines changed

singer/src/main/java/com/pinterest/singer/writer/s3/S3Writer.java

+20-30
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import java.util.regex.Pattern;
4040

4141
import software.amazon.awssdk.services.s3.S3Client;
42-
import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
4342

4443
/**
4544
* A LogStreamWriter for Singer that writes to S3 (writer.type=s3).
@@ -68,7 +67,6 @@ public class S3Writer implements LogStreamWriter {
6867
// Custom Thresholds
6968
private int maxFileSizeMB;
7069
private int minUploadTime;
71-
private int maxRetries;
7270
private Pattern filenamePattern;
7371
private List<String> fileNameTokens = new ArrayList<>();
7472
private boolean filenameParsingEnabled = false;
@@ -129,8 +127,6 @@ public S3Writer(LogStream logStream, S3WriterConfig s3WriterConfig, S3Uploader s
129127
private void initialize() {
130128
this.maxFileSizeMB = s3WriterConfig.getMaxFileSizeMB();
131129
this.minUploadTime = s3WriterConfig.getMinUploadTimeInSeconds();
132-
this.maxRetries = s3WriterConfig.getMaxRetries();
133-
134130
if (s3WriterConfig.isSetFilenamePattern() && s3WriterConfig.isSetFilenameTokens()) {
135131
this.filenameParsingEnabled = true;
136132
this.filenamePattern = Pattern.compile(s3WriterConfig.getFilenamePattern());
@@ -182,13 +178,16 @@ public boolean isCommittableWriter() {
182178
}
183179

184180
/**
185-
* Takes the fullPathPrefix and removes all slashes and replaces them with underscores.
181+
* Get or construct buffer file name based on the log stream name.
182+
* The buffer file naming convention is "log_name.dir_name.file_name.buffer".
183+
*
184+
* @return the buffer file name
186185
*/
187-
public static String sanitizeFileName(String fullPathPrefix) {
188-
if (fullPathPrefix.startsWith("/")) {
189-
fullPathPrefix = fullPathPrefix.substring(1);
190-
}
191-
return fullPathPrefix.replace("/", "_");
186+
public String getBufferFileName() {
187+
return (bufferFile != null) ? bufferFile.getName()
188+
: logName + "." + logStream.getLogDir().substring(1)
189+
.replace("/", "_") + "."
190+
+ logStream.getLogStreamName() + ".buffer";
192191
}
193192

194193
/**
@@ -203,13 +202,13 @@ public static String sanitizeFileName(String fullPathPrefix) {
203202
public synchronized void startCommit(boolean isDraining) throws LogStreamWriterException {
204203
try {
205204
if (!bufferFile.exists()) {
206-
bufferFile.createNewFile();
205+
resetBufferFile();
207206
}
208207

209208
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(bufferFile, true));
210209

211210
} catch (IOException e) {
212-
throw new RuntimeException("Failed to create buffer file: " + bufferFile.getName(), e);
211+
throw new RuntimeException("Failed to create buffer file: " + getBufferFileName(), e);
213212
}
214213
if (uploadFuture == null) {
215214
scheduleUploadTask();
@@ -250,7 +249,7 @@ private void scheduleUploadTask() {
250249
private void uploadDiskBufferedFileToS3() throws IOException {
251250
File
252251
fileToUpload =
253-
new File(BUFFER_DIR, bufferFile.getName() + "." + FORMATTER.format(new Date()));
252+
new File(BUFFER_DIR, getBufferFileName() + "." + FORMATTER.format(new Date()));
254253
String fileFormat = generateS3ObjectKey();
255254
try {
256255
Files.move(bufferFile.toPath(), fileToUpload.toPath());
@@ -266,7 +265,7 @@ private void uploadDiskBufferedFileToS3() throws IOException {
266265
}
267266
fileToUpload.delete();
268267
} catch (IOException e) {
269-
LOG.error("Failed to rename buffer file " + bufferFile.getName(), e);
268+
LOG.error("Failed to rename buffer file " + getBufferFileName(), e);
270269
}
271270
}
272271

@@ -318,24 +317,15 @@ private void writeMessageToBuffer(LogMessageAndPosition logMessageAndPosition)
318317
* @throws IOException
319318
*/
320319
private void resetBufferFile() throws IOException {
321-
String
322-
bufferFileName =
323-
sanitizeFileName(logStream.getFullPathPrefix()) + ".buffer." + UUID.randomUUID()
324-
.toString().substring(0, 8);
325-
bufferFile = new File(BUFFER_DIR, bufferFileName);
326-
bufferFile.createNewFile();
320+
bufferFile = new File(BUFFER_DIR, getBufferFileName());
321+
if (!bufferFile.createNewFile()) {
322+
LOG.info(
323+
"Buffer file for log stream {} already exists, continue with existing buffer file: {}",
324+
logName, getBufferFileName());
325+
}
327326
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(bufferFile, true));
328327
}
329328

330-
/**
331-
* Helper function to get the remaining part of the host name after the cluster prefix,
332-
* typically a UUID.
333-
*/
334-
public static String extractHostSuffix(String inputStr) {
335-
String[] parts = inputStr.split("-");
336-
return parts[parts.length - 1];
337-
}
338-
339329
private Matcher extractTokensFromFilename(String logFileName) {
340330
Matcher matcher = filenamePattern.matcher(logFileName);
341331
if (!matcher.matches()) {
@@ -507,7 +497,7 @@ public void close() throws IOException {
507497
uploadDiskBufferedFileToS3();
508498
bufferFile.delete();
509499
} catch (IOException e) {
510-
LOG.error("Failed to close bufferedWriter or upload buffer file: " + bufferFile.getName(),
500+
LOG.error("Failed to close bufferedWriter or upload buffer file: " + getBufferFileName(),
511501
e);
512502
}
513503
}

singer/src/test/java/com/pinterest/singer/writer/s3/S3WriterTest.java

+42-80
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ public void setUp() {
6363
s3WriterConfig.setMaxRetries(3);
6464

6565
// Initialize the S3Writer with mock dependencies
66-
s3Writer =
67-
new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
66+
s3Writer = new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
6867
}
6968

7069
@After
@@ -75,62 +74,7 @@ public void tearDown() throws IOException {
7574
}
7675
// reset hostname
7776
SingerUtils.setHostname(SingerUtils.getHostname(), "-");
78-
}
79-
80-
@Test
81-
public void testSanitizeFileName() {
82-
String fullPathPrefix = "/var/logs/app";
83-
String expected = "var_logs_app";
84-
String result = s3Writer.sanitizeFileName(fullPathPrefix);
85-
assertEquals(expected, result);
86-
87-
fullPathPrefix = "var/logs/app";
88-
expected = "var_logs_app";
89-
result = s3Writer.sanitizeFileName(fullPathPrefix);
90-
assertEquals(expected, result);
91-
92-
fullPathPrefix = "/var/logs/app/";
93-
expected = "var_logs_app_";
94-
result = s3Writer.sanitizeFileName(fullPathPrefix);
95-
assertEquals(expected, result);
96-
97-
fullPathPrefix = "/";
98-
expected = "";
99-
result = s3Writer.sanitizeFileName(fullPathPrefix);
100-
assertEquals(expected, result);
101-
102-
fullPathPrefix = "";
103-
expected = "";
104-
result = s3Writer.sanitizeFileName(fullPathPrefix);
105-
assertEquals(expected, result);
106-
}
107-
108-
@Test
109-
public void testExtractHostSuffix() {
110-
String hostname = "app-server-12345";
111-
String expected = "12345";
112-
String result = S3Writer.extractHostSuffix(hostname);
113-
assertEquals(expected, result);
114-
115-
hostname = "app-12345";
116-
expected = "12345";
117-
result = S3Writer.extractHostSuffix(hostname);
118-
assertEquals(expected, result);
119-
120-
hostname = "12345";
121-
expected = "12345";
122-
result = S3Writer.extractHostSuffix(hostname);
123-
assertEquals(expected, result);
124-
125-
hostname = "app-server";
126-
expected = "server";
127-
result = S3Writer.extractHostSuffix(hostname);
128-
assertEquals(expected, result);
129-
130-
hostname = "";
131-
expected = "";
132-
result = S3Writer.extractHostSuffix(hostname);
133-
assertEquals(expected, result);
77+
s3Writer.close();
13478
}
13579

13680
@Test
@@ -146,21 +90,8 @@ public void testWriteLogMessageToCommit() throws Exception {
14690
s3Writer.endCommit(1, false);
14791

14892
// Verify that the messages are written to the buffer file
149-
String
150-
bufferFileNamePrefix =
151-
s3Writer.sanitizeFileName(logStream.getFullPathPrefix()) + ".buffer.";
152-
File tmpDir = new File(tempPath);
153-
File bufferFile = null;
154-
File [] tmpFiles = tmpDir.listFiles();
155-
boolean bufferFileExists = false;
156-
for (File file : tmpFiles) {
157-
if (file.getName().startsWith(bufferFileNamePrefix)) {
158-
bufferFileExists = true;
159-
bufferFile = file;
160-
break;
161-
}
162-
}
163-
assertTrue(bufferFileExists);
93+
File bufferFile = new File(tempPath + "/" + s3Writer.getBufferFileName());
94+
assertTrue(bufferFile.exists());
16495
String content = new String(Files.readAllBytes(bufferFile.toPath()));
16596
assertTrue(content.contains("test message"));
16697
}
@@ -210,6 +141,37 @@ public void testUploadIsScheduled() throws Exception {
210141
verify(mockS3Uploader, atLeastOnce()).upload(any(S3ObjectUpload.class));
211142
}
212143

144+
@Test
145+
public void testResumeFromExistingBufferFile() throws Exception {
146+
// Prepare log message
147+
ByteBuffer messageBuffer = ByteBuffer.wrap("This is message 1 :".getBytes());
148+
LogMessage logMessage = new LogMessage(messageBuffer);
149+
LogMessageAndPosition logMessageAndPosition = new LogMessageAndPosition(logMessage, null);
150+
151+
// Write log message to commit
152+
s3Writer.startCommit(false);
153+
s3Writer.writeLogMessageToCommit(logMessageAndPosition, false);
154+
155+
// Create a new S3Writer with the same buffer file and write another message to simulate resuming
156+
S3Writer
157+
newS3Writer =
158+
new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
159+
messageBuffer = ByteBuffer.wrap(" This is message 2".getBytes());
160+
logMessage = new LogMessage(messageBuffer);
161+
logMessageAndPosition = new LogMessageAndPosition(logMessage, null);
162+
163+
// Write log message to commit
164+
newS3Writer.startCommit(false);
165+
newS3Writer.writeLogMessageToCommit(logMessageAndPosition, false);
166+
167+
// Verify that the messages are written to the buffer file
168+
File bufferFile = new File(tempPath + "/" + newS3Writer.getBufferFileName());
169+
assertTrue(bufferFile.exists());
170+
String content = new String(Files.readAllBytes(bufferFile.toPath()));
171+
assertTrue(content.contains("This is message 1 : This is message 2"));
172+
newS3Writer.close();
173+
}
174+
213175
@Test
214176
public void testObjectKeyGeneration() {
215177
// Custom and default tokens used
@@ -223,24 +185,25 @@ public void testObjectKeyGeneration() {
223185
s3WriterConfig.setBucket("bucket-name");
224186
s3WriterConfig.setFilenamePattern("(?<namespace>[^-]+)-(?<filename>[^.]+)\\.(?<index>\\d+)");
225187
s3WriterConfig.setFilenameTokens(Arrays.asList("namespace", "filename", "index"));
226-
s3Writer =
227-
new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
188+
s3Writer = new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
189+
228190
// Check key prefix
229191
String[] objectKeyParts = s3Writer.generateS3ObjectKey().split("/");
230192
assertEquals(4, objectKeyParts.length);
231193
assertEquals("my-path", objectKeyParts[0]);
232194
assertEquals("my_namespace", objectKeyParts[1]);
233195
assertEquals(logStream.getSingerLog().getSingerLogConfig().getName(), objectKeyParts[2]);
196+
234197
// Check last part of object key
235198
String[] keySuffixParts = objectKeyParts[3].split("\\.");
236199
assertEquals(3, keySuffixParts.length);
237200
assertEquals("test_log-0", keySuffixParts[0]);
238201
assertNotEquals("{{S}}", keySuffixParts[1]);
239202
assertEquals(2, keySuffixParts[1].length());
203+
240204
// Custom tokens provided but filename pattern does not match
241205
s3WriterConfig.setFilenamePattern("(?<filename>[^.]+)\\.(?<index>\\d+).0");
242-
s3Writer =
243-
new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
206+
s3Writer = new S3Writer(logStream, s3WriterConfig, mockS3Uploader, tempPath);
244207
objectKeyParts = s3Writer.generateS3ObjectKey().split("/");
245208
assertEquals("%{namespace}", objectKeyParts[1]);
246209
keySuffixParts = objectKeyParts[3].split("\\.");
@@ -280,10 +243,9 @@ public void testClose() throws Exception {
280243

281244
// Verify that the buffer file was correctly handled
282245
String
283-
bufferFileName =
284-
s3Writer.sanitizeFileName(logStream.getFullPathPrefix()) + ".buffer.log";
246+
bufferFileName = s3Writer.getBufferFileName();
285247
File bufferFile = new File(FilenameUtils.concat(tempPath, bufferFileName));
286-
assertTrue(!bufferFile.exists());
248+
assertFalse(bufferFile.exists());
287249
assertEquals(0, bufferFile.length());
288250
verify(mockS3Uploader, atLeastOnce()).upload(any(S3ObjectUpload.class));
289251
}

0 commit comments

Comments
 (0)