/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.streams.kstream;
import org.apache.kafka.common.annotation.InterfaceStability;
import org.apache.kafka.streams.processor.TimestampExtractor;
/**
* A session based window specification used for aggregating events into sessions.
* <p>
* Sessions represent a period of activity separated by a defined gap of inactivity.
* Any events processed that fall within the inactivity gap of any existing sessions are merged into the existing sessions.
* If the event falls outside of the session gap then a new session will be created.
* <p>
* For example, if we have a session gap of 5 and the following data arrives:
* <pre>
* +--------------------------------------+
* | key | value | time |
* +-----------+-------------+------------+
* | A | 1 | 10 |
* +-----------+-------------+------------+
* | A | 2 | 12 |
* +-----------+-------------+------------+
* | A | 3 | 20 |
* +-----------+-------------+------------+
* </pre>
* We'd have 2 sessions for key A.
* One starting from time 10 and ending at time 12 and another starting and ending at time 20.
* The length of the session is driven by the timestamps of the data within the session.
* Thus, session windows are no fixed-size windows (c.f. {@link TimeWindows} and {@link JoinWindows}).
* <p>
* If we then received another record:
* <pre>
* +--------------------------------------+
* | key | value | time |
* +-----------+-------------+------------+
* | A | 4 | 16 |
* +-----------+-------------+------------+
* </pre>
* The previous 2 sessions would be merged into a single session with start time 10 and end time 20.
* The aggregate value for this session would be the result of aggregating all 4 values.
* <p>
* For time semantics, see {@link TimestampExtractor}.
*
* @see TimeWindows
* @see UnlimitedWindows
* @see JoinWindows
* @see KGroupedStream#count(SessionWindows, String)
* @see KGroupedStream#count(SessionWindows, org.apache.kafka.streams.processor.StateStoreSupplier)
* @see KGroupedStream#reduce(Reducer, SessionWindows, String)
* @see KGroupedStream#reduce(Reducer, SessionWindows, org.apache.kafka.streams.processor.StateStoreSupplier)
* @see KGroupedStream#aggregate(Initializer, Aggregator, Merger, SessionWindows, org.apache.kafka.common.serialization.Serde, String)
* @see KGroupedStream#aggregate(Initializer, Aggregator, Merger, SessionWindows, org.apache.kafka.common.serialization.Serde, org.apache.kafka.streams.processor.StateStoreSupplier)
* @see TimestampExtractor
*/
@InterfaceStability.Unstable
public final class SessionWindows {
private final long gapMs;
private long maintainDurationMs;
private SessionWindows(final long gapMs) {
this.gapMs = gapMs;
maintainDurationMs = Windows.DEFAULT_MAINTAIN_DURATION_MS;
}
/**
* Create a new window specification with the specified inactivity gap in milliseconds.
*
* @param inactivityGapMs the gap of inactivity between sessions in milliseconds
* @return a new window specification with default maintain duration of 1 day
*
* @throws IllegalArgumentException if {@code inactivityGapMs} is zero or negative
*/
public static SessionWindows with(final long inactivityGapMs) {
if (inactivityGapMs <= 0) {
throw new IllegalArgumentException("Gap time (inactivityGapMs) cannot be zero or negative.");
}
return new SessionWindows(inactivityGapMs);
}
/**
* Set the window maintain duration (retention time) in milliseconds.
* This retention time is a guaranteed <i>lower bound</i> for how long a window will be maintained.
*
* @return itself
* @throws IllegalArgumentException if {@code durationMs} is smaller than window gap
*/
public SessionWindows until(final long durationMs) throws IllegalArgumentException {
if (durationMs < gapMs) {
throw new IllegalArgumentException("Window retention time (durationMs) cannot be smaller than window gap.");
}
maintainDurationMs = durationMs;
return this;
}
/**
* Return the specified gap for the session windows in milliseconds.
*
* @return the inactivity gap of the specified windows
*/
public long inactivityGap() {
return gapMs;
}
/**
* Return the window maintain duration (retention time) in milliseconds.
* <p>
* For {@code SessionWindows} the maintain duration is at least as small as the window gap.
*
* @return the window maintain duration
*/
public long maintainMs() {
return Math.max(maintainDurationMs, gapMs);
}
}