Skip to content
This repository was archived by the owner on Jul 16, 2024. It is now read-only.

Commit b136aa7

Browse files
author
praspaliauskas
committed
X-Ray support added
1 parent 38e4dd5 commit b136aa7

File tree

4 files changed

+71
-9
lines changed

4 files changed

+71
-9
lines changed

README.md

100644100755
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ By leveraging this framework, you can build a cost-effective pipeline to run ad
2020
* Lambda execution role with
2121
* [S3 read/write access](http://docs.aws.amazon.com/lambda/latest/dg/with-s3-example-create-iam-role.html)
2222
* Cloudwatch log access (logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents)
23+
* X-Ray write access (xray:PutTraceSegments, xray:PutTelemetryRecords)
2324

2425
Check policy.json for a sample that you can use or extend.
2526

2627
* To execute the driver locally, make sure that you configure your AWS profile with access to:
2728
* [S3](http://docs.aws.amazon.com/AmazonS3/latest/dev/example-policies-s3.html)
2829
* [Lambda](http://docs.aws.amazon.com/lambda/latest/dg/lambda-api-permissions-ref.html)
30+
* [X-Ray](https://docs.aws.amazon.com/xray/latest/devguide/xray-permissions.html)
2931

3032
### Quickstart::Step by Step ###
3133

@@ -52,7 +54,9 @@ To run the example, you must have the AWS CLI set up. Your credentials must have
5254

5355
$ cat driverconfig.json
5456

55-
6. Run the driver
57+
6. [Run AWS X-Ray Daemon locally](https://docs.aws.amazon.com/xray/latest/devguide/xray-daemon-local.html), otherwise you will not be able to see traces from the local driver in AWS X-Ray console. However, traces from Reducer Coordinator Lambda functions will be present.
58+
59+
7. Run the driver
5660

5761
$ python driver.py
5862

policy.json

100644100755
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@
6464
"arn:aws:s3:::MY-S3-BUCKET/*"
6565
]
6666
},
67+
68+
{
69+
"Sid": "StmXrayAllow",
70+
"Effect": "Allow",
71+
"Action": [
72+
"xray:PutTraceSegments",
73+
"xray:PutTelemetryRecords"
74+
],
75+
"Resource": [
76+
"*"
77+
]
78+
},
79+
6780
{
6881
"Effect": "Allow",
6982
"Action": [

src/python/driver.py

100644100755
Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,40 @@
3232
from multiprocessing.dummy import Pool as ThreadPool
3333
from functools import partial
3434

35+
from botocore.client import Config
36+
import logging
37+
from aws_xray_sdk.core import xray_recorder
38+
from aws_xray_sdk.core import patch_all
39+
patch_all()
40+
logging.basicConfig(level='WARNING')
41+
logging.getLogger('aws_xray_sdk').setLevel(logging.ERROR)
42+
# collect all tracing samples
43+
rules={"version": 1, "default": {"fixed_target": 1,"rate": 1}}
44+
xray_recorder.configure(sampling_rules=rules)
45+
46+
xray_recorder.begin_segment('Map Reduce Driver')
3547
# create an S3 session
3648
s3 = boto3.resource('s3')
3749
s3_client = boto3.client('s3')
38-
lambda_client = boto3.client('lambda')
50+
51+
# Setting longer timeout for reading lambda results and larger connections pool
52+
lambda_config=Config(read_timeout=120, max_pool_connections=50)
53+
lambda_client = boto3.client('lambda',config=lambda_config)
3954

4055
JOB_INFO = 'jobinfo.json'
4156

4257
### UTILS ####
58+
@xray_recorder.capture('zipLambda')
4359
def zipLambda(fname, zipname):
4460
# faster to zip with shell exec
4561
subprocess.call(['zip', zipname] + glob.glob(fname) + glob.glob(JOB_INFO) +
4662
glob.glob("lambdautils.py"))
4763

64+
@xray_recorder.capture('write_to_s3')
4865
def write_to_s3(bucket, key, data, metadata):
4966
s3.Bucket(bucket).put_object(Key=key, Body=data, Metadata=metadata)
5067

68+
@xray_recorder.capture('write_job_config')
5169
def write_job_config(job_id, job_bucket, n_mappers, r_func, r_handler):
5270
fname = "jobinfo.json";
5371
with open(fname, 'w') as f:
@@ -70,15 +88,14 @@ def write_job_config(job_id, job_bucket, n_mappers, r_func, r_handler):
7088
config = json.loads(open('driverconfig.json', 'r').read())
7189

7290
# 1. Get all keys to be processed
91+
xray_recorder.begin_subsegment('Get all keys to be processed')
7392
# init
7493
bucket = config["bucket"]
7594
job_bucket = config["jobBucket"]
7695
region = config["region"]
7796
lambda_memory = config["lambdaMemory"]
7897
concurrent_lambdas = config["concurrentLambdas"]
7998

80-
#all_keys = s3_client.list_objects(Bucket=bucket, Prefix=config["prefix"])["Contents"]
81-
8299
# Fetch all the keys that match the prefix
83100
all_keys = []
84101
for obj in s3.Bucket(bucket).objects.filter(Prefix=config["prefix"]).all():
@@ -87,9 +104,13 @@ def write_job_config(job_id, job_bucket, n_mappers, r_func, r_handler):
87104
bsize = lambdautils.compute_batch_size(all_keys, lambda_memory)
88105
batches = lambdautils.batch_creator(all_keys, bsize)
89106
n_mappers = len(batches)
107+
document = xray_recorder.current_subsegment()
108+
document.put_metadata("Batch size: ", bsize, "Processing initialization")
109+
document.put_metadata("Mappers: ", n_mappers, "Processing initialization")
110+
xray_recorder.end_subsegment() #Get all keys to be processed
90111

91112
# 2. Create the lambda functions
92-
113+
xray_recorder.begin_subsegment('Prepare Lambda functions')
93114
L_PREFIX = "BL"
94115

95116
# Lambda functions
@@ -103,18 +124,24 @@ def write_job_config(job_id, job_bucket, n_mappers, r_func, r_handler):
103124
zipLambda(config["mapper"]["name"], config["mapper"]["zip"])
104125
zipLambda(config["reducer"]["name"], config["reducer"]["zip"])
105126
zipLambda(config["reducerCoordinator"]["name"], config["reducerCoordinator"]["zip"])
127+
xray_recorder.end_subsegment() #Prepare Lambda functions
106128

107129
# mapper
130+
xray_recorder.begin_subsegment('Create mapper Lambda function')
108131
l_mapper = lambdautils.LambdaManager(lambda_client, s3_client, region, config["mapper"]["zip"], job_id,
109132
mapper_lambda_name, config["mapper"]["handler"])
110133
l_mapper.update_code_or_create_on_noexist()
134+
xray_recorder.end_subsegment() #Create mapper Lambda function
111135

112136
# Reducer func
137+
xray_recorder.begin_subsegment('Create reducer Lambda function')
113138
l_reducer = lambdautils.LambdaManager(lambda_client, s3_client, region, config["reducer"]["zip"], job_id,
114139
reducer_lambda_name, config["reducer"]["handler"])
115140
l_reducer.update_code_or_create_on_noexist()
141+
xray_recorder.end_subsegment() #Create reducer Lambda function
116142

117143
# Coordinator
144+
xray_recorder.begin_subsegment('Create reducer coordinator Lambda function')
118145
l_rc = lambdautils.LambdaManager(lambda_client, s3_client, region, config["reducerCoordinator"]["zip"], job_id,
119146
rc_lambda_name, config["reducerCoordinator"]["handler"])
120147
l_rc.update_code_or_create_on_noexist()
@@ -124,29 +151,36 @@ def write_job_config(job_id, job_bucket, n_mappers, r_func, r_handler):
124151

125152
# create event source for coordinator
126153
l_rc.create_s3_eventsource_notification(job_bucket)
154+
xray_recorder.end_subsegment() #Create reducer coordinator Lambda function
127155

128156
# Write Jobdata to S3
157+
xray_recorder.begin_subsegment('Write job data to S3')
129158
j_key = job_id + "/jobdata";
130159
data = json.dumps({
131160
"mapCount": n_mappers,
132161
"totalS3Files": len(all_keys),
133162
"startTime": time.time()
134163
})
164+
xray_recorder.current_subsegment().put_metadata("Job data: ", data, "Write job data to S3");
135165
write_to_s3(job_bucket, j_key, data, {})
166+
xray_recorder.end_subsegment() #Write job data to S3
136167

137168
### Execute ###
138169

139170
mapper_outputs = []
140171

141172
#2. Invoke Mappers
173+
xray_recorder.begin_subsegment('Invoke mappers')
142174
def invoke_lambda(batches, m_id):
175+
xray_recorder.begin_segment('Invoke mapper Lambda')
143176
'''
144177
lambda invoke function
145178
'''
146179
# TODO: Increase timeout
147180

148181
#batch = [k['Key'] for k in batches[m_id-1]]
149182
batch = [k.key for k in batches[m_id-1]]
183+
xray_recorder.current_segment().put_annotation("batch_for_mapper_"+str(m_id), str(batch));
150184
#print "invoking", m_id, len(batch)
151185
resp = lambda_client.invoke(
152186
FunctionName = mapper_lambda_name,
@@ -162,7 +196,7 @@ def invoke_lambda(batches, m_id):
162196
out = eval(resp['Payload'].read())
163197
mapper_outputs.append(out)
164198
print "mapper output", out
165-
199+
xray_recorder.end_segment()
166200
# Exec Parallel
167201
print "# of Mappers ", n_mappers
168202
pool = ThreadPool(n_mappers)
@@ -175,16 +209,20 @@ def invoke_lambda(batches, m_id):
175209
nm = min(concurrent_lambdas, n_mappers)
176210
results = pool.map(invoke_lambda_partial, Ids[mappers_executed: mappers_executed + nm])
177211
mappers_executed += nm
212+
xray_recorder.current_subsegment().put_metadata("Mapper lambdas executed: ", mappers_executed, "Invoke mappers");
178213

179214
pool.close()
180215
pool.join()
181216

182217
print "all the mappers finished"
218+
xray_recorder.end_subsegment() #Invoke mappers
183219

184220
# Delete Mapper function
221+
xray_recorder.begin_subsegment('Delete mappers')
185222
l_mapper.delete_function()
223+
xray_recorder.end_subsegment() #Delete mappers
186224

187-
######## COST ######
225+
xray_recorder.begin_subsegment('Calculate cost')
188226

189227
# Calculate costs - Approx (since we are using exec time reported by our func and not billed ms)
190228
total_lambda_secs = 0
@@ -247,8 +285,12 @@ def invoke_lambda(batches, m_id):
247285
print "S3 Cost", s3_cost
248286
print "Total Cost: ", lambda_cost + s3_cost
249287
print "Total Lines:", total_lines
250-
288+
xray_recorder.end_subsegment() #Calculate cost
251289

252290
# Delete Reducer function
291+
xray_recorder.begin_subsegment('Delete reducers')
253292
l_reducer.delete_function()
254293
l_rc.delete_function()
294+
xray_recorder.end_subsegment() #Delete reducers
295+
296+
xray_recorder.end_segment() #Map Reduce Driver

src/python/lambdautils.py

100644100755
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def __init__ (self, l, s3, region, codepath, job_id, fname, handler, lmem=1536):
3030
self.timeout = 300
3131
self.function_arn = None # set after creation
3232

33+
# TracingConfig parameter switches X-Ray tracing on/off.
34+
# Change value to 'Mode':'PassThrough' to switch it off
3335
def create_lambda_function(self):
3436
runtime = 'python2.7';
3537
response = self.awslambda.create_function(
@@ -42,7 +44,8 @@ def create_lambda_function(self):
4244
Runtime = runtime,
4345
Description = self.function_name,
4446
MemorySize = self.memory,
45-
Timeout = self.timeout
47+
Timeout = self.timeout,
48+
TracingConfig={'Mode':'Active'}
4649
)
4750
self.function_arn = response['FunctionArn']
4851
print response

0 commit comments

Comments
 (0)