[WIP] Decision making module for automatic migration#745
Conversation
529d286 to
99ece95
Compare
| /* calculate weighted moving average | ||
| WMA= [nDlatest+(n-1)Dsecond latest+...+Doldest]/[n+(n-1) +...+1], where n+(n-1) +...+1=n(n+1)/2 | ||
| */ | ||
| if (bucketSize < this.bucketSize) { // when bucket is not full |
There was a problem hiding this comment.
- We don't have to maintain all the samples(of bucket size). We can just store either numerator of PreviousWMA(in case of weighted moving avg) OR PreviousEMA(in case of exponential moving avg) and use it for calculating the new W/EMA value.
- In WMA, we don't need to calculate denominator (n*(n+1))/2 each time. Its just a summation. You can just add the new sample count to the existing one and use it. For example summation for 5 samples is 15. Summation for 6th sample is 15 + 6 = 21 i.e., also equal to (6*7)/2.
As discussed, please use exponential moving average.
Please have a look at these links below.
(https://sciencing.com/calculate-exponential-moving-averages-8221813.html)
https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp
https://www.investopedia.com/ask/answers/071414/whats-difference-between-moving-average-and-weighted-moving-average.asp
There was a problem hiding this comment.
Will simplify storage, fetch metrics and ema in this metrics aggregator in a separate PR. You can merge it into this one.
There was a problem hiding this comment.
Agreed, this whole class can be deleted.
6ed63f6 to
7313c60
Compare
| metric.latency = (t2 - t1) / 2; | ||
| // TODO: This is data transfer time. Need to calculate the rate | ||
| metric.rate = (t3 - t2) - (t2 - t1); | ||
| metric.rate = ((t3 - t2) - (t2 - t1)); |
There was a problem hiding this comment.
Don't add these unnecessary brackets.
There was a problem hiding this comment.
PR #742 addresses this comment. Currently metric rate is calculate as
metric.rate = ((RANDOM_DATA_SIZE * 8 * 1000.0 * 1000.0)
/ (((t3 - t2) - (t2 - t1)) * 1024.0 * 1024.0));
For code clarity we are maintaining these brackets.
| } | ||
| }; | ||
| serverManager.registerKernelServer(info, onServerRemove); | ||
| metricAggregator.pushCPUMetric(info.getHost(), info.cpu); |
There was a problem hiding this comment.
This looks confusing. I ask myself where is OMS server pushing these metrics to? Maybe I will find out later.
There was a problem hiding this comment.
MetricAggregator class has been removed and now metric are managed Node metric are managed by KernelServerManager in OMS. updateMetric is used for updating Processor count, latency and transfer rate metric.
| // private PriorityQueue<ReplicaStat> replicaStatsHeap; | ||
| private MigrationPredictor predictor; | ||
| private transient volatile ResettableTimer timer; | ||
| public static int METRIC_WATCH_FREQUENCY = 30000; // in milli seconds |
There was a problem hiding this comment.
Need to javadoc these two constants, and what they mean.
There was a problem hiding this comment.
Added javadoc and updated METRIC_WATCH_FREQUENCY variable name to MIGRATION_EVALUATION_INTERVAL.
| public MetricWatcher(Aggregator aggregator, MicroServiceManager microServiceManager) { | ||
| this.aggregator = aggregator; | ||
| this.microServiceManager = microServiceManager; | ||
| this.predictor = new MigrationPredictor(aggregator, microServiceManager); |
There was a problem hiding this comment.
Please initialize timer in the constructor too. Otherwise it's uninitialized after the constructor has been called, which his bad.
There was a problem hiding this comment.
Moved timer initialisation code from start method to constructor as per comment. Changes are taken care in PR #793.
| new TimerTask() { | ||
| public void run() { | ||
| logger.info("metric watch triggered"); | ||
| TreeSet<ReplicaStat> replicaStatsHeap = |
There was a problem hiding this comment.
Surely it's much more efficient (and simpler) to maintain the heap constantly over time? Rather than build a new heap every 30 seconds, only to throw it away and rebuild again?
There was a problem hiding this comment.
Current approach for recreating heap is considered because
- Replica of micro service are disposable resource in Amino.Run. So if we maintain one heap and update metric as and when migration decision triggered, Decision module will have to maintain pruning thread to remove disposed replicas.
- Also TreeSet/PriorityQueue heap implementation do not have update functionality.
But still constructing TreeSet instance every 30 sec was not good approach. Now Only one instance of TreeSet is maintained. Every 30 sec TreeSet get populated and cleared after getting sorted values.
| logger.warning( | ||
| String.format( | ||
| "skipping Replica [%s] evaluation as micro service not available")); | ||
| e.printStackTrace(); |
There was a problem hiding this comment.
This is messy, but OK. Under what conditions is the microservice not found? Surely it should always be found?
| String.format( | ||
| "skipping kernel server [%s] as time optimization is negative", | ||
| futureKS)); | ||
| return 0; |
There was a problem hiding this comment.
Wait. Isn't the logic here wrong? The loop is over all inbound clients to the microservice. In this code you're not going to migrate if any of the clients will see a slowdown? The logic needs to be that the aggregate of all the clients needs to improve. That aggregate could either be the sum, or the average, or the median, or whatever.
Right?
There was a problem hiding this comment.
By this check , We are ensuring that because of migration to specific kernel server if any application faces negative optimisation (processing time degraded), we ignore migrating to that kernel server.
| * @throws MicroServiceNotFoundException | ||
| */ | ||
| protected void pin(ServerPolicy server, InetSocketAddress host) | ||
| public void pin(ServerPolicy server, InetSocketAddress host) |
There was a problem hiding this comment.
I don't really like making this public, but OK. It would be good to find a way to prevent everything else from calling this.
There was a problem hiding this comment.
Reverted above code changes. Used alternate approach for calling pin in migration.
Current approach:
- Get group policy Event handler from MicroServiceManager.
- Using event handler, call onNotification method and pass ReplicaID and Kernel server info as argument (Added new class MigrationNotification).
- Added code in group policy for handling MigrationNotification and called pin for server policy stub (Using ReplicaID) maintained in group policy.
| serverPolicy.setReplicaId(replicaId); | ||
|
|
||
| // TODO added this code for checking migration when only one DM is set | ||
| serverPolicyStub.setIsLastPolicy(true); |
There was a problem hiding this comment.
Yes, we need to remove this please. It's not clear to me why it's needed. Can't you just test with a single DM and get the same effect?
There was a problem hiding this comment.
This code was added to ensure server policy stub also maintains valid value for last policy flag. The same stub is later used for calling pin during migration where it checks for last policy.
Current approach:
- Get group policy Event handler from MicroServiceManager.
- Using event handler, call
onNotificationmethod and pass ReplicaID and Kernel server info as argument (Added new classMigrationNotification). - Added code in group policy for handling
MigrationNotificationand calledpinfor server policy stub (Using ReplicaID) maintained in group policy.
| args = [project.property('omsIpFlag'), project.property('omsIp'), project.property('omsPortFlag'), project.property('omsPort')] | ||
| args = [project.property('omsIpFlag'), project.property('omsIp'), project.property('omsPortFlag'), project.property('omsPort'), | ||
| project.property('kernelServerIpFlag'), project.property('kernelServerIp'), project.property('kernelServerPortFlag'), | ||
| project.property('kernelServerPort4'), project.property('kernelServerLabelFlag'), project.property('kernelServerLabel4')] |
There was a problem hiding this comment.
This looks like a mess. What's going on here?
There was a problem hiding this comment.
This code is added to start Kernel server along side Application client. In gradle task we are passing arguments required for kernel server and application to start.
| } | ||
|
|
||
| // Customize runapp for this example | ||
| task runappwithgetname(type: JavaExec) { |
There was a problem hiding this comment.
Yes, lets fix this properly. Please don't merge this code.
| // TODO: Currently for collecting metric on logging server sleep is introduced. | ||
| // When metric server will be available new application will get added to demonstrate metric collection | ||
| Thread.sleep(20000); | ||
| Thread.sleep(200000); |
There was a problem hiding this comment.
Definitely remove this. Why do you need it at all?
There was a problem hiding this comment.
Removed as per comment. But addressed in PR #793
| Thread.sleep(200000); | ||
| } finally { | ||
| if (oid != null) { | ||
| // TODO currently removing micro service delete for Migration testing |
There was a problem hiding this comment.
What does this comment mean? It doesn't seem to make sense.
There was a problem hiding this comment.
For testing the migration, I have commented microservice deletion code. Added this comment to revert it back. In PR #793, I reverted back the commented code.
|
|
||
| private static Registry getRegistry(String omsIp, int omsPort) throws Exception { | ||
| new KernelServerImpl(new InetSocketAddress("127.0.0.2", 11111), new InetSocketAddress(omsIp,omsPort)); | ||
| private static Registry getRegistry(String[] args, String omsIp, int omsPort) throws Exception { |
quinton-hoole
left a comment
There was a problem hiding this comment.
See comments. Nice job, mostly.
e0c454d to
a3d12ff
Compare
a3d12ff to
8a64348
Compare
4a8cce3 to
23a14db
Compare
23a14db to
0950723
Compare
| import amino.run.kernel.common.ServerInfo; | ||
| import amino.run.kernel.server.KernelServer; | ||
| import amino.run.kernel.server.KernelServerImpl; | ||
| import amino.run.oms.migrationDecision.MetricWatcher; |
There was a problem hiding this comment.
General convention for package name is to use lowercase
https://www.oracle.com/technetwork/java/codeconventions-135099.html
| private MicroServiceManager microServiceManager; | ||
| private KernelServerManager serverManager; | ||
| private MigrationPredictor predictor; | ||
| private transient volatile ResettableTimer timer; |
There was a problem hiding this comment.
Why transient volatile ?
|
|
||
| /** Starts metric watcher */ | ||
| public void start() { | ||
| if (timer == null) { |
There was a problem hiding this comment.
Instead if timer != null, then simply return can be more clear ?
| averageExecutionTime += replicaMetric.getValue().processTime; | ||
|
|
||
| data = replicaMetric.getValue().dataSize; | ||
| nodeMetric = replicaNodeMetric.get(replicaMetric.getKey()); |
There was a problem hiding this comment.
What we need was nodeMetric from client to the kernelserver. But we took from kernelserver to the client. Did we assume both to be same ? They both are not necessary to be same.
|
Closing this PR as PR's remote branch is no longer linked with forked repository after Amino.Run repository moved to public. |
PR has following changes:
Note: