Skip to content

Commit 7516c6c

Browse files
use syncmap
Signed-off-by: Adrian Cole <[email protected]>
1 parent 27892e6 commit 7516c6c

File tree

3 files changed

+33
-46
lines changed

3 files changed

+33
-46
lines changed

internal/infrastructure/host/infra.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ type Infra struct {
4646
EnvoyGateway *egv1a1.EnvoyGateway
4747

4848
// proxyContextMap store the context of each running proxy by its name for lifecycle management.
49-
proxyContextMap map[string]*proxyContext
50-
// mu protects proxyContextMap from concurrent access.
51-
mu sync.RWMutex
49+
proxyContextMap sync.Map
5250

5351
// TODO: remove this field once it supports the configurable homeDir
5452
sdsConfigPath string
@@ -82,7 +80,6 @@ func NewInfra(runnerCtx context.Context, cfg *config.Server, logger logging.Logg
8280
HomeDir: defaultHomeDir,
8381
Logger: logger,
8482
EnvoyGateway: cfg.EnvoyGateway,
85-
proxyContextMap: make(map[string]*proxyContext),
8683
sdsConfigPath: defaultLocalCertPathDir,
8784
defaultEnvoyImage: egv1a1.DefaultEnvoyProxyImage,
8885
Stdout: cfg.Stdout,

internal/infrastructure/host/proxy_infra.go

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ type proxyContext struct {
3434

3535
// Close implements the Manager interface.
3636
func (i *Infra) Close() error {
37-
i.mu.RLock()
38-
names := make([]string, 0, len(i.proxyContextMap))
39-
for name := range i.proxyContextMap {
40-
names = append(names, name)
41-
}
42-
i.mu.RUnlock()
37+
// Collect all proxy names first to avoid holding locks during stopEnvoy calls
38+
var names []string
39+
i.proxyContextMap.Range(func(key, value interface{}) bool {
40+
names = append(names, key.(string))
41+
return true
42+
})
4343

4444
for _, name := range names {
4545
i.stopEnvoy(name)
@@ -60,10 +60,7 @@ func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) e
6060
proxyInfra := infra.GetProxyInfra()
6161
proxyName := utils.GetHashedName(proxyInfra.Name, 64)
6262
// Return directly if the proxy is running.
63-
i.mu.RLock()
64-
_, running := i.proxyContextMap[proxyName]
65-
i.mu.RUnlock()
66-
if running {
63+
if _, loaded := i.proxyContextMap.Load(proxyName); loaded {
6764
return nil
6865
}
6966

@@ -99,9 +96,7 @@ func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) e
9996
func (i *Infra) runEnvoy(ctx context.Context, envoyVersion, name string, args []string) {
10097
pCtx, cancel := context.WithCancel(ctx)
10198
exit := make(chan struct{}, 1)
102-
i.mu.Lock()
103-
i.proxyContextMap[name] = &proxyContext{cancel: cancel, exit: exit}
104-
i.mu.Unlock()
99+
i.proxyContextMap.Store(name, &proxyContext{cancel: cancel, exit: exit})
105100
go func() {
106101
// Run blocks until pCtx is done or the process exits where the latter doesn't happen when
107102
// Envoy successfully starts up. So, this will not return until pCtx is done in practice.
@@ -134,18 +129,12 @@ func (i *Infra) DeleteProxyInfra(_ context.Context, infra *ir.Infra) error {
134129

135130
// stopEnvoy stops the Envoy process by its name. It will block until the process completely stopped.
136131
func (i *Infra) stopEnvoy(proxyName string) {
137-
i.mu.Lock()
138-
pCtx, ok := i.proxyContextMap[proxyName]
139-
i.mu.Unlock()
140-
132+
value, ok := i.proxyContextMap.LoadAndDelete(proxyName)
141133
if ok {
134+
pCtx := value.(*proxyContext)
142135
pCtx.cancel() // Cancel causes the Envoy process to exit.
143136
<-pCtx.exit // Wait for the Envoy process to completely exit.
144137
close(pCtx.exit) // Close the channel to avoid leaking.
145-
146-
i.mu.Lock()
147-
delete(i.proxyContextMap, proxyName)
148-
i.mu.Unlock()
149138
}
150139
}
151140

internal/infrastructure/host/proxy_infra_test.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,12 @@ func newMockInfra(t *testing.T, cfg *config.Server) *Infra {
4545
require.NoError(t, err)
4646

4747
infra := &Infra{
48-
HomeDir: homeDir,
49-
Logger: logging.DefaultLogger(io.Discard, egv1a1.LogLevelInfo),
50-
EnvoyGateway: cfg.EnvoyGateway,
51-
proxyContextMap: make(map[string]*proxyContext),
52-
sdsConfigPath: proxyDir,
53-
Stdout: io.Discard,
54-
Stderr: io.Discard,
48+
HomeDir: homeDir,
49+
Logger: logging.DefaultLogger(io.Discard, egv1a1.LogLevelInfo),
50+
EnvoyGateway: cfg.EnvoyGateway,
51+
sdsConfigPath: proxyDir,
52+
Stdout: io.Discard,
53+
Stderr: io.Discard,
5554
}
5655
return infra
5756
}
@@ -102,11 +101,10 @@ func TestInfra_runEnvoy_stopEnvoy(t *testing.T) {
102101
stdout := &bytes.Buffer{}
103102
stderr := &bytes.Buffer{}
104103
i := &Infra{
105-
proxyContextMap: make(map[string]*proxyContext),
106-
HomeDir: tmpdir,
107-
Logger: logging.DefaultLogger(stdout, egv1a1.LogLevelInfo),
108-
Stdout: stdout,
109-
Stderr: stderr,
104+
HomeDir: tmpdir,
105+
Logger: logging.DefaultLogger(stdout, egv1a1.LogLevelInfo),
106+
Stdout: stdout,
107+
Stderr: stderr,
110108
}
111109
// Ensures that run -> stop will successfully stop the envoy and we can
112110
// run it again without any issues.
@@ -116,9 +114,11 @@ func TestInfra_runEnvoy_stopEnvoy(t *testing.T) {
116114
"admin: {address: {socket_address: {address: '127.0.0.1', port_value: 9901}}}",
117115
}
118116
i.runEnvoy(t.Context(), "", "test", args)
119-
require.Len(t, i.proxyContextMap, 1)
117+
_, ok := i.proxyContextMap.Load("test")
118+
require.True(t, ok, "expected proxy context to be stored")
120119
i.stopEnvoy("test")
121-
require.Empty(t, i.proxyContextMap)
120+
_, ok = i.proxyContextMap.Load("test")
121+
require.False(t, ok, "expected proxy context to be removed")
122122
// If the cleanup didn't work, the error due to "address already in use" will be tried to be written to the nil logger,
123123
// which will panic.
124124
}
@@ -165,11 +165,10 @@ func TestInfra_runEnvoy_OutputRedirection(t *testing.T) {
165165
stderr := buffers[1]
166166

167167
i := &Infra{
168-
proxyContextMap: make(map[string]*proxyContext),
169-
HomeDir: tmpdir,
170-
Logger: logging.DefaultLogger(stdout, egv1a1.LogLevelInfo),
171-
Stdout: stdout,
172-
Stderr: stderr,
168+
HomeDir: tmpdir,
169+
Logger: logging.DefaultLogger(stdout, egv1a1.LogLevelInfo),
170+
Stdout: stdout,
171+
Stderr: stderr,
173172
}
174173

175174
// Run envoy with an invalid config to force it to write to stderr and exit quickly
@@ -179,15 +178,17 @@ func TestInfra_runEnvoy_OutputRedirection(t *testing.T) {
179178
}
180179

181180
i.runEnvoy(t.Context(), "", "test", args)
182-
require.Len(t, i.proxyContextMap, 1)
181+
_, ok := i.proxyContextMap.Load("test")
182+
require.True(t, ok, "expected proxy context to be stored")
183183

184184
// Wait a bit for envoy to fail
185185
require.Eventually(t, func() bool {
186186
return stderr.Len() > 0 || stdout.Len() > 0
187187
}, 5*time.Second, 100*time.Millisecond, "expected output to be written to buffers")
188188

189189
i.stopEnvoy("test")
190-
require.Empty(t, i.proxyContextMap)
190+
_, ok = i.proxyContextMap.Load("test")
191+
require.False(t, ok, "expected proxy context to be removed")
191192

192193
// Verify that output was captured in buffers (either stdout or stderr should have content)
193194
totalOutput := stdout.Len() + stderr.Len()

0 commit comments

Comments
 (0)