Skip to content

Commit 5263c85

Browse files
feat(exec): Add --no-session flag for improved performance
Fixes: #26588 For use cases like HPC, where `podman exec` is called in rapid succession, the standard exec process can become a bottleneck due to container locking and database I/O for session tracking. This commit introduces a new `--no-session` flag to `podman exec`. When used, this flag invokes a new, lightweight backend implementation (`ExecNoSession`) that: - Skips container locking, reducing lock contention. - Bypasses the creation, tracking, and removal of exec sessions in the database. - Executes the command directly and retrieves the exit code without persisting session state. Signed-off-by: Ryan McCann <ryan_mccann@student.uml.edu> Signed-off-by: ryanmccann1024 <ryan_mccann@student.uml.edu>
1 parent 4999775 commit 5263c85

File tree

7 files changed

+110
-10
lines changed

7 files changed

+110
-10
lines changed

cmd/podman/containers/exec.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var (
5151
execOpts entities.ExecOptions
5252
execDetach bool
5353
execCidFile string
54+
execNoSession bool
5455
)
5556

5657
func execFlags(cmd *cobra.Command) {
@@ -100,6 +101,10 @@ func execFlags(cmd *cobra.Command) {
100101
flags.Int32(waitFlagName, 0, "Total seconds to wait for container to start")
101102
_ = flags.MarkHidden(waitFlagName)
102103

104+
if !registry.IsRemote() {
105+
flags.BoolVar(&execNoSession, "no-session", false, "Do not create a database session for the exec process")
106+
}
107+
103108
if registry.IsRemote() {
104109
_ = flags.MarkHidden("preserve-fds")
105110
}
@@ -121,6 +126,12 @@ func init() {
121126
}
122127

123128
func exec(cmd *cobra.Command, args []string) error {
129+
if execNoSession {
130+
if execDetach || cmd.Flags().Changed("detach-keys") {
131+
return errors.New("--no-session cannot be used with --detach or --detach-keys")
132+
}
133+
}
134+
124135
nameOrID, command, err := determineTargetCtrAndCmd(args, execOpts.Latest, execCidFile != "")
125136
if err != nil {
126137
return err
@@ -168,17 +179,21 @@ func exec(cmd *cobra.Command, args []string) error {
168179
}
169180
}
170181

171-
if !execDetach {
172-
streams := define.AttachStreams{}
173-
streams.OutputStream = os.Stdout
174-
streams.ErrorStream = os.Stderr
175-
if execOpts.Interactive {
176-
streams.InputStream = bufio.NewReader(os.Stdin)
177-
streams.AttachInput = true
178-
}
179-
streams.AttachOutput = true
180-
streams.AttachError = true
182+
streams := define.AttachStreams{}
183+
streams.OutputStream = os.Stdout
184+
streams.ErrorStream = os.Stderr
185+
if execOpts.Interactive {
186+
streams.InputStream = bufio.NewReader(os.Stdin)
187+
streams.AttachInput = true
188+
}
189+
streams.AttachOutput = true
190+
streams.AttachError = true
181191

192+
if execNoSession {
193+
exitCode, err := registry.ContainerEngine().ContainerExecNoSession(registry.Context(), nameOrID, execOpts, streams)
194+
registry.SetExitCode(exitCode)
195+
return err
196+
} else if !execDetach {
182197
exitCode, err := registry.ContainerEngine().ContainerExec(registry.Context(), nameOrID, execOpts, streams)
183198
registry.SetExitCode(exitCode)
184199
return err
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
####> This option file is used in:
2+
####> podman exec
3+
####> If file is edited, make sure the changes
4+
####> are applicable to all of those.
5+
#### **--no-session**
6+
7+
Do not create a database session for the exec process. This can improve performance but the exec session will not be visible to other podman commands.

docs/source/markdown/podman-exec.1.md.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Start the exec session, but do not attach to it. The command runs in the backgro
3131

3232
@@option latest
3333

34+
@@option no-session
35+
3436
@@option preserve-fd
3537

3638
@@option preserve-fds

libpod/container_exec.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,3 +1306,60 @@ func justWriteExecExitCode(c *Container, sessionID string, exitCode int, emitEve
13061306
// Finally, save our changes.
13071307
return c.save()
13081308
}
1309+
1310+
// ExecNoSession is a high-level wrapper for execNoSession.
1311+
func (c *Container) ExecNoSession(config *ExecConfig, streams *define.AttachStreams, resize <-chan resize.TerminalSize) (int, error) {
1312+
return c.execNoSession(config, streams, resize, false)
1313+
}
1314+
1315+
// execNoSession executes a command in a container without creating a persistent exec session.
1316+
// It skips database operations and minimizes container locking for performance.
1317+
func (c *Container) execNoSession(config *ExecConfig, streams *define.AttachStreams, _ <-chan resize.TerminalSize, _ bool) (exitCode int, retErr error) {
1318+
sessionID := stringid.GenerateRandomID()
1319+
1320+
session := &ExecSession{
1321+
Id: sessionID,
1322+
Config: config,
1323+
}
1324+
1325+
if !c.batched {
1326+
c.lock.Lock()
1327+
if err := c.syncContainer(); err != nil {
1328+
c.lock.Unlock()
1329+
return -1, err
1330+
}
1331+
}
1332+
1333+
opts, err := prepareForExec(c, session)
1334+
1335+
if !c.batched {
1336+
c.lock.Unlock()
1337+
}
1338+
1339+
if err != nil {
1340+
return -1, err
1341+
}
1342+
1343+
defer func() {
1344+
if err := c.cleanupExecBundle(sessionID); err != nil {
1345+
logrus.Errorf("cleanup of no-session exec %s failed: %v", sessionID, err)
1346+
}
1347+
}()
1348+
1349+
_, attachChan, err := c.ociRuntime.ExecContainer(c, sessionID, opts, streams, nil)
1350+
if err != nil {
1351+
return -1, err
1352+
}
1353+
1354+
err = <-attachChan
1355+
if err != nil && !errors.Is(err, define.ErrDetach) {
1356+
return -1, err
1357+
}
1358+
1359+
exitCode, err = c.readExecExitCode(sessionID)
1360+
if err != nil {
1361+
return -1, fmt.Errorf("retrieving no-session exec %s exit code: %w", sessionID, err)
1362+
}
1363+
1364+
return exitCode, nil
1365+
}

pkg/domain/entities/engine_container.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type ContainerEngine interface { //nolint:interfacebloat
2626
ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error)
2727
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
2828
ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error)
29+
ContainerExecNoSession(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error)
2930
ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error)
3031
ContainerExists(ctx context.Context, nameOrID string, options ContainerExistsOptions) (*BoolReport, error)
3132
ContainerExport(ctx context.Context, nameOrID string, options ContainerExportOptions) error

pkg/domain/infra/abi/containers.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,20 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, o
913913
return define.TranslateExecErrorToExitCode(ec, err), err
914914
}
915915

916+
func (ic *ContainerEngine) ContainerExecNoSession(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) {
917+
ctr, err := ic.Libpod.LookupContainer(nameOrID)
918+
if err != nil {
919+
return 0, err
920+
}
921+
922+
execConfig, err := makeExecConfig(options, ic.Libpod)
923+
if err != nil {
924+
return define.ExecErrorCodeGeneric, err
925+
}
926+
927+
return ctr.ExecNoSession(execConfig, &streams, nil)
928+
}
929+
916930
func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) {
917931
err := checkExecPreserveFDs(options)
918932
if err != nil {

pkg/domain/infra/tunnel/containers.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,10 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, o
624624
return inspectOut.ExitCode, nil
625625
}
626626

627+
func (ic *ContainerEngine) ContainerExecNoSession(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) {
628+
return 0, errors.New("--no-session is not supported for the remote client")
629+
}
630+
627631
func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (retSessionID string, retErr error) {
628632
createConfig := makeExecConfig(options)
629633

0 commit comments

Comments
 (0)