• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperledger / fabric-x-committer / 26212692047

21 May 2026 07:44AM UTC coverage: 91.834% (+0.3%) from 91.503%
26212692047

Pull #605

github

cendhu
Fix numWaitingTxsForStatus not counting rejected transactions

Problem:
The coordinator numWaitingTxsForStatus counter only incremented by
len(blk.Txs), ignoring len(blk.Rejected). When a block contained
rejected transactions, the counter was never decremented back to zero
because sendTxStatus decrements by the actual number of statuses
returned (which includes rejected txs). This caused the waiting
transaction count to go negative, breaking the
NumberOfWaitingTransactionsForStatus API and any callers that rely on
it to determine when all transactions have been processed.

Root cause:
In receiveAndProcessBlock, the counter increment only used len(blk.Txs)
without len(blk.Rejected), despite the metrics counter on the line
above correctly including both via len(blk.Txs)+len(blk.Rejected).

Fix:
Change numWaitingTxsForStatus to add len(blk.Txs) + len(blk.Rejected),
matching the metrics and ensuring the counter is correctly decremented
to zero when all transaction statuses (including rejected) are sent
back to the sidecar.

Added TestWaitingTxsCountReturnsToZeroForBlockWithRejectedTxs to
verify the counter returns to zero after processing a block that
contains both normal and rejected transactions.

Signed-off-by: Senthilnathan <cendhu@gmail.com>
Pull Request #605: [coordinator] Fix numWaitingTxsForStatus not counting rejected transactions

1 of 1 new or added line in 1 file covered. (100.0%)

822 existing lines in 48 files now uncovered.

10256 of 11168 relevant lines covered (91.83%)

82267.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/utils/sync_map.go
1
/*
2
Copyright IBM Corp. All Rights Reserved.
3

4
SPDX-License-Identifier: Apache-2.0
5
*/
6

7
package utils
8

9
import (
10
        "iter"
11
        "sync"
12
)
13

14
// SyncMap is a wrapper for sync.Map.
15
// It enhances readability by explicitly specifying the key and value types at compile time.
16
// This also minimizes boilerplate code needed for type casting.
17
type SyncMap[K, V any] struct {
18
        m sync.Map
19
}
20

21
// Store mirrors sync.Map's method.
22
// See [sync.Map] for detailed documentation.
23
func (m *SyncMap[K, V]) Store(key K, value V) {
44,521✔
24
        m.m.Store(key, value)
44,521✔
25
}
44,521✔
26

27
// Load mirrors sync.Map's method.
28
// See [sync.Map] for detailed documentation.
29
func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
164,476✔
30
        return makeReturn[V](m.m.Load(key))
164,476✔
31
}
164,476✔
32

33
// Swap mirrors sync.Map's method.
34
// See [sync.Map] for detailed documentation.
UNCOV
35
func (m *SyncMap[K, V]) Swap(key K, value V) (previous V, loaded bool) {
1✔
UNCOV
36
        return makeReturn[V](m.m.Swap(key, value))
1✔
UNCOV
37
}
1✔
38

39
// LoadOrStore mirrors sync.Map's method.
40
// See [sync.Map] for detailed documentation.
41
func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
85,776✔
42
        return makeReturn[V](m.m.LoadOrStore(key, value))
85,776✔
43
}
85,776✔
44

45
// LoadAndDelete mirrors sync.Map's method.
46
// See [sync.Map] for detailed documentation.
47
func (m *SyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
42,477✔
48
        return makeReturn[V](m.m.LoadAndDelete(key))
42,477✔
49
}
42,477✔
50

51
// Delete mirrors sync.Map's method.
52
// See [sync.Map] for detailed documentation.
53
func (m *SyncMap[K, V]) Delete(key K) {
80,523✔
54
        m.m.Delete(key)
80,523✔
55
}
80,523✔
56

57
// CompareAndSwap mirrors sync.Map's method.
58
// See [sync.Map] for detailed documentation.
59
func (m *SyncMap[K, V]) CompareAndSwap(key K, oldVal, newVal V) (swapped bool) {
21✔
60
        return m.m.CompareAndSwap(key, oldVal, newVal)
21✔
61
}
21✔
62

63
// CompareAndDelete mirrors sync.Map's method.
64
// See [sync.Map] for detailed documentation.
65
func (m *SyncMap[K, V]) CompareAndDelete(key K, value V) (deleted bool) {
35✔
66
        return m.m.CompareAndDelete(key, value)
35✔
67
}
35✔
68

69
// Range mirrors sync.Map's method.
70
// See [sync.Map] for detailed documentation.
71
func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) {
41,308✔
72
        m.m.Range(func(k, v any) bool {
42,593✔
73
                nonNilK, _ := k.(K) //nolint:revive // all keys are of type K.
1,285✔
74
                nonNilV, _ := v.(V) //nolint:revive // all values are of type V.
1,285✔
75
                return f(nonNilK, nonNilV)
1,285✔
76
        })
1,285✔
77
}
78

79
// Clear mirrors sync.Map's method.
80
// See [sync.Map] for detailed documentation.
81
func (m *SyncMap[K, V]) Clear() {
213✔
82
        m.m.Clear()
213✔
83
}
213✔
84

85
// IterItems allows iterating over the items' key value pair.
86
// Example:
87
//
88
//        for k, v := range m.IterItems() {
89
//            fmt.Printf("%v: %v\n", k, v)
90
//        }
UNCOV
91
func (m *SyncMap[K, V]) IterItems() iter.Seq2[K, V] {
1✔
UNCOV
92
        return func(yield func(K, V) bool) {
2✔
UNCOV
93
                m.Range(yield)
1✔
UNCOV
94
        }
1✔
95
}
96

97
// IterKeys allows iterating over the items' keys.
98
// Example:
99
//
100
//        for k := range m.IterKeys() {
101
//            fmt.Printf("%v\n", k)
102
//        }
103
func (m *SyncMap[K, V]) IterKeys() iter.Seq[K] {
41,223✔
104
        return func(yield func(K) bool) {
82,446✔
105
                m.Range(func(key K, _ V) bool {
41,336✔
106
                        return yield(key)
113✔
107
                })
113✔
108
        }
109
}
110

111
// IterValues allows iterating over the items' values.
112
// Example:
113
//
114
//        for v := range m.IterValues() {
115
//            fmt.Printf("%v\n", v)
116
//        }
117
func (m *SyncMap[K, V]) IterValues() iter.Seq[V] {
83✔
118
        return func(yield func(V) bool) {
166✔
119
                m.Range(func(_ K, value V) bool {
1,250✔
120
                        return yield(value)
1,167✔
121
                })
1,167✔
122
        }
123
}
124

125
// Count counts the number of items in the map.
126
// It iterates over the map, so it is best to avoid it in production.
127
func (m *SyncMap[K, V]) Count() int {
133✔
128
        count := 0
133✔
129
        m.m.Range(func(_, _ any) bool {
219✔
130
                count++
86✔
131
                return true
86✔
132
        })
86✔
133
        return count
133✔
134
}
135

136
func makeReturn[T any](v any, retOK bool) (value T, ok bool) {
292,726✔
137
        if v != nil {
583,424✔
138
                value = v.(T) //nolint:errcheck,revive,forcetypeassert // all values are of type T.
290,698✔
139
        }
290,698✔
140
        return value, retOK
292,726✔
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc