Exposing an interface that allows the fine tuning of the alpha parameter used to calculate the exponential moving average of measured latencies

This commit is contained in:
Nelson R. Perez 2018-10-18 17:36:34 -05:00
parent 3a19808ac5
commit 63eebf11c4
5 changed files with 91 additions and 13 deletions

View file

@ -58,6 +58,7 @@ import cy.agorise.graphenej.network.NodeProvider;
import cy.agorise.graphenej.operations.CustomOperation;
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
import cy.agorise.graphenej.operations.TransferOperation;
import cy.agorise.graphenej.stats.ExponentialMovingAverage;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.Nullable;
@ -79,29 +80,69 @@ public class NetworkService extends Service {
public static final int NORMAL_CLOSURE_STATUS = 1000;
// Time to wait before retrying a connection attempt
private final int DEFAULT_RETRY_DELAY = 5000;
private final int DEFAULT_RETRY_DELAY = 500;
/**
* Constant to be used as a key in order to pass the user name information, in case the
* provided API nodes might require this information.
*/
public static final String KEY_USERNAME = "key_username";
/**
* Constant to be used as a key in order to pass the password information, in case the
* provided API nodes might require this information.
* <p>
* This information should be passed as an intent extra when calling the bindService
* or startService methods.
*/
public static final String KEY_PASSWORD = "key_password";
/**
* Constant used as a key in order to specify which APIs the application will be requiring.
* <p>
* This information should be passed as an intent extra when calling the bindService
* or startService methods.
*/
public static final String KEY_REQUESTED_APIS = "key_requested_apis";
/**
* Constant used as a key in order to let the NetworkService know whether or not it should
* start a recurring node latency verification task.
* <p>
* This information should be passed as an intent extra when calling the bindService
* or startService methods.
*/
public static final String KEY_ENABLE_LATENCY_VERIFIER = "key_enable_latency_verifier";
/**
* Constant used as a key in order to specify the alpha (or smoothing) factor to be used in
* the exponential moving average calculated from the different latency samples. This only
* makes sense if the latency verification feature is enabled of course.
* <p>
* This information should be passed as an intent extra when calling the bindService
* or startService methods.
*/
public static final String KEY_NODE_LATENCY_SMOOTHING_FACTOR = "key_node_latency_smoothing_factor";
/**
* Key used to pass via intent a boolean extra to specify whether the connection should
* be automatically established.
* <p>
* This information should be passed as an intent extra when calling the bindService
* or startService methods.
*/
public static final String KEY_AUTO_CONNECT = "key_auto_connect";
/**
* Key used to pass via intent a list of node URLs. The value passed should be a String
* containing a simple comma separated list of URLs.
*
* <p>
* For example:
*
* wss://domain1.com/ws,wss://domain2.com/ws,wss://domain3.com/ws
* <p>
* This information should be passed as an intent extra when calling the bindService
* or startService methods.
*/
public static final String KEY_NODE_URLS = "key_node_urls";
@ -264,9 +305,10 @@ public class NetworkService extends Service {
// a first round of measurements in order to be sure to select the
// best node.
if(verifyNodeLatency){
double alpha = extras.getDouble(KEY_NODE_LATENCY_SMOOTHING_FACTOR, ExponentialMovingAverage.DEFAULT_ALPHA);
ArrayList<FullNode> fullNodes = new ArrayList<>();
for(String url : urls){
fullNodes.add(new FullNode(url));
fullNodes.add(new FullNode(url, alpha));
}
nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes);
fullNodePublishSubject = nodeLatencyVerifier.start();

View file

@ -15,6 +15,8 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import cy.agorise.graphenej.stats.ExponentialMovingAverage;
/**
* This class should be instantiated at the application level of the android app.
*
@ -52,6 +54,7 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
private List<String> mCustomNodeUrls = new ArrayList<>();
private boolean mAutoConnect;
private boolean mVerifyLatency;
private double alpha;
/**
* Runnable used to schedule a service disconnection once the app is not visible to the user for
@ -194,6 +197,7 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
private List<String> customNodeUrls;
private boolean autoconnect = true;
private boolean verifyNodeLatency;
private double alpha = ExponentialMovingAverage.DEFAULT_ALPHA;
/**
* Sets the user name, if required to connect to a node.
@ -269,6 +273,17 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
return this;
}
/**
* Sets the node latency verification's exponential moving average alpha parameter.
* @param alpha The alpha parameter to use when computing the exponential moving average of the
* measured latencies.
* @return The Builder instance.
*/
public Builder setLatencyAverageAlpha(double alpha){
this.alpha = alpha;
return this;
}
/**
* Method used to build a {@link NetworkServiceManager} instance with all of the characteristics
* passed as parameters.

View file

@ -8,14 +8,35 @@ import cy.agorise.graphenej.stats.ExponentialMovingAverage;
public class FullNode implements Comparable {
private String mUrl;
private ExponentialMovingAverage latency;
private ExponentialMovingAverage mLatency;
private boolean isConnected;
private FullNode(){}
/**
* Constructor used to specify both the node URL and the alpha parameter that one wishes to set the
* exponential moving average with.
* <p>
* The alpha parameter represents the degree of weighting decrease, and can be specified as any value
* between 0 and 1. A higher alpha discounts older observations faster.
*
* @param url The node URL.
* @param alpha The alpha parameter used to compute the exponential moving average.
*/
public FullNode(String url, double alpha){
mLatency = new ExponentialMovingAverage(alpha);
mUrl = url;
}
/**
* Constructor used to specify only the node URL.
* <p>
* The alpha parameter is set to the value specified at {@link ExponentialMovingAverage#DEFAULT_ALPHA}
*
* @param url The node URL.
*/
public FullNode(String url){
latency = new ExponentialMovingAverage(ExponentialMovingAverage.DEFAULT_ALPHA);
this.mUrl = url;
this(url, ExponentialMovingAverage.DEFAULT_ALPHA);
}
/**
@ -39,7 +60,7 @@ public class FullNode implements Comparable {
* @return The exponential moving average object instance
*/
public ExponentialMovingAverage getLatencyAverage(){
return latency;
return mLatency;
}
/**
@ -47,7 +68,7 @@ public class FullNode implements Comparable {
* @return The latest latency average value
*/
public double getLatencyValue() {
return latency.getAverage();
return mLatency.getAverage();
}
public boolean isConnected() {
@ -59,17 +80,17 @@ public class FullNode implements Comparable {
}
/**
* Method that updates the latency average with a new value.
* @param latency Most recent latency sample to be added to the exponential average
* Method that updates the mLatency average with a new value.
* @param latency Most recent mLatency sample to be added to the exponential average
*/
public void addLatencyValue(double latency) {
this.latency.updateValue(latency);
this.mLatency.updateValue(latency);
}
@Override
public int compareTo(Object o) {
FullNode node = (FullNode) o;
return (int) Math.ceil(latency.getAverage() - node.getLatencyValue());
return (int) Math.ceil(mLatency.getAverage() - node.getLatencyValue());
}
@Override

View file

@ -154,7 +154,6 @@ public class NodeLatencyVerifier {
long before = timestamps.get(fullNode);
delay = after - before;
}
fullNode.addLatencyValue(delay);
subject.onNext(fullNode);
}else{

View file

@ -38,6 +38,7 @@ public class SampleApplication extends Application {
.setCustomNodeUrls(nodes)
.setAutoConnect(true)
.setNodeLatencyVerification(true)
.setLatencyAverageAlpha(0.1f)
.build(this);
// Registering this class as a listener to all activity's callback cycle events, in order to