-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change DataCache.Add() to never allow overwriting existing values #1177
Changes from 6 commits
6ee22a8
11bd6a7
be31a2a
20c6e40
29cebaf
115372a
34a8ae0
105f7e9
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 |
---|---|---|
|
@@ -47,19 +47,19 @@ private bool CheckValidators(ApplicationEngine engine) | |
internal override bool Initialize(ApplicationEngine engine) | ||
{ | ||
if (!base.Initialize(engine)) return false; | ||
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxBlockSize), new StorageItem | ||
engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize), () => new StorageItem | ||
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 should be the same, if we already know that the item doesn't exists, why we need to call |
||
{ | ||
Value = BitConverter.GetBytes(1024u * 256u) | ||
}); | ||
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxTransactionsPerBlock), new StorageItem | ||
engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock), () => new StorageItem | ||
{ | ||
Value = BitConverter.GetBytes(512u) | ||
}); | ||
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem | ||
engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_FeePerByte), () => new StorageItem | ||
{ | ||
Value = BitConverter.GetBytes(1000L) | ||
}); | ||
engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_BlockedAccounts), new StorageItem | ||
engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_BlockedAccounts), () => new StorageItem | ||
{ | ||
Value = new UInt160[0].ToByteArray() | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -487,8 +487,14 @@ public void TestUpdatePoolForBlockPersisted() | |
var key2 = CreateStorageKey(Prefix_FeePerByte); | ||
key1.ScriptHash = NativeContract.Policy.Hash; | ||
key2.ScriptHash = NativeContract.Policy.Hash; | ||
snapshot.Storages.Add(key1, item1); | ||
snapshot.Storages.Add(key2, item2); | ||
snapshot.Storages.GetAndChange(key1, () => new StorageItem | ||
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. @ixje, this change is expected, right? Since the value was previously existing. 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. @vncoelho I don't remember, but it is most likely caused by the TestBlockchain isolation problem. I think the best way forward is to have that fixed first, reset all changes to the unit tests and try again. Some of the changes (like here) have likely been applied because of the isolation problem and might not be needed anymore once that is fixed 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. AHeauehuaea, @ixje, this change was done by me, please check the last commits. The question was to be sure this was consistent. All UTs were fixed, the PR is kind of ready to merge. |
||
{ | ||
Value = item1.Value | ||
}); | ||
snapshot.Storages.GetAndChange(key2, () => new StorageItem | ||
{ | ||
Value = item2.Value | ||
}); | ||
|
||
var tx1 = CreateTransaction(); | ||
var tx2 = CreateTransaction(); | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,6 +10,10 @@ | |||||||||||
using Neo.VM; | ||||||||||||
using System.Linq; | ||||||||||||
|
||||||||||||
|
||||||||||||
using Neo.Cryptography; | ||||||||||||
using System; | ||||||||||||
|
||||||||||||
namespace Neo.UnitTests.SmartContract.Native | ||||||||||||
{ | ||||||||||||
[TestClass] | ||||||||||||
|
@@ -231,21 +235,6 @@ public void TestCheckPolicy() | |||||||||||
{ | ||||||||||||
Transaction tx = Blockchain.GenesisBlock.Transactions[0]; | ||||||||||||
var snapshot = Blockchain.Singleton.GetSnapshot(); | ||||||||||||
|
||||||||||||
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. @erikzhang @eryeer @shargon, double check this because perhaps this test was wrong. 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. How about change it like this. [TestMethod]
public void TestCheckPolicy()
{
Transaction tx = Blockchain.GenesisBlock.Transactions[0];
var snapshot = Blockchain.Singleton.GetSnapshot();
StorageKey storageKey = new StorageKey
{
ScriptHash = NativeContract.Policy.Hash,
Key = new byte[sizeof(byte)]
};
storageKey.Key[0] = 15;
var item = snapshot.Storages.GetAndChange(storageKey);
item.Value = new UInt160[] { tx.Sender }.ToByteArray();
NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeFalse();
snapshot = Blockchain.Singleton.GetSnapshot();
NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeTrue();
} 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 tried, it says 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. Using this code, the test will pass. We need to change the return value of 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. Anhhh, I see, but it would be better to DeleteKey, no? 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.
Because it is private, we can only use the 15 instead of Prefix_BlockedAccounts
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 partially understood! Thanks for the explanation, @eryeer. However, I still need to figure out this part:
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. 😄 Here is the initalization. neo/src/neo/SmartContract/Native/PolicyContract.cs Lines 62 to 65 in 0f75741
So whenever we get the BlockedAccounts from Internal storage, the value will be new UInt160[0].ToByteArray() by default. We need to change the defult value instead of create a new one to add to storage.
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 think that I understood. This would also work: [TestMethod]
public void TestCheckPolicy()
{
Transaction tx = Blockchain.GenesisBlock.Transactions[0];
var snapshot = Blockchain.Singleton.GetSnapshot();
StorageKey storageKey = new StorageKey
{
ScriptHash = NativeContract.Policy.Hash,
Key = new byte[sizeof(byte)]
};
storageKey.Key[0] = 15;
//var item = snapshot.Storages.GetAndChange(storageKey);
//item.Value = new UInt160[] { tx.Sender }.ToByteArray();
snapshot.Storages.Delete(storageKey);
snapshot.Storages.GetAndChange(storageKey, () => new StorageItem
{
Value = new UInt160[] { tx.Sender }.ToByteArray()
});
snapshot.Commit();
NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeFalse();
snapshot = Blockchain.Singleton.GetSnapshot();
snapshot.Storages.Delete(storageKey);
snapshot.Storages.GetAndChange(storageKey, () => new StorageItem
{
Value = new UInt160[0].ToByteArray()
});
snapshot.Commit();
NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeTrue();
} 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. Oh yes, congratulations! |
||||||||||||
StorageKey storageKey = new StorageKey | ||||||||||||
{ | ||||||||||||
ScriptHash = NativeContract.Policy.Hash, | ||||||||||||
Key = new byte[sizeof(byte)] | ||||||||||||
}; | ||||||||||||
storageKey.Key[0] = 15; | ||||||||||||
snapshot.Storages.Add(storageKey, new StorageItem | ||||||||||||
{ | ||||||||||||
Value = new UInt160[] { tx.Sender }.ToByteArray(), | ||||||||||||
}); | ||||||||||||
|
||||||||||||
NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeFalse(); | ||||||||||||
|
||||||||||||
snapshot = Blockchain.Singleton.GetSnapshot(); | ||||||||||||
NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeTrue(); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
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.
When we are using
Add()
, we assume that the data does not exist. If we can't assume that, we should useGetAndChange()
. So, since we are usingAdd()
, there is no need to add another data read operation.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.
The idea behind this PR (and the above code change) is to create consistent behaviour of the cache by not just validating that the value exists in cache, but to also validate that it does not exist in the real backend. You previously choose this exact solution (#1173 (comment)) instead of always overriding the data (described in the linked comment as option 1).
If we don't choose either option 1 or 2 we will have inconsistent behaviour as described in #1173
I don't mind which of the 2 we choose, but right now the feedback looks like you don't want to change anything?! 😕 I think it is wrong to assume that data does not exist and I think you agreed with that when you wrote the code initially, otherwise this check would not exists
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.
The overhead of this check is very small, so just do it by the way.
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 did not understand the point as well, @erikzhang.
The implementation here seems to be option 2 of that aforementioned issue.
Do you want to leave the code as in the original implementation?
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 don't want to change it. The purpose of
Add()
is to add records when they do not exist, and save the cost of a read operation. If you are not sure that the record exists or not, you should useGetAndChange()
.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 see, @erikzhang.
It looks like to be more efficient in the original code, yes? In the sense of avoiding a second check with
if (TryGetInternal(key) != null)
.@ixje, if that is the case, perhaps it is better to close PR and issue and open another one for commenting the code properly in order to avoid future misinterpretations.
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.
So everybody basically wasted their time in creating the PR, reviewing it and fixing unit tests (still thanks for that @vncoelho) because 1.5 months after opening the PR and the initial review we're suddenly trying to save performance on a db operation that takes < 10 microseconds (according to leveldb benchmarks for random reads) while elsewhere in the code base millisecond delays are accepted.
Don't get me wrong here. I'm all cool with keeping performance in mind for reaching the highest TPS, but how are we supposed to make a sound decision what makes sense to spend time on when performance criteria seems randomly applied?
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.
@ixje, I really apologize for that.
I really did not have enough knowledge for checking it before.
In fact, I would like to thank you for your dedication. In addition, your insights helped me to investigate further.
Perhaps, there are methods that are not being used from the
DataCache
, such asGetOrAdd
.We need to solve these questions and improve what needs to be improved.