Skip to content
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

Attempt to fix bad registry errors, fixes #432 and #433 #439

Merged
merged 1 commit into from Jun 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 39 additions & 9 deletions common/src/main/java/org/mvndaemon/mvnd/common/DaemonRegistry.java
Expand Up @@ -16,9 +16,9 @@

package org.mvndaemon.mvnd.common;

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
Expand Down Expand Up @@ -139,6 +139,10 @@ public void storeStopEvent(final DaemonStopEvent stopEvent) {

public List<DaemonStopEvent> getStopEvents() {
read();
return doGetDaemonStopEvents();
}

protected List<DaemonStopEvent> doGetDaemonStopEvents() {
return new ArrayList<>(stopEvents);
}

Expand Down Expand Up @@ -242,12 +246,28 @@ private void doUpdate(Runnable updater) {
return;
} catch (IOException e) {
throw new RuntimeException("Could not lock offset 0 of " + registryFile);
} catch (IllegalStateException | ArrayIndexOutOfBoundsException e) {
String absPath = registryFile.toAbsolutePath().normalize().toString();
LOGGER.warn("Invalid daemon registry info, " +
"trying to recover from this issue. " +
"If you keep getting this warning, " +
"try deleting the `registry.bin` file at [" + absPath + "]", e);
this.reset();
return;
}
}
throw new RuntimeException("Could not lock " + registryFile + " within " + LOCK_TIMEOUT_MS + " ms");
}
}

private void reset() {
infosMap.clear();
stopEvents.clear();
BufferCaster.cast(buffer).clear();
buffer.putInt(0); // reset daemon count
buffer.putInt(0); // reset stop event count
}

private static final int PROCESS_ID = getProcessId0();

private static int getProcessId0() {
Expand All @@ -271,7 +291,7 @@ private static int getProcessId0() {
}
}

private String readString() {
protected String readString() {
int sz = buffer.getShort();
if (sz == -1) {
return null;
Expand All @@ -284,16 +304,26 @@ private String readString() {
return new String(buf, StandardCharsets.UTF_8);
}

private void writeString(String str) {
protected void writeString(String str) {
if (str == null) {
buffer.putShort((short) -1);
} else if (str.length() > 1024) {
throw new IllegalStateException("String too long: " + str);
} else {
byte[] buf = str.getBytes(StandardCharsets.UTF_8);
buffer.putShort((short) buf.length);
buffer.put(buf);
return;
}
byte[] buf = str.getBytes(StandardCharsets.UTF_8);
if (buf.length > 1024) {
LOGGER.warn("Attempting to write string longer than 1024 bytes: '{}'. Please raise an issue.", str);
str = str.substring(0, 1033);
while (buf.length > 1024) {
str = str.substring(0, str.length() - 12) + "…";
buf = str.getBytes(StandardCharsets.UTF_8);
}
}
buffer.putShort((short) buf.length);
buffer.put(buf);
}

protected ByteBuffer buffer() {
return buffer;
}

public String toString() {
Expand Down
Expand Up @@ -17,6 +17,8 @@

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Locale;
Expand Down Expand Up @@ -50,7 +52,53 @@ public void testReadWrite() throws IOException {
assertNotNull(reg2.getAll());
assertEquals(1, reg2.getAll().size());
}
}

@Test
public void testRecovery() throws IOException {
Path temp = File.createTempFile("reg", ".data").toPath();
temp.toFile().deleteOnExit();
try (TestDaemonRegistry reg1 = new TestDaemonRegistry(temp)) {
// first store daemon
byte[] token = new byte[16];
new Random().nextBytes(token);
reg1.store(new DaemonInfo("12345678", "/java/home/",
"/data/reg/", 0x12345678, 7502, token,
Locale.getDefault().toLanguageTag(), Arrays.asList("-Xmx"),
DaemonState.Idle, System.currentTimeMillis(), System.currentTimeMillis()));
assertEquals(1, reg1.getAll().size());
// store an invalid event to trigger recovery
StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1024; i++) {
sb.append('…');
}
reg1.storeStopEvent(new DaemonStopEvent("11111",
System.currentTimeMillis(),
DaemonExpirationStatus.QUIET_EXPIRE,
sb.toString()));
assertEquals(1, reg1.doGetDaemonStopEvents().size());
// check if registry is reset
assertEquals(0, reg1.getAll().size());
assertEquals(0, reg1.doGetDaemonStopEvents().size());
}
}

static class TestDaemonRegistry extends DaemonRegistry {
public TestDaemonRegistry(Path registryFile) {
super(registryFile);
}

@Override
protected void writeString(String str) {
ByteBuffer buffer = buffer();
if (str == null) {
buffer.putShort((short) -1);
} else {
byte[] buf = str.getBytes(StandardCharsets.UTF_8);
buffer.putShort((short) buf.length);
buffer.put(buf);
}
}
}

}