I’m a big fan of simplicity, especially when it comes to demos and examples. There’s nothing that sets my teeth on edge faster than a “demonstration” meant to show me a single feature or capability, but begins by showing me a 3,000 line fully automated web application with containers and microservices. In building the demos for my “absolutely simple” series, I’ve worked hard to stick to the spirit of one of my favorite quotes:
“If you can’t explain it simply, you don’t understand it well enough.”
– Albert Einstein
The idea was that I would strip my examples down to the barest essentials – not a single line of code more than what was needed. No additional elements, files, containers or whatnot.
Of course, the intent isn’t to stop at that (admittedly simplistic) level, but to use it as a foundation to establish a basic understanding of the tool, technique, or feature; and then build to more real-world scenarios from there.
As I worked on the next entry in my “absolutely simple” series, I tried to build a demo for application performance tracing (or APM). In most of my previous efforts, I always had a sense that – given enough time – I could probably have made my example simpler still. More stripped-down. More “atomic”, if you will.
However, in my quest to create the simplest possible example of APM, I ran into the essence of a different Einstein quote:
“Everything Should Be Made as Simple as Possible, But Not Simpler”
Albert Einstein (paraphrased)
I build a demo of an app that was just 10 lines, but refused to show up New Relic’s APM screen.
import newrelic.agent
newrelic.agent.initialize('newrelic.ini')
from passeo import passeo
@newrelic.agent.background_task(name="NameyMcNameyFace", group='GroupyMcGroupFace')
def execute_task():
print(passeo().generate(length=10, numbers=True, symbols=True, uppercase=True, lowercase=False, space=True, save=True))
execute_task()
newrelic.agent.shutdown_agent(timeout=10)
For context, this script leverages the open source Passeo library, which generates passwords and/or checks existing passwords to ensure the pass a basic strength check. The problem (from an APM perspective) is that my script was designed as a “run and done” type utility rather than a persistent application. Perfect for the busy sysadmin (my kind of people), but very bad if you want to trace the performance of an application over time. Because effectively, there IS no time.
Once a friend helped frame the root cause, I found a way to bulk it up a bit:
import newrelic.agent
newrelic.agent.initialize('newrelic.ini') #This is required!
from passeo import passeo
import requests
import json
import time
inputurl = "https://random-word-api.herokuapp.com/word"
@newrelic.agent.background_task(name="NameyMcNameyFace", group='GroupyMcGroupFace')
def execute_task():
getword = requests.get(inputurl).json() # Call the api to retrieve the statistics
print("Password is "+getword[0])
print(passeo().strengthcheck(getword[0]))
counter = 0
while True:
execute_task()
time.sleep(5)
newrelic.agent.shutdown_agent(timeout=100)
The modified code does three things missing from the original version:
- First, by adding some pauses and delays, each operation takes a little longer, giving the APM agent time to sink it’s teeth into everything going on.
- Having the code make a call to an external API (to grab a random word out of the dictionary) also adds to the overall execution time.
- And finally, I had to add a special delay to the New Relic APM agent, so it didn’t clean itself up too quickly and miss some of the trace data.
With those changes in place, my application tracing now shows up.
This isn’t the actual “absolutely simple APM” blog, so my goal isn’t to describe how APM works, or the other steps I took to get this instrumented.
Instead, I wanted to share my reflections on the experience of building the blog, and how it taught me the limits of simplicity, and had to embrace just a bit more complexity to get where I needed to go.