Skip to content

Commit e346250

Browse files
committed
txgraph: give Clusters a range of intended tx counts (preparation)
1 parent e93b0f0 commit e346250

File tree

1 file changed

+48
-9
lines changed

1 file changed

+48
-9
lines changed

src/txgraph.cpp

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ class Cluster
147147
m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
148148
}
149149

150+
/** Get the smallest number of transactions this Cluster is intended for. */
151+
virtual DepGraphIndex GetMinIntendedTxCount() const noexcept = 0;
152+
/** Get the maximum number of transactions this Cluster supports. */
153+
virtual DepGraphIndex GetMaxTxCount() const noexcept = 0;
150154
/** Total memory usage currently for this Cluster, including all its dynamic memory, plus Cluster
151155
* structure itself, and ClusterSet::m_clusters entry. */
152156
virtual size_t TotalMemoryUsage() const noexcept = 0;
@@ -246,11 +250,18 @@ class GenericClusterImpl final : public Cluster
246250
std::vector<DepGraphIndex> m_linearization;
247251

248252
public:
253+
/** The smallest number of transactions this Cluster implementation is intended for. */
254+
static constexpr DepGraphIndex MIN_INTENDED_TX_COUNT{1};
255+
/** The largest number of transactions this Cluster implementation supports. */
256+
static constexpr DepGraphIndex MAX_TX_COUNT{SetType::Size()};
257+
249258
GenericClusterImpl() noexcept = delete;
250259
/** Construct an empty GenericClusterImpl. */
251260
explicit GenericClusterImpl(uint64_t sequence) noexcept;
252261

253262
size_t TotalMemoryUsage() const noexcept final;
263+
constexpr DepGraphIndex GetMinIntendedTxCount() const noexcept final { return MIN_INTENDED_TX_COUNT; }
264+
constexpr DepGraphIndex GetMaxTxCount() const noexcept final { return MAX_TX_COUNT; }
254265
DepGraphIndex GetDepGraphIndexRange() const noexcept final { return m_depgraph.PositionRange(); }
255266
LinearizationIndex GetTxCount() const noexcept final { return m_linearization.size(); }
256267
uint64_t GetTotalTxSize() const noexcept final;
@@ -587,8 +598,11 @@ class TxGraphImpl final : public TxGraph
587598
* count. */
588599
std::unique_ptr<Cluster> CreateEmptyCluster(DepGraphIndex tx_count) noexcept
589600
{
590-
(void)tx_count;
591-
return CreateEmptyGenericCluster();
601+
if (tx_count >= GenericClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= GenericClusterImpl::MAX_TX_COUNT) {
602+
return CreateEmptyGenericCluster();
603+
}
604+
assert(false);
605+
return {};
592606
}
593607

594608
// Functions for handling Refs.
@@ -1135,10 +1149,12 @@ bool GenericClusterImpl::Split(TxGraphImpl& graph, int level) noexcept
11351149
auto component = m_depgraph.FindConnectedComponent(todo);
11361150
auto component_size = component.Count();
11371151
auto split_quality = component_size <= 2 ? QualityLevel::OPTIMAL : new_quality;
1138-
if (first && component == todo && SetType::Fill(component_size) == component) {
1152+
if (first && component == todo && SetType::Fill(component_size) == component && component_size >= MIN_INTENDED_TX_COUNT) {
11391153
// The existing Cluster is an entire component, without holes. Leave it be, but update
11401154
// its quality. If there are holes, we continue, so that the Cluster is reconstructed
1141-
// without holes, reducing memory usage.
1155+
// without holes, reducing memory usage. If the component's size is below the intended
1156+
// transaction count for this Cluster implementation, continue so that it can get
1157+
// converted.
11421158
Assume(todo == m_depgraph.Positions());
11431159
graph.SetClusterQuality(level, m_quality, m_setindex, split_quality);
11441160
// If this made the quality ACCEPTABLE or OPTIMAL, we need to compute and cache its
@@ -1740,23 +1756,38 @@ void TxGraphImpl::Merge(std::span<Cluster*> to_merge, int level) noexcept
17401756
size_t max_size_pos{0};
17411757
DepGraphIndex max_size = to_merge[max_size_pos]->GetTxCount();
17421758
GetClusterSet(level).m_cluster_usage -= to_merge[max_size_pos]->TotalMemoryUsage();
1759+
DepGraphIndex total_size = max_size;
17431760
for (size_t i = 1; i < to_merge.size(); ++i) {
17441761
GetClusterSet(level).m_cluster_usage -= to_merge[i]->TotalMemoryUsage();
17451762
DepGraphIndex size = to_merge[i]->GetTxCount();
1763+
total_size += size;
17461764
if (size > max_size) {
17471765
max_size_pos = i;
17481766
max_size = size;
17491767
}
17501768
}
17511769
if (max_size_pos != 0) std::swap(to_merge[0], to_merge[max_size_pos]);
17521770

1753-
// Merge all further Clusters in the group into the first one, and delete them.
1754-
for (size_t i = 1; i < to_merge.size(); ++i) {
1755-
to_merge[0]->Merge(*this, level, *to_merge[i]);
1771+
size_t start_idx = 1;
1772+
Cluster* into_cluster = to_merge[0];
1773+
if (total_size > into_cluster->GetMaxTxCount()) {
1774+
// The into_merge cluster is too small to fit all transactions being merged. Construct a
1775+
// a new Cluster using an implementation that matches the total size, and merge everything
1776+
// in there.
1777+
auto new_cluster = CreateEmptyCluster(total_size);
1778+
into_cluster = new_cluster.get();
1779+
InsertCluster(level, std::move(new_cluster), QualityLevel::OPTIMAL);
1780+
start_idx = 0;
1781+
}
1782+
1783+
// Merge all further Clusters in the group into the result (first one, or new one), and delete
1784+
// them.
1785+
for (size_t i = start_idx; i < to_merge.size(); ++i) {
1786+
into_cluster->Merge(*this, level, *to_merge[i]);
17561787
DeleteCluster(*to_merge[i], level);
17571788
}
1758-
to_merge[0]->Compact();
1759-
GetClusterSet(level).m_cluster_usage += to_merge[0]->TotalMemoryUsage();
1789+
into_cluster->Compact();
1790+
GetClusterSet(level).m_cluster_usage += into_cluster->TotalMemoryUsage();
17601791
}
17611792

17621793
void TxGraphImpl::ApplyDependencies(int level) noexcept
@@ -2546,6 +2577,14 @@ void TxGraphImpl::SanityCheck() const
25462577
assert(cluster.IsOversized() || cluster.GetTotalTxSize() <= m_max_cluster_size);
25472578
// OVERSIZED clusters are singletons.
25482579
assert(!cluster.IsOversized() || cluster.GetTxCount() == 1);
2580+
// Transaction counts cannot exceed the Cluster implementation's maximum
2581+
// supported transactions count.
2582+
assert(cluster.GetTxCount() <= cluster.GetMaxTxCount());
2583+
// Unless a Split is yet to be applied, the number of transactions must not be
2584+
// below the Cluster implementation's intended transaction count.
2585+
if (!cluster.NeedsSplitting()) {
2586+
assert(cluster.GetTxCount() >= cluster.GetMinIntendedTxCount());
2587+
}
25492588

25502589
// Check the sequence number.
25512590
assert(cluster.m_sequence < m_next_sequence_counter);

0 commit comments

Comments
 (0)