all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Fiona Ebner <f.ebner@proxmox.com>
To: Thomas Lamprecht <t.lamprecht@proxmox.com>, pve-devel@lists.proxmox.com
Subject: Re: [PATCH manager 12/19] vzdump: allow jobs to run alongside each other
Date: Fri, 6 Nov 2026 15:49:00 +0100	[thread overview]
Message-ID: <21889e70-4dd4-4470-b9d7-48a80dbfc5fa@proxmox.com> (raw)
In-Reply-To: <20261101225849.884587-13-t.lamprecht@proxmox.com>

Am 02.11.26 um 12:01 AM schrieb Thomas Lamprecht:
> diff --git a/PVE/API2/VZDump.pm b/PVE/API2/VZDump.pm
> index 73291189f..3e31505f3 100644
> --- a/PVE/API2/VZDump.pm
> +++ b/PVE/API2/VZDump.pm
> @@ -137,7 +137,7 @@ __PACKAGE__->register_method({
>              my $vzdump = PVE::VZDump->new($cmdline, $param, $skiplist);
>  
>              my $LOCK_FH = eval {
> -                my $lock_fh = $vzdump->getlock($upid); # only one process allowed
> +                my $lock_fh = $vzdump->getlock($upid);
>                  $vzdump->init_job();
>                  $lock_fh;
>              };
> diff --git a/PVE/VZDump.pm b/PVE/VZDump.pm
> index 33fe8793e..f597f7ed7 100644
> --- a/PVE/VZDump.pm
> +++ b/PVE/VZDump.pm
> @@ -46,6 +46,7 @@ PVE::JSONSchema::register_standard_option(
>  my @posix_filesystems = qw(ext3 ext4 nfs nfs4 reiserfs xfs);
>  
>  my $lockfile = '/var/run/vzdump.lock';
> +my $lockdir = '/run/vzdump';
>  my $logdir = '/var/log/vzdump';
>  
>  my @plugins = qw();
> @@ -682,6 +683,14 @@ sub new {
>  
>      $self->{'default-prune-backups'} = $defaults->{'prune-backups'};
>  
> +    # external providers are not known to cope with other backups running at the same time
> +    my $uses_provider = $opts->{storage} && eval {
> +        PVE::Storage::storage_has_feature(
> +            PVE::Storage::config(), $opts->{storage}, 'backup-provider',
> +        );
> +    };

Should log a message/warning if something goes wrong in the eval to help
debug issues.

> +    $self->{exclusive} = !$opts->{overlap} || $uses_provider;
> +
>      return $self;
>  }
>  
> @@ -781,14 +790,32 @@ sub getlock {
>      my $maxwait = $self->{opts}->{lockwait} || $self->{lockwait};

Pre-existing, but it seems like $self->{lockwait} is never set.

>  
>      die "missing UPID" if !$upid; # should not happen
> +    $self->{upid} = $upid;
> +
> +    mkdir $lockdir;

Might be nicer to add it to the package's tmpfiles config, like /run/pve
already is.

>  
>      my $SERVER_FLCK;
>      if (!open($SERVER_FLCK, '>>', "$lockfile")) {
>          debugmsg('err', "can't open lock on file '$lockfile' - $!", undef, 1);
>          die "can't open lock on file '$lockfile' - $!";
>      }
> +    open(my $gate, '>>', "$lockdir/gate.lock")
> +        or die "can't open lock on file '$lockdir/gate.lock' - $!\n";
>  
> -    if (!flock($SERVER_FLCK, LOCK_EX | LOCK_NB)) {
> +    # Jobs that may overlap share the global lock. A job waiting to get it exclusively holds the
> +    # gate meanwhile, so overlapping jobs starting later cannot keep it waiting forever.
> +    my $mode = $self->{exclusive} ? LOCK_EX : LOCK_SH;
> +    my $acquire = sub {
> +        my ($flags) = @_;
> +
> +        flock($gate, $mode | $flags) or return 0;
> +        my $locked = flock($SERVER_FLCK, $mode | $flags);
> +        flock($gate, LOCK_UN);
> +
> +        return $locked;
> +    };
> +
> +    if (!$acquire->(LOCK_NB)) {
>          if (!$maxwait) {
>              debugmsg('err', "can't acquire lock '$lockfile' (wait = 0)", undef, 1);
>              die "can't acquire lock '$lockfile' (wait = 0)";
> @@ -800,7 +827,7 @@ sub getlock {
>  
>              local $SIG{ALRM} = sub { alarm(0); die "got timeout\n"; };
>  
> -            if (!flock($SERVER_FLCK, LOCK_EX)) {
> +            if (!$acquire->(0)) {
>                  my $err = $!;

This might not be the error code from the failure of flock($SERVER_FLCK,
...), which we are interested in, since there is an flock($gate, ...)
call after it.

>                  close($SERVER_FLCK);
>                  alarm(0);
> @@ -1635,12 +1662,145 @@ my sub finish_guest_backup {
>      }
>  }
>  
> +my sub try_lock_file {
> +    my ($path) = @_;
> +
> +    open(my $fh, '>>', $path) or die "unable to open lock file '$path' - $!\n";
> +    return $fh if flock($fh, LOCK_EX | LOCK_NB);
> +
> +    close($fh);
> +    return undef;
> +}
> +
> +my sub try_guest_lock {
> +    my ($self, $vmid) = @_;
> +
> +    my $fh = try_lock_file("$lockdir/guest-$vmid.lock") or return undef;
> +    truncate($fh, 0);
> +    print $fh "$self->{upid}\n";
> +    $fh->flush();
> +
> +    return $fh;
> +}
> +
> +# Slots above a lowered limit stay in use until their backups end, no new one starts before.
> +my sub try_slot {
> +    my ($prefix, $limit) = @_;
> +
> +    for my $path (glob("$prefix-*.lock")) {
> +        my ($slot) = $path =~ m/^\Q$prefix\E-(\d+)\.lock$/ or next;
> +        next if $slot <= $limit;
> +        my $fh = try_lock_file("$prefix-$slot.lock") or return undef;

We could start re-using lower slots already, even if there is a
remaining slot in-use above the limit. We just need to make sure that
the count of in-use slots stays below the limit.

Otherwise, in a situation with a single long-running task with a slot
above the limit, we have to wait until that single high-slot task is
finished, which seems unfortunate.

> +        close($fh);
> +    }
> +    for my $slot (1 .. $limit) {
> +        my $fh = try_lock_file("$prefix-$slot.lock");
> +        return $fh if $fh;
> +    }
> +    return undef;
> +}
> +
> +my sub try_node_slot {
> +    return try_slot("$lockdir/slot", node_backup_concurrency());
> +}
> +
> +my sub log_wait {
> +    my ($self, $reason) = @_;
> +
> +    return if ($self->{'waiting-for'} // '') eq $reason;
> +    $self->{'waiting-for'} = $reason;
> +    debugmsg('info', "waiting for $reason");
> +}
> +
> +my sub guest_moved_to {
> +    my ($vmid) = @_;
> +
> +    PVE::Cluster::cfs_update();
> +    my $node = PVE::Cluster::get_vmlist()->{ids}->{$vmid}->{node};
> +
> +    return defined($node) && $node ne PVE::INotify::nodename() ? $node : undef;
> +}
> +
> +my sub claim_next_guest {
> +    my ($self, $queue, $deferred) = @_;
> +
> +    my $maxwait = ($self->{opts}->{lockwait} || 0) * 60;

The default according to the schema would be 3 hours. But getlock()
already relies on the value being initialized by new(), so we could do
the same here.

> +
> +    for (my $i = 0; $i < scalar($queue->@*); $i++) {
> +        my $task = $queue->[$i];
> +        my $vmid = $task->{vmid};
> +
> +        my $guest_lock = try_guest_lock($self, $vmid);
> +        if (!$guest_lock) {
> +            my $holder = PVE::Tools::file_read_firstline("$lockdir/guest-$vmid.lock") // '';

Nit: could use PVE::File

> +            if (!defined($deferred->{$vmid})) {
> +                $deferred->{$vmid} = time();
> +                debugmsg('info', "VM $vmid is being backed up by '$holder', deferring");
> +            } elsif (time() - $deferred->{$vmid} >= $maxwait) {
> +                splice($queue->@*, $i--, 1);
> +                skip_task($task, "still being backed up by '$holder'");
> +            }
> +            next;
> +        }
> +




  reply	other threads:[~2026-11-06 14:49 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-01 22:58 [PATCH cluster,storage,guest-common,container,manager,docs 00/19] parallel guest backups and backup concurrency limits Thomas Lamprecht
2026-11-01 22:58 ` [PATCH cluster 01/19] datacenter config: add backup concurrency setting Thomas Lamprecht
2026-11-01 22:58 ` [PATCH cluster 02/19] datacenter config: backup concurrency: add cluster-wide limit Thomas Lamprecht
2026-11-01 22:58 ` [PATCH storage 03/19] add backup concurrency option to storages Thomas Lamprecht
2026-11-01 22:58 ` [PATCH guest-common 04/19] vzdump: add options for parallel guest backups and overlapping jobs Thomas Lamprecht
2026-11-01 22:58 ` [PATCH container 05/19] vzdump: honor bandwidth limit for backups to Proxmox Backup Server Thomas Lamprecht
2026-11-06 10:28   ` applied: " Fiona Ebner
2026-11-01 22:58 ` [PATCH manager 06/19] vzdump: notification: capture guest logs right after each backup Thomas Lamprecht
2026-11-06 14:49   ` Fiona Ebner
2026-11-01 22:58 ` [PATCH manager 07/19] vzdump: stop all running backup jobs of the node Thomas Lamprecht
2026-11-06 14:48   ` Fiona Ebner
2026-11-01 22:58 ` [PATCH manager 08/19] vzdump: show the error if a job could not get the global lock Thomas Lamprecht
2026-11-01 22:58 ` [PATCH manager 09/19] vzdump: run job-init hook and set up target once the job may start Thomas Lamprecht
2026-11-06 14:48   ` Fiona Ebner
2026-11-01 22:58 ` [PATCH manager 10/19] vzdump: list guests as skipped if the job could not start Thomas Lamprecht
2026-11-01 22:58 ` [PATCH manager 11/19] vzdump: back up guests of a job in parallel Thomas Lamprecht
2026-11-06 14:49   ` Fiona Ebner
2026-11-01 22:58 ` [PATCH manager 12/19] vzdump: allow jobs to run alongside each other Thomas Lamprecht
2026-11-06 14:49   ` Fiona Ebner [this message]
2026-11-01 22:58 ` [PATCH manager 13/19] ui: options: add backup concurrency for datacenter and nodes Thomas Lamprecht
2026-11-01 22:58 ` [PATCH manager 14/19] ui: backup job: add concurrency and overlap options Thomas Lamprecht
2026-11-01 22:58 ` [PATCH manager 15/19] vzdump: limit concurrent guest backups cluster-wide Thomas Lamprecht
2026-11-06 14:49   ` Fiona Ebner
2026-11-01 22:58 ` [PATCH manager 16/19] vzdump: honor backup concurrency limits of storages Thomas Lamprecht
2026-11-01 22:58 ` [PATCH manager 17/19] api: backup info: add endpoints for cluster-wide backup slots Thomas Lamprecht
2026-11-01 22:58 ` [PATCH manager 18/19] ui: add cluster-wide and storage backup concurrency limits Thomas Lamprecht
2026-11-01 22:58 ` [PATCH docs 19/19] vzdump: document concurrent backups and their limits Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=21889e70-4dd4-4470-b9d7-48a80dbfc5fa@proxmox.com \
    --to=f.ebner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    --cc=t.lamprecht@proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal