whitepaper: 383/183 — wave6 defect tables + PDF rebuild
This commit is contained in:
parent
3eebda37e1
commit
eb9612e4bf
5 changed files with 314 additions and 5 deletions
228
defects/rocksdb/unit/RocksdbLockTxnIdsAlgorithm.java
Normal file
228
defects/rocksdb/unit/RocksdbLockTxnIdsAlgorithm.java
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* rocksdb-001: LockInfo.txn_ids linear scan — O(T²) shared-lock churn.
|
||||
*
|
||||
* Models the RocksDB PointLockManager / PerKeyPointLockManager defect:
|
||||
* - Slow path: autovector<TransactionID> + std::find() -> O(T) per unlock
|
||||
* - Fast path: unordered_set<TransactionID> -> O(1) per unlock
|
||||
*
|
||||
* File: utilities/transactions/lock/point/point_lock_manager.cc
|
||||
* Lines: 791, 1513, 1706
|
||||
*/
|
||||
public class RocksdbLockTxnIdsAlgorithm {
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Slow path: List<Long> + linear indexOf — mirrors autovector + std::find
|
||||
// -----------------------------------------------------------------------
|
||||
static class SlowLockInfo {
|
||||
List<Long> txnIds = new ArrayList<>();
|
||||
|
||||
void addHolder(long txnId) {
|
||||
txnIds.add(txnId);
|
||||
}
|
||||
|
||||
/** Returns number of element comparisons performed (for counting). */
|
||||
long unlock(long txnId) {
|
||||
long ops = 0;
|
||||
for (int i = 0; i < txnIds.size(); i++) {
|
||||
ops++;
|
||||
if (txnIds.get(i) == txnId) {
|
||||
txnIds.remove(i);
|
||||
return ops;
|
||||
}
|
||||
}
|
||||
return ops; // not found
|
||||
}
|
||||
|
||||
/** Shared-lock reentrant check (AcquireLocked path). */
|
||||
long isReentrant(long txnId) {
|
||||
long ops = 0;
|
||||
for (Long id : txnIds) {
|
||||
ops++;
|
||||
if (id == txnId) return ops;
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Fast path: HashSet<Long> — mirrors unordered_set<TransactionID>
|
||||
// -----------------------------------------------------------------------
|
||||
static class FastLockInfo {
|
||||
Set<Long> txnIds = new HashSet<>();
|
||||
|
||||
void addHolder(long txnId) {
|
||||
txnIds.add(txnId);
|
||||
}
|
||||
|
||||
/** Returns 1 (O(1) hash lookup). */
|
||||
long unlock(long txnId) {
|
||||
txnIds.remove(txnId);
|
||||
return 1L;
|
||||
}
|
||||
|
||||
/** Shared-lock reentrant check — O(1). */
|
||||
long isReentrant(long txnId) {
|
||||
return txnIds.contains(txnId) ? 1L : 1L; // always 1 op
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Result holder
|
||||
// -----------------------------------------------------------------------
|
||||
static class Result {
|
||||
final String label;
|
||||
final long totalOps;
|
||||
final boolean correct;
|
||||
|
||||
Result(String label, long totalOps, boolean correct) {
|
||||
this.label = label;
|
||||
this.totalOps = totalOps;
|
||||
this.correct = correct;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test: T concurrent readers each hold shared lock on one key.
|
||||
// Unlock all T — count total comparisons.
|
||||
// -----------------------------------------------------------------------
|
||||
static Result runSlow(int T) {
|
||||
SlowLockInfo info = new SlowLockInfo();
|
||||
for (long id = 0; id < T; id++) {
|
||||
info.addHolder(id);
|
||||
}
|
||||
|
||||
long totalOps = 0;
|
||||
// Unlock each reader in reverse order (worst case for linear scan)
|
||||
for (long id = T - 1; id >= 0; id--) {
|
||||
totalOps += info.unlock(id);
|
||||
}
|
||||
|
||||
// Verify all holders removed
|
||||
boolean correct = info.txnIds.isEmpty();
|
||||
return new Result("slow", totalOps, correct);
|
||||
}
|
||||
|
||||
static Result runFast(int T) {
|
||||
FastLockInfo info = new FastLockInfo();
|
||||
for (long id = 0; id < T; id++) {
|
||||
info.addHolder(id);
|
||||
}
|
||||
|
||||
long totalOps = 0;
|
||||
for (long id = T - 1; id >= 0; id--) {
|
||||
totalOps += info.unlock(id);
|
||||
}
|
||||
|
||||
boolean correct = info.txnIds.isEmpty();
|
||||
return new Result("fast", totalOps, correct);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test: reentrant check (AcquireLocked path) with T holders
|
||||
// -----------------------------------------------------------------------
|
||||
static Result runSlowReentrant(int T) {
|
||||
SlowLockInfo info = new SlowLockInfo();
|
||||
for (long id = 0; id < T; id++) {
|
||||
info.addHolder(id);
|
||||
}
|
||||
|
||||
long totalOps = 0;
|
||||
// Check reentrant for last-added txn (worst case: scans whole list)
|
||||
for (int k = 0; k < T; k++) {
|
||||
totalOps += info.isReentrant((long)(T - 1));
|
||||
}
|
||||
|
||||
boolean correct = true; // structural only
|
||||
return new Result("slow-reentrant", totalOps, correct);
|
||||
}
|
||||
|
||||
static Result runFastReentrant(int T) {
|
||||
FastLockInfo info = new FastLockInfo();
|
||||
for (long id = 0; id < T; id++) {
|
||||
info.addHolder(id);
|
||||
}
|
||||
|
||||
long totalOps = 0;
|
||||
for (int k = 0; k < T; k++) {
|
||||
totalOps += info.isReentrant((long)(T - 1));
|
||||
}
|
||||
|
||||
boolean correct = true;
|
||||
return new Result("fast-reentrant", totalOps, correct);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main
|
||||
// -----------------------------------------------------------------------
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
int[] sizes = {10, 100, 500, 1000};
|
||||
|
||||
for (int T : sizes) {
|
||||
// --- Unlock path ---
|
||||
Result slow = runSlow(T);
|
||||
Result fast = runFast(T);
|
||||
|
||||
// Slow should be O(T^2/2) comparisons (average T/2 per unlock, T unlocks)
|
||||
long expectedSlowMin = (long) T * (T / 4); // lower bound
|
||||
long expectedSlowMax = (long) T * T; // upper bound
|
||||
long expectedFast = (long) T; // exactly T unlocks × 1 op each
|
||||
|
||||
boolean slowOk = slow.correct
|
||||
&& slow.totalOps >= expectedSlowMin
|
||||
&& slow.totalOps <= expectedSlowMax;
|
||||
boolean fastOk = fast.correct && fast.totalOps == expectedFast;
|
||||
|
||||
System.out.printf(
|
||||
"UNLOCK T=%4d slow_ops=%6d fast_ops=%4d speedup=%.1fx slow=%s fast=%s%n",
|
||||
T, slow.totalOps, fast.totalOps,
|
||||
(double) slow.totalOps / fast.totalOps,
|
||||
slowOk ? "PASS" : "FAIL",
|
||||
fastOk ? "PASS" : "FAIL"
|
||||
);
|
||||
|
||||
total += 2;
|
||||
if (slowOk) passed++;
|
||||
if (fastOk) passed++;
|
||||
|
||||
// --- Reentrant path ---
|
||||
Result slowR = runSlowReentrant(T);
|
||||
Result fastR = runFastReentrant(T);
|
||||
|
||||
long expectedSlowRMin = (long) T * (T / 4);
|
||||
long expectedSlowRMax = (long) T * T;
|
||||
long expectedFastR = (long) T;
|
||||
|
||||
boolean slowROk = slowR.correct
|
||||
&& slowR.totalOps >= expectedSlowRMin
|
||||
&& slowR.totalOps <= expectedSlowRMax;
|
||||
boolean fastROk = fastR.correct && fastR.totalOps == expectedFastR;
|
||||
|
||||
System.out.printf(
|
||||
"REENT T=%4d slow_ops=%6d fast_ops=%4d speedup=%.1fx slow=%s fast=%s%n",
|
||||
T, slowR.totalOps, fastR.totalOps,
|
||||
(double) slowR.totalOps / fastR.totalOps,
|
||||
slowROk ? "PASS" : "FAIL",
|
||||
fastROk ? "PASS" : "FAIL"
|
||||
);
|
||||
|
||||
total += 2;
|
||||
if (slowROk) passed++;
|
||||
if (fastROk) passed++;
|
||||
}
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
if (passed != total) {
|
||||
throw new AssertionError("Some tests FAILED");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue