Skip to content

Task 1-3: Firebase Listener Health Check Scheduled

Phase: 1 - Observability Priority: Medium Module: firebaselistener Depends on: Không có Reference: docs/BountyHunter-Backend/details/feature-batch-async-processing/SPEC.md

Background

Firebase listeners trong firebaselistener module không có health monitoring. Nếu Firebase SDK mất kết nối, listeners ngừng hoạt động mà không có alert.

Tasks

File mới: firebaselistener/service/FirebaseHealthCheckService.java

DI Note: FirebaseHealthCheckService is a Spring-managed @Service bean. FirebaseDatabase.getInstance() is a static SDK call — no Spring injection needed for Firebase itself. Ensure the Firebase Admin SDK dependency is on the classpath (firebase-admin jar). Required imports: - com.google.firebase.database.DatabaseReference - com.google.firebase.database.FirebaseDatabase - com.google.firebase.database.DataSnapshot - com.google.firebase.database.DatabaseError - com.google.firebase.database.ValueEventListener - org.springframework.scheduling.annotation.Scheduled - java.time.Instant

@Service
@Slf4j
public class FirebaseHealthCheckService {

    private volatile boolean lastHealthy = true;
    private Instant lastHealthCheckTime = Instant.now();

    @Scheduled(fixedDelay = 30000)
    public void checkFirebaseConnection() {
        try {
            DatabaseReference connRef = FirebaseDatabase.getInstance()
                .getReference(".info/connected");

            connRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot snapshot) {
                    Boolean connected = snapshot.getValue(Boolean.class);
                    lastHealthCheckTime = Instant.now();

                    if (Boolean.TRUE.equals(connected)) {
                        if (!lastHealthy) {
                            log.info("[FIREBASE_HEALTH] Connection restored");
                        }
                        lastHealthy = true;
                    } else {
                        log.error("[FIREBASE_HEALTH] Firebase reports NOT connected!");
                        lastHealthy = false;
                    }
                }

                @Override
                public void onCancelled(DatabaseError error) {
                    log.error("[FIREBASE_HEALTH] Health check cancelled: {}", error.getMessage());
                    lastHealthy = false;
                }
            });
        } catch (Exception e) {
            log.error("[FIREBASE_HEALTH] Exception during health check", e);
            lastHealthy = false;
        }
    }

    public boolean isHealthy() {
        return lastHealthy;
    }
}
  • [ ] Expose health status via Spring Actuator HealthIndicator
  • [ ] Alert khi lastHealthy=false persists > 2 checks

Verification / Acceptance Criteria

  • [ ] FirebaseHealthCheckService bean starts without errors; checkFirebaseConnection() scheduler fires every 30 s
  • [ ] When Firebase is reachable, isHealthy() returns true and logs [FIREBASE_HEALTH] Connection restored only on recovery (not on every tick)
  • [ ] When Firebase connection is simulated as unavailable (mock FirebaseDatabase), isHealthy() returns false and an error is logged
  • [ ] lastHealthCheckTime is updated on every successful onDataChange callback
  • [ ] Spring Actuator HealthIndicator (follow-up item) reports DOWN when isHealthy() == false

Files to Create

  • firebaselistener/service/FirebaseHealthCheckService.java