public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Laurențiu Leahu-Vlăducu" <l.leahu-vladucu@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: Re: [pve-devel] [PATCH cluster 2/3] RRD: fetch data from old rrd file if present and needed
Date: Fri, 12 Sep 2025 19:48:50 +0200	[thread overview]
Message-ID: <944e9387-caaa-4f87-9255-81294a2ffb5a@proxmox.com> (raw)
In-Reply-To: <20250905135517.4005478-3-a.lauterer@proxmox.com>

Reviewed-by: Laurențiu Leahu-Vlăducu <l.leahu-vladucu@proxmox.com>
Tested-by: Laurențiu Leahu-Vlăducu <l.leahu-vladucu@proxmox.com>


On 05.09.25 15:55, Aaron Lauterer wrote:
> One side effect of the RRD migration is that MAX spikes are flattened
> due to the new more fine grained resolution.
> 
> If we request RRD data that was still covered in the old rrd file, we
> can fetch those data from there and then append newer data from the new
> RRD files. This way, we still have the older more coarse resolution for
> the old data and keep spikes present.
> 
> Old RRD files can be either in {rrd}.old or just {rrd} without the .old
> suffix. This depends on if the rrd migration tool has been run on the
> host or not.
> 
> One side effect of this more dynamic approach is that the step size
> determined by RRDs::fetch will not match up in many situations.
> Therefore we drop the check for the step size!
> 
> For example, if we choose Year as the time frame, but the new file only
> has a much shorter amound of data present, RRDs::fetch will use a
> smaller step size, if it can still cover the requested data with it.
> 
> Visually this will result in very small step sizes, that should get
> wider once we get more or all data from the new RRD files, where it
> needs to then switch to the longer step sizes to cover the requested
> time frame.
> 
> For the PNG generation we rely on rrds graphing functionality which can
> also handle multiple data lines. With the way we generate the data line from
> the old line, it should look like one line.
> 
> Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
> ---
>   src/PVE/RRD.pm | 154 ++++++++++++++++++++++++++++++++++++++-----------
>   1 file changed, 119 insertions(+), 35 deletions(-)
> 
> diff --git a/src/PVE/RRD.pm b/src/PVE/RRD.pm
> index 932191d..42e0049 100644
> --- a/src/PVE/RRD.pm
> +++ b/src/PVE/RRD.pm
> @@ -7,6 +7,32 @@ use RRDs;
>   
>   use PVE::Tools;
>   
> +my $get_rrd_data = sub {
> +    my ($rrd, $cf, $is_node, $reso, $args, $res) = @_;
> +    my ($start, $step, $names, $data) = RRDs::fetch($rrd, $cf, @$args);
> +
> +    my $err = RRDs::error;
> +    die "RRD error: $err\n" if $err;
> +
> +    my $fields = scalar(@$names);
> +    for my $line (@$data) {
> +        my $entry = { 'time' => $start };
> +        $start += $step;
> +        for (my $i = 0; $i < $fields; $i++) {
> +            my $name = $names->[$i];
> +            if (defined(my $val = $line->[$i])) {
> +                $entry->{$name} = $val;
> +                $entry->{memavailable} = $val
> +                    if $is_node && $name eq 'memfree' && !exists($entry->{memavailable});
> +            } else {
> +                # leave empty fields undefined
> +                # maybe make this configurable?
> +            }
> +        }
> +        push @$res, $entry;
> +    }
> +};
> +
>   sub create_rrd_data {
>       my ($rrdname, $timeframe, $cf) = @_;
>   
> @@ -33,54 +59,71 @@ sub create_rrd_data {
>           decade => [86400 * 7, 570], # 1 week resolution, 10 years
>       };
>   
> +    my $is_node = !!($rrdname =~ /^pve-node/);
> +    $cf = "AVERAGE" if !$cf;
> +    my $res = [];
> +
>       if ($rrdname =~ /^pve2/) {
>           $setup = $setup_pve2;
>           $timeframe = "year" if $timeframe eq "decade"; # we only store up to one year in the old format
>       }
> -    my $is_node = !!($rrdname =~ /^pve-node/);
>   
>       my ($reso, $count) = @{ $setup->{$timeframe} };
>       my $ctime = $reso * int(time() / $reso);
>       my $req_start = $ctime - $reso * $count;
>   
> -    $cf = "AVERAGE" if !$cf;
> -
> -    my @args = (
> -        "-s" => $req_start,
> -        "-e" => $ctime - 1,
> -        "-r" => $reso,
> -    );
> -
> -    my $socket = "/var/run/rrdcached.sock";
> -    push @args, "--daemon" => "unix:$socket" if -S $socket;
> -
> -    my ($start, $step, $names, $data) = RRDs::fetch($rrd, $cf, @args);
> -
> -    my $err = RRDs::error;
> -    die "RRD error: $err\n" if $err;
> -
> -    die "got wrong time resolution ($step != $reso)\n"
> -        if $step != $reso;
> +    my $last_old;
> +    # check if we have old rrd file and if the start point is still covered by
> +    # it, fetch that data from it for any data not available in the old file we
> +    # will fetch it from the new file.
> +    if ($rrdname =~ /pve-(?<type>node|vm|storage)-[0-9]*\.[0-9]*\/(?<resource>.*)/) {
> +        my $old_rrd = "${rrddir}/pve2-$+{type}/$+{resource}";
> +        my $old_exists = 0;
> +
> +        # we can have already migrated rrd files that have the .old suffix too
> +        if (-e $old_rrd) {
> +            $old_exists = 1;
> +        } elsif (-e "${old_rrd}.old") {
> +            $old_exists = 1;
> +            $old_rrd = "${old_rrd}.old";
> +        }
>   
> -    my $res = [];
> -    my $fields = scalar(@$names);
> -    for my $line (@$data) {
> -        my $entry = { 'time' => $start };
> -        $start += $step;
> -        for (my $i = 0; $i < $fields; $i++) {
> -            my $name = $names->[$i];
> -            if (defined(my $val = $line->[$i])) {
> -                $entry->{$name} = $val;
> -                $entry->{memavailable} = $val
> -                    if $is_node && $name eq 'memfree' && !exists($entry->{memavailable});
> +        if ($old_exists) {
> +            $last_old = RRDs::last($old_rrd);
> +            if ($req_start < $last_old) {
> +                my ($reso_old, $count_old) = @{ $setup_pve2->{$timeframe} };
> +                my $ctime_old = $reso_old * int(time() / $reso_old);
> +                my $req_start_old = $ctime_old - $reso_old * $count_old;
> +                my $args = [];
> +                push(@$args, "-s" => $req_start_old);
> +                push(@$args, "-e" => $last_old);
> +                push(@$args, "-r" => $reso_old);
> +
> +                my $socket = "/var/run/rrdcached.sock";
> +                push @$args, "--daemon" => "unix:$socket" if -S $socket;
> +
> +                $get_rrd_data->($old_rrd, $cf, $is_node, $reso_old, $args, $res);
>               } else {
> -                # leave empty fields undefined
> -                # maybe make this configurable?
> +                $last_old = undef;
>               }
>           }
> -        push @$res, $entry;
>       }
>   
> +    my $args = [];
> +    if ($last_old) {
> +        push(@$args, "-s" => $last_old);
> +    } else {
> +        push(@$args, "-s" => $req_start);
> +    }
> +
> +    push(@$args, "-e" => $ctime - 1);
> +    push(@$args, "-r" => $reso);
> +
> +    my $socket = "/var/run/rrdcached.sock";
> +    push @$args, "--daemon" => "unix:$socket" if -S $socket;
> +
> +    $get_rrd_data->($rrd, $cf, $is_node, $reso, $args, $res);
> +
>       return $res;
>   }
>   
> @@ -126,6 +169,33 @@ sub create_rrd_graph {
>       }
>   
>       my ($reso, $count) = @{ $setup->{$timeframe} };
> +    my $ctime = $reso * int(time() / $reso);
> +    my $req_start = $ctime - $reso * $count;
> +
> +    my $last_old;
> +    my $old_rrd;
> +    my $old_exists = 0;
> +    my $use_old;
> +    # check if we have old rrd file and if the start point is still covered
> +    # by it
> +    if ($rrdname =~ /pve-(?<type>node|vm|storage)-[0-9]*\.[0-9]*\/(?<resource>.*)/) {
> +        $old_rrd = "${rrddir}/pve2-$+{type}/$+{resource}";
> +
> +        # we can have already migrated rrd files that have the .old suffix too
> +        if (-e $old_rrd) {
> +            $old_exists = 1;
> +        } elsif (-e "${old_rrd}.old") {
> +            $old_exists = 1;
> +            $old_rrd = "${old_rrd}.old";
> +        }
> +
> +        if ($old_exists) {
> +            $last_old = RRDs::last($old_rrd);
> +            if ($req_start < $last_old) {
> +                $use_old = 1;
> +            }
> +        }
> +    }
>   
>       my @args = (
>           "--imgformat" => "PNG",
> @@ -147,13 +217,27 @@ sub create_rrd_graph {
>       my $i = 0;
>       foreach my $id (@ids) {
>           my $col = $coldef[$i++] || die "fixme: no color definition";
> -        push @args, "DEF:${id}=$rrd:${id}:$cf";
>           my $dataid = $id;
> +        my $linedef = "DEF:${dataid}=$rrd:${id}:$cf";
> +        $linedef = "${linedef}:start=${last_old}" if $use_old; # avoid eventual overlap
> +
> +        push @args, "${linedef}";
> +
>           if ($id eq 'cpu' || $id eq 'iowait') {
> -            push @args, "CDEF:${id}_per=${id},100,*";
> +            push @args, "CDEF:${dataid}_per=${id},100,*";
>               $dataid = "${id}_per";
>           }
>           push @args, "LINE2:${dataid}${col}:${id}";
> +
> +        if ($use_old) {
> +            my $dataid = "${id}old";
> +            push @args, "DEF:${dataid}=$old_rrd:${id}:${cf}";
> +            if ($id eq 'cpu' || $id eq 'iowait') {
> +                push @args, "CDEF:${dataid}_per=${dataid},100,*";
> +                $dataid = "${dataid}_per";
> +            }
> +            push @args, "LINE2:${dataid}${col}";
> +        }
>       }
>   
>       push @args, '--full-size-mode';



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

  reply	other threads:[~2025-09-12 17:49 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-05 13:55 [pve-devel] [PATCH cluster/container/manager/qemu-server/storage 0/7] combine and simplify RRD handling Aaron Lauterer
2025-09-05 13:55 ` [pve-devel] [PATCH cluster 1/3] rrd: fix rrd time frames Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-05 13:55 ` [pve-devel] [PATCH cluster 2/3] RRD: fetch data from old rrd file if present and needed Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu [this message]
2025-09-05 13:55 ` [pve-devel] [PATCH cluster 3/3] pmxcfs: status.c: always use 9.0 rrd files Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-05 13:55 ` [pve-devel] [PATCH manager 1/1] status: rrddata: use fixed pve-node-9.0 path Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-05 13:55 ` [pve-devel] [PATCH qemu-server 1/1] status: rrddata: use fixed pve-vm-9.0 path Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-05 13:55 ` [pve-devel] [PATCH container " Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-05 13:55 ` [pve-devel] [PATCH storage 1/1] status: rrddata: use fixed pve-storage-9.0 path Aaron Lauterer
2025-09-12 17:48   ` Laurențiu Leahu-Vlăducu
2025-09-12 17:47 ` [pve-devel] [PATCH cluster/container/manager/qemu-server/storage 0/7] combine and simplify RRD handling Laurențiu Leahu-Vlăducu
  -- strict thread matches above, loose matches on Subject: below --
2025-09-04 14:09 [pve-devel] [RFC many 0/3] " Aaron Lauterer
2025-09-04 14:09 ` [pve-devel] [PATCH cluster 2/3] RRD: fetch data from old rrd file if present and needed Aaron Lauterer

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=944e9387-caaa-4f87-9255-81294a2ffb5a@proxmox.com \
    --to=l.leahu-vladucu@proxmox.com \
    --cc=pve-devel@lists.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal