Part 4: Testing and Deployment


What Would I Have Done Differently (and Will Do Differently Next Time)?

  • Bootstrapping
  • Testing
  • Authentication and Authorization
  • Pub/Sub and Processing
  • Monitoring and Alerting

Bootstrapping

Testing



                                                         Test pyramid from RayWenderlich
There should be lots of unit tests that are quick to develop, catch small bugs, and are quick to execute and debug. At the next level up are integration tests which test points of integration, or in the case of microservice architecture, typically test a single service. These tests take more effort to develop, catch larger bugs, and are a little slower to execute and debug. At the top level are UI tests, or end-to-end tests, that are the most difficult to develop but potentially spot some of the largest bugs.

  • Tests run against an actual environment to see if integration are hooked up properly
  • Tests should succeed independent of the state of the environment or be idempotent
  • Tests should not corrupt the state of your development environment

Authentication and Authorization

def login_required(f):
"""
Decorator to require user to have token and user in header
:return: boolean for if they are logged in
"""
def wrap(request: Request):
if (not request.headers or 'token' not in request.headers
or 'uid' not in request.headers):
return Response(body={"error": "Not authorized"}, status_code=403)
uid = request.headers['uid']
token = request.headers['token']
dynamo = DynamoDb()
verified_session = dynamo.check_token(uid=uid, token=token)
if verified_session:
return f(request)
return Response(body={"error": "Not authorized"}, status_code=403)
return wrap
class DynamoDb:
def __init__(self):
self.db = boto3.resource('dynamodb', endpoint_url=os.environ.get("ENDPOINT_URL", None))
self.token_table_name = os.environ["TOKEN_TABLE_NAME"]
def check_token(self, uid: int, token: str) -> bool:
"""
Checks dynamo to see if user exists in keys and if token matches
:param uid: user id
:param token: generated token on login
:return: bool of whether the token matches
"""
table = self.db.Table(self.token_table_name)
epoch_time_now = int(time.time())
try:
response = table.query(
KeyConditionExpression=Key('Uid').eq(uid),
FilterExpression=Key('TimeToLive').gt(str(epoch_time_now))
)
if len(response.get('Items', [])) > 0 and response['Items'][0]['Token'] == token:
return True
return False
except ClientError as e:
return False
def insert_token(self, uid: int, token: str):
"""
Insert new token into dynamo table
:param uid: user id
:param token: token for session
"""
table = self.db.Table(self.token_table_name)
ttl = datetime.datetime.today() + datetime.timedelta(minutes=30)
expiry_datetime = int(time.mktime(ttl.timetuple()))
try:
table.put_item(
Item={
'Uid': uid,
'Token': token,
'TimeToLive': str(expiry_datetime)
}
)
except Exception as e:
print(e)

Pub/Sub and Processing

  • Processing speeds
  • Additionally complexity for achieving transactional semantics
  • Bugs not caught till runtime resulting in additional unit testing or manual integration testing
  • Ambiguity in scaling the codebase for additional complexity
  1. Kafka Schema Registry to ensure inter-service communication adhered to the contract between services
  2. Kafka GUI for managing and monitoring Kafka

Monitoring and Alerting

  • Issues aren’t detected, causing SLA breaches, outages, etc.
  • Issues can’t be responded to fast
  • Redesigns and refactoring may be needed to account for monitoring requirements
  • Operations are interrupted

Post a Comment

0 Comments