Cajitas que piensan

Segmented log

Basado en los apuntes de Unmesh Joshi

Imaginemos que nuestro sistema cayo, levante y quiere cargar en memoria el WAL, pero el archivo pesa 15 gb, ahora pasa a ser nuestro cuello de botella.

Deriamos poder ir borrando las operaciones ya confirmadas en disco, por lo cual empieza a tener sentido manejar N archivos de WAL, creando uno nuevo cuando un archivo supera cierto tamaño, por lo cual empieza a ser necesario tener ids únicos para cada archivo y además que tengan cierto orden.

Asumiendo que en cada log entry guardamos

 private final Long entryId;
 private final byte[] data;
 private final EntryType entryType;
 private long timeStamp;

Al querer escribir en el WAL chequearíamos si hay o no que crear otro archivo.

public Long writeEntry(WALEntry entry) {
    maybeRoll();
    return openSegment.writeEntry(entry);
}

private void maybeRoll() {
    if (openSegment.
            size() >= config.getMaxLogSize()) {
        openSegment.flush();
        sortedSavedSegments.add(openSegment);
        long lastId = openSegment.getLastLogEntryId();
        openSegment = WALSegment.open(lastId, config.getWalDir());
    }
}

public static String createFileName(Long startIndex) {
    return logPrefix + "_" + startIndex + logSuffix;
}

public static Long getBaseOffsetFromFileName(String fileName) {
    String[] nameAndSuffix = fileName.split(logSuffix);
    String[] prefixAndOffset = nameAndSuffix[0].split("_");
    if (prefixAndOffset[0].equals(logPrefix))
        return Long.parseLong(prefixAndOffset[1]);

    return -1l;
}

Con logs segmentados, podríamos ir leyendo en 2 pasos, sabiendo que fue lo ultimo que se guardo en disco, al levantar leemos todo a partir de ahi:

public List<WALEntry> readFrom(Long startIndex) {
    List<WALSegment> segments = getAllSegmentsContainingLogGreaterThan(startIndex);
    return readWalEntriesFrom(startIndex, segments);
}

private List<WALSegment> getAllSegmentsContainingLogGreaterThan(Long startIndex) {
    List<WALSegment> segments = new ArrayList<>();
    //Start from the last segment to the first segment with starting offset less than startIndex
    //This will get all the segments which have log entries more than the startIndex
    for (int i = sortedSavedSegments.size() - 1; i >= 0; i--) {
        WALSegment walSegment = sortedSavedSegments.get(i);
        segments.add(walSegment);

        if (walSegment.getBaseOffset() <= startIndex) {
            break; // break for the first segment with baseoffset less than startIndex
        }
    }

    if (openSegment.getBaseOffset() <= startIndex) {
        segments.add(openSegment);
    }

    return segments;
}