Skip to content
This repository has been archived by the owner on Aug 23, 2020. It is now read-only.

Transaction pinning: #1599 #1632

Draft
wants to merge 31 commits into
base: dev
Choose a base branch
from
Draft

Conversation

ovanwijk
Copy link
Contributor

@ovanwijk ovanwijk commented Sep 30, 2019

Description

Allows IOTA Nodes to use a separate data storage in order to pinTransactions through the webapi.
A pinned transaction is a transaction that will not be deleted during snapshotting. In order to not interfere with the current implementation of the persistence layer and the snapshotting code this solution uses a separate database file to store permanently stored transactions.

It allows for arbitrary transaction storages through 'pinTransactionsTrytes' and it allows for copying transactions by 'pinTransactionHashes'.

Unpinning is also possible. This will remove the transactions from the permanent storage provider.

'isPinned' as api is introduced to check if transactions are pinned or not.

Pinned transactions can be accessed through the normal api calls like 'findTransactions' and 'getTrytes'.

Example: in order to pin all transactions from an address just call findTransactions(address) and then use the result in the 'pinTransactionHashes'.

The RocksDBPPPimpl.java code also offers inplace replacement code for updating indexes for deleting data. This could be used 1:1 for solving #1307

Fixes # (issue)
#1599

Type of change

  • Enhancement (a non-breaking change which adds functionality)

How Has This Been Tested?

Adds a set of tests that use the database file to save, delete and retrieve data.

Checklist:

  • My code follows the style guidelines for this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

…s based on the current TxModel and rocksdb persistence layer.
- Introduced an interface for permanent storage.
- Added a permanent storage PersitenceProvider
- Added mergeTwo() function to genericly handle class independant merging.
- Changed Tangle.load() to handle multiple persistence providers and handle with mergable results (like searching addresses in two persistence providers)
- Tangle.load() didn't handle this because persistenceProvider.get never returned null, only a new (empty) instance of the requested object. The bytes() wasnt uniform in implementation and would for example throw an exception on Milestone, the MilestoneViewModel did the null check. Therefore the introduction of isEmpty() for Persistables.
- Added functions to the tangle to handle permanent storage
- Added API's:
   * pinTransactionHashes (hashes:[])
   * pinTransactionTrytes (trytes:[])
   * increaseTransactionCount (hashes:[])
   * decreaseTransactionCount (hashes:[])
   * getTransactionsCount (hashes:[])

 - or others to test this branch: set default settings for permanent storage
# Conflicts:
#	src/main/java/com/iota/iri/Iota.java
#	src/main/java/com/iota/iri/model/StateDiff.java
#	src/main/java/com/iota/iri/model/persistables/Hashes.java
#	src/main/java/com/iota/iri/model/persistables/Milestone.java
#	src/main/java/com/iota/iri/model/persistables/Transaction.java
#	src/main/java/com/iota/iri/storage/Persistable.java
#	src/main/java/com/iota/iri/storage/Tangle.java
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Sep 30, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
@iotaledger iotaledger deleted a comment Oct 1, 2019
Copy link
Contributor

@GalRogozinski GalRogozinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an initial review.
Overall it looks good :-)

After you fix my few nits I will take another look.
Talk to me in private if you are being blocked

break;
}
default: {
throw new NotImplementedException("No such database type.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to use UnsupportedOperationException, just because it doesn't have a dependency on a 3rd party library

src/main/java/com/iota/iri/service/API.java Outdated Show resolved Hide resolved
src/main/java/com/iota/iri/service/API.java Outdated Show resolved Hide resolved
src/main/java/com/iota/iri/service/API.java Outdated Show resolved Hide resolved
src/main/java/com/iota/iri/service/API.java Outdated Show resolved Hide resolved
Copy link
Contributor

@GalRogozinski GalRogozinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked at it a bit more now
The design change I am asking for is rather important from my POV because it will save us work later I believe

if (dbResult != null && dbResult.length > 0) {
return new TransactionViewModel(TransactionViewModel.trits(dbResult), Hash.NULL_HASH);
}
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, we always return a new empty transaction object, which is obviously bad.
In #1591 we said we will return an instance of an immutable static dummy object instead. I prefer that you do that.

If you prefer to return null, please explain why and make sure the code is impervious to null pointer exceptions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making an empty static object would be fine with me as well but I think that is outside of the scope for this pull request. the function getTransaction is part of the PermanentPersistenceProvider and not of the PersistenceProvider.

I chose null because it is more explicit than an empty object. In the get function of the PersistenceProvider interface this is explicitly handled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a possible way to go about it.

However, the reason I preferred to go about an empty object is because then you have to remember to always do null checks. If you forget (like in safeDeleteTransaction :-P) you later have to go back and add it...

The thing is that probably the PermanentPersistenceProvider and PersistenceProvider will have to be similar in this sense eventually, so might as well get it right from the beginning :-)

I only asked you to add this empty object in PermanentPersistenceProvider and not in the old code. If you feel strongly against this then just know:

  1. You will have to add lots of null checks ;-)
  2. It will just make things messier later

@VisibleForTesting
void safeDeleteTransaction(WriteBatch writeBatch, Hash key) throws Exception {
byte[] keyBytes = key.bytes();
TransactionViewModel tx = getTransaction(key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if this returns null?
See my other comment and #1591

void addToIndex(WriteBatch writeBatch, ColumnFamilyHandle column, Indexable key, Indexable indexValue)
throws Exception {
byte[] indexBytes = indexValue.bytes();
byte[] dbResult = db.get(column, key.bytes());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be better to use the exists() method that checks mayExists first?

/***
* Implements the retrieval functions of a the normal persistence provider and the permanent persistence provider
*/
public class RocksDBPPPImpl implements PermanentPersistenceProvider, PersistenceProvider {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now having a deeper look here, I would like to offer a different design :-)

Currently, it is possible with the current design it is theoretically possible to implement a new PersistanceProvider that will replace RocksDb. You have actually attempted to do it yourself :-)

Here you tightly coupled RocksDb with the PermenantPersistanceProvider...
This means that if we ever want to replace RocksDB it will be much harder (there are actually thoughts about doing this now - #1661).

Also I have noticed lots of evil copy paste... Here we are believers in DRY (don't repeat yourself).

So maybe you can create PermenantPersistanceProvider that contains a RocksDbPersistanceProvider?
This way if we write a new PersistnaceProvider, switching will be a breeze?

It will also adhere better to the single responsibility principle that we have a class that only deals with wrapping the RocksDb api and a class that deals with the logic of your task :-)

# Conflicts:
#	src/main/java/com/iota/iri/Iota.java
@GalRogozinski GalRogozinski mentioned this pull request Nov 18, 2019
1 task
@GalRogozinski
Copy link
Contributor

Hey @ovanwijk, you want another review?
I think I have comments you didn't tend to, which is why this is being ignored

# Conflicts:
#	src/main/java/com/iota/iri/Iota.java
return found;
}

private void initDB(String path, String logPath) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants