5f3a5af2c2cb43a602f0b23b489d9c1d228d8047
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.websocketmanager.utils;
23
24 import java.time.Duration;
25 import java.time.Instant;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * Problems of to many notifications during mount of thousand of devices:
31  * <ul>
32  * <li>Overload ODLUX with notification flood -> ODLUX App can not control notifications rate
33  * <li>Notification processing blocks user -> App design with notifications popups
34  * </ul>
35  * Rate filter
36  * <ul>
37  * <li>Do not use a thread -> Do nothing if there are no notifications
38  * <li>Parameter1 integrationTime : Measurement or integration time for period
39  * <li>Parameter2 readMaxCount : Specifies event number per interval indicating overload
40  * <li>Start measurement on event received that comes later then
41  * </ul>
42  *
43  * <pre>
44  *  Example (e: Event received, rateMaxCount=3)
45  *         eee                           e  e e e e e  e  e e e e    e         e                e
46  *  ---//--|--------------|-----//-------|--------------|--------------|--------------|---//----|--------------|
47  *         P1             P2             P1             P2             P3             P7        P1
48  *Overload              no             no            yes            yes             no              no
49  *
50  *
51  *Intention to use:
52  *   1. Construct with parameters for WS stream to handle
53  *   2.
54  * </pre>
55  */
56
57 public class RateFilter {
58
59     private static final Logger LOG = LoggerFactory.getLogger(RateFilter.class.getName());
60
61     private final Duration integrationTime; // Integration time to measure event rate
62     private final long rateMaxCount; //Rate for dropping packets
63     private Instant timeStampPeriodStart; //Time stamp period beginn
64     private Instant timeStampLastEvent; //Measurement interval
65     private long rateCount; // >0: integration running 0: no integration running
66     private boolean overload; //true means in overload status. Change at end of period only.
67     private GetNow get;
68
69     /**
70      * Allow testing with own timestamp provider
71      */
72     public interface GetNow {
73         Instant now();
74     }
75
76     public RateFilter(Duration integrationTime, long rateMaxCount, GetNow getNowMethod) {
77         this.integrationTime = integrationTime;
78         this.rateMaxCount = rateMaxCount;
79         this.get = getNowMethod;
80         this.timeStampLastEvent = Instant.MIN;
81     }
82
83     public RateFilter(Duration integrationTime, long rateMaxCount) {
84         this(integrationTime, rateMaxCount, () -> Instant.now());
85     }
86
87     public synchronized boolean getOverloadStatus() {
88         return overload;
89     }
90
91     /**
92      * Handle filter on event received
93      */
94     public synchronized void filterEvent() {
95         final Instant now = get.now();
96         final Duration durationSinceLastEvent = Duration.between(timeStampLastEvent, now);
97         this.timeStampLastEvent = now;
98
99         if (durationSinceLastEvent.compareTo(integrationTime) >= 0) {
100             //No measurement. Sync and start with period
101             LOG.debug("Sync");
102             timeStampPeriodStart = now;
103             rateCount = 1; //Reset event count .. is part of the
104         } else {
105             //Within period
106             Duration durationPeriod = Duration.between(timeStampPeriodStart, now);
107             rateCount++;
108             boolean endOfPeriod = durationPeriod.compareTo(integrationTime) >= 0;
109             LOG.debug("Period start{}: now:{} end:{} dur:{} int:{}", timeStampPeriodStart, now, endOfPeriod, durationPeriod, integrationTime);
110             if (endOfPeriod) {
111                 //Only if end of Period
112                 overload = rateCount > rateMaxCount;
113                 LOG.debug("Reset overload {}", overload);
114                 timeStampPeriodStart = timeStampPeriodStart.plus(integrationTime);
115                 rateCount = 0;
116             }
117         }
118     }
119
120     @Override
121     public String toString() {
122         StringBuilder builder = new StringBuilder();
123         builder.append("RateFilter [integrationTime=");
124         builder.append(integrationTime);
125         builder.append(", rateMaxCount=");
126         builder.append(rateMaxCount);
127         builder.append(", timeStampPeriodStart=");
128         builder.append(timeStampPeriodStart);
129         builder.append(", timeStampLastEvent=");
130         builder.append(timeStampLastEvent);
131         builder.append(", rateCount=");
132         builder.append(rateCount);
133         builder.append(", overload=");
134         builder.append(overload);
135         builder.append("]");
136         return builder.toString();
137     }
138 }