| [ Web Proxy ] |
| Viewing: https://raw.githubusercontent.com/lmdbjava/lmdbjava/gh-267/src/main/java/org/lmdbjava/Verifier.java | [Back] [Original] |
Due to the large variety of operating systems and Java platforms typically used with LmdbJava, * this class provides a convenient verification of correct operating behavior through a potentially * long duration set of tests that carefully verify correct storage and retrieval of successively * larger database entries. * *
The verifier currently operates by incrementing a long identifier that
* deterministically maps to a given {@link Dbi} and value size. The key is simply the long
* identifier. The value commences with a CRC that includes the identifier and the random
* bytes of the value. Each entry is written out, and then the prior entry is retrieved using its
* key. The prior entry's value is evaluated for accuracy and then deleted. Transactions are
* committed in batches to ensure successive transactions correctly retrieve the results of earlier
* transactions.
*
*
Please note the verification approach may be modified in the future. * *
If an exception is raised by this class, please: * *
The caller must provide an {@link Env} configured with a suitable local storage location, * maximum DBIs equal to {@link #DBI_COUNT}, and a map size large enough to accommodate the * intended verification duration. * *
ALL EXISTING DATA IN THE DATABASE WILL BE DELETED. The caller must not interact with the
* Env in any way (eg querying, transactions etc) while the verifier is executing.
*
* @param env target that complies with the above requirements (required)
*/
public Verifier(final Env env) {
requireNonNull(env);
this.env = env;
key.order(BIG_ENDIAN);
deleteDbis();
createDbis();
}
/**
* Run the verifier until {@link #stop()} is called or an exception occurs.
*
*
Successful return of this method indicates no faults were detected. If any fault was * detected the exception message will detail the exact point that the fault was encountered. * * @return number of database rows successfully verified */ @Override public Long call() { try { while (proceed.get()) { transactionControl(); write(id); if (id > 0) { fetchAndDelete(id - 1); } id++; } } finally { if (txn != null) { txn.close(); } } return id; } /** * Execute the verifier for the given duration. * *
This provides a simple way to execute the verifier for those applications which do not wish * to manage threads directly. * * @param duration amount of time to execute * @param unit units used to express the duration * @return number of database rows successfully verified */ public long runFor(final long duration, final TimeUnit unit) { final long deadline = System.currentTimeMillis() + unit.toMillis(duration); final ExecutorService es = Executors.newSingleThreadExecutor(); final Future future = es.submit(this); try { while (System.currentTimeMillis() < deadline && !future.isDone()) { Thread.sleep(unit.toMillis(1)); } } catch (final InterruptedException ignored) { } finally { stop(); } final long result; try { result = future.get(); } catch (final InterruptedException | ExecutionException ex) { throw new IllegalStateException(ex); } finally { es.shutdown(); } return result; } private void createDbis() { for (int i = 0; i < DBI_COUNT; i++) { dbis.add(env.openDbi(Verifier.class.getSimpleName() + i, MDB_CREATE)); } } private void deleteDbis() { for (final byte[] existingDbiName : env.getDbiNames()) { final Dbi existingDbi = env.openDbi(existingDbiName); try (Txn txn = env.txnWrite()) { existingDbi.drop(txn, true); txn.commit(); } } } private void fetchAndDelete(final long forId) { final Dbi dbi = getDbi(forId); updateKey(forId); final ByteBuffer fetchedValue; try { fetchedValue = dbi.get(txn, key); } catch (final LmdbException ex) { throw new IllegalStateException("DB get id=" + forId, ex); } if (fetchedValue == null) { throw new IllegalStateException("DB not found id=" + forId); } verifyValue(forId, fetchedValue); try { dbi.delete(txn, key); } catch (final LmdbException ex) { throw new IllegalStateException("DB del id=" + forId, ex); } } private Dbi getDbi(final long forId) { return dbis.get((int) (forId % dbis.size())); } /** Request the verifier to stop execution. */ private void stop() { proceed.set(false); } private void transactionControl() { if (id % BATCH_SIZE == 0) { if (txn != null) { txn.commit(); txn.close(); } rnd.nextBytes(ba); txn = env.txnWrite(); } } private void updateKey(final long forId) { key.clear(); key.putLong(forId); key.flip(); } private void updateValue(final long forId) { final int rndSize = valueSize(forId); crc.reset(); crc.update((int) forId); crc.update(ba, CRC_LENGTH, rndSize); final long crcVal = crc.getValue(); val.clear(); val.putLong(crcVal); val.put(ba, CRC_LENGTH, rndSize); val.flip(); } private int valueSize(final long forId) { final int mod = (int) (forId % BATCH_SIZE); final int base = 1_024 * mod; final int value = base == 0 ? 512 : base; return value - CRC_LENGTH - KEY_LENGTH; // aim to minimise partial pages } private void verifyValue(final long forId, final ByteBuffer bb) { final int rndSize = valueSize(forId); final int expected = rndSize + CRC_LENGTH; if (bb.limit() != expected) { throw new IllegalStateException( "Limit error id=" + forId + " exp=" + expected + " limit=" + bb.limit()); } final long crcRead = bb.getLong(); crc.reset(); crc.update((int) forId); crc.update(bb); final long crcVal = crc.getValue(); if (crcRead != crcVal) { throw new IllegalStateException("CRC error id=" + forId); } } private void write(final long forId) { final Dbi dbi = getDbi(forId); updateKey(forId); updateValue(forId); try { dbi.put(txn, key, val); } catch (final LmdbException ex) { throw new IllegalStateException("DB put id=" + forId, ex); } } }
| Web Proxy Viewer | New URL | Original Page |