public override void Process(K key, V value)
        {
            // If the key or value is null we don't need to proceed
            if (key == null || value == null)
            {
                log.Warn($"Skipping record due to null key or value. key=[{key}] value=[{value}] topic=[{Context.Topic}] partition=[{Context.Partition}] offset=[{Context.Offset}]");
                return;
            }

            ValueAndTimestamp <V> oldAggAndTimestamp = store.Get(key);
            V    oldAgg, newAgg;
            long newTimestamp;

            if (oldAggAndTimestamp == null)
            {
                oldAgg       = default;
                newAgg       = value;
                newTimestamp = Context.Timestamp;
            }
            else
            {
                oldAgg       = oldAggAndTimestamp.Value;
                newAgg       = reducer.Apply(oldAgg, value);
                newTimestamp = Math.Max(Context.Timestamp, oldAggAndTimestamp.Timestamp);
            }

            store.Put(key, ValueAndTimestamp <V> .Make(newAgg, newTimestamp));
            tupleForwarder.MaybeForward(key, newAgg, sendOldValues ? oldAgg : default, newTimestamp);
        public override void Process(K key, Change <V> value)
        {
            // the keys should never be null
            if (key == null)
            {
                throw new StreamsException($"Record key for KTable reduce operator with state {queryableStoreName} should not be null.");
            }

            ValueAndTimestamp <V> oldAggAndTimestamp = store.Get(key);
            V    oldAgg = oldAggAndTimestamp != null ? oldAggAndTimestamp.Value : default;
            V    intermediateAgg;
            long newTimestamp = Context.Timestamp;

            // first try to remove the old value
            if (oldAggAndTimestamp != null && value.OldValue != null && oldAgg != null)
            {
                intermediateAgg = substractor.Apply(oldAgg, value.OldValue);
                newTimestamp    = Math.Max(Context.Timestamp, oldAggAndTimestamp.Timestamp);
            }
            else
            {
                intermediateAgg = oldAgg;
            }

            // then try to add the new value
            V newAgg;

            if (value.NewValue != null)
            {
                if (intermediateAgg == null)
                {
                    newAgg = value.NewValue;
                }
                else
                {
                    newAgg = adder.Apply(intermediateAgg, value.NewValue);
                }

                if (oldAggAndTimestamp != null)
                {
                    newTimestamp = Math.Max(Context.Timestamp, oldAggAndTimestamp.Timestamp);
                }
            }
            else
            {
                newAgg = intermediateAgg;
            }

            // update the store with the new value
            store.Put(key, ValueAndTimestamp <V> .Make(newAgg, newTimestamp));
            tupleForwarder.MaybeForward(key, newAgg, sendOldValues ? oldAgg : default, newTimestamp);