Java Developer Project Based Scenario based Interview Questions and Answers

Questions

Users report that sometimes their photo/video uploads fail in your Spring Boot API. It works fine for smaller files but fails for larger files (100MB+).

  • How do you debug this issue?
  • What AWS S3 configurations could affect large file uploads?
  • Would you use a multi-part upload? How?
  • What changes would you make to increase upload reliability?

Your service processes and compresses videos before uploading them to AWS S3. Over time, the JVM memory usage spikes, leading to OOM errors.

  • How do you identify which part of the service is causing high memory usage?
  • How would you profile memory usage in a running Spring Boot app?
  • Would you process videos synchronously or asynchronously?
  • What caching mechanisms can help reduce memory pressure?

 

Your application uses AWS Cognito for user authentication, but some users report random login failures.

  • How would you debug intermittent authentication failures?
  • How do you verify JWT tokens in Spring Boot?
  • configurations in AWS Cognito might cause these issues?
  • Would you implement a refresh token mechanism? How?

 

Your service needs to handle 1M+ media uploads per day.

  • Would you use Kafka or SQS for processing uploads asynchronously? Why?
  • How do you ensure uploads don’t overload the database?
  • How can you scale storage and prevent S3 throttling?

 

 

Your Media Service calls the Notification Service to send an email after an upload. Sometimes, the notification service fails, causing the entire request to fail.

  • How do you prevent cascading failures in microservices?
  • Would you use a Circuit Breaker (Resilience4j)?
  • How would you implement a retry mechanism?

 

 

Users want to stream videos instead of downloading them.

  • How would you design a video streaming backend using Spring Boot?
  • Would you store videos in S3 or use a CDN (CloudFront)?
  • How do you optimize buffering and streaming latency?

 

 

The following API sometimes fails for large file uploads. Fix it.

 

@PostMapping(“/upload”)

public String uploadFile(@RequestParam(“file”) MultipartFile file) {

    try {

        byte[] bytes = file.getBytes(); // Potential memory issue

        Path path = Paths.get(“/uploads/” + file.getOriginalFilename());

        Files.write(path, bytes);

        return “Upload successful!”;

    } catch (IOException e) {

        return “Upload failed!”;

    }

}

  • What’s wrong with this implementation?
  • How would you optimize it for handling large files?

 

Your media upload API works fine initially but crashes after multiple uploads due to OutOfMemoryError (OOM).

 

@RestController

@RequestMapping(“/media”)

public class MediaController {

   

    private List<byte[]> uploadedFiles = new ArrayList<>(); // Potential Memory Leak

 

    @PostMapping(“/upload”)

    public ResponseEntity<String> uploadFile(@RequestParam(“file”) MultipartFile file) throws IOException {

        byte[] fileData = file.getBytes(); // Storing in memory – BAD

        uploadedFiles.add(fileData); // Memory keeps growing – BAD

 

        // Simulate saving to disk (not actually doing it)

        System.out.println(“File uploaded: ” + file.getOriginalFilename());

        return ResponseEntity.ok(“File uploaded successfully”);

    }

}

🛑 Issues:

  1. Memory Leak: Uploaded files are stored in a List (uploadedFiles), causing JVM heap memory to fill up.
  2. File Handling: The entire file is read into a byte array (file.getBytes()), which won’t work for large files.

 

 

Fix It!

  • Identify the memory leak and explain why it happens.
  • Refactor the code to prevent OutOfMemoryError using a streaming approach.
  • Implement a more efficient way to handle large file uploads.

 

Questions & Answers

  1. How do you debug this issue (failure for large file uploads)?
  1. Reproduce the Issue:
    • Use tools like Postman or curl to replicate the file upload scenario with files of various sizes.
    • Check the API logs for any error messages or stack traces related to file uploads.
  2. Analyze Logs:
    • Look for errors like RequestEntityTooLarge or SocketTimeoutException in your logs.
  3. Monitor Network and API Gateway:
    • Use tools like AWS CloudWatch to monitor network traffic and API Gateway logs.
    • Check if there’s a timeout or rate-limiting configuration causing the issue.
  4. Review Application Code:
    • Check the controller and service methods handling file uploads. For example:

 

@PostMapping(“/upload”)

public ResponseEntity<String> uploadFile(@RequestParam(“file”) MultipartFile file) {

    // Process file upload logic

}

 

  1. What AWS S3 configurations could affect large file uploads?
  1. S3 Multipart Upload Settings:
    • Ensure the multipart threshold is set appropriately. AWS recommends multipart uploads for files larger than 5MB.
  2. Pre-signed URL Expiry:
    • Check if pre-signed URLs are expiring before the upload completes.
  3. AWS API Gateway Timeout:
    • Default timeout is 29 seconds. Increase this if uploads take longer.
  4. Bucket Policy and Permissions:
    • Ensure the bucket allows PutObject actions with appropriate size limits.

 

  1. Would you use a multi-part upload? How?

Yes, multi-part upload is ideal for handling large files. AWS S3 splits the file into parts, uploads them independently, and then combines them.

Implementation Example:

AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();

InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(“bucket-name”, “key-name”);

InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest);

 

List<PartETag> partETags = new ArrayList<>();

File file = new File(“large-file.mp4”);

long contentLength = file.length();

long partSize = 5 * 1024 * 1024; // 5 MB

 

try (FileInputStream fis = new FileInputStream(file)) {

    for (int i = 0; contentLength > 0; i++) {

        UploadPartRequest uploadRequest = new UploadPartRequest()

            .withBucketName(“bucket-name”)

            .withKey(“key-name”)

            .withUploadId(initResponse.getUploadId())

            .withPartNumber(i + 1)

            .withFileOffset(i * partSize)

            .withInputStream(fis)

            .withPartSize(Math.min(partSize, contentLength));

        partETags.add(s3Client.uploadPart(uploadRequest).getPartETag());

        contentLength -= partSize;

    }

    CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(

        “bucket-name”, “key-name”, initResponse.getUploadId(), partETags);

    s3Client.completeMultipartUpload(compRequest);

} catch (Exception e) {

    s3Client.abortMultipartUpload(new AbortMultipartUploadRequest(“bucket-name”, “key-name”, initResponse.getUploadId()));

}

 

  1. What changes would you make to increase upload reliability?
  1. Increase Timeouts:
    • Update the Spring Boot property:

 

spring.servlet.multipart.max-file-size=200MB

spring.servlet.multipart.max-request-size=200MB

    • Adjust the S3 client timeout settings.
  1. Retry Logic:
    • Implement retry logic for failed uploads using libraries like Spring Retry or Resilience4j.
  2. Chunking Files:
    • Divide files into smaller parts before uploading.
  3. Pre-signed URLs:
    • Use pre-signed URLs for direct client-to-S3 uploads, reducing server load.

 

Memory Usage and Video Compression Issue:

  1. How do you identify which part of the service is causing high memory usage?
  • Heap Dump Analysis:
    • Capture a heap dump using tools like jmap or Spring Boot Actuator.
    • Analyze it with tools like Eclipse MAT (Memory Analyzer Tool) or VisualVM.
  • Enable GC Logs:
    • Add JVM options:

 

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log

  • Profiling Tools:
    • Use tools like YourKit, JProfiler, or Java Mission Control to identify memory bottlenecks.

 

  1. How would you profile memory usage in a running Spring Boot app?
  1. Enable Spring Boot Actuator:

 

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

  1. Use endpoints like /actuator/metrics and /actuator/heapdump.
  2. Attach profiling tools (e.g., JProfiler) to the running process:

 

jprofiler.sh -attach <PID>

 

  1. Would you process videos synchronously or asynchronously?

Asynchronous Processing:

  • Use message queues like RabbitMQ or AWS SQS to offload video processing tasks.
  • Example:

@Async

public CompletableFuture<Void> processVideo(File videoFile) {

    // Video processing logic

}

This approach prevents the server from blocking while processing videos.

 

  1. What caching mechanisms can help reduce memory pressure?
  • In-memory Cache:
    • Use caching libraries like Caffeine or Redis for temporary data storage.
  • Offload to Redis:
    • Store frequently accessed data in Redis instead of keeping it in memory.
  • Eviction Policies:
    • Implement LRU (Least Recently Used) or TTL (Time-to-Live) policies.

 

