prometheus / client_python

Prometheus instrumentation library for Python applications
Apache License 2.0
3.94k stars 795 forks source link

Fix timestamp comparison #1038

Closed magmax closed 4 months ago

magmax commented 4 months ago

Fixes #1037, where comparison between Timestamp were wrong.

I've chosen this solution after testing different ones because it is the faster one. Files and speed tests here.

Different implementations:

Changing the full class by a namedtuple

from collections import namedtuple

class TimestampNamedtuple(namedtuple("Timestamp", ["sec", "nsec"])):
    """A nanosecond-resolution timestamp."""

    def __new__(cls, sec: float, nsec: float) -> None:
        if nsec < 0 or nsec >= 1e9:
            raise ValueError(f"Invalid value for nanoseconds in Timestamp: {nsec}")
        if sec < 0:
            nsec = -nsec
        return super().__new__(cls, int(sec), int(nsec))

    def __str__(self) -> str:
        return f"{self.sec}.{self.nsec:09d}"

    def __repr__(self) -> str:
        return f"Timestamp({self.sec}, {self.nsec})"

    def __float__(self) -> float:
        return float(self.sec) + float(self.nsec) / 1e9

Here there is no need to implement comparison operators because they are already implemented in the parent class.

This was the worse implementation, around 50% slower than the chosen one.

tuples

    def __gt__(self, other: "Timestamp") -> bool:
        return (self.sec, self.nsec) > (other.sec, other.nsec)

logical operators

    def __gt__(self, other: "Timestamp") -> bool:
        return self.sec > other.sec or (self.sec == other.sec and self.nsec > other.nsec)