{
  "$schema" : "https://json-schema.org/draft/2020-12/schema",
  "$id" : "https://voltdb.com/voltsp/schemas/emitter/sliding-window",
  "title" : "Sliding-window Emitter Configuration",
  "description" : "The `sliding-window` creates a per-worker sliding time window. In this implementation a window aggregate is stored only in the machine's memory.\nThe aggregate is emitted to configured sink via an emit point.\n\nA sliding window has a fixed size (windowSpan) and slides forward by a specified interval (slideBy).\nUnlike tumbling windows, sliding windows can overlap, meaning each event may belong to multiple windows.\nFor example, with a 1-hour window that slides every 15 minutes, an event is aggregated by up to 4 overlapping windows.\nAn aggregate is defined by `VoltAggregateBuilder`, which consumes an event.\n\nIf `eventTimeExtractor` is provided, it is used to source the time of the event.\n\nIf 'keyExtractor' is provided, an aggregate is created per worker and per key.\nNote that the window is a local, per-worker keyed aggregate, not a machine-wide keyed aggregate.\n\nIn case of downstream system error this `sliding-window` will re-process events.\n",
  "type" : "object",
  "properties" : {
    "sliding-window" : {
      "type" : "object",
      "properties" : {
        "windowSpan" : {
          "type" : "string",
          "pattern" : "^(\\d+[smhd]|P(T|\\d+[YMD]).*)",
          "description" : "Defines the size of the sliding window. This is the time span of events included in each window."
        },
        "slideBy" : {
          "type" : "string",
          "pattern" : "^(\\d+[smhd]|P(T|\\d+[YMD]).*)",
          "description" : "Defines how much the window slides forward.\nThis determines the interval between consecutive windows.\nIf slideBy is smaller than windowSpan, windows will overlap.\nIf slideBy equals windowSpan, it behaves like a tumbling window.\nSlideBy cannot be greater than windowSpan.\n"
        },
        "keyExtractor" : {
          "description" : "If set, the window will aggregate only events with the same key value.\nThe key has to conform to Java equal and hashCode contract.\n",
          "anyOf" : [ {
            "type" : "string",
            "description" : "Java method reference or JavaScript code"
          }, {
            "type" : "object",
            "properties" : {
              "implementation" : {
                "type" : "string",
                "description" : "Java method reference or JavaScript code"
              },
              "type" : {
                "type" : "string",
                "description" : "The return type of the extractor"
              }
            },
            "required" : [ "implementation" ]
          } ]
        },
        "aggregateDefinition" : {
          "type" : "object",
          "description" : "The aggregate is defined by the provided builder",
          "properties" : {
            "builder" : {
              "type" : "array",
              "items" : {
                "type" : "object",
                "properties" : {
                  "type" : {
                    "type" : "string",
                    "description" : "The aggregation operation (e.g., count, sum, min, max, first, last, distinct)"
                  }
                },
                "required" : [ "type" ]
              }
            },
            "from" : {
              "type" : "string",
              "description" : "The fully qualified name of the class that contains the aggregate functions"
            }
          },
          "required" : [ "from", "builder" ]
        },
        "timeConfig" : {
          "type" : "object",
          "description" : "Configuration for event-time handling and late-event tolerance.\nUse it to control how the window determines time and how long it waits before closing when events arrive late.\n",
          "properties" : {
            "eventTimeExtractor" : {
              "type" : "string",
              "description" : "If provided, the event time is sourced from the event itself. This enables event-time bucketing,\nwatermark advancement, lateness detection and delayWindowClose. Without it, timestamps come from\nthe worker's wall clock at the moment of processing.\n\nRecommended whenever events can arrive out of order, may be replayed from history, or whenever\ncorrectness depends on the real-world time the event happened (billing, fraud detection, ...).\n"
            },
            "delayWindowClose" : {
              "type" : "string",
              "pattern" : "^(\\d+[smhd]|P(T|\\d+[YMD]).*)",
              "description" : "This is an optional configuration.\n\nIt postpones window closing by the configured duration so that slightly out-of-order or late events\nstill have a chance to land in their intended window.\n\nIt is meaningful only when an eventTimeExtractor is configured. Without an extractor, timestamps\nare the worker's wall clock at the moment of processing and are monotonic, so this setting only\nadds emission latency without any correctness benefit; leave it at 0 in that case.\n\nIt is recommended to keep its value relatively small - for example 1-10 seconds, just to handle disruptions in event arrival, given that the upstream system is still available.\nThis configuration should not be used to mitigate upstream systems downtime.\n\nOnce the window is closed, any late events are routed to exception handler.\n",
              "default" : "0s"
            },
            "idleTimeout" : {
              "type" : "string",
              "pattern" : "^(\\d+[smhd]|P(T|\\d+[YMD]).*)",
              "description" : "Inactivity timeout after which a window is emitted and closed.\n\nWhile the pipeline is busy, quiet keys are detected as part of normal batch processing\nand emitted once they cross this threshold. While the pipeline is fully silent, idle\ncleanup is driven by the periodic pipeline heartbeat (60s).\n\nIf idleTimeout is not set, idle cleanup is disabled and the window only closes via its\nprimary trigger (count, time span, session gap, ...).\n\nThe clock used to measure silence is controlled by idleTimeoutClock.\n"
            },
            "idleTimeoutClock" : {
              "type" : "string",
              "description" : "Specifies the clock used to measure inactivity for idleTimeout:\n- PROCESSING_TIME - silence measured in real time. Quiet keys flush after\n  idleTimeout of real time, regardless of event-time progression.\n  Suitable for live streams where the idle SLA is real time.\n- EVENT_TIME - silence measured in event time (requires eventTimeExtractor).\n  Quiet keys flush only as the watermark advances by idleTimeout.\n  Suitable for replays / historical processing where results must be\n  deterministic.\n",
              "enum" : [ "processing_time", "event_time", "PROCESSING_TIME", "EVENT_TIME" ]
            },
            "watermarkStrategy" : {
              "type" : "string",
              "description" : "Specifies the strategy for calculating watermarks. The watermark is monotonic and never moves backward.\nIf a key extractor is configured, watermark progression is tracked independently per key.\n\nThe choice is meaningful only when an eventTimeExtractor is configured (timestamps come from events).\nWithout an extractor, timestamps are the worker's wall clock at the moment of processing and arrive\nmonotonically; MIN_EVENT_TIME pins the per-batch watermark to the first event's timestamp and\nMAX_EVENT_TIME to the last, so the two still differ by roughly the batch's processing duration but\nthe lag is typically negligible for second-scale or larger windows.\n\nAvailable strategies:\n- MIN_EVENT_TIME - uses the minimum observed event time in the batch as the watermark.\n  This is the most conservative option and reduces the risk of marking slightly older events as late.\n- MAX_EVENT_TIME - uses the largest observed event time in the batch as the watermark.\n  This advances the watermark faster, which can close windows sooner, but may classify older events as late earlier.\n\nIf you are unsure, use MIN_EVENT_TIME.\n",
              "default" : "MIN_EVENT_TIME",
              "enum" : [ "min_event_time", "max_event_time", "MIN_EVENT_TIME", "MAX_EVENT_TIME" ]
            }
          },
          "additionalProperties" : false
        },
        "exceptionHandler" : {
          "type" : "string",
          "description" : "Custom exception handler enabling interception of all errors related to this emitter."
        },
        "triggers" : {
          "type" : "array",
          "description" : "List of trigger configurations that create sub-pipelines",
          "items" : {
            "type" : "object",
            "properties" : {
              "trigger" : {
                "type" : "string",
                "enum" : [ "org.voltdb.stream.api.pipeline.emitter.ExceptionTrigger#onError", "org.voltdb.stream.plugin.window.api.WindowTrigger#atWindowEnd", "org.voltdb.stream.plugin.window.api.WindowTrigger#atAggregate" ],
                "description" : "The trigger point that activates this sub-pipeline"
              },
              "processors" : {
                "type" : "array",
                "description" : "Optional list of processors to apply before the sink or emitter",
                "items" : {
                  "$ref" : "#/$defs/processor"
                }
              },
              "sink" : {
                "$ref" : "#/$defs/sink"
              },
              "emitter" : {
                "$ref" : "#/$defs/emitter"
              }
            },
            "oneOf" : [ {
              "required" : [ "trigger", "sink" ]
            }, {
              "required" : [ "trigger", "emitter" ]
            } ]
          }
        }
      },
      "required" : [ "windowSpan", "slideBy", "aggregateDefinition", "triggers" ],
      "additionalProperties" : false
    }
  },
  "required" : [ "sliding-window" ],
  "additionalProperties" : false
}
