Skip to content

Commit d8382db

Browse files
authored
Merge pull request #22 from PasarGuard/optimize-user-management-map-based-storage
Optimize user management map based storage
2 parents 4f8568b + a176149 commit d8382db

1 file changed

Lines changed: 141 additions & 129 deletions

File tree

backend/xray/config.go

Lines changed: 141 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ const (
2323
Shadowsocks = "shadowsocks"
2424
)
2525

26+
// extractIDFromEmail extracts the ID part from an email in the format "id.username"
27+
// If no dot is found, returns the full email string
28+
func extractIDFromEmail(email string) string {
29+
if idx := strings.Index(email, "."); idx != -1 {
30+
return email[:idx]
31+
}
32+
return email
33+
}
34+
2635
type Config struct {
2736
LogConfig *conf.LogConfig `json:"log"`
2837
RouterConfig *conf.RouterConfig `json:"routing"`
@@ -68,19 +77,23 @@ func (i *Inbound) syncUsers(users []*common.User) {
6877
switch i.Protocol {
6978
case Vmess:
7079
clients := make([]*api.VmessAccount, 0, len(users))
71-
7280
for _, user := range users {
7381
if user.GetProxies().GetVmess() == nil {
7482
continue
7583
}
76-
account, err := api.NewVmessAccount(user)
77-
if err != nil {
78-
log.Println("error for user", user.GetEmail(), ":", err)
79-
}
8084
if slices.Contains(user.Inbounds, i.Tag) {
85+
account, err := api.NewVmessAccount(user)
86+
if err != nil {
87+
log.Println("error for user", user.GetEmail(), ":", err)
88+
continue
89+
}
8190
clients = append(clients, account)
8291
}
8392
}
93+
// Keep sorted by ID (part before dot in email) for binary search
94+
slices.SortFunc(clients, func(a, b *api.VmessAccount) int {
95+
return strings.Compare(extractIDFromEmail(a.Email), extractIDFromEmail(b.Email))
96+
})
8497
i.Settings["clients"] = clients
8598

8699
case Vless:
@@ -89,15 +102,20 @@ func (i *Inbound) syncUsers(users []*common.User) {
89102
if user.GetProxies().GetVless() == nil {
90103
continue
91104
}
92-
account, err := api.NewVlessAccount(user)
93-
if err != nil {
94-
log.Println("error for user", user.GetEmail(), ":", err)
95-
}
96105
if slices.Contains(user.Inbounds, i.Tag) {
106+
account, err := api.NewVlessAccount(user)
107+
if err != nil {
108+
log.Println("error for user", user.GetEmail(), ":", err)
109+
continue
110+
}
97111
newAccount := checkVless(i, *account)
98112
clients = append(clients, &newAccount)
99113
}
100114
}
115+
// Keep sorted by ID (part before dot in email) for binary search
116+
slices.SortFunc(clients, func(a, b *api.VlessAccount) int {
117+
return strings.Compare(extractIDFromEmail(a.Email), extractIDFromEmail(b.Email))
118+
})
101119
i.Settings["clients"] = clients
102120

103121
case Trojan:
@@ -110,6 +128,10 @@ func (i *Inbound) syncUsers(users []*common.User) {
110128
clients = append(clients, api.NewTrojanAccount(user))
111129
}
112130
}
131+
// Keep sorted by ID (part before dot in email) for binary search
132+
slices.SortFunc(clients, func(a, b *api.TrojanAccount) int {
133+
return strings.Compare(extractIDFromEmail(a.Email), extractIDFromEmail(b.Email))
134+
})
113135
i.Settings["clients"] = clients
114136

115137
case Shadowsocks:
@@ -126,8 +148,11 @@ func (i *Inbound) syncUsers(users []*common.User) {
126148
clients = append(clients, &newAccount)
127149
}
128150
}
151+
// Keep sorted by ID (part before dot in email) for binary search
152+
slices.SortFunc(clients, func(a, b *api.ShadowsocksAccount) int {
153+
return strings.Compare(extractIDFromEmail(a.Email), extractIDFromEmail(b.Email))
154+
})
129155
i.Settings["clients"] = clients
130-
131156
} else {
132157
clients := make([]*api.ShadowsocksTcpAccount, 0, len(users))
133158
for _, user := range users {
@@ -138,6 +163,10 @@ func (i *Inbound) syncUsers(users []*common.User) {
138163
clients = append(clients, api.NewShadowsocksTcpAccount(user))
139164
}
140165
}
166+
// Keep sorted by ID (part before dot in email) for binary search
167+
slices.SortFunc(clients, func(a, b *api.ShadowsocksTcpAccount) int {
168+
return strings.Compare(extractIDFromEmail(a.Email), extractIDFromEmail(b.Email))
169+
})
141170
i.Settings["clients"] = clients
142171
}
143172
}
@@ -148,180 +177,163 @@ func (i *Inbound) updateUser(account api.Account) {
148177
defer i.mu.Unlock()
149178

150179
email := account.GetEmail()
151-
switch account.(type) {
180+
searchID := extractIDFromEmail(email)
181+
switch a := account.(type) {
152182
case *api.VmessAccount:
153-
clients, ok := i.Settings["clients"].([]*api.VmessAccount)
154-
if !ok {
155-
clients = []*api.VmessAccount{}
156-
}
157-
158-
for x, client := range clients {
159-
if client.Email == email {
160-
clients = append(clients[:x], clients[x+1:]...)
161-
break
162-
}
183+
clients, _ := i.Settings["clients"].([]*api.VmessAccount)
184+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.VmessAccount, id string) int {
185+
return strings.Compare(extractIDFromEmail(c.Email), id)
186+
}); found {
187+
clients = append(clients[:idx], clients[idx+1:]...)
163188
}
164-
165-
i.Settings["clients"] = append(clients, account.(*api.VmessAccount))
189+
insertIdx, _ := slices.BinarySearchFunc(clients, searchID, func(c *api.VmessAccount, id string) int {
190+
return strings.Compare(extractIDFromEmail(c.Email), id)
191+
})
192+
clients = append(clients, nil)
193+
copy(clients[insertIdx+1:], clients[insertIdx:])
194+
clients[insertIdx] = a
195+
i.Settings["clients"] = clients
166196

167197
case *api.VlessAccount:
168-
clients, ok := i.Settings["clients"].([]*api.VlessAccount)
169-
if !ok {
170-
clients = []*api.VlessAccount{}
171-
}
172-
173-
for x, client := range clients {
174-
if client.Email == email {
175-
clients = append(clients[:x], clients[x+1:]...)
176-
break
177-
}
198+
clients, _ := i.Settings["clients"].([]*api.VlessAccount)
199+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.VlessAccount, id string) int {
200+
return strings.Compare(extractIDFromEmail(c.Email), id)
201+
}); found {
202+
clients = append(clients[:idx], clients[idx+1:]...)
178203
}
179-
180-
i.Settings["clients"] = append(clients, account.(*api.VlessAccount))
204+
insertIdx, _ := slices.BinarySearchFunc(clients, searchID, func(c *api.VlessAccount, id string) int {
205+
return strings.Compare(extractIDFromEmail(c.Email), id)
206+
})
207+
clients = append(clients, nil)
208+
copy(clients[insertIdx+1:], clients[insertIdx:])
209+
clients[insertIdx] = a
210+
i.Settings["clients"] = clients
181211

182212
case *api.TrojanAccount:
183-
clients, ok := i.Settings["clients"].([]*api.TrojanAccount)
184-
if !ok {
185-
clients = []*api.TrojanAccount{}
186-
}
187-
188-
for x, client := range clients {
189-
if client.Email == email {
190-
clients = append(clients[:x], clients[x+1:]...)
191-
break
192-
}
213+
clients, _ := i.Settings["clients"].([]*api.TrojanAccount)
214+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.TrojanAccount, id string) int {
215+
return strings.Compare(extractIDFromEmail(c.Email), id)
216+
}); found {
217+
clients = append(clients[:idx], clients[idx+1:]...)
193218
}
194-
195-
i.Settings["clients"] = append(clients, account.(*api.TrojanAccount))
219+
insertIdx, _ := slices.BinarySearchFunc(clients, searchID, func(c *api.TrojanAccount, id string) int {
220+
return strings.Compare(extractIDFromEmail(c.Email), id)
221+
})
222+
clients = append(clients, nil)
223+
copy(clients[insertIdx+1:], clients[insertIdx:])
224+
clients[insertIdx] = a
225+
i.Settings["clients"] = clients
196226

