Skip to content

VectorHasher can lose timestamp precision in generic value-id paths #18597

Description

@rui-mo

Description

VectorHasher currently supports value IDs for TIMESTAMP, but its timestamp normalization is millisecond-based.

inline int64_t toInt64(Timestamp timestamp) const {
return timestamp.toMillis();
}

The direct valueId(Timestamp) and lookupValueId(Timestamp) overloads correctly reject timestamps whose nanoseconds are not millisecond-aligned. But some generic paths bypass these overloads and call toInt64(Timestamp) directly.

template <>
inline uint64_t VectorHasher::valueId(Timestamp value) {
if (FOLLY_UNLIKELY(
value.getNanos() % Timestamp::kNanosecondsInMillisecond != 0)) {
// The timestamp is in nanosecond or microsecond precision. The values are
// not mappable to milliseconds without precision loss.
setRangeOverflow();
setDistinctOverflow();
return kUnmappable;
}
return valueId(value.toMillis());
}

template <>
inline uint64_t VectorHasher::lookupValueId(Timestamp timestamp) const {
return timestamp.getNanos() % 1'000'000 != 0
? kUnmappable
: lookupValueId(timestamp.toMillis());
}

In particular:
VectorHasher::tryMapToRange<T>() uses toInt64(values[row]) in the range fast path.
VectorHasher::analyzeValue<T>() uses toInt64(value) when collecting row-wise stats.
Overflow state from an unmappable timestamp can also be lost during merge() if the overflowed hasher is treated as empty after its range/distinct stats are cleared.

This can cause distinct timestamp values to be assigned the same value id. For example:

Timestamp::fromMillis(1)
Timestamp::fromMicros(1'001)

Current Limitation

Spark timestamps have microsecond precision so timestamp join or aggregation keys may commonly be not millisecond-aligned. This means Spark timestamp keys with microsecond precision cannot benefit from value-id / normalized-key / array hash-table fast paths.

Discussion

  1. Would it make sense to make timestamp value-id precision configurable?

Possible modes:

  • milliseconds: current default behavior;
  • microseconds: useful for Spark-compatible execution.
  1. Could this be a query config and a VectorHasher constructor option?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions