Instrumenting JVM code to submit timers and counters to AWS CloudWatch - java

How do you instrument timers and counters in JVM code such that they are passed to AWS CloudWatch? I am interested in manual instrumentation, e.g.: adding timer.start(), timer.stop(), and counter.incr() calls where desired.
Our current solution is to use Dropwizard Metrics for the instrumentation, and this project does a great job of sending the metrics to AWS. The problem with this stack is that Dropwizard Metrics has its own internal aggregates that are based on a expontentially decaying reservoir. As others have mentioned, this behavior is not easily modified. The result is confusing and incorrect data in AWS Cloudwatch. For example:
Averages slowly creep toward the actual value, resulting in a sloped line where a flat line is expected
Min and max values get temporarily stuck at some prior value, resulting in a perfect flat line where a continuously changing value is expected
I think a better solution might be to aggregate no more than a minute of data before submitting it to AWS.

Related

How to meter timer for a concurent kind of job triggered using http endpoint

So we have the app where the http endpoint triggers an async job to completion. The start of the job creates a job id and all the methods and stuff use this for correlation.
We are trying to use the timer for timing the http downloads to third party in the job run sequence. So in our method we create a new timer using registry and give it the job id as the tag. Rest of the stuff is same as mentioned in the docs, we use the wrap over the callable and do our work. The data is exported to new relic.
Once the job finishes the data is visible in new relic and prometheus and its right. But I found that after some time the timer values get reset to zero. SO the percentile values like 95% and avg and 99% all become zero in both prometheus and new relic. What i realized that micrometer clears off the values as it expects the meters to be more global to the app and each http request or job run to update the same meters. But in our case its not like that. We want to do stats over a single job run, so that we can see the stats and events in new relic and all.
Is it not possible in micrometer or am i doing something wrong?
I think you're looking for a distributed tracing system like Zipkin, not a metrics collection system. Zipkin, etc. are optimized around understanding latency contributions of subsystems within a particular request.
See more here on the distinction between metrics and tracing.
Note that the Prometheus spec itself requires decaying quantile values.

AWS Lambda Performance issues

I use aws api gateway integrated with aws lambda(java), but I'm seeing some serious problems in this approach. The concept of removing the server and having your app scaled out of the box is really nice but here are the problem I'm facing. My lambda is doing 2 simple things- validate the payload received from the client and then send it to a kinesis stream for further processing from another lambda(you will ask why I don't send directly to the stream and only use 1 lambda for all of the operations. Let's just say that I want to separate the logic and have a layer of abstraction and also be able to tell the client that he's sending invalid data.).
In the implementation of the lambda I integrated the spring DI. So far so good. I started making performance testing. I simulated 50 concurrent users making 4 requests each with 5 seconds between the requests. So what happened- In the lambda's coldstart I initialize the spring's application context but it seems that having so many simultaneous requests when the lambda was not started is doing some strange things. Here's a screenshot of the times the context was initialized for.
What we can see from the screenshot is that the times for initializing the context have big difference. My assumption of what happening is that when so many requests are received and there's no "active" lambda it initializes a lambda container for every one of them and in the same time it "blocks" some of them(the ones with the big times of 18s) until the others already started are ready. So maybe it has some internal limit of the containers it can start at the same time. The problem is that if you don't have equally distributed traffic this will happen from time to time and some of the requests will timeout. We don't want this to happen.
So next thing was to do some tests without spring container as my thought was "ok, the initialization is heavy, let's just make plain old java objects initialization". And unfortunatelly the same thing happened(maybe just reduced the 3s container initialization for some of the requests). Here is a more detailed screenshot of the test data:
So I logged the whole lambda execution time(from construction to the end), the kinesis client initialization and the actual sending of the data to the stream as these are the heaviest operations in the lambda. We still have these big times of 18s or something but the interesting thing is that the times are somehow proportional. So if the whole lambda takes 18s, around 7-8s is the client initialization and 6-7 for sending the data to the stream and 4-5 seconds left for the other operations in the lambda which for the moment is only validation. On the other hand if we take one of the small times(which means that it reuses an already started lambda),i.e. 820ms, it takes 100ms for the kinesis client initialization and 340 for the data sending and 400ms for the validation. So this pushes me again to the thoughts that internally it makes some sleeps because of some limits. The next screenshot is showing what is happening on the next round of requests when the lamda is already started:
So we don't have this big times, yes we still have some relatively big delta in some of the request(which for me is also strange), but the things looks much better.
So I'm looking for a clarification from someone who knows actually what is happening under the hood, because this is not a good behavior for a serious application which is using the cloud because of it's "unlimited" possibilities.
And another question is related to another limit of the lambda-200 concurrent invocations in all lambdas within an account in a region. For me this is also a big limitation for a big application with lots of traffic. So as my business case in the moment(I don't know for the future) is more or less fire and forget the request. And I'm starting to think of changing the logic in the way that the gateway sends the data directly to the stream and the other lambda is taking care of the validation and the further processing. Yes, I'm loosing the current abstraction(which I don't need at the moment) but I'm increasing the application availability many times. What do you think?
The lambda execution time spikes to 18s because AWS launches new containers w/ your code to handle the incoming requests. The bootstrap time is ~18s.
Assigning more RAM can significantly improve the performance of your lambda function, because you have more RAM, CPU and networking throughput!
And another question is related to another limit of the lambda-200 concurrent invocations in all lambdas within an account in a region.
You can ask to the AWS Support to increase that limit. I asked to increase that limit to 10,000 invocation/second and the AWS Support did it quickly!
You can proxy straight to the Kinesis stream via API Gateway. You would lose some control in terms of validation and transformation, but you won't have the cold start latency that you're seeing from Lambda.
You can use the API Gateway mapping template to transform the data and if validation is important, you could potentially do that at the processing Lambda on the other side of the stream.

Deploying code to Amazon EC2 instance

I'm using Java to create EC2 instances from within Eclipse. Now I would like to push parts of the application to these instances so that these can process whatever needs processing and then send the results back to my machine.
What I'm trying to do is something along the lines of:
assignWork(){
workPerformed = workQueue;
workPerInstance = workQueue/numberOfInstances;
while(workQueue > 0){
netxInstance.doWork(workPerformed,workPerInstance);
workPerformer -= workPerInstance;
}
}
doWork(start, end){
while(start>end){
//process stuff
start--;
}
}
This way I could control exactly how many AMI's to instantiates depending on the volume of work at hand. I could instantiate them, send them specific code to process and then terminate them as soon as I receive the results.
Is this possible just using the AWS JDK?
It is, but consider that...
If you have SLAs, and they fall within SQS Limits (Maximum 4 Days), you could consider publishing your task queues into SNS/SQS, and use CloudWatch to track the number of needed instances.
If you have a clear division of roles (more like a workflow), and the long-running tasks are not of much concern and you can retry, also consider using AWS SWF instead. It goes a bit beyond of a SQS/SNS Combo, and I think it could fit nicely with CloudWatch (thats just a theory, I haven't looked further). Cons are the extreme assh*le AWS Flow Framework for writing the Workflow Processes
If your workload is predictable (say, around 5K processes to process today), meaning you have no need for real-time and you can batch those requests, then consider using Elastic MapReduce for this. Being Hadoop-based, it offers some such niceties, such as being able to resize your cluster on demand, and the obvious case of not having any vendor lock in at all.
Actually, if you want that manage and without many surprises, consider looking at options such as PiCloud and IronWorker. They were really made for situations just like the one you've just described.
If you have only a Queue and EC2, you can surely automate that. It only depends on how badly you want to coordinate these tasks, but I'm sure its possible.

How to tell MapReduce how many mappers to use at the same time?

I am writing an indexing app for MapReduce.
I was able to split inputs with NLineInputFormat, and now I've got few hundred mappers in my app. However, only 2/mashine of those are active at the same time, the rest are "PENDING". I believe that such a behavior slows the app significantly.
How do I make hadoop run at least 100 of those at the same time per machine?
I am using the old hadoop api syntax. Here's what I've tried so far:
conf.setNumMapTasks(1000);
conf.setNumTasksToExecutePerJvm(500);
none of those seem to have any effect.
Any ideas how I can make the mappers actually RUN in parallel?
The JobConf.setNumMapTasks() is just a hint to the MR framework and I am not sure the effect of calling it. In your case the total number of map tasks across the whole job should be equal to the total number of lines in the input divided by the number of lines configured in the NLineInputFormat. You can find more details on the total number of map/reduce tasks across the whole job here.
The description for mapred.tasktracker.map.tasks.maximum says
The maximum number of map tasks that will be run simultaneously by a task tracker.
You need to configure the mapred.tasktracker.map.tasks.maximum (which is defaulted to 2) to change the number of map tasks run parallely on a particular node by the task tracker. I could not get the documentation for 0.20.2, so I am not sure if the parameter exists or if the same parameter name is used in 0.20.2 release.

How to expose HTTP return (error) codes with JMX?

We would like to monitor HTTP error codes (e.g. per minute) for later graphing.
How can we expose HTTP return (error) codes with JMX?
Are there code samples or have you done something like that? Is this contained in Tomcat per default?
About the implementation: Should the code increase a JMX counter and set it to 0 every minute? Thanks for ideas.
If you're looking to derive rates of errors and graph those, you're better off having the server return a total count of errors. That way the client can then derive a rate independent of the server (e.g. per minute/per hour/per day).
More importantly, you won't miss any temporary surges in the data. If the server is deriving a rate and your client process doesn't pick it up (if it's not monitoring at that particular time, say), then it's lost forever. Whereas maintaining a count will record the total number of errors.
SNMP agents (and similar) take this approach, allowing clients to monitor and derive values as they see fit.
Add a JMX bean which has one field per HTTP error you care about. Increment each field as errors drop in. The JMX console will turn that into a nice curve. I wouldn't reset the value but that mostly depends on what features your console has to show a statistic on a value.

Categories