197227
case *api.ShadowsocksTcpAccount:
198-
clients, ok := i.Settings["clients"].([]*api.ShadowsocksTcpAccount)
199-
if !ok {
200-
clients = []*api.ShadowsocksTcpAccount{}
201-
}
202-
203-
for x, client := range clients {
204-
if client.Email == email {
205-
clients = append(clients[:x], clients[x+1:]...)
206-
break
207-
}
228+
clients, _ := i.Settings["clients"].([]*api.ShadowsocksTcpAccount)
229+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.ShadowsocksTcpAccount, id string) int {
230+
return strings.Compare(extractIDFromEmail(c.Email), id)
231+
}); found {
232+
clients = append(clients[:idx], clients[idx+1:]...)
208233
}
209-
210-
i.Settings["clients"] = append(clients, account.(*api.ShadowsocksTcpAccount))
234+
insertIdx, _ := slices.BinarySearchFunc(clients, searchID, func(c *api.ShadowsocksTcpAccount, id string) int {
235+
return strings.Compare(extractIDFromEmail(c.Email), id)
236+
})
237+
clients = append(clients, nil)
238+
copy(clients[insertIdx+1:], clients[insertIdx:])
239+
clients[insertIdx] = a
240+
i.Settings["clients"] = clients
211241

212242
case *api.ShadowsocksAccount:
213-
clients, ok := i.Settings["clients"].([]*api.ShadowsocksAccount)
214-
if !ok {
215-
clients = []*api.ShadowsocksAccount{}
243+
clients, _ := i.Settings["clients"].([]*api.ShadowsocksAccount)
244+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.ShadowsocksAccount, id string) int {
245+
return strings.Compare(extractIDFromEmail(c.Email), id)
246+
}); found {
247+
clients = append(clients[:idx], clients[idx+1:]...)
216248
}
217-
218-
for x, client := range clients {
219-
if client.Email == email {
220-
clients = append(clients[:x], clients[x+1:]...)
221-
break
222-
}
249+
method, ok := i.Settings["method"].(string)
250+
var newAccount *api.ShadowsocksAccount
251+
if ok {
252+
na := checkShadowsocks2022(method, *a)
253+
newAccount = &na
254+
} else {
255+
newAccount = a
223256
}
224-
225-
method := i.Settings["method"].(string)
226-
newAccount := checkShadowsocks2022(method, *account.(*api.ShadowsocksAccount))
227-
i.Settings["clients"] = append(clients, &newAccount)
228-
229-
default:
230-
return
257+
insertIdx, _ := slices.BinarySearchFunc(clients, searchID, func(c *api.ShadowsocksAccount, id string) int {
258+
return strings.Compare(extractIDFromEmail(c.Email), id)
259+
})
260+
clients = append(clients, nil)
261+
copy(clients[insertIdx+1:], clients[insertIdx:])
262+
clients[insertIdx] = newAccount
263+
i.Settings["clients"] = clients
231264
}
232265
}
233266

234267
func (i *Inbound) removeUser(email string) {
235268
i.mu.Lock()
236269
defer i.mu.Unlock()
237270

271+
searchID := extractIDFromEmail(email)
238272
switch Protocol(i.Protocol) {
239273
case Vmess:
240-
clients, ok := i.Settings["clients"].([]*api.VmessAccount)
241-
if !ok {
242-
clients = []*api.VmessAccount{}
243-
}
244-
245-
for x, client := range clients {
246-
if client.Email == email {
247-
clients = append(clients[:x], clients[x+1:]...)
248-
break
249-
}
274+
clients, _ := i.Settings["clients"].([]*api.VmessAccount)
275+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.VmessAccount, id string) int {
276+
return strings.Compare(extractIDFromEmail(c.Email), id)
277+
}); found {
278+
clients = append(clients[:idx], clients[idx+1:]...)
250279
}
251280
i.Settings["clients"] = clients
252281

253282
case Vless:
254-
clients, ok := i.Settings["clients"].([]*api.VlessAccount)
255-
if !ok {
256-
clients = []*api.VlessAccount{}
257-
}
258-
259-
for x, client := range clients {
260-
if client.Email == email {
261-
clients = append(clients[:x], clients[x+1:]...)
262-
break
263-
}
283+
clients, _ := i.Settings["clients"].([]*api.VlessAccount)
284+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.VlessAccount, id string) int {
285+
return strings.Compare(extractIDFromEmail(c.Email), id)
286+
}); found {
287+
clients = append(clients[:idx], clients[idx+1:]...)
264288
}
265289
i.Settings["clients"] = clients
266290

