private void putToCache(StateRecord r) { if (useCache) { synchronized (cachedRecords) { cachedRecords.put(r.getId(), new WeakReference<StateRecord>(r)); } synchronized (cachedRecordsById) { cachedRecordsById.put(r.getRecordId(), new WeakReference<StateRecord>(r)); } } }
@Override public void destroy(StateRecord record) { long recordId = record.getRecordId(); if (recordId == 0) { throw new IllegalStateException("can't destroy record without recordId"); } protect(() -> { synchronized (writeLock) { db.update("DELETE FROM ledger WHERE id = ?", recordId); } synchronized (cachedRecords) { cachedRecords.remove(record.getId()); } return null; }); }
@Override public Approvable getItem(final StateRecord record) { return protect(() -> { try (ResultSet rs = inPool(db -> db.queryRow("select * from items where id = ?", record.getRecordId()))) { if (rs == null) return null; return Contract.fromPackedTransaction(rs.getBytes("packed")); } catch (Exception e) { e.printStackTrace(); throw e; } }); }
@Override public void destroy(StateRecord record) { long recordId = record.getRecordId(); if (recordId == 0) { throw new IllegalStateException("can't destroy record without recordId"); } protect(() -> { inPool(d -> { d.update("DELETE FROM items WHERE id = ?", recordId); d.update("DELETE FROM ledger WHERE id = ?", recordId); return null; }); synchronized (cachedRecords) { cachedRecords.remove(record.getId()); } synchronized (cachedRecordsById) { cachedRecordsById.remove(record.getRecordId()); } return null; }); }
@Override public void putItem(StateRecord record, Approvable item, Instant keepTill) { if (item instanceof Contract) { try (PooledDb db = dbPool.db()) { try ( PreparedStatement statement = db.statement( "insert into items(id,packed,keepTill) values(?,?,?);" ) ) { statement.setLong(1, record.getRecordId()); statement.setBytes(2, ((Contract) item).getPackedTransaction()); statement.setLong(3, keepTill.getEpochSecond()); db.updateWithStatement(statement); } catch (Exception e) { e.printStackTrace(); throw e; } } catch (SQLException se) { se.printStackTrace(); throw new Failure("item save failed:" + se); } catch (Exception e) { e.printStackTrace(); } } }
@Override public void putKeepingItem(StateRecord record, Approvable item) { if (item instanceof Contract) { try (PooledDb db = dbPool.db()) { try ( PreparedStatement statement = db.statement( "insert into keeping_items (id,hash,origin,packed) values(?,?,?,?);" ) ) { if (record != null) statement.setLong(1, record.getRecordId()); else statement.setNull(1, Types.INTEGER); statement.setBytes(2, ((Contract) item).getId().getDigest()); statement.setBytes(3, ((Contract) item).getOrigin().getDigest()); statement.setBytes(4, ((Contract) item).getPackedTransaction()); db.updateWithStatement(statement); } catch (Exception e) { e.printStackTrace(); throw e; } } catch (SQLException se) { se.printStackTrace(); throw new Failure("keeping item save failed:" + se); } catch (Exception e) { e.printStackTrace(); } } }
@Override public String toString() { return "State<"+getId()+"/"+getRecordId()+":"+getState()+":"+getCreatedAt()+"/"+getExpiresAt()+">"; } }
if (stateRecord.getRecordId() == 0) { try ( PreparedStatement statement = StateRecord.unixTime(stateRecord.getExpiresAt()), stateRecord.getLockedByRecordId(), stateRecord.getRecordId() );
if (stateRecord.getRecordId() == 0) { try ( PreparedStatement statement = StateRecord.unixTime(stateRecord.getExpiresAt()), stateRecord.getLockedByRecordId(), stateRecord.getRecordId() );
protected void assertSameRecords(StateRecord r, StateRecord r1) { assertEquals(r.getId(), r1.getId()); assertEquals(r.getState(), r1.getState()); assertAlmostSame(r.getCreatedAt(), r1.getCreatedAt()); assertEquals(r.getRecordId(), r1.getRecordId()); assertEquals(r.getLockedByRecordId(), r1.getLockedByRecordId()); }
for (int i = 0; i < subContracts.size(); i++) { StateRecord sr = n.getLedger().getRecord(subContracts.get(i).getId()); sr.setLockedByRecordId(sr.getRecordId()); sr.save();
for (int i = 0; i < subContracts.size(); i++) { StateRecord sr = n.getLedger().getRecord(subContracts.get(i).getId()); sr.setLockedByRecordId(sr.getRecordId()); sr.save();
@Test public void checkLockOwner() throws Exception { ledger.enableCache(true); StateRecord existing = ledger.findOrCreate(HashId.createRandom()); existing.approve(); StateRecord r = ledger.findOrCreate(HashId.createRandom()); StateRecord r1 = r.lockToRevoke(existing.getId()); existing.reload(); r.reload(); assertSameRecords(existing, r1); assertEquals(ItemState.LOCKED, existing.getState()); assertEquals(r.getRecordId(), existing.getLockedByRecordId()); StateRecord currentOwner = ledger.getLockOwnerOf(existing); System.out.println("existing: " + existing.getId()); System.out.println("locker: " + r.getId()); System.out.println("locked: " + r1.getId()); System.out.println("currentOwner: " + currentOwner.getId()); assertSameRecords(r, currentOwner); }
@Test public void recordExpiration() throws Exception { // todo: expired can't be get - it should be dropped by the database HashId hashId = HashId.createRandom(); StateRecord r = ledger.findOrCreate(hashId); assertNotNull(r.getExpiresAt()); assert(r.getExpiresAt().isAfter(ZonedDateTime.now())); long recordId = r.getRecordId(); ZonedDateTime inFuture = ZonedDateTime.now().plusHours(2); r.setExpiresAt(inFuture); StateRecord r1 = ledger.getRecord(hashId); assertNotEquals(r1.getExpiresAt(), inFuture); r.save(); r1 = ledger.getRecord(hashId); assertAlmostSame(r.getExpiresAt(), r1.getExpiresAt()); r.setExpiresAt(ZonedDateTime.now().minusHours(1)); r.save(); r1 = ledger.getRecord(hashId); assertNull(r1); }
@Test public void recordExpiration() throws Exception { // todo: expired can't be get - it should be dropped by the database HashId hashId = HashId.createRandom(); StateRecord r = ledger.findOrCreate(hashId); long recordId = r.getRecordId(); ZonedDateTime inFuture = ZonedDateTime.now().plusHours(2); r.setExpiresAt(inFuture); StateRecord r1 = ledger.getRecord(hashId); assertNotEquals(r1.getExpiresAt(), inFuture); r.save(); r1 = ledger.getRecord(hashId); assertAlmostSame(r.getExpiresAt(), r1.getExpiresAt()); r.setExpiresAt(ZonedDateTime.now().minusHours(1)); r.save(); r1 = ledger.getRecord(hashId); assertNull(r1); }
originRecord.setLockedByRecordId(newRevisionRecord.getRecordId()); newContractRecord.setState(ItemState.LOCKED_FOR_CREATION); newContractRecord.setLockedByRecordId(newRevisionRecord.getRecordId()); newRevisionRecord.setState(finalI < N/2 ? ItemState.PENDING_POSITIVE : ItemState.PENDING_NEGATIVE);
@Test public void createOutputLockRecord() throws Exception { ledger.enableCache(true); StateRecord owner = ledger.findOrCreate(HashId.createRandom()); StateRecord other = ledger.findOrCreate(HashId.createRandom()); HashId id = HashId.createRandom(); StateRecord r1 = owner.createOutputLockRecord(id); r1.reload(); assertEquals(id, r1.getId()); assertEquals(ItemState.LOCKED_FOR_CREATION, r1.getState()); assertEquals(owner.getRecordId(), r1.getLockedByRecordId()); StateRecord r2 = owner.createOutputLockRecord(id); assertSame(r2, r1); assertNull(owner.createOutputLockRecord(other.getId())); // And hacked low level operation must fail too assertNull(ledger.createOutputLockRecord(owner.getRecordId(), other.getId())); }
@Test public void createOutputLockRecord() throws Exception { ledger.enableCache(true); StateRecord owner = ledger.findOrCreate(HashId.createRandom()); StateRecord other = ledger.findOrCreate(HashId.createRandom()); HashId id = HashId.createRandom(); StateRecord r1 = owner.createOutputLockRecord(id); r1.reload(); assertEquals(id, r1.getId()); assertEquals(ItemState.LOCKED_FOR_CREATION, r1.getState()); assertEquals(owner.getRecordId(), r1.getLockedByRecordId()); StateRecord r2 = owner.createOutputLockRecord(id); assertSame(r2, r1); assertNull(owner.createOutputLockRecord(other.getId())); // And hacked low level operation must fail too assertNull(ledger.createOutputLockRecord(owner.getRecordId(), other.getId())); }
@Test public void lockForRevoking() throws Exception { ledger.enableCache(true); StateRecord existing = ledger.findOrCreate(HashId.createRandom()); existing.approve(); StateRecord existing2 = ledger.findOrCreate(HashId.createRandom()); existing2.approve(); StateRecord r = ledger.findOrCreate(HashId.createRandom()); StateRecord r1 = r.lockToRevoke(existing.getId()); existing.reload(); r.reload(); assertSameRecords(existing, r1); assertEquals(ItemState.LOCKED, existing.getState()); assertEquals(r.getRecordId(), existing.getLockedByRecordId()); StateRecord r2 = r.lockToRevoke(existing.getId()); existing.reload(); r.reload(); assertSameRecords(existing, r1); assertSameRecords(existing, r2); assertSame(r1, r2); assertEquals(ItemState.LOCKED, existing.getState()); assertEquals(r.getRecordId(), existing.getLockedByRecordId()); StateRecord r3 = r.lockToRevoke(existing2.getId()); existing2.reload(); assertSameRecords(existing2, r3); assertEquals(ItemState.LOCKED, existing2.getState()); assertEquals(r.getRecordId(), existing2.getLockedByRecordId()); }
@Test public void lockForRevoking() throws Exception { ledger.enableCache(true); StateRecord existing = ledger.findOrCreate(HashId.createRandom()); existing.approve(); StateRecord existing2 = ledger.findOrCreate(HashId.createRandom()); existing2.approve(); StateRecord r = ledger.findOrCreate(HashId.createRandom()); StateRecord r1 = r.lockToRevoke(existing.getId()); existing.reload(); r.reload(); assertSameRecords(existing, r1); assertEquals(ItemState.LOCKED, existing.getState()); assertEquals(r.getRecordId(), existing.getLockedByRecordId()); // we lock again the same record, everything should be still ok: StateRecord r2 = r.lockToRevoke(existing.getId()); assertNotNull(r2); existing.reload(); r.reload(); assertSameRecords(existing, r1); assertSameRecords(existing, r2); assertSame(r1, r2); assertEquals(ItemState.LOCKED, existing.getState()); assertEquals(r.getRecordId(), existing.getLockedByRecordId()); StateRecord r3 = r.lockToRevoke(existing2.getId()); existing2.reload(); assertSameRecords(existing2, r3); assertEquals(ItemState.LOCKED, existing2.getState()); assertEquals(r.getRecordId(), existing2.getLockedByRecordId()); }