AWS Cognito Authentication Failures:

  1. How would you debug intermittent authentication failures?
  1. Logs:
    • Check API Gateway and CloudWatch logs for failed authentication attempts.
  2. Network Issues:
    • Use tools like Wireshark to analyze network latency or packet drops.
  3. Token Expiry:
    • Validate if the access token is expiring prematurely.

 

  1. How do you verify JWT tokens in Spring Boot?

public boolean validateToken(String token) {

    try {

        Jwts.parserBuilder()

            .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes()))

            .build()

            .parseClaimsJws(token);

        return true;

    } catch (JwtException | IllegalArgumentException e) {

        return false;

    }

}

 

  1. What configurations in AWS Cognito might cause these issues?
  • Misconfigured App Clients:
    • Ensure the app client secret and allowed redirects are set up correctly.
  • Rate Limiting:
    • Check if requests are throttled due to high traffic.

 

  1. Would you implement a refresh token mechanism? How?

Yes, to refresh tokens:

  1. Enable refresh tokens in Cognito App Client settings.
  2. Use an endpoint to refresh tokens:

@PostMapping(“/refresh”)

public ResponseEntity<?> refreshToken(@RequestBody String refreshToken) {

    return cognitoService.refreshToken(refreshToken);

}

  1. Validate the refresh token using AWS SDK.

 

  1. Would you use Kafka or SQS for processing uploads asynchronously? Why?
  • Kafka:
    • Pros:
      • High throughput and low latency, making it suitable for handling millions of events per day.
      • Ability to persist messages for a configurable retention period.
      • Supports horizontal scaling via consumer groups.
    • Use Case:
      • If the system requires real-time processing, complex event pipelines, or multi-consumer processing, Kafka is the better choice.
  • SQS:
    • Pros:
      • Simpler setup with AWS-managed infrastructure.
      • Built-in at-least-once delivery guarantees.
      • Highly integrated with other AWS services.
    • Use Case:
      • If simplicity, cost, and integration with AWS services (e.g., Lambda) are priorities.

Recommendation: For 1M+ uploads/day, Kafka is preferable due to its scalability and ability to handle massive throughput. If you are already within the AWS ecosystem and need simplicity, SQS can work well.

 

  1. How do you ensure uploads don’t overload the database?
  1. Batch Inserts:
    • Aggregate metadata into batches before inserting into the database.
    • Example:

jdbcTemplate.batchUpdate(“INSERT INTO uploads (user_id, file_name, upload_time) VALUES (?, ?, ?)”, batchArgs);

  1. Queue for Write Operations:
    • Use Kafka/SQS to decouple file uploads from database writes. This ensures backpressure is absorbed by the message queue.
  2. Database Indexing:
    • Properly index frequently queried fields, such as user_id or upload_time.
  3. Database Sharding:
    • Distribute the load by partitioning the database based on fields like user_id.
  4. Rate Limiting:
    • Throttle requests at the API Gateway or service level to avoid overwhelming the database.

 

  1. How can you scale storage and prevent S3 throttling?
  1. S3 Throttling Prevention:
    • Use randomized prefixes in your S3 object keys to distribute writes across partitions.
      • Example: Instead of uploads/user123/file1.mp4, use uploads/ab23/user123/file1.mp4.
    • Enable S3 Transfer Acceleration to speed up uploads.
  2. Scale Storage:
    • S3 is inherently scalable, but to optimize:
      • Use lifecycle policies to archive older data into Glacier.
      • Use S3 Intelligent Tiering to reduce costs while scaling.
  3. Multi-Region Buckets:
    • Deploy buckets in multiple regions to reduce latency and balance upload traffic.
  4. Use Pre-Signed URLs:
    • Offload the upload process to clients, allowing them to upload directly to S3 without routing through your backend.

 

Cascading Failures in Microservices

  1. How do you prevent cascading failures in microservices?
  1. Asynchronous Communication:
    • Use message queues (e.g., Kafka or SQS) to decouple services. The media service can enqueue a notification request, rather than waiting for the notification service response.
  2. Timeouts and Retries:
    • Set timeouts for inter-service calls to prevent long wait times during failures.
  3. Fallback Mechanisms:
    • Provide a fallback action if the notification service is unavailable (e.g., store failed notifications in a queue for later processing).
  4. Bulkheads:
    • Use resource isolation (e.g., thread pools) to prevent failures in one service from affecting others.
  1. Would you use a Circuit Breaker (Resilience4j)?

Yes, a circuit breaker is ideal for preventing cascading failures when a dependent service becomes unresponsive.

How It Works:

  • Closed State: All requests pass through normally.
  • Open State: Requests fail immediately when the notification service exceeds the failure threshold.
  • Half-Open State: Allows limited requests to test if the notification service has recovered.

Example Using Resilience4j:

CircuitBreakerConfig config = CircuitBreakerConfig.custom()

    .failureRateThreshold(50)

    .waitDurationInOpenState(Duration.ofSeconds(30))

    .slidingWindowSize(10)

    .build();

 

CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);

CircuitBreaker circuitBreaker = registry.circuitBreaker(“notificationService”);

 

Supplier<String> decoratedSupplier = CircuitBreaker.decorateSupplier(circuitBreaker, () -> {

    return notificationService.sendNotification();

});

 

Try<String> result = Try.ofSupplier(decoratedSupplier);

 

  1. How would you implement a retry mechanism?

Retries ensure transient failures are handled without overwhelming the dependent service.

Retry with Exponential Backoff (Resilience4j):

RetryConfig config = RetryConfig.custom()

    .maxAttempts(3)

    .waitDuration(Duration.ofSeconds(2)) // Initial wait

    .retryExceptions(TimeoutException.class, IOException.class)

    .build();

 

RetryRegistry registry = RetryRegistry.of(config);

Retry retry = registry.retry(“notificationService”);

 

Supplier<String> retryableSupplier = Retry.decorateSupplier(retry, () -> {

    return notificationService.sendNotification();

});

 

Try<String> result = Try.ofSupplier(retryableSupplier);

Features:

  • Limits the number of retry attempts to prevent overload.
  • Exponential backoff reduces pressure during retries.

 

  1. Designing a Video Streaming Backend Using Spring Boot

Design Approach:

  1. Storing and Serving Videos:
    • Store videos in AWS S3 for scalability.
    • Use CloudFront (CDN) to deliver content with low latency and high availability.
    • Encode and compress videos into adaptive bitrate formats like HLS (HTTP Live Streaming).
  2. Spring Boot Streaming API:
    • Use HTTP range requests to support partial content delivery.
    • Provide endpoints for video metadata and content.
  3. Backend Flow:
    • A client requests a video.
    • The backend fetches the video URL (or metadata) from S3 or the CDN.
    • The client streams the video using adaptive bitrate.

 

Would You Use S3 or a CDN (CloudFront)?

  • S3:
    • Great for storing video files persistently.
    • However, directly streaming from S3 can lead to high latency and costs.
  • CDN (CloudFront):
    • Delivers content from edge locations closest to users, reducing latency.
    • Supports caching to minimize S3 requests.

Recommendation: Use CloudFront for video streaming and S3 for storage.

 

How Do You Optimize Buffering and Streaming Latency?

  1. Adaptive Bitrate Streaming:
    • Encode videos into multiple resolutions and bitrates (e.g., 144p, 360p, 1080p).
    • Use HLS or DASH protocols to dynamically switch bitrates based on network conditions.
  2. Pre-Signed URLs:
    • Generate short-lived pre-signed URLs to restrict access and prevent unauthorized downloads.
  3. Low Latency Settings:
    • Optimize CloudFront for low-latency streaming by enabling edge caching and compression.
    • Reduce video segment size in HLS (e.g., 2-4 seconds).
  4. Parallel Loading:
    • Use multi-threading for fetching video segments in the client.

 

Sample Code for Streaming with Spring Boot:

@GetMapping(“/stream/{videoId}”)

public ResponseEntity<Resource> streamVideo(@PathVariable String videoId,

                                            @RequestHeader(value = “Range”, required = false) String range) throws IOException {

    File videoFile = new File(“/videos/” + videoId + “.mp4”);

    long fileSize = videoFile.length();

 

    // Set the range for partial content delivery

    if (range != null) {

        String[] ranges = range.replace(“bytes=”, “”).split(“-“);

        long start = Long.parseLong(ranges[0]);

        long end = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileSize – 1;

 

        RandomAccessFile randomAccessFile = new RandomAccessFile(videoFile, “r”);

        randomAccessFile.seek(start);

        byte[] buffer = new byte[(int) (end – start + 1)];

        randomAccessFile.read(buffer);

 

        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)

                             .header(“Content-Type”, “video/mp4”)

                             .header(“Content-Range”, “bytes ” + start + “-” + end + “/” + fileSize)

                             .body(new ByteArrayResource(buffer));

    }

 

    // Default response for full video

    return ResponseEntity.ok()

                         .header(“Content-Type”, “video/mp4”)

                         .body(new FileSystemResource(videoFile));

}

 

  1. Fixing Large File Upload API

What’s Wrong with This Implementation?

  1. Memory Issue:
    • Reading the entire file into memory using file.getBytes() can lead to OutOfMemoryError for large files.
  2. Inefficient File Writing:
    • Writing the file to disk all at once is not scalable.

 

Optimized Code for Large File Uploads Using Streaming:

@PostMapping(“/upload”)

public String uploadFile(@RequestParam(“file”) MultipartFile file) {

    Path uploadPath = Paths.get(“/uploads/” + file.getOriginalFilename());

 

    try (InputStream inputStream = file.getInputStream();

         OutputStream outputStream = Files.newOutputStream(uploadPath, StandardOpenOption.CREATE)) {

        

        byte[] buffer = new byte[8192]; // 8 KB buffer

        int bytesRead;

        while ((bytesRead = inputStream.read(buffer)) != -1) {

            outputStream.write(buffer, 0, bytesRead);

        }

        return “Upload successful!”;

    } catch (IOException e) {

        return “Upload failed!”;

    }

}

Why This Works:

  • The file is streamed in chunks, preventing the JVM from loading the entire file into memory.

 

  1. Fixing OutOfMemoryError (OOM) in MediaController

Identifying the Memory Leak:

  1. Cause:
    • uploadedFiles stores all uploaded files in memory (byte[]).
    • Each file is read entirely into memory (file.getBytes()), compounding the issue for large files.
  2. Why It Happens:
    • The JVM heap memory fills up as the application keeps adding file data to the uploadedFiles list without releasing it.

 

Refactored Code to Avoid OOM with Streaming:

@RestController

@RequestMapping(“/media”)

public class MediaController {

 

    @PostMapping(“/upload”)

    public ResponseEntity<String> uploadFile(@RequestParam(“file”) MultipartFile file) {

        Path uploadPath = Paths.get(“/uploads/” + file.getOriginalFilename());

 

        try (InputStream inputStream = file.getInputStream();

             OutputStream outputStream = Files.newOutputStream(uploadPath, StandardOpenOption.CREATE)) {

 

            byte[] buffer = new byte[8192]; // Stream in chunks

            int bytesRead;

            while ((bytesRead = inputStream.read(buffer)) != -1) {

                outputStream.write(buffer, 0, bytesRead);

            }

 

            return ResponseEntity.ok(“File uploaded successfully”);

        } catch (IOException e) {

            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(“Upload failed!”);

        }

    }

}

Key Improvements:

  1. Avoids loading the entire file into memory.
  2. Streams the file directly to disk in chunks.

 

Additional Optimizations for Large File Handling

  1. Temporary Storage:
    • Use a temporary directory (e.g., /tmp/) for uploads before moving the file to permanent storage.
  2. File Size Limits:
    • Set a maximum file size in the Spring Boot configuration:

spring.servlet.multipart.max-file-size=100MB

spring.servlet.multipart.max-request-size=100MB

  1. Asynchronous Processing:
    • Offload file upload handling to a background thread pool to avoid blocking the main thread.