267291
case Trojan:
268-
clients, ok := i.Settings["clients"].([]*api.TrojanAccount)
269-
if !ok {
270-
clients = []*api.TrojanAccount{}
271-
}
272-
273-
for x, client := range clients {
274-
if client.Email == email {
275-
clients = append(clients[:x], clients[x+1:]...)
276-
break
277-
}
292+
clients, _ := i.Settings["clients"].([]*api.TrojanAccount)
293+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.TrojanAccount, id string) int {
294+
return strings.Compare(extractIDFromEmail(c.Email), id)
295+
}); found {
296+
clients = append(clients[:idx], clients[idx+1:]...)
278297
}
279298
i.Settings["clients"] = clients
280299

281300
case Shadowsocks:
282301
method, methodOk := i.Settings["method"].(string)
283302
if methodOk && strings.HasPrefix(method, "2022-blake3") {
284-
clients, ok := i.Settings["clients"].([]*api.ShadowsocksAccount)
285-
if !ok {
286-
clients = []*api.ShadowsocksAccount{}
287-
}
288-
289-
for x, client := range clients {
290-
if client.Email == email {
291-
clients = append(clients[:x], clients[x+1:]...)
292-
break
293-
}
303+
clients, _ := i.Settings["clients"].([]*api.ShadowsocksAccount)
304+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.ShadowsocksAccount, id string) int {
305+
return strings.Compare(extractIDFromEmail(c.Email), id)
306+
}); found {
307+
clients = append(clients[:idx], clients[idx+1:]...)
294308
}
295309
i.Settings["clients"] = clients
296-
297310
} else {
298-
clients, ok := i.Settings["clients"].([]*api.ShadowsocksTcpAccount)
299-
if !ok {
300-
clients = []*api.ShadowsocksTcpAccount{}
301-
}
302-
303-
for x, client := range clients {
304-
if client.Email == email {
305-
clients = append(clients[:x], clients[x+1:]...)
306-
break
307-
}
311+
clients, _ := i.Settings["clients"].([]*api.ShadowsocksTcpAccount)
312+
if idx, found := slices.BinarySearchFunc(clients, searchID, func(c *api.ShadowsocksTcpAccount, id string) int {
313+
return strings.Compare(extractIDFromEmail(c.Email), id)
314+
}); found {
315+
clients = append(clients[:idx], clients[idx+1:]...)
308316
}
309317
i.Settings["clients"] = clients
310318
}
311-
default:
312-
return
313319
}
314320
}
315321

316322
type Stats struct{}
317323

318324
func (c *Config) ToBytes() ([]byte, error) {
325+
// Acquire read locks for all inbounds
319326
for _, i := range c.InboundConfigs {
320327
i.mu.RLock()
321-
defer i.mu.RUnlock()
322328
}
323329

324330
b, err := json.Marshal(c)
331+
332+
// Release all locks
333+
for _, i := range c.InboundConfigs {
334+
i.mu.RUnlock()
335+
}
336+
325337
if err != nil {
326338
return nil, err
327339
}

0 commit comments

Comments
 (0)