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

hyperledger / fabric-x-committer / 31094313559

06 Aug 2026 10:42AM UTC coverage: 90.832% (+0.08%) from 90.756%
31094313559

push

github

web-flow
[serve, connection] Hand the pre-allocated listener over to its consumer (#747)

#### Type of change

- Bug fix
- Test update

#### Description

- `ServerConfig.Listener` now clears the config's reference to the
pre-allocated listener when it
hands it to a server, making the server its sole owner. Releasing a
reservation
(`ClosePreAllocatedListener`) can therefore no longer close a listener a
server is accepting on,
and a non-nil `preAllocatedListener` now means exactly "the config holds
this port".
- `reserveWhileServerDown` drops its `ClosePreAllocatedListener` call:
once the restarted server has
taken the reservation there is nothing left to close, and re-reserving
the same port retries
through `ListenRetryExecute` until the stopping server releases it, so
the helper no longer
  depends on the asynchronous stop having completed first.
- `reserveWhileServerDown` also reserves the HTTP port, which was left
free for the whole outage.

#### Additional details (Optional)

To observe the failure, start a server on a pre-allocated listener,
cancel its context and
immediately close the reservation, in a loop: the asynchronous `Stop`
usually fires grpc-go's quit
event first, but when the close wins, `Serve` returns
`accept tcp 127.0.0.1:<port>: use of closed network connection`.

#### Related issues

- resolves #746

Signed-off-by: Liran Funaro <liran.funaro@gmail.com>

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

17 existing lines in 7 files now uncovered.

10780 of 11868 relevant lines covered (90.83%)

33593.99 hits per line

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

80.33
/utils/utils.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
        "context"
11
        "crypto/rand"
12
        "encoding/json"
13
        "fmt"
14
        "io"
15
        "math/big"
16
        pseudorand "math/rand"
17
        "os"
18
        "strings"
19

20
        "github.com/cockroachdb/errors"
21
        "github.com/hyperledger/fabric-x-common/api/applicationpb"
22
        "github.com/hyperledger/fabric-x-common/api/committerpb"
23
        "google.golang.org/grpc/peer"
24
)
25

26
// ErrActiveStream represents the error when attempting to create a new stream while one is already active.
27
// The system only allows a single active stream at any given time.
28
var ErrActiveStream = errors.New("a stream is already active. Only one active stream is allowed at a time")
29

30
// FileExists returns true if a file path exists.
31
func FileExists(path string) bool {
8✔
32
        _, err := os.Stat(path)
8✔
33
        return !os.IsNotExist(err)
8✔
34
}
8✔
35

36
// LazyJSON will lazily marshal a struct for logging purposes.
37
type LazyJSON struct {
38
        O      any
39
        Indent string
40
}
41

42
// String marshals the give object as JSON.
43
func (lj *LazyJSON) String() string {
61✔
44
        if lj.O == nil {
61✔
45
                return "{}"
×
46
        }
×
47
        var p []byte
61✔
48
        var err error
61✔
49
        if lj.Indent != "" {
62✔
50
                p, err = json.MarshalIndent(lj.O, "", lj.Indent)
1✔
51
        } else {
61✔
52
                p, err = json.Marshal(lj.O)
60✔
53
        }
60✔
54
        if err != nil {
61✔
55
                return fmt.Sprintf("cannot marshal object: %v", err)
×
56
        }
×
57
        return string(p)
61✔
58
}
59

60
// Must panics given an error.
61
func Must(err error, msg ...string) {
7,309,982✔
62
        if err != nil {
7,309,982✔
63
                panic(errors.Wrap(err, strings.Join(msg, " ")))
×
64
        }
65
}
66

67
// Mustf panics given an error.
68
func Mustf(err error, format string, args ...any) {
16✔
69
        if err != nil {
16✔
70
                panic(errors.Wrapf(err, format, args...))
×
71
        }
72
}
73

74
// MustRead reads a byte array of the given size from the source.
75
// It panics if the read fails, or cannot read the requested size.
76
// "crypto/rand" and "math/rand" never fail and always returns the correct length.
77
func MustRead(source io.Reader, size int) []byte {
6,079,353✔
78
        value := make([]byte, size)
6,079,353✔
79
        n, err := source.Read(value)
6,079,353✔
80
        Must(err)
6,079,353✔
81
        if n != size {
6,079,353✔
82
                panic(errors.Errorf("expected size of %d, got %d", size, n))
×
83
        }
84
        return value
6,079,353✔
85
}
86

87
// ProcessErr wraps a non-nil error with a message using %w for unwrapping.
88
// Returns nil if the error is nil, otherwise returns the wrapped error.
89
// Example to the call of the function: utils.ProcessErr(g.Wait(), "sidecar has been stopped").
90
func ProcessErr(err error, msg string) error {
292✔
91
        if err != nil {
584✔
92
                return fmt.Errorf("%s: %w", msg, err) //nolint:wrapcheck
292✔
93
        }
292✔
UNCOV
94
        return nil
×
95
}
96

97
// CountAppearances returns the number of appearances each item have.
98
func CountAppearances[T comparable](items []T) map[T]int {
590✔
99
        countMap := make(map[T]int)
590✔
100
        for _, item := range items {
90,301✔
101
                countMap[item]++
89,711✔
102
        }
89,711✔
103
        return countMap
590✔
104
}
105

106
// ExtractServerAddress returns the stream's server (local) address.
107
func ExtractServerAddress(ctx context.Context) string {
849✔
108
        p, ok := peer.FromContext(ctx)
849✔
109
        if !ok || p == nil || p.LocalAddr == nil {
849✔
110
                return ""
×
111
        }
×
112
        return p.LocalAddr.String()
849✔
113
}
114

115
// IsConfigTx returns true if the namespaces indicate a config transaction.
116
func IsConfigTx(namespaces []*applicationpb.TxNamespace) bool {
29,366✔
117
        return len(namespaces) == 1 && namespaces[0].NsId == committerpb.ConfigNamespaceID
29,366✔
118
}
29,366✔
119

120
// RandIntN returns a true random (crypto/rand) number in the range [0, n).
121
// If it fails to read the crypto/rand stream, it falls back to a pseudo random number generator.
122
func RandIntN(n uint64) uint64 {
3,379✔
123
        // crypto/rand works with big.Int.
3,379✔
124
        var maxNumber big.Int
3,379✔
125
        maxNumber.SetUint64(n)
3,379✔
126
        res, _ := rand.Int(rand.Reader, &maxNumber)
3,379✔
127
        if res == nil {
3,379✔
128
                return uint64(pseudorand.Intn(int(n))) //nolint:gosec // fallback.
×
129
        }
×
130
        return res.Uint64()
3,379✔
131
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc