Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Approval voting overlay db #3366

Merged
14 commits merged into from
Jul 8, 2021

Conversation

Lldenaurois
Copy link
Contributor

@Lldenaurois Lldenaurois commented Jun 25, 2021

Closes #3311

This PR refactors the old Action logic in Approval Voting; opting for the more efficient OverlayedBackend pattern proposed by @rphmeier in the ChainSelection subsystem.

Note that the tests could be refactored further to reduce duplication of code, however this will be addressed in the PR for #3365. In addition, the tests in tests.rs could be further refactored to remove the use of TestStore and instead make use of an in-memory dyn KeyValueDB. Due to this PR already getting quite large, I opted to address these items in the next refactor for cleaning up test logic.

Otherwise, would it be preferential to address the superfluous use of TestStore in this PR?

This commit introduces a Backend trait and attempts to move away
from the Action model via an OverlayBackend as in the ChainSelection
subsystem.
@Lldenaurois Lldenaurois added B0-silent Changes should not be mentioned in any release notes I5-tests Tests need fixing, improving or augmenting. A0-please_review Pull request needs code review. C1-low PR touches the given topic and has a low impact on builders. labels Jun 25, 2021
@Lldenaurois Lldenaurois added the I8-refactor Code needs refactoring. label Jun 25, 2021
This commit modifies all tests to ensure tests are passing.
This commit addresses some oversights in the prior commit.

1. Inner errors in backend.write were swallowed
2. One-off write functions removed to avoid useless abstraction
3. Touch-ups in general
This commit removes the TestDB from tests.rs and replaces it with
an in-memory kvdb.
node/subsystem/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Member

@ordian ordian left a comment

Choose a reason for hiding this comment

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

Looks good modulo nits. Not sure if this warrants a burnin on Rococo, but it won't hurt.

node/core/approval-voting/src/backend.rs Outdated Show resolved Hide resolved
node/core/approval-voting/src/ops.rs Outdated Show resolved Hide resolved
node/core/approval-voting/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@drahnr drahnr left a comment

Choose a reason for hiding this comment

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

First iteration looks good - a few small nits, logic check will be done in the second pass.

}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
use super::ops;

&self,
candidate_hash: &CandidateHash,
) -> SubsystemResult<Option<CandidateEntry>> {
super::ops::load_candidate_entry(&*self.inner, &self.config, candidate_hash)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
super::ops::load_candidate_entry(&*self.inner, &self.config, candidate_hash)
ops::load_candidate_entry(&*self.inner, &self.config, candidate_hash)

inner: &'a B,

// `None` means unchanged
stored_block_range: Option<StoredBlockRange>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a fan of this implicit encoding set with Options, could we not store BackendWriteOp directly? It costs a few extra bytes for the hash being present in addition, but from a clarity pov this would be advantageous.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's actually a great suggestion... I would suggest we merge this PR and then follow-up in an additional PR, but more than happy to explore the right approach in this PR.

Paging @rphmeier

Copy link
Contributor Author

@Lldenaurois Lldenaurois Jun 30, 2021

Choose a reason for hiding this comment

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

So basically, instead of a HashMap<K, Option<V>>, you are suggesting we use

HashMap<K, BackendWriteOp> ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Would the idea there be to have a single map K -> BackendWriteOp? Otherwise I think it could get less clear, not more, because the type system would then express the possibility to have a BackendWriteOp::WriteCandidateEntry in the block_entries map, for example.

Reusing Option for this is a little semantically wrong, but maybe a new enum is a better solution:

enum OverlayValue<T> { Present(T), Deleted }

// Semantically - `None` means 'defer to underlying'
stored_block_range: Option<OverlayValue<StoredBlockRange>>,

// Same here, when calling 'get' for a particular `Hash`.
block_entries: HashMap<Hash, OverlayValue<BlockEntry>>,

Comment on lines 64 to 68
Some(range) => if range.0 >= canon_number {
return Ok(())
} else {
range
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Some(range) => if range.0 >= canon_number {
return Ok(())
} else {
range
},
Some(range) if range.0 >= canon_number => return Ok(()),
Some(range) => range,

Comment on lines 223 to 227
Some(range) => if range.1 <= number {
Some(StoredBlockRange(range.0, number + 1))
} else {
None
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Some(range) => if range.1 <= number {
Some(StoredBlockRange(range.0, number + 1))
} else {
None
}
Some(range) if range.1 <= number => Some(StoredBlockRange(range.0, number + 1)),
Some(_) => None,

// All the block heights we visited but didn't necessarily delete everything from.
let mut visited_heights = HashMap::new();

let visit_and_remove_block_entry = |
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: If this does not capture state, make it a full fn.

Copy link
Contributor

@drahnr drahnr left a comment

Choose a reason for hiding this comment

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

LGTM, logic appears good from what I can see 👍

node/core/approval-voting/src/ops.rs Show resolved Hide resolved
node/core/approval-voting/src/ops.rs Show resolved Hide resolved

pub(super) struct DbBackend {
inner: Arc<dyn KeyValueDB>,
pub(super) config: Config,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would prefer this to be a getter fn config(&self) -> &Config to protect mutability of the config.

}

self.inner.write(tx)?;
Ok(())
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok(()) here is redundant if the ?; is omitted from the above line, right?

Copy link
Contributor

@drahnr drahnr Jul 6, 2021

Choose a reason for hiding this comment

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

The difference is the current impl does an implicit type conversion, if the returned Error types are the same, it indeed can be omitted.

Copy link
Contributor

@rphmeier rphmeier left a comment

Choose a reason for hiding this comment

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

Generally looks like the right direction. Could use a few organizational tweaks I've outlined in the comments.

// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.

//!
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs better module docs 😅

backing_group: GroupIndex,
our_assignment: Option<OurAssignment>,
our_approval_sig: Option<ValidatorSignature>,
pub tranches: Vec<TrancheEntry>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like any of this stuff being pub. What happens when you remove it that is causing issues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was due to the code move because the struct is created explicitly in tests. I added a constructor helper and pushed. Should be good to ship

@rphmeier rphmeier closed this Jul 8, 2021
@rphmeier rphmeier reopened this Jul 8, 2021
@rphmeier
Copy link
Contributor

rphmeier commented Jul 8, 2021

bot merge

@ghost
Copy link

ghost commented Jul 8, 2021

Trying merge.

This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders. I5-tests Tests need fixing, improving or augmenting. I8-refactor Code needs refactoring.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Approval Voting: Refactor Actions to be less racy and fragile
4 participants