Added more traces
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Pünkösd Marcell 2021-08-05 17:54:11 +02:00
parent 17927b472d
commit d807bc6b00
4 changed files with 304 additions and 162 deletions

View File

@ -6,6 +6,7 @@ from minio.error import NoSuchKey
from schemas import AIModelSchema, DefaultSchema, InfoSchema from schemas import AIModelSchema, DefaultSchema, InfoSchema
from marshmallow.exceptions import ValidationError from marshmallow.exceptions import ValidationError
from utils import multipart_required, storage, ensure_buckets from utils import multipart_required, storage, ensure_buckets
import opentracing
class CNNView(FlaskView): class CNNView(FlaskView):
@ -21,88 +22,155 @@ class CNNView(FlaskView):
def post(self): def post(self):
# get important data from the request # get important data from the request
try: with opentracing.tracer.start_active_span('parseAndValidate'):
info = self.info_schema.loads(request.form.get('info')) try:
except ValidationError as e: info = self.info_schema.loads(request.form.get('info'))
abort(400, str(e)) except ValidationError as e:
return abort(400, str(e))
# check for conflict # check for conflict
m = AIModel.query.filter_by(id=info['id']).first() m = AIModel.query.filter_by(id=info['id']).first()
if m: if m:
abort(409) return abort(409)
# get and validate file # get and validate file
model_file = request.files['modelFile'] model_file = request.files['modelFile']
if model_file.content_length <= 0: if model_file.content_length <= 0:
abort(411, f"Content length for modelFile is not a positive integer or missing.") return abort(411, f"Content length for modelFile is not a positive integer or missing.")
weights_file = request.files['weightsFile'] weights_file = request.files['weightsFile']
if weights_file.content_length <= 0: if weights_file.content_length <= 0:
abort(411, f"Content length for weightsFile is not a positive integer or missing.") return abort(411, f"Content length for weightsFile is not a positive integer or missing.")
# create bucket if necessary # create bucket if necessary
ensure_buckets() with opentracing.tracer.start_active_span('ensureBuckets'):
ensure_buckets()
# Create the entry in the db # Create the entry in the db
m = AIModel(id=info['id'], type=AIModelType.cnn, target_class_name=info['target_class_name']) with opentracing.tracer.start_active_span('sqlalchemy.create'):
m = AIModel(id=info['id'], type=AIModelType.cnn, target_class_name=info['target_class_name'])
db.session.add(m)
# Put files into MinIO # Put files into MinIO
storage.connection.put_object( with opentracing.tracer.start_active_span('putObjectsInMinio'):
current_app.config['MINIO_CNN_BUCKET_NAME'], with opentracing.tracer.start_active_span(
self.MODEL_DIRECTORY + str(m.id), 'minio.putObject',
model_file, tags={
model_file.content_length, "bucket": current_app.config['MINIO_CNN_BUCKET_NAME'],
content_type=model_file.content_type "object_name": self.MODEL_DIRECTORY + str(m.id),
) "length": model_file.content_length,
"component": "model"
}
):
storage.connection.put_object(
current_app.config['MINIO_CNN_BUCKET_NAME'],
self.MODEL_DIRECTORY + str(m.id),
model_file,
model_file.content_length,
content_type=model_file.content_type
)
with opentracing.tracer.start_active_span(
'minio.putObject',
tags={
"bucket": current_app.config['MINIO_CNN_BUCKET_NAME'],
"object_name": self.WEIGHTS_DIRECTORY + str(m.id),
"length": model_file.content_type,
"component": "weights"
}
):
storage.connection.put_object(
current_app.config['MINIO_CNN_BUCKET_NAME'],
self.WEIGHTS_DIRECTORY + str(m.id),
weights_file,
weights_file.content_length,
content_type=weights_file.content_type
)
storage.connection.put_object( with opentracing.tracer.start_active_span('sqlalchemy.commit'):
current_app.config['MINIO_CNN_BUCKET_NAME'], db.session.commit()
self.WEIGHTS_DIRECTORY + str(m.id),
weights_file,
weights_file.content_length,
content_type=weights_file.content_type
)
db.session.add(m)
db.session.commit()
return jsonify(self.aimodel_schema.dump(m)), 200 return jsonify(self.aimodel_schema.dump(m)), 200
def delete(self, id_: str): def delete(self, id_: str):
if id_ == "$default": with opentracing.tracer.start_active_span(
default = Default.query.filter_by(type=AIModelType.cnn).first_or_404() 'sqlalchemy.select',
m = default.aimodel tags={"aimodel_type": AIModelType.cnn, "id": id_}
else: ):
m = AIModel.query.filter_by(type=AIModelType.cnn, id=id_).first_or_404() if id_ == "$default":
default = Default.query.filter_by(type=AIModelType.cnn).first_or_404()
m = default.aimodel
else:
m = AIModel.query.filter_by(type=AIModelType.cnn, id=id_).first_or_404()
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'], self.WEIGHTS_DIRECTORY + str(m.id)) with opentracing.tracer.start_active_span('removeFromMinio'):
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'], self.MODEL_DIRECTORY + str(m.id)) with opentracing.tracer.start_active_span(
'minio.removeObject',
tags={
"bucket": current_app.config['MINIO_CNN_BUCKET_NAME'],
"object_name": self.WEIGHTS_DIRECTORY + str(m.id),
"component": "weights"
}
):
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'],
self.WEIGHTS_DIRECTORY + str(m.id))
db.session.delete(m) with opentracing.tracer.start_active_span(
db.session.commit() 'minio.removeObject',
tags={
"bucket": current_app.config['MINIO_CNN_BUCKET_NAME'],
"object_name": self.MODEL_DIRECTORY + str(m.id),
"component": "model"
}
):
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'],
self.MODEL_DIRECTORY + str(m.id))
with opentracing.tracer.start_active_span(
'sqlalchemy.delete',
tags={"aimodel_type": AIModelType.cnn, "id": id_}
):
db.session.delete(m)
with opentracing.tracer.start_active_span('sqlalchemy.commit'):
db.session.commit()
return '', 204 return '', 204
@route('<id_>/file') @route('<id_>/file')
def get_file(self, id_: str): def get_file(self, id_: str):
if id_ == "$default": with opentracing.tracer.start_active_span(
default = Default.query.filter_by(type=AIModelType.cnn).first_or_404() 'sqlalchemy.select',
m = default.aimodel tags={"aimodel_type": AIModelType.cnn, "id": id_}
else: ):
m = AIModel.query.filter_by(type=AIModelType.cnn, id=id_).first_or_404() if id_ == "$default":
default = Default.query.filter_by(type=AIModelType.cnn).first_or_404()
m = default.aimodel
else:
m = AIModel.query.filter_by(type=AIModelType.cnn, id=id_).first_or_404()
if "weights" in request.args: if "weights" in request.args:
path = self.WEIGHTS_DIRECTORY + str(m.id) path = self.WEIGHTS_DIRECTORY + str(m.id)
component = "weights"
else: else:
path = self.MODEL_DIRECTORY + str(m.id) path = self.MODEL_DIRECTORY + str(m.id)
component = "model"
try: with opentracing.tracer.start_active_span(
data = storage.connection.get_object(current_app.config['MINIO_CNN_BUCKET_NAME'], path) 'minio.getObject',
except NoSuchKey: tags={
abort(500, "The ID is stored in the database but not int the Object Store") "bucket": current_app.config['MINIO_CNN_BUCKET_NAME'],
"object_name": path,
"component": component
}
):
# Note: this only initiates the download, the file download itself is streamed
try:
data = storage.connection.get_object(current_app.config['MINIO_CNN_BUCKET_NAME'], path)
except NoSuchKey:
return abort(500, "The ID is stored in the database but not int the Object Store")
return Response(data.stream(), mimetype=data.headers['Content-type']) return Response(data.stream(), mimetype=data.headers['Content-type'])

View File

@ -9,6 +9,8 @@ from model import db, AIModel, AIModelType, Default
from schemas import AIModelSchema, DefaultSchema from schemas import AIModelSchema, DefaultSchema
import opentracing
class RootView(FlaskView): class RootView(FlaskView):
route_base = '/' route_base = '/'
@ -21,7 +23,9 @@ class RootView(FlaskView):
## Shared stuff goes here ## Shared stuff goes here
def index(self): def index(self):
models = AIModel.query.all() with opentracing.tracer.start_active_span('sqlalchemy.select'):
models = AIModel.query.all()
return jsonify(self.aimodels_schema.dump(models)) return jsonify(self.aimodels_schema.dump(models))
@route('/<type_>') @route('/<type_>')
@ -29,9 +33,11 @@ class RootView(FlaskView):
try: try:
aimodel_type = AIModelType[type_] aimodel_type = AIModelType[type_]
except KeyError: except KeyError:
abort(404, "Unknown type") return abort(404, "Unknown type")
with opentracing.tracer.start_active_span('sqlalchemy.select', tags={"aimodel_type": aimodel_type}):
models = AIModel.query.filter_by(type=aimodel_type).all()
models = AIModel.query.filter_by(type=aimodel_type).all()
return jsonify(self.aimodels_schema.dump(models)), 200 return jsonify(self.aimodels_schema.dump(models)), 200
@route('/<type_>/<id_>') @route('/<type_>/<id_>')
@ -39,32 +45,41 @@ class RootView(FlaskView):
try: try:
aimodel_type = AIModelType[type_] aimodel_type = AIModelType[type_]
except KeyError: except KeyError:
abort(404, "Unknown type") return abort(404, "Unknown type")
if id_ == "$default": with opentracing.tracer.start_active_span(
default = Default.query.filter_by(type=aimodel_type).first_or_404() 'sqlalchemy.select',
m = default.aimodel tags={"aimodel_type": aimodel_type, "id": id_}
else: ):
m = AIModel.query.filter_by(type=aimodel_type, id=id_).first_or_404() if id_ == "$default":
default = Default.query.filter_by(type=aimodel_type).first_or_404()
m = default.aimodel
else:
m = AIModel.query.filter_by(type=aimodel_type, id=id_).first_or_404()
# Append download links # Append download links
details = self.aimodel_schema.dump(m) with opentracing.tracer.start_active_span(
'compileResponseDict',
tags={"id": id_}
):
details = self.aimodel_schema.dump(m)
# Vagy ez, vagy visszateszem a saját view-jébe és duplikálva lesz az egész # Vagy ez, vagy visszateszem a saját view-jébe és duplikálva lesz az egész
if aimodel_type == AIModelType.cnn: if aimodel_type == AIModelType.cnn:
details.update({ details.update({
"files": { "files": {
"model": url_for("CNNView:get_file", id_=m.id), "model": url_for("CNNView:get_file", id_=m.id),
"weights": url_for("CNNView:get_file", id_=m.id, weights=''), "weights": url_for("CNNView:get_file", id_=m.id, weights=''),
} }
}) })
elif aimodel_type == AIModelType.svm: elif aimodel_type == AIModelType.svm:
details.update({ details.update({
"files": { "files": {
"model": url_for("SVMView:get_file", id_=m.id), "model": url_for("SVMView:get_file", id_=m.id),
"means": url_for("SVMView:get_file", id_=m.id, means=''), "means": url_for("SVMView:get_file", id_=m.id, means=''),
} }
}) })
return jsonify(details) return jsonify(details)
@ -75,18 +90,24 @@ class RootView(FlaskView):
try: try:
aimodel_type = AIModelType[type_] aimodel_type = AIModelType[type_]
except KeyError: except KeyError:
abort(404, "Unknown type") return abort(404, "Unknown type")
try: try:
req = self.default_schema.load(request.json) req = self.default_schema.load(request.json)
except ValidationError as e: except ValidationError as e:
abort(400, str(e)) return abort(400, str(e))
m = AIModel.query.filter_by(type=aimodel_type, id=req['id']).first_or_404() with opentracing.tracer.start_active_span('sqlalchemy.select'):
m = AIModel.query.filter_by(type=aimodel_type, id=req['id']).first_or_404()
Default.query.filter_by(type=aimodel_type).delete() with opentracing.tracer.start_active_span('sqlalchemy.delete'):
new_default = Default(type=aimodel_type, aimodel=m) Default.query.filter_by(type=aimodel_type).delete()
db.session.add(new_default)
db.session.commit() with opentracing.tracer.start_active_span('sqlalchemy.create'):
new_default = Default(type=aimodel_type, aimodel=m)
db.session.add(new_default)
with opentracing.tracer.start_active_span('sqlalchemy.commit'):
db.session.commit()
return '', 204 return '', 204

View File

@ -9,6 +9,7 @@ from schemas import AIModelSchema, InfoSchema
from marshmallow.exceptions import ValidationError from marshmallow.exceptions import ValidationError
from utils import storage, ensure_buckets, multipart_required from utils import storage, ensure_buckets, multipart_required
from pyAudioAnalysis.audioTrainTest import load_model from pyAudioAnalysis.audioTrainTest import load_model
import opentracing
class SVMView(FlaskView): class SVMView(FlaskView):
@ -24,94 +25,130 @@ class SVMView(FlaskView):
def post(self): def post(self):
# get important data from the request # get important data from the request
try: with opentracing.tracer.start_active_span('parseAndValidate'):
info = self.info_schema.loads(request.form.get('info')) try:
except ValidationError as e: info = self.info_schema.loads(request.form.get('info'))
abort(400, str(e)) except ValidationError as e:
return abort(400, str(e))
# check for conflict # check for conflict
m = AIModel.query.filter_by(id=info['id']).first() m = AIModel.query.filter_by(id=info['id']).first()
if m: if m:
abort(409) return abort(409)
# get and validate file # get and validate file
model_file = request.files['modelFile'] model_file = request.files['modelFile']
if model_file.content_length <= 0: if model_file.content_length <= 0:
abort(411, f"Content length for modelFile is not a positive integer or missing.") return abort(411, f"Content length for modelFile is not a positive integer or missing.")
means_file = request.files['meansFile'] means_file = request.files['meansFile']
if means_file.content_length <= 0: if means_file.content_length <= 0:
abort(411, f"Content length for meansFile is not a positive integer or missing.") return abort(411, f"Content length for meansFile is not a positive integer or missing.")
# create bucket if necessary # create bucket if necessary
ensure_buckets() with opentracing.tracer.start_active_span('ensureBuckets'):
ensure_buckets()
# Temporarily save the file, because pyAudioAnalysis can only read files # Temporarily save the file, because pyAudioAnalysis can only read files
temp_model_handle, temp_model_filename = tempfile.mkstemp() with opentracing.tracer.start_active_span('tempfile.save'):
temp_means_filename = temp_model_filename + "MEANS" temp_model_handle, temp_model_filename = tempfile.mkstemp()
temp_means_filename = temp_model_filename + "MEANS"
os.close(temp_model_handle) # BRUUUUH os.close(temp_model_handle) # BRUUUUH
model_file.save(temp_model_filename) model_file.save(temp_model_filename)
means_file.save(temp_means_filename) means_file.save(temp_means_filename)
try: with opentracing.tracer.start_active_span('pyAudioAnalysis.readModel'):
try:
_, _, _, classes, mid_window, mid_step, short_window, short_step, compute_beat \ _, _, _, classes, mid_window, mid_step, short_window, short_step, compute_beat \
= load_model(temp_model_filename) = load_model(temp_model_filename)
if info['target_class_name'] not in classes: if info['target_class_name'] not in classes:
abort(400, f"This model does not have a class named {info['target_class_name']}") return abort(400, f"This model does not have a class named {info['target_class_name']}")
# Because of pyAudiomeme the files already saved, so we just use the file uploader functions # Because of pyAudiomeme the files already saved, so we just use the file uploader functions
storage.connection.fput_object( storage.connection.fput_object(
current_app.config['MINIO_SVM_BUCKET_NAME'], current_app.config['MINIO_SVM_BUCKET_NAME'],
self.MODEL_DIRECTORY + str(info['id']), self.MODEL_DIRECTORY + str(info['id']),
temp_model_filename temp_model_filename
)
storage.connection.fput_object(
current_app.config['MINIO_SVM_BUCKET_NAME'],
self.MEANS_DIRECTORY + str(info['id']),
temp_means_filename
)
finally:
os.remove(temp_model_filename)
os.remove(temp_means_filename)
with opentracing.tracer.start_active_span('sqlalchemy.create'):
m = AIModel(id=info['id'], type=AIModelType.svm, target_class_name=info['target_class_name'])
db.session.add(m)
d = SVMDetails(
aimodel=m,
mid_window=mid_window,
mid_step=mid_step,
short_window=short_window,
short_step=short_step,
compute_beat=compute_beat
) )
db.session.add(d)
storage.connection.fput_object( with opentracing.tracer.start_active_span('sqlalchemy.commit'):
current_app.config['MINIO_SVM_BUCKET_NAME'], db.session.commit()
self.MEANS_DIRECTORY + str(info['id']),
temp_means_filename
)
finally:
os.remove(temp_model_filename)
os.remove(temp_means_filename)
m = AIModel(id=info['id'], type=AIModelType.svm, target_class_name=info['target_class_name'])
d = SVMDetails(
aimodel=m,
mid_window=mid_window,
mid_step=mid_step,
short_window=short_window,
short_step=short_step,
compute_beat=compute_beat
)
db.session.add(m)
db.session.add(d)
db.session.commit()
return jsonify(self.aimodel_schema.dump(m)), 200 return jsonify(self.aimodel_schema.dump(m)), 200
def delete(self, id_: str): def delete(self, id_: str):
if id_ == "$default": with opentracing.tracer.start_active_span(
default = Default.query.filter_by(type=AIModelType.svm).first_or_404() 'sqlalchemy.select',
m = default.aimodel tags={"aimodel_type": AIModelType.svm, "id": id_}
else: ):
m = AIModel.query.filter_by(type=AIModelType.svm, id=id_).first_or_404() if id_ == "$default":
default = Default.query.filter_by(type=AIModelType.svm).first_or_404()
m = default.aimodel
else:
m = AIModel.query.filter_by(type=AIModelType.svm, id=id_).first_or_404()
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'], self.MEANS_DIRECTORY + str(m.id)) with opentracing.tracer.start_active_span('removeFromMinio'):
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'], self.MODEL_DIRECTORY + str(m.id)) with opentracing.tracer.start_active_span(
'minio.removeObject',
tags={
"bucket": current_app.config['MINIO_SVM_BUCKET_NAME'],
"object_name": self.MEANS_DIRECTORY + str(m.id),
"component": "means"
}
):
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'],
self.MEANS_DIRECTORY + str(m.id))
db.session.delete(m) with opentracing.tracer.start_active_span(
db.session.commit() 'minio.removeObject',
tags={
"bucket": current_app.config['MINIO_SVM_BUCKET_NAME'],
"object_name": self.MODEL_DIRECTORY + str(m.id),
"component": "model"
}
):
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'],
self.MODEL_DIRECTORY + str(m.id))
with opentracing.tracer.start_active_span(
'sqlalchemy.delete',
tags={"aimodel_type": AIModelType.svm, "id": id_}
):
db.session.delete(m)
with opentracing.tracer.start_active_span('sqlalchemy.commit'):
db.session.commit()
return '', 204 return '', 204
@ -119,20 +156,34 @@ class SVMView(FlaskView):
@route('<id_>/file') @route('<id_>/file')
def get_file(self, id_: str): def get_file(self, id_: str):
if id_ == "$default": with opentracing.tracer.start_active_span(
default = Default.query.filter_by(type=AIModelType.svm).first_or_404() 'sqlalchemy.select',
m = default.aimodel tags={"aimodel_type": AIModelType.svm, "id": id_}
else: ):
m = AIModel.query.filter_by(type=AIModelType.svm, id=id_).first_or_404() if id_ == "$default":
default = Default.query.filter_by(type=AIModelType.svm).first_or_404()
m = default.aimodel
else:
m = AIModel.query.filter_by(type=AIModelType.svm, id=id_).first_or_404()
if "means" in request.args: if "means" in request.args:
path = self.MEANS_DIRECTORY + str(m.id) path = self.MEANS_DIRECTORY + str(m.id)
component = "means"
else: else:
path = self.MODEL_DIRECTORY + str(m.id) path = self.MODEL_DIRECTORY + str(m.id)
component = "model"
try: with opentracing.tracer.start_active_span(
data = storage.connection.get_object(current_app.config['MINIO_SVM_BUCKET_NAME'], path) 'minio.getObject',
except NoSuchKey: tags={
abort(500, "The ID is stored in the database but not int the Object Store") "bucket": current_app.config['MINIO_SVM_BUCKET_NAME'],
"object_name": path,
"component": component
}
):
try:
data = storage.connection.get_object(current_app.config['MINIO_SVM_BUCKET_NAME'], path)
except NoSuchKey:
return abort(500, "The ID is stored in the database but not int the Object Store")
return Response(data.stream(), mimetype=data.headers['Content-type']) return Response(data.stream(), mimetype=data.headers['Content-type'])

View File

@ -1,11 +1,11 @@
requests requests
blinker blinker
Flask Flask~=1.1.2
marshmallow marshmallow~=3.7.1
Flask-Classful Flask-Classful
Flask-SQLAlchemy Flask-SQLAlchemy
SQLAlchemy-Utils SQLAlchemy-Utils
SQLAlchemy SQLAlchemy~=1.3.19
marshmallow-sqlalchemy~=0.26.1 marshmallow-sqlalchemy~=0.26.1
marshmallow-enum marshmallow-enum
psycopg2-binary psycopg2-binary
@ -13,6 +13,7 @@ minio~=6.0.0
flask_minio flask_minio
sentry-sdk sentry-sdk
py-healthcheck py-healthcheck
Werkzeug~=1.0.1
cython cython
@ -25,7 +26,7 @@ joblib==0.14.1
kiwisolver==1.2.0 kiwisolver==1.2.0
matplotlib==3.2.1 matplotlib==3.2.1
numpy==1.18.2 numpy==1.18.2
pyAudioAnalysis==0.3.0 pyAudioAnalysis==0.3.6
pydub==0.23.1 pydub==0.23.1
pyparsing==2.4.6 pyparsing==2.4.6
python-dateutil==2.8.1 python-dateutil==2.8.1
@ -35,4 +36,5 @@ simplejson==3.17.0
six==1.14.0 six==1.14.0
jaeger-client jaeger-client
Flask-Opentracing Flask-Opentracing
opentracing~=2.4.0