com.facebook.buck.slb.ServerHealthState.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.slb.ServerHealthState.java

Source

/*
 * Copyright 2015-present Facebook, Inc.
 *
 * Licensed 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 com.facebook.buck.slb;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import java.net.URI;
import java.util.List;
import java.util.ListIterator;

public class ServerHealthState {
    private static final int MAX_STORED_SAMPLES = 100;

    private final int maxSamplesStored;
    private final URI server;
    private final List<LatencySample> pingLatencies;
    private final List<RequestSample> requests;

    public ServerHealthState(URI server) {
        this(server, MAX_STORED_SAMPLES);
    }

    public ServerHealthState(URI server, int maxSamplesStored) {
        Preconditions.checkArgument(maxSamplesStored > 0,
                "The maximum number of samples stored must be positive instead of [%s].", maxSamplesStored);
        this.maxSamplesStored = maxSamplesStored;
        this.server = server;
        this.pingLatencies = Lists.newLinkedList();
        this.requests = Lists.newLinkedList();
    }

    /**
     * NOTE: Assumes nowMillis is roughly non-decreasing in consecutive calls.
     * @param nowMillis
     * @param latencyMillis
     */
    public void reportPingLatency(long nowMillis, long latencyMillis) {
        synchronized (pingLatencies) {
            pingLatencies.add(new LatencySample(nowMillis, latencyMillis));
            keepWithinSizeLimit(pingLatencies);
        }
    }

    /**
     * NOTE: Assumes nowMillis is roughly non-decreasing in consecutive calls.
     * @param nowMillis
     */
    public void reportRequestSuccess(long nowMillis) {
        reportRequest(nowMillis, true);
    }

    /**
     * NOTE: Assumes nowMillis is roughly non-decreasing in consecutive calls.
     * @param nowMillis
     */
    public void reportRequestError(long nowMillis) {
        reportRequest(nowMillis, false);
    }

    private void reportRequest(long nowMillis, boolean wasSuccessful) {
        synchronized (requests) {
            requests.add(new RequestSample(nowMillis, wasSuccessful));
            keepWithinSizeLimit(requests);
        }
    }

    public int getPingLatencySampleCount() {
        synchronized (pingLatencies) {
            return pingLatencies.size();
        }
    }

    public int getRequestSampleCount() {
        synchronized (requests) {
            return requests.size();
        }
    }

    private <T> void keepWithinSizeLimit(List<T> list) {
        while (list.size() > maxSamplesStored) {
            // Assume list is time sorted; always remove oldest sample.
            list.remove(0);
        }
    }

    /**
     * @param nowMillis Current timestamp.
     * @param timeRangeMillis Time range for 'nowMillis' to compute the errorPercentage for.
     *
     * @return Value in the interval [0.0, 1.0].
     */
    public float getErrorPercentage(long nowMillis, int timeRangeMillis) {
        int errorCount = 0;
        int requestCount = 0;
        long initialMillis = nowMillis - timeRangeMillis;
        synchronized (requests) {
            ListIterator<RequestSample> iterator = requests.listIterator(requests.size());
            while (iterator.hasPrevious()) {
                RequestSample sample = iterator.previous();
                long requestMillis = sample.getEpochMillis();
                if (requestMillis >= initialMillis && requestMillis <= nowMillis) {
                    if (!sample.wasSuccessful()) {
                        ++errorCount;
                    }

                    ++requestCount;
                }
            }
        }

        if (requestCount == 0) {
            return 0;
        } else {
            return errorCount / ((float) requestCount);
        }
    }

    public URI getServer() {
        return server;
    }

    public long getPingLatencyMillis(long nowMillis, int timeRangeMillis) {
        int count = 0;
        int sum = 0;
        long initialMillis = nowMillis - timeRangeMillis;
        synchronized (pingLatencies) {
            ListIterator<LatencySample> iterator = pingLatencies.listIterator(pingLatencies.size());
            while (iterator.hasPrevious()) {
                LatencySample sample = iterator.previous();
                if (sample.getEpochMillis() >= initialMillis && sample.getEpochMillis() <= nowMillis) {
                    sum += sample.getLatencyMillis();
                    ++count;
                }
            }
        }

        if (count > 0) {
            return sum / count;
        } else {
            return -1;
        }
    }

    public String toString(long nowMillis, int timeRangeMillis) {
        return "ServerHealthState{" + "server=" + server + ", latencyMillis="
                + getPingLatencyMillis(nowMillis, timeRangeMillis) + ", errorCount="
                + getErrorPercentage(nowMillis, timeRangeMillis) + '}';
    }

    private static final class RequestSample {
        private final long epochMillis;
        private final boolean wasSuccessful;

        private RequestSample(long epochMillis, boolean wasSuccessful) {
            this.epochMillis = epochMillis;
            this.wasSuccessful = wasSuccessful;
        }

        public long getEpochMillis() {
            return epochMillis;
        }

        public boolean wasSuccessful() {
            return wasSuccessful;
        }
    }

    private static final class LatencySample {
        private final long latencyMillis;
        private final long epochMillis;

        public LatencySample(long epochMillis, long latencyMillis) {
            this.latencyMillis = latencyMillis;
            this.epochMillis = epochMillis;
        }

        public long getLatencyMillis() {
            return latencyMillis;
        }

        public long getEpochMillis() {
            return epochMillis;
        }
    }
}