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:
parent
3a19808ac5
commit
63eebf11c4
5 changed files with 91 additions and 13 deletions
|
@ -58,6 +58,7 @@ import cy.agorise.graphenej.network.NodeProvider;
|
||||||
import cy.agorise.graphenej.operations.CustomOperation;
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||||
import cy.agorise.graphenej.operations.TransferOperation;
|
import cy.agorise.graphenej.operations.TransferOperation;
|
||||||
|
import cy.agorise.graphenej.stats.ExponentialMovingAverage;
|
||||||
import io.reactivex.Observer;
|
import io.reactivex.Observer;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.annotations.Nullable;
|
import io.reactivex.annotations.Nullable;
|
||||||
|
@ -79,29 +80,69 @@ public class NetworkService extends Service {
|
||||||
public static final int NORMAL_CLOSURE_STATUS = 1000;
|
public static final int NORMAL_CLOSURE_STATUS = 1000;
|
||||||
|
|
||||||
// Time to wait before retrying a connection attempt
|
// 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";
|
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";
|
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";
|
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";
|
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
|
* Key used to pass via intent a boolean extra to specify whether the connection should
|
||||||
* be automatically established.
|
* 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";
|
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
|
* 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.
|
* containing a simple comma separated list of URLs.
|
||||||
*
|
* <p>
|
||||||
* For example:
|
* For example:
|
||||||
*
|
*
|
||||||
* wss://domain1.com/ws,wss://domain2.com/ws,wss://domain3.com/ws
|
* 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";
|
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
|
// a first round of measurements in order to be sure to select the
|
||||||
// best node.
|
// best node.
|
||||||
if(verifyNodeLatency){
|
if(verifyNodeLatency){
|
||||||
|
double alpha = extras.getDouble(KEY_NODE_LATENCY_SMOOTHING_FACTOR, ExponentialMovingAverage.DEFAULT_ALPHA);
|
||||||
ArrayList<FullNode> fullNodes = new ArrayList<>();
|
ArrayList<FullNode> fullNodes = new ArrayList<>();
|
||||||
for(String url : urls){
|
for(String url : urls){
|
||||||
fullNodes.add(new FullNode(url));
|
fullNodes.add(new FullNode(url, alpha));
|
||||||
}
|
}
|
||||||
nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes);
|
nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes);
|
||||||
fullNodePublishSubject = nodeLatencyVerifier.start();
|
fullNodePublishSubject = nodeLatencyVerifier.start();
|
||||||
|
|
|
@ -15,6 +15,8 @@ import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import cy.agorise.graphenej.stats.ExponentialMovingAverage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class should be instantiated at the application level of the android app.
|
* 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 List<String> mCustomNodeUrls = new ArrayList<>();
|
||||||
private boolean mAutoConnect;
|
private boolean mAutoConnect;
|
||||||
private boolean mVerifyLatency;
|
private boolean mVerifyLatency;
|
||||||
|
private double alpha;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runnable used to schedule a service disconnection once the app is not visible to the user for
|
* 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 List<String> customNodeUrls;
|
||||||
private boolean autoconnect = true;
|
private boolean autoconnect = true;
|
||||||
private boolean verifyNodeLatency;
|
private boolean verifyNodeLatency;
|
||||||
|
private double alpha = ExponentialMovingAverage.DEFAULT_ALPHA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the user name, if required to connect to a node.
|
* Sets the user name, if required to connect to a node.
|
||||||
|
@ -269,6 +273,17 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
return this;
|
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
|
* Method used to build a {@link NetworkServiceManager} instance with all of the characteristics
|
||||||
* passed as parameters.
|
* passed as parameters.
|
||||||
|
|
|
@ -8,14 +8,35 @@ import cy.agorise.graphenej.stats.ExponentialMovingAverage;
|
||||||
public class FullNode implements Comparable {
|
public class FullNode implements Comparable {
|
||||||
|
|
||||||
private String mUrl;
|
private String mUrl;
|
||||||
private ExponentialMovingAverage latency;
|
private ExponentialMovingAverage mLatency;
|
||||||
private boolean isConnected;
|
private boolean isConnected;
|
||||||
|
|
||||||
private FullNode(){}
|
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){
|
public FullNode(String url){
|
||||||
latency = new ExponentialMovingAverage(ExponentialMovingAverage.DEFAULT_ALPHA);
|
this(url, ExponentialMovingAverage.DEFAULT_ALPHA);
|
||||||
this.mUrl = url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,7 +60,7 @@ public class FullNode implements Comparable {
|
||||||
* @return The exponential moving average object instance
|
* @return The exponential moving average object instance
|
||||||
*/
|
*/
|
||||||
public ExponentialMovingAverage getLatencyAverage(){
|
public ExponentialMovingAverage getLatencyAverage(){
|
||||||
return latency;
|
return mLatency;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +68,7 @@ public class FullNode implements Comparable {
|
||||||
* @return The latest latency average value
|
* @return The latest latency average value
|
||||||
*/
|
*/
|
||||||
public double getLatencyValue() {
|
public double getLatencyValue() {
|
||||||
return latency.getAverage();
|
return mLatency.getAverage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
|
@ -59,17 +80,17 @@ public class FullNode implements Comparable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that updates the latency average with a new value.
|
* Method that updates the mLatency average with a new value.
|
||||||
* @param latency Most recent latency sample to be added to the exponential average
|
* @param latency Most recent mLatency sample to be added to the exponential average
|
||||||
*/
|
*/
|
||||||
public void addLatencyValue(double latency) {
|
public void addLatencyValue(double latency) {
|
||||||
this.latency.updateValue(latency);
|
this.mLatency.updateValue(latency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Object o) {
|
public int compareTo(Object o) {
|
||||||
FullNode node = (FullNode) o;
|
FullNode node = (FullNode) o;
|
||||||
return (int) Math.ceil(latency.getAverage() - node.getLatencyValue());
|
return (int) Math.ceil(mLatency.getAverage() - node.getLatencyValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -154,7 +154,6 @@ public class NodeLatencyVerifier {
|
||||||
long before = timestamps.get(fullNode);
|
long before = timestamps.get(fullNode);
|
||||||
delay = after - before;
|
delay = after - before;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullNode.addLatencyValue(delay);
|
fullNode.addLatencyValue(delay);
|
||||||
subject.onNext(fullNode);
|
subject.onNext(fullNode);
|
||||||
}else{
|
}else{
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class SampleApplication extends Application {
|
||||||
.setCustomNodeUrls(nodes)
|
.setCustomNodeUrls(nodes)
|
||||||
.setAutoConnect(true)
|
.setAutoConnect(true)
|
||||||
.setNodeLatencyVerification(true)
|
.setNodeLatencyVerification(true)
|
||||||
|
.setLatencyAverageAlpha(0.1f)
|
||||||
.build(this);
|
.build(this);
|
||||||
|
|
||||||
// Registering this class as a listener to all activity's callback cycle events, in order to
|
// Registering this class as a listener to all activity's callback cycle events, in order to
|
||||||
|
|
Loading…
Reference in a new issue