Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8294518

Return ChronoUnit.FOREVER.getDuration() for unlimited timespans in JFR events

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 20
    • hotspot
    • None
    • jfr
    • behavioral
    • low
    • Hide
      There is a small risk of existing code invoking RecordedObject::getDuration(String) to detect an unlimited duration by checking against the following values:

      Duration.ofSeconds(Long.MAX_VALUE)
      Duration.ofMillis(Long.MAX_VALUE)
      Duration.ofNanos(Long.MAX_VALUE)
      Duration.ofNanos(1000 * Long.MAX_VALUE) // Overflow!

      That said, it' more likely that unlimited duration is detected by invoking RecordedObject::getLong(String) and compare the results to Long.MAX_VALUE as it would cover all the above cases.




      Show
      There is a small risk of existing code invoking RecordedObject::getDuration(String) to detect an unlimited duration by checking against the following values: Duration.ofSeconds(Long.MAX_VALUE) Duration.ofMillis(Long.MAX_VALUE) Duration.ofNanos(Long.MAX_VALUE) Duration.ofNanos(1000 * Long.MAX_VALUE) // Overflow! That said, it' more likely that unlimited duration is detected by invoking RecordedObject::getLong(String) and compare the results to Long.MAX_VALUE as it would cover all the above cases.
    • Java API
    • JDK

      Summary

      Update jdk.jfr.consumer.RecordedObject::getDuration(String) so java.time.temporal.ChronoUnit.FOREVER.getDuration() is returned if an event timespan is Long.MAX_VALUE.

      Problem

      JFR allows events to be defined with durational values, for example:

       class ActiveRecordingEvent extends jdk.jfr.Event {
         @Timespan(Timespan.MILLISECONDS)
         long maxAge;
         @Timespan(Timespan.MILLISECONDS)
         long recordingDuration;
         ...
      }

      Sometimes durational values need to unlimited. In the above the example, the recording duration is unlimited and the retention policy is to keep data forever. JDK events have indicated this by using Long.MAX_VALUE, for example:

      ActiveRecordingEvent event = new ActiveRecordingEvent();
      event.recordingDuration = Long.MAX_VALUE;
      event.maxAge = Long.MAX_VALUE;
      ...
      event.commit();

      The value is written in the recording file using the same datatype as the event field, i.e. long. To consume the data, a user can invoke RecordedObject::getDuration(String). The parser looks at the value and the Timespan unit, and converts it to java.time.Duration. In the current implementation, it results in a call to Duration.ofMillis(long), Duration.ofSecond(long), Duration.ofNanos(long) etc. depending on the specified unit.

      This is confusing as there are multiple values to indicate an unlimited duration and it's not the maximum value that a java.time.Duration object can hold.

      Solution

      It would be better if the parser would detect if a value is Long.MAX_VALUE and then return java.time.temporal.ChronoUnit.FOREVER.getDuration(), which is equivalent to Duration.ofSeconds(Long.MAX_VALUE, 999_999_999).

      Tools for displaying data, such as the jfr tool in JDK, could easily detect this with an equals check and write something reasonable, for example "Infinite" or "Forever".

      Specification

      jdk.jfr.consumer.RecordedObject

       /**
        * Returns the value of a timespan field.
        * <p>
        * This method can be used on fields annotated with {@code @Timespan}, and of
        * the following types: {@code long}, {@code int}, {@code short}, {@code char},
        * and {@code byte}.
      + * <p>
      + * If the committed event value was {@code Long.MAX_VALUE},
      + * regardless of the unit set by {@code @Timespan}, this method returns
      + * {@link ChronoUnit.FOREVER.getDuration()}.
        * <p>
        * It's possible to index into a nested object using {@code "."} (for example,
        * {@code "aaa.bbb"}).
        * <p>
        * A field might change or be removed in a future JDK release. A best practice
        * for callers of this method is to validate the field before attempting access.
        *
        * @param name of the field to get, not {@code null}
        *
        * @return a time span represented as a {@code Duration}, not {@code null}
        *
        * @throws IllegalArgumentException if the field doesn't exist, or the field
        *         value can't be converted to a {@code Duration} object
        *
        * @see #hasField(String)
        * @see #getValue(String)
        */
       public final Duration getDuration(String name) {

      jdk.jfr.Timespan

       /**
        * Event field annotation, specifies that the value is a duration.
      + * <p>
      + * If the annotated value equals {@code Long.MAX_VALUE), it represents forever.
        *
        * @since 9
        */
        @MetadataDefinition
        @ContentType
        @Label("Timespan")
        @Description("A duration, measured in nanoseconds by default")
        @Retention(RetentionPolicy.RUNTIME)
        @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
        public @interface Timespan {

            egahlin Erik Gahlin
            egahlin Erik Gahlin
            Markus Grönlund
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: