Troubleshooting the error

Let’s find the line of code that resulted into this error! We are going to use AWS Cloudwatch ServiceLens, AWS X-Ray services to help us investigate the root cause

  • Make sure the AWS region is set to us-west-2 region (US Oregon) in top right corner of your AWS Management Console
  • In the AWS Management Console choose Services then search Amazon CloudWatch.
  • Click on Service Map under ServiceLens
  • You will see a Service Map graphics showing end to end flow of the requests

  • Locate the node in the graph that is marked with legand having error/fault. In this case, its the Amazon DynamoDb node.
  • Click on DynamoDB node to further see all the requests that generated error.

  • You can locate the trace for a specific RequestId or you can search for all the RequestIds that failed with HTTP Error code (in this case - 409)
  • Below is the screen showing all the requests summary for the node. Click on View in X-Ray Analytics to further narrow down the root cause

  • Click on View traces to narrow down based on the http response codes. Locate the traces with response code of 409 and click on the URL as shown below

  • AWS X-Ray console showing details of the trace captured as below. A trace consists of multiple sub-segments. click on the sub-segment that generated the error (i.e. dynamodb ) to get more details

  • Click on Exceptions tab to see sub-segment details. You will see the actual backend exception leading to the HTTP Error. Locate the Application microservice implementation class name in the call stack and locate that in your source code to understand the root cause

  • ApplicationService.java has the method createApplication that has the line to add a dynamodb.putItem as shown below. It has an conditionExpression to check if the userId and applicationId already exists in the dynamodb table.

Conditional Writes and Optimistic Locking with Version Number are ways to ensure the client-side item that you are updating (or deleting) is the same as the item in Amazon DynamoDB. If you use this strategy, your database writes are protected from being overwritten by the writes of others, and vice versa

@Override
      public Application createApplication(final CreateApplicationInput createApplicationInput) {
        log.info("Creating application with input {}", createApplicationInput);
        ApplicationRecord applicationRecord = modelMapper.map(createApplicationInput,
              ApplicationRecord.class);
        applicationRecord.setCreatedAt(Instant.now(clock));
        applicationRecord.setVersion(1L);
        applicationRecord.setUserId(securityContext.getUserPrincipal().getName());
        try {
          dynamodb.putItem(PutItemRequest.builder()
                .tableName(tableName)
                .item(applicationRecord.toAttributeMap())
                .conditionExpression(
                      String.format("attribute_not_exists(%s) AND attribute_not_exists(%s)",
                            ApplicationRecord.USER_ID_ATTRIBUTE_NAME,
                            ApplicationRecord.APPLICATION_ID_ATTRIBUTE_NAME))
                .build());
        } catch (ConditionalCheckFailedException e) {
          throw new ConflictApiException(new ConflictException()
                .errorCode("ApplicationAlreadyExist")
                .message(String.format("Application %s already exists.",
                      createApplicationInput.getApplicationId())));
        }
        return modelMapper.map(applicationRecord, Application.class);
}

**Congratulations! you have found the code causing your API to return an error!