-
Notifications
You must be signed in to change notification settings - Fork 370
Fix: TransactionValidator instance based / TransactionRequester fixed #914
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,8 +23,6 @@ public class TransactionRequester { | |
|
||
public static final int MAX_TX_REQ_QUEUE_SIZE = 10000; | ||
|
||
private static volatile long lastTime = System.currentTimeMillis(); | ||
|
||
private static double P_REMOVE_REQUEST; | ||
private static boolean initialized = false; | ||
private final SecureRandom random = new SecureRandom(); | ||
|
@@ -84,48 +82,49 @@ private boolean transactionsToRequestIsFull() { | |
|
||
|
||
public Hash transactionToRequest(boolean milestone) throws Exception { | ||
final long beginningTime = System.currentTimeMillis(); | ||
// determine which set of transactions to operate on | ||
Set<Hash> primarySet = milestone ? milestoneTransactionsToRequest : transactionsToRequest; | ||
Set<Hash> alternativeSet = milestone ? transactionsToRequest : milestoneTransactionsToRequest; | ||
Set<Hash> requestSet = primarySet.size() == 0 ? alternativeSet : primarySet; | ||
|
||
// determine the first hash in our set that needs to be processed | ||
Hash hash = null; | ||
Set<Hash> requestSet; | ||
if(milestone) { | ||
requestSet = milestoneTransactionsToRequest; | ||
if(requestSet.size() == 0) { | ||
requestSet = transactionsToRequest; | ||
} | ||
} else { | ||
requestSet = transactionsToRequest; | ||
if(requestSet.size() == 0) { | ||
requestSet = milestoneTransactionsToRequest; | ||
} | ||
} | ||
synchronized (syncObj) { | ||
// repeat while we have transactions that shall be requested | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For next time instead of adding comments you can just refactor the code to be more readable. |
||
while (requestSet.size() != 0) { | ||
// remove the first item in our set for further examination | ||
Iterator<Hash> iterator = requestSet.iterator(); | ||
hash = iterator.next(); | ||
iterator.remove(); | ||
|
||
// if we have received the transaction in the mean time .... | ||
if (TransactionViewModel.exists(tangle, hash)) { | ||
// ... dump a log message ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is completely extraneous. |
||
log.info("Removed existing tx from request list: " + hash); | ||
messageQ.publish("rtl %s", hash); | ||
} else { | ||
if (!transactionsToRequestIsFull()) { | ||
requestSet.add(hash); | ||
} | ||
break; | ||
|
||
// ... and continue to the next element in the set | ||
continue; | ||
} | ||
|
||
// ... otherwise -> re-add it at the end of the set ... | ||
// | ||
// Note: we always have enough space since we removed the element before | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you positive about that? Anyhows, now you introduced a logic change that should be tested... I personally want to refactor every single class here but I want to do it correctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am positive about that! In line 71 (requestTransaction) we DO check if !transactionsToRequestIsFull() if it is a normal transaction. the request set for milestoneTransactions is not limited in size, which makes sense since all of the milestones are relevant for our node. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
requestSet.add(hash); | ||
|
||
// ... and abort our loop to continue processing with the element we found | ||
break; | ||
} | ||
} | ||
|
||
// randomly drop "non-milestone" transactions so we don't keep on asking for non-existent transactions forever | ||
if(random.nextDouble() < P_REMOVE_REQUEST && !requestSet.equals(milestoneTransactionsToRequest)) { | ||
synchronized (syncObj) { | ||
transactionsToRequest.remove(hash); | ||
} | ||
} | ||
|
||
long now = System.currentTimeMillis(); | ||
if ((now - lastTime) > 10000L) { | ||
lastTime = now; | ||
//log.info("Transactions to request = {}", numberOfTransactionsToRequest() + " / " + TransactionViewModel.getNumberOfStoredTransactions() + " (" + (now - beginningTime) + " ms ). " ); | ||
} | ||
// return our result | ||
return hash; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with removing
static
on allpublic
methods because you don't want to have a global scopeTransactionValidator
logic. However on private methods I do prefer to keep thestatic
modifier.This is because of 2 reasons:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Atm we use this method to filter old transactions that are still being gossiped about in the network. Since the cut off time is the timestamp of the global snapshot that is not really a big issue in the current environment since it is quite unlikely that somebody has a wrong time set in his node that dates back before the global snapshot.
When we introduce local snapshots and consequently local cutoff times it can however happen (and it does), that we face a transaction with an invalid timestamp that dates back before our cutoff time, even tho it was issued after.
To still be able to consider this transaction valid if we need it for solidification we have to check if the transaction was part of the TransactionRequester. And to be able to access the transactionRequester instance - this method has to be non static.
Regarding 2: I don't think that these kind of micro-optimizations play any role at all - especially if they help us solve a problem that otherwise would break the synced state of a node.
see: iotadevelopment@40df07b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
If you need access to the object than there is no choice.
I still like making private methods static if they don't need the instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though I am fine with this change, note that the fact that the
TransactionValidator
has aTransactionRequester
instance is a violation of the Single Responsibility Principle, and in my opinion it shouldn't be there.TransactionValidator
should ultimately become a stateless Service.Of course that it is not your fault that IRI is this way and definitely you should be more concerned about getting local snapshots done rather than fixing IRI architecture.
I just wanted to merge now and I looked at the code again and had to share this thought.