Exemple #1
0
  public H2LogStore(String path) {
    this.logger = LogManager.getLogger(this.getClass());
    this.startIndex = new AtomicLong();
    this.nextIndex = new AtomicLong();
    this.lastEntry = new LogEntry(0, null, LogValueType.Application);
    try {
      Class.forName("org.h2.Driver");
      this.connection = DriverManager.getConnection("jdbc:h2:" + path, "sa", "");
      this.connection.setAutoCommit(false);
      boolean isNew = true;
      ResultSet tables = this.connection.createStatement().executeQuery("SHOW TABLES");
      while (tables.next()) {
        if (TABLE_NAME.equalsIgnoreCase(tables.getString(1))) {
          isNew = false;
          break;
        }
      }

      tables.close();
      if (isNew) {
        this.connection.createStatement().execute(CREATE_SEQUENCE_SQL);
        this.connection.createStatement().execute(CREATE_TABLE_SQL);
        this.connection.commit();
        this.startIndex.set(1);
        this.nextIndex.set(1);
      } else {
        ResultSet rs =
            this.connection.createStatement().executeQuery("SELECT MIN(id), MAX(id) FROM LogStore");
        if (rs.next()) {
          this.startIndex.set(rs.getLong(1));
          this.nextIndex.set(rs.getLong(2) + 1);
        } else {
          this.startIndex.set(1);
          this.nextIndex.set(1);
        }

        rs.close();
        rs =
            this.connection
                .createStatement()
                .executeQuery("SELECT TOP 1 * FROM LogStore ORDER BY id DESC");
        if (rs.next()) {
          this.lastEntry =
              new LogEntry(rs.getLong(2), rs.getBytes(4), LogValueType.fromByte(rs.getByte(3)));
        }

        rs.close();
      }
    } catch (Throwable error) {
      this.logger.error("failed to load or create log store database", error);
      throw new RuntimeException("failed to load or create a log store", error);
    }
  }
Exemple #2
0
  @Override
  public void applyLogPack(long index, byte[] logPack) {
    if (index < this.startIndex.get()) {
      throw new IllegalArgumentException("logIndex out of range");
    }

    try {
      ByteArrayInputStream memoryStream = new ByteArrayInputStream(logPack);
      GZIPInputStream gzipStream = new GZIPInputStream(memoryStream);
      byte[] sizeBuffer = new byte[Integer.BYTES];
      PreparedStatement ps = this.connection.prepareStatement(TRIM_TABLE_SQL);
      ps.setLong(1, index - 1);
      ps.execute();
      ps = this.connection.prepareStatement(UPDATE_SEQUENCE_SQL);
      ps.setLong(1, index);
      ps.execute();
      while (this.read(gzipStream, sizeBuffer)) {
        int size = BinaryUtils.bytesToInt(sizeBuffer, 0);
        byte[] entryData = new byte[size - Integer.BYTES];
        if (!this.read(gzipStream, entryData)) {
          throw new RuntimeException("bad log pack, no able to read the log entry data");
        }

        ByteBuffer buffer = ByteBuffer.wrap(entryData);
        long term = buffer.getLong();
        byte valueType = buffer.get();
        byte[] value = new byte[size - Long.BYTES - 1 - Integer.BYTES];
        buffer.get(value);
        ps = this.connection.prepareStatement(INSERT_ENTRY_SQL);
        ps.setLong(1, term);
        ps.setByte(2, valueType);
        ps.setBytes(3, value);
        ps.execute();
        this.lastEntry = new LogEntry(term, value, LogValueType.fromByte(valueType));
      }

      this.connection.commit();
      gzipStream.close();
    } catch (Throwable error) {
      this.logger.error("failed to apply log pack", error);
      throw new RuntimeException("log store error", error);
    }
  }
Exemple #3
0
  @Override
  public boolean compact(long lastLogIndex) {
    if (lastLogIndex < this.startIndex.get()) {
      throw new IllegalArgumentException("index out of range");
    }

    try {
      PreparedStatement ps = this.connection.prepareStatement(COMPACT_TABLE_SQL);
      ps.setLong(1, lastLogIndex);
      ps.execute();
      if (this.nextIndex.get() - 1 <= lastLogIndex) {
        ps = this.connection.prepareStatement(UPDATE_SEQUENCE_SQL);
        ps.setLong(1, lastLogIndex + 1);
        ps.execute();
      }

      this.connection.commit();
      this.startIndex.set(lastLogIndex + 1);
      if (this.nextIndex.get() - 1 <= lastLogIndex) {
        this.nextIndex.set(lastLogIndex + 1);
      }

      // reload last entry
      ResultSet rs =
          this.connection
              .createStatement()
              .executeQuery("SELECT TOP 1 * FROM LogStore ORDER BY id DESC");
      if (rs.next()) {
        this.lastEntry =
            new LogEntry(rs.getLong(2), rs.getBytes(4), LogValueType.fromByte(rs.getByte(3)));
      } else {
        this.lastEntry = new LogEntry(0, null, LogValueType.Application);
      }

      rs.close();
      return true;
    } catch (Throwable error) {
      this.logger.error("failed to compact the log store", error);
      throw new RuntimeException("log store error", error);
    }
  }
Exemple #4
0
  @Override
  public LogEntry getLogEntryAt(long index) {
    if (index < this.startIndex.get()) {
      throw new IllegalArgumentException("index out of range");
    }

    try {
      PreparedStatement ps = this.connection.prepareStatement(SELECT_ENTRY_SQL);
      ps.setLong(1, index);
      ResultSet rs = ps.executeQuery();
      while (rs.next()) {
        return new LogEntry(rs.getLong(2), rs.getBytes(4), LogValueType.fromByte(rs.getByte(3)));
      }

      rs.close();
      return null;
    } catch (Throwable error) {
      this.logger.error("failed to retrieve an entry at a specific index", error);
      throw new RuntimeException("log store error", error);
    }
  }
Exemple #5
0
  @Override
  public LogEntry[] getLogEntries(long start, long end) {
    if (start > end || start < this.startIndex.get()) {
      throw new IllegalArgumentException("index out of range");
    }

    try {
      PreparedStatement ps = this.connection.prepareStatement(SELECT_RANGE_SQL);
      ps.setLong(1, start);
      ps.setLong(2, end);
      ResultSet rs = ps.executeQuery();
      List<LogEntry> entries = new ArrayList<LogEntry>();
      while (rs.next()) {
        entries.add(
            new LogEntry(rs.getLong(2), rs.getBytes(4), LogValueType.fromByte(rs.getByte(3))));
      }

      rs.close();
      return entries.toArray(new LogEntry[0]);
    } catch (Throwable error) {
      this.logger.error("failed to retrieve a range of entries", error);
      throw new RuntimeException("log store error", error);
    }
  }