add : TriesInMemory flag

This commit is contained in:
Shivam Sharma 2022-10-17 18:00:30 +05:30
parent 02e84ff6f5
commit 225a826e0e
9 changed files with 61 additions and 48 deletions

View file

@ -122,6 +122,7 @@ syncmode = "full"
# noprefetch = false # noprefetch = false
# preimages = false # preimages = false
# txlookuplimit = 2350000 # txlookuplimit = 2350000
# triesinmemory = 128
[accounts] [accounts]
# allow-insecure-unlock = true # allow-insecure-unlock = true

View file

@ -92,7 +92,6 @@ const (
txLookupCacheLimit = 1024 txLookupCacheLimit = 1024
maxFutureBlocks = 256 maxFutureBlocks = 256
maxTimeFutureBlocks = 30 maxTimeFutureBlocks = 30
TriesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch. // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
// //
@ -132,6 +131,7 @@ type CacheConfig struct {
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
Preimages bool // Whether to store preimage of trie key to the disk Preimages bool // Whether to store preimage of trie key to the disk
TriesInMemory uint64 // Number of recent tries to keep in memory
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
} }
@ -144,6 +144,7 @@ var DefaultCacheConfig = &CacheConfig{
TrieTimeLimit: 5 * time.Minute, TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256, SnapshotLimit: 256,
SnapshotWait: true, SnapshotWait: true,
TriesInMemory: 128,
} }
// BlockChain represents the canonical chain given a database with a genesis // BlockChain represents the canonical chain given a database with a genesis
@ -829,7 +830,7 @@ func (bc *BlockChain) Stop() {
if !bc.cacheConfig.TrieDirtyDisabled { if !bc.cacheConfig.TrieDirtyDisabled {
triedb := bc.stateCache.TrieDB() triedb := bc.stateCache.TrieDB()
for _, offset := range []uint64{0, 1, TriesInMemory - 1} { for _, offset := range []uint64{0, 1, DefaultCacheConfig.TriesInMemory - 1} {
if number := bc.CurrentBlock().NumberU64(); number > offset { if number := bc.CurrentBlock().NumberU64(); number > offset {
recent := bc.GetBlockByNumber(number - offset) recent := bc.GetBlockByNumber(number - offset)
@ -1297,7 +1298,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -int64(block.NumberU64())) bc.triegc.Push(root, -int64(block.NumberU64()))
if current := block.NumberU64(); current > TriesInMemory { if current := block.NumberU64(); current > DefaultCacheConfig.TriesInMemory {
// If we exceeded our memory allowance, flush matured singleton nodes to disk // If we exceeded our memory allowance, flush matured singleton nodes to disk
var ( var (
nodes, imgs = triedb.Size() nodes, imgs = triedb.Size()
@ -1307,7 +1308,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Cap(limit - ethdb.IdealBatchSize) triedb.Cap(limit - ethdb.IdealBatchSize)
} }
// Find the next state trie we need to commit // Find the next state trie we need to commit
chosen := current - TriesInMemory chosen := current - DefaultCacheConfig.TriesInMemory
// If we exceeded out time allowance, flush an entire trie to disk // If we exceeded out time allowance, flush an entire trie to disk
if bc.gcproc > bc.cacheConfig.TrieTimeLimit { if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
@ -1319,8 +1320,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
} else { } else {
// If we're exceeding limits but haven't reached a large enough memory gap, // If we're exceeding limits but haven't reached a large enough memory gap,
// warn the user that the system is becoming unstable. // warn the user that the system is becoming unstable.
if chosen < lastWrite+TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit { if chosen < lastWrite+DefaultCacheConfig.TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/TriesInMemory) log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", DefaultCacheConfig.TriesInMemory, "optimum", float64(chosen-lastWrite)/float64((DefaultCacheConfig.TriesInMemory)))
} }
// Flush an entire trie and restart the counters // Flush an entire trie and restart the counters
triedb.Commit(header.Root, true, nil) triedb.Commit(header.Root, true, nil)

View file

@ -1652,7 +1652,7 @@ func TestTrieForkGC(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
// Generate a bunch of fork blocks, each side forking from the canonical chain // Generate a bunch of fork blocks, each side forking from the canonical chain
forks := make([]*types.Block, len(blocks)) forks := make([]*types.Block, len(blocks))
@ -1681,7 +1681,7 @@ func TestTrieForkGC(t *testing.T) {
} }
} }
// Dereference all the recent tries and ensure no past trie is left in // Dereference all the recent tries and ensure no past trie is left in
for i := 0; i < TriesInMemory; i++ { for i := 0; i < int(DefaultCacheConfig.TriesInMemory); i++ {
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root()) chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root()) chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
} }
@ -1700,8 +1700,8 @@ func TestLargeReorgTrieGC(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }) competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*int(DefaultCacheConfig.TriesInMemory)+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
// Import the shared chain and the original canonical one // Import the shared chain and the original canonical one
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
@ -1736,7 +1736,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil { if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
t.Fatalf("failed to finalize competitor chain: %v", err) t.Fatalf("failed to finalize competitor chain: %v", err)
} }
for i, block := range competitor[:len(competitor)-TriesInMemory] { for i, block := range competitor[:len(competitor)-int(DefaultCacheConfig.TriesInMemory)] {
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil { if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
t.Fatalf("competitor %d: competing chain state missing", i) t.Fatalf("competitor %d: competing chain state missing", i)
} }
@ -1892,7 +1892,7 @@ func TestLowDiffLongChain(t *testing.T) {
// We must use a pretty long chain to ensure that the fork doesn't overtake us // We must use a pretty long chain to ensure that the fork doesn't overtake us
// until after at least 128 blocks post tip // until after at least 128 blocks post tip
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) { blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1}) b.SetCoinbase(common.Address{1})
b.OffsetTime(-9) b.OffsetTime(-9)
}) })
@ -1910,7 +1910,7 @@ func TestLowDiffLongChain(t *testing.T) {
} }
// Generate fork chain, starting from an early block // Generate fork chain, starting from an early block
parent := blocks[10] parent := blocks[10]
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) { fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2}) b.SetCoinbase(common.Address{2})
}) })
@ -1979,7 +1979,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Set the terminal total difficulty in the config // Set the terminal total difficulty in the config
gspec.Config.TerminalTotalDifficulty = big.NewInt(0) gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
} }
blocks, _ := GenerateChain(&chainConfig, genesis, genEngine, db, 2*TriesInMemory, func(i int, gen *BlockGen) { blocks, _ := GenerateChain(&chainConfig, genesis, genEngine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key) tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
if err != nil { if err != nil {
t.Fatalf("failed to create tx: %v", err) t.Fatalf("failed to create tx: %v", err)
@ -1991,9 +1991,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
t.Fatalf("block %d: failed to insert into chain: %v", n, err) t.Fatalf("block %d: failed to insert into chain: %v", n, err)
} }
lastPrunedIndex := len(blocks) - TriesInMemory - 1 lastPrunedIndex := len(blocks) - int(DefaultCacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex] lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory] firstNonPrunedBlock := blocks[len(blocks)-int(DefaultCacheConfig.TriesInMemory)]
// Verify pruning of lastPrunedBlock // Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) { if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
@ -2019,7 +2019,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Generate fork chain, make it longer than canon // Generate fork chain, make it longer than canon
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
parent := blocks[parentIndex] parent := blocks[parentIndex]
fork, _ := GenerateChain(&chainConfig, parent, genEngine, db, 2*TriesInMemory, func(i int, b *BlockGen) { fork, _ := GenerateChain(&chainConfig, parent, genEngine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2}) b.SetCoinbase(common.Address{2})
}) })
// Prepend the parent(s) // Prepend the parent(s)
@ -2046,6 +2046,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// That is: the sidechain for import contains some blocks already present in canon chain. // That is: the sidechain for import contains some blocks already present in canon chain.
// So the blocks are // So the blocks are
// [ Cn, Cn+1, Cc, Sn+3 ... Sm] // [ Cn, Cn+1, Cc, Sn+3 ... Sm]
//
// ^ ^ ^ pruned // ^ ^ ^ pruned
func TestPrunedImportSide(t *testing.T) { func TestPrunedImportSide(t *testing.T) {
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false))) //glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
@ -2851,7 +2852,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
// Generate and import the canonical chain // Generate and import the canonical chain
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*int(DefaultCacheConfig.TriesInMemory), nil)
diskdb := rawdb.NewMemoryDatabase() diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
@ -2863,14 +2864,14 @@ func TestSideImportPrunedBlocks(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", n, err) t.Fatalf("block %d: failed to insert into chain: %v", n, err)
} }
lastPrunedIndex := len(blocks) - TriesInMemory - 1 lastPrunedIndex := len(blocks) - int(DefaultCacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex] lastPrunedBlock := blocks[lastPrunedIndex]
// Verify pruning of lastPrunedBlock // Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) { if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64()) t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
} }
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory] firstNonPrunedBlock := blocks[len(blocks)-int(DefaultCacheConfig.TriesInMemory)]
// Verify firstNonPrunedBlock is not pruned // Verify firstNonPrunedBlock is not pruned
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) { if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64()) t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
@ -3369,7 +3370,6 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
// to the destructset in case something is created "onto" an existing item. // to the destructset in case something is created "onto" an existing item.
// We need to either roll back the snapDestructs, or not place it into snapDestructs // We need to either roll back the snapDestructs, or not place it into snapDestructs
// in the first place. // in the first place.
//
func TestInitThenFailCreateContract(t *testing.T) { func TestInitThenFailCreateContract(t *testing.T) {
var ( var (
// Generate a canonical chain to act as the main dataset // Generate a canonical chain to act as the main dataset

View file

@ -364,6 +364,9 @@ type CacheConfig struct {
// TxLookupLimit sets the maximum number of blocks from head whose tx indices are reserved. // TxLookupLimit sets the maximum number of blocks from head whose tx indices are reserved.
TxLookupLimit uint64 `hcl:"txlookuplimit,optional" toml:"txlookuplimit,optional"` TxLookupLimit uint64 `hcl:"txlookuplimit,optional" toml:"txlookuplimit,optional"`
// Number of block states to keep in memory (default = 128)
TriesInMemory uint64 `hcl:"triesinmemory,optional" toml:"triesinmemory,optional"`
} }
type AccountsConfig struct { type AccountsConfig struct {
@ -509,6 +512,7 @@ func DefaultConfig() *Config {
NoPrefetch: false, NoPrefetch: false,
Preimages: false, Preimages: false,
TxLookupLimit: 2350000, TxLookupLimit: 2350000,
TriesInMemory: 128,
}, },
Accounts: &AccountsConfig{ Accounts: &AccountsConfig{
Unlock: []string{}, Unlock: []string{},

View file

@ -304,6 +304,13 @@ func (c *Command) Flags() *flagset.Flagset {
Default: c.cliConfig.Cache.Preimages, Default: c.cliConfig.Cache.Preimages,
Group: "Cache", Group: "Cache",
}) })
f.Uint64Flag(&flagset.Uint64Flag{
Name: "cache.triesinmemory",
Usage: "Number of block states (tries) to keep in memory (default = 128)",
Value: &c.cliConfig.Cache.TriesInMemory,
Default: c.cliConfig.Cache.TriesInMemory,
Group: "Cache",
})
f.Uint64Flag(&flagset.Uint64Flag{ f.Uint64Flag(&flagset.Uint64Flag{
Name: "txlookuplimit", Name: "txlookuplimit",
Usage: "Number of recent blocks to maintain transactions index for (default = about 56 days, 0 = entire chain)", Usage: "Number of recent blocks to maintain transactions index for (default = about 56 days, 0 = entire chain)",

View file

@ -316,7 +316,7 @@ func TestGetStaleCodeLes4(t *testing.T) { testGetStaleCode(t, 4) }
func testGetStaleCode(t *testing.T, protocol int) { func testGetStaleCode(t *testing.T, protocol int) {
netconfig := testnetConfig{ netconfig := testnetConfig{
blocks: core.TriesInMemory + 4, blocks: int(core.DefaultCacheConfig.TriesInMemory) + 4,
protocol: protocol, protocol: protocol,
nopruning: true, nopruning: true,
} }
@ -430,7 +430,7 @@ func TestGetStaleProofLes4(t *testing.T) { testGetStaleProof(t, 4) }
func testGetStaleProof(t *testing.T, protocol int) { func testGetStaleProof(t *testing.T, protocol int) {
netconfig := testnetConfig{ netconfig := testnetConfig{
blocks: core.TriesInMemory + 4, blocks: int(core.DefaultCacheConfig.TriesInMemory) + 4,
protocol: protocol, protocol: protocol,
nopruning: true, nopruning: true,
} }

View file

@ -1058,7 +1058,7 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge
// If local ethereum node is running in archive mode, advertise ourselves we have // If local ethereum node is running in archive mode, advertise ourselves we have
// all version state data. Otherwise only recent state is available. // all version state data. Otherwise only recent state is available.
stateRecent := uint64(core.TriesInMemory - blockSafetyMargin) stateRecent := uint64(core.DefaultCacheConfig.TriesInMemory - blockSafetyMargin)
if server.archiveMode { if server.archiveMode {
stateRecent = 0 stateRecent = 0
} }

View file

@ -297,7 +297,7 @@ func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Refuse to search stale state data in the database since looking for // Refuse to search stale state data in the database since looking for
// a non-exist key is kind of expensive. // a non-exist key is kind of expensive.
local := bc.CurrentHeader().Number.Uint64() local := bc.CurrentHeader().Number.Uint64()
if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultCacheConfig.TriesInMemory <= local {
p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local)
p.bumpInvalid() p.bumpInvalid()
continue continue
@ -396,7 +396,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Refuse to search stale state data in the database since looking for // Refuse to search stale state data in the database since looking for
// a non-exist key is kind of expensive. // a non-exist key is kind of expensive.
local := bc.CurrentHeader().Number.Uint64() local := bc.CurrentHeader().Number.Uint64()
if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultCacheConfig.TriesInMemory <= local {
p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local)
p.bumpInvalid() p.bumpInvalid()
continue continue

View file

@ -377,7 +377,7 @@ func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Ha
sendList = sendList.add("serveHeaders", nil) sendList = sendList.add("serveHeaders", nil)
sendList = sendList.add("serveChainSince", uint64(0)) sendList = sendList.add("serveChainSince", uint64(0))
sendList = sendList.add("serveStateSince", uint64(0)) sendList = sendList.add("serveStateSince", uint64(0))
sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4)) sendList = sendList.add("serveRecentState", uint64(core.DefaultCacheConfig.TriesInMemory-4))
sendList = sendList.add("txRelay", nil) sendList = sendList.add("txRelay", nil)
sendList = sendList.add("flowControl/BL", testBufLimit) sendList = sendList.add("flowControl/BL", testBufLimit)
sendList = sendList.add("flowControl/MRR", testBufRecharge) sendList = sendList.add("flowControl/MRR", testBufRecharge)