@@ -73,6 +73,7 @@ type Options struct {
73
73
ReportMetadataInterval time.Duration
74
74
ServiceBannerRefreshInterval time.Duration
75
75
Syscaller agentproc.Syscaller
76
+ ProcessManagementInterval time.Duration
76
77
}
77
78
78
79
type Client interface {
@@ -125,6 +126,14 @@ func New(options Options) Agent {
125
126
prometheusRegistry = prometheus .NewRegistry ()
126
127
}
127
128
129
+ if options .Syscaller == nil {
130
+ options .Syscaller = agentproc.UnixSyscaller {}
131
+ }
132
+
133
+ if options .ProcessManagementInterval == 0 {
134
+ options .ProcessManagementInterval = time .Second
135
+ }
136
+
128
137
ctx , cancelFunc := context .WithCancel (context .Background ())
129
138
a := & agent {
130
139
tailnetListenPort : options .TailnetListenPort ,
@@ -148,6 +157,8 @@ func New(options Options) Agent {
148
157
sshMaxTimeout : options .SSHMaxTimeout ,
149
158
subsystems : options .Subsystems ,
150
159
addresses : options .Addresses ,
160
+ syscaller : options .Syscaller ,
161
+ processManagementInterval : options .ProcessManagementInterval ,
151
162
152
163
prometheusRegistry : prometheusRegistry ,
153
164
metrics : newAgentMetrics (prometheusRegistry ),
@@ -200,9 +211,10 @@ type agent struct {
200
211
201
212
connCountReconnectingPTY atomic.Int64
202
213
203
- prometheusRegistry * prometheus.Registry
204
- metrics * agentMetrics
205
- syscaller agentproc.Syscaller
214
+ prometheusRegistry * prometheus.Registry
215
+ metrics * agentMetrics
216
+ processManagementInterval time.Duration
217
+ syscaller agentproc.Syscaller
206
218
}
207
219
208
220
func (a * agent ) TailnetConn () * tailnet.Conn {
@@ -1263,15 +1275,9 @@ func (a *agent) startReportingConnectionStats(ctx context.Context) {
1263
1275
var prioritizedProcs = []string {"coder" }
1264
1276
1265
1277
func (a * agent ) manageProcessPriorityLoop (ctx context.Context ) {
1266
- ticker := time .NewTicker (time . Minute )
1278
+ ticker := time .NewTicker (a . processManagementInterval )
1267
1279
defer ticker .Stop ()
1268
1280
1269
- const (
1270
- procDir = agentproc .DefaultProcDir
1271
- niceness = 10
1272
- oomScoreAdj = 100
1273
- )
1274
-
1275
1281
if val := a .envVars [EnvProcMemNice ]; val == "" || runtime .GOOS != "linux" {
1276
1282
a .logger .Info (ctx , "process priority not enabled, agent will not manage process niceness/oom_score_adj " ,
1277
1283
slog .F ("env_var" , EnvProcMemNice ),
@@ -1281,81 +1287,103 @@ func (a *agent) manageProcessPriorityLoop(ctx context.Context) {
1281
1287
return
1282
1288
}
1283
1289
1290
+ // Do once before falling into loop.
1291
+ if err := a .manageProcessPriority (ctx ); err != nil {
1292
+ a .logger .Error (ctx , "manage process priority" ,
1293
+ slog .F ("dir" , agentproc .DefaultProcDir ),
1294
+ slog .Error (err ),
1295
+ )
1296
+ }
1297
+
1284
1298
for {
1285
1299
select {
1286
1300
case <- ticker .C :
1287
- procs , err := agentproc .List (a .filesystem , a .syscaller , agentproc .DefaultProcDir )
1288
- if err != nil {
1289
- a .logger .Error (ctx , "failed to list procs" ,
1301
+ if err := a .manageProcessPriority (ctx ); err != nil {
1302
+ a .logger .Error (ctx , "manage process priority" ,
1290
1303
slog .F ("dir" , agentproc .DefaultProcDir ),
1291
1304
slog .Error (err ),
1292
1305
)
1293
- continue
1294
1306
}
1295
- for _ , proc := range procs {
1296
- // Trim off the path e.g. "./coder" -> "coder"
1297
- name := filepath .Base (proc .Name ())
1298
- // If the process is prioritized we should adjust
1299
- // it's oom_score_adj and avoid lowering its niceness.
1300
- if slices .Contains (prioritizedProcs , name ) {
1301
- err = proc .SetOOMAdj (oomScoreAdj )
1302
- if err != nil {
1303
- a .logger .Error (ctx , "unable to set proc oom_score_adj" ,
1304
- slog .F ("name" , proc .Name ()),
1305
- slog .F ("pid" , proc .PID ),
1306
- slog .F ("oom_score_adj" , oomScoreAdj ),
1307
- slog .Error (err ),
1308
- )
1309
- continue
1310
- }
1311
1307
1312
- a .logger .Debug (ctx , "decreased process oom_score" ,
1313
- slog .F ("name" , proc .Name ()),
1314
- slog .F ("pid" , proc .PID ),
1315
- slog .F ("oom_score_adj" , oomScoreAdj ),
1316
- )
1317
- continue
1318
- }
1308
+ case <- ctx .Done ():
1309
+ return
1310
+ }
1311
+ }
1312
+ }
1319
1313
1320
- score , err := proc .Niceness (a .syscaller )
1321
- if err != nil {
1322
- a .logger .Error (ctx , "unable to get proc niceness" ,
1323
- slog .F ("name" , proc .Name ()),
1324
- slog .F ("pid" , proc .PID ),
1325
- slog .Error (err ),
1326
- )
1327
- continue
1328
- }
1329
- if score != 20 {
1330
- a .logger .Error (ctx , "skipping process due to custom niceness" ,
1331
- slog .F ("name" , proc .Name ()),
1332
- slog .F ("pid" , proc .PID ),
1333
- slog .F ("niceness" , score ),
1334
- )
1335
- continue
1336
- }
1314
+ func (a * agent ) manageProcessPriority (ctx context.Context ) error {
1315
+ const (
1316
+ procDir = agentproc .DefaultProcDir
1317
+ niceness = 10
1318
+ oomScoreAdj = 100
1319
+ )
1337
1320
1338
- err = proc .SetNiceness (a .syscaller , niceness )
1339
- if err != nil {
1340
- a .logger .Error (ctx , "unable to set proc niceness" ,
1341
- slog .F ("name" , proc .Name ()),
1342
- slog .F ("pid" , proc .PID ),
1343
- slog .F ("niceness" , niceness ),
1344
- slog .Error (err ),
1345
- )
1346
- continue
1347
- }
1321
+ procs , err := agentproc .List (a .filesystem , a .syscaller , agentproc .DefaultProcDir )
1322
+ if err != nil {
1323
+ return xerrors .Errorf ("list: %w" , err )
1324
+ }
1348
1325
1349
- a .logger .Debug (ctx , "deprioritized process" ,
1326
+ for _ , proc := range procs {
1327
+ // Trim off the path e.g. "./coder" -> "coder"
1328
+ name := filepath .Base (proc .Name ())
1329
+ // If the process is prioritized we should adjust
1330
+ // it's oom_score_adj and avoid lowering its niceness.
1331
+ if slices .Contains (prioritizedProcs , name ) {
1332
+ err = proc .SetOOMAdj (oomScoreAdj )
1333
+ if err != nil {
1334
+ a .logger .Error (ctx , "unable to set proc oom_score_adj" ,
1350
1335
slog .F ("name" , proc .Name ()),
1351
1336
slog .F ("pid" , proc .PID ),
1352
- slog .F ("niceness" , niceness ),
1337
+ slog .F ("oom_score_adj" , oomScoreAdj ),
1338
+ slog .Error (err ),
1353
1339
)
1340
+ continue
1354
1341
}
1355
- case <- ctx .Done ():
1356
- return
1342
+
1343
+ a .logger .Debug (ctx , "decreased process oom_score" ,
1344
+ slog .F ("name" , proc .Name ()),
1345
+ slog .F ("pid" , proc .PID ),
1346
+ slog .F ("oom_score_adj" , oomScoreAdj ),
1347
+ )
1348
+ continue
1357
1349
}
1350
+
1351
+ score , err := proc .Niceness (a .syscaller )
1352
+ if err != nil {
1353
+ a .logger .Error (ctx , "unable to get proc niceness" ,
1354
+ slog .F ("name" , proc .Name ()),
1355
+ slog .F ("pid" , proc .PID ),
1356
+ slog .Error (err ),
1357
+ )
1358
+ continue
1359
+ }
1360
+ if score != 20 {
1361
+ a .logger .Error (ctx , "skipping process due to custom niceness" ,
1362
+ slog .F ("name" , proc .Name ()),
1363
+ slog .F ("pid" , proc .PID ),
1364
+ slog .F ("niceness" , score ),
1365
+ )
1366
+ continue
1367
+ }
1368
+
1369
+ err = proc .SetNiceness (a .syscaller , niceness )
1370
+ if err != nil {
1371
+ a .logger .Error (ctx , "unable to set proc niceness" ,
1372
+ slog .F ("name" , proc .Name ()),
1373
+ slog .F ("pid" , proc .PID ),
1374
+ slog .F ("niceness" , niceness ),
1375
+ slog .Error (err ),
1376
+ )
1377
+ continue
1378
+ }
1379
+
1380
+ a .logger .Debug (ctx , "deprioritized process" ,
1381
+ slog .F ("name" , proc .Name ()),
1382
+ slog .F ("pid" , proc .PID ),
1383
+ slog .F ("niceness" , niceness ),
1384
+ )
1358
1385
}
1386
+ return nil
1359
1387
}
1360
1388
1361
1389
// isClosed returns whether the API is closed or not.
0 commit comments