Here's the above code with some additional comments for explanation
class ProxyQueryRange(PrometheusProxy):
def get(self, request):
data = []
futures = []
resultType = None
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
# In Promgen, we put multiple prometheus servers behind nginx so
# our Shard object contains our url like shard-name.example.com
# and we filter by the shards enabled to proxy
# Elsewhere in Promgen, we have a list of prometheus servers per
# shard that could be accessed like shard.prometheus_set but that
# isn't used here
for host in models.Shard.objects.filter(proxy=True):
# util.get is just a light wrapper around Python request's
# request.get to add a User-Agent and Referer header to help
# with debugging where requests come from. In general, we take
# the query, as-is, from Grafana, and send that to each of our
# upstream Prometheus shards
futures.append(executor.submit(util.get, '{}/api/v1/query_range?{}'.format(host.url, request.META['QUERY_STRING']), headers=self.headers))
for future in concurrent.futures.as_completed(futures):
try:
result = future.result()
logger.debug('Appending data from %s', result.request.url)
_json = result.json()
# While it is a bit naieve to just append data in this way,
# it seems to handle ~90% of our use cases so it seemed a
# reasonable way to do this in a simple to understand way.
# We can always query a specific shard directly if the data
# looks strange
data += _json['data']['result']
resultType = _json['data']['resultType']
except:
logger.exception('Error with response')
return JsonResponse({
'status': 'success',
'data': {
'resultType': resultType,
'result': data,
}
})