Zadanie ubite razem z kontenerem znika z ekranu Zasoby po osmiu godzinach
Deploy PROD / deploy (push) Successful in 19s
Deploy PROD / deploy (push) Successful in 19s
Egress zapisuje stan zadan w Redisie, a status koncowy ustawia sam proces obslugujacy nagranie. Gdy proces zostanie ubity razem z kontenerem (restart, wyczerpanie pamieci), status nigdy sie nie zmienia: zadanie figuruje jako EGRESS_ACTIVE **na zawsze**. `StopEgress` konczy sie wtedy sukcesem i NIC NIE ZMIENIA, bo nie ma juz czego zatrzymywac. Sprawdzone na zywo: wpis wisial po restarcie Egress, panel pokazywal go jako osierocone zadanie, a klikniecie "Zatrzymaj" logowalo "Zatrzymano Egress" i zostawialo wszystko po staremu. Zuzycie procesora bylo przy tym zerowe, czyli zadanie faktycznie nie istnialo. Ekran mowil wiec nieprawde i nie dawalo sie tego odkrecic z poziomu aplikacji: wpis usunieto recznie z Redisa. Liczy sie teraz takze WIEK zadania, nie tylko jego status. Osiem godzin: dluzej niz najdluzszy sensowny webinar razem z montazem, a krocej niz doba, zeby wpis po nocnym restarcie nie straszyl rano. `startedAt` jest w nanosekundach od epoki. Proto podaje sam `int64` bez jednostki, ale webhooki LiveKit niosa tam wartosci 19-cyfrowe, a `duration` w `FileInfo` jest liczone tak samo. Zero oznacza zadanie, ktore nie zdazylo wystartowac: traktujemy je jak biezace, zeby swiezo zlecone nagranie nie znikalo z ekranu, zanim ruszy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,15 @@ public class LiveKitEgressService {
|
|||||||
|
|
||||||
private static final SecureRandom RANDOM = new SecureRandom();
|
private static final SecureRandom RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Po jakim czasie zadanie u dostawcy uznajemy za martwe, niezaleznie od
|
||||||
|
* jego statusu.
|
||||||
|
*
|
||||||
|
* Osiem godzin: dluzej niz najdluzszy sensowny webinar razem z montazem,
|
||||||
|
* a krocej niz doba, zeby wpis po nocnym restarcie nie straszyl rano.
|
||||||
|
*/
|
||||||
|
private static final java.time.Duration STALE_EGRESS_AFTER = java.time.Duration.ofHours(8);
|
||||||
|
|
||||||
private final SettingsService settings;
|
private final SettingsService settings;
|
||||||
private final WebinarRepository webinarRepository;
|
private final WebinarRepository webinarRepository;
|
||||||
private final RecordingRepository recordingRepository;
|
private final RecordingRepository recordingRepository;
|
||||||
@@ -350,11 +359,43 @@ public class LiveKitEgressService {
|
|||||||
if (!response.isSuccessful() || response.body() == null) {
|
if (!response.isSuccessful() || response.body() == null) {
|
||||||
throw new ValidationException("Serwer LiveKit odpowiedzial: " + describe(response.code()));
|
throw new ValidationException("Serwer LiveKit odpowiedzial: " + describe(response.code()));
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* ZADANIE UBITE ZOSTAJE W REJESTRZE JAKO AKTYWNE
|
||||||
|
*
|
||||||
|
* Egress zapisuje stan w Redisie, a status koncowy ustawia sam
|
||||||
|
* proces obslugujacy nagranie. Gdy proces zostanie ubity razem
|
||||||
|
* z kontenerem (restart, wyczerpanie pamieci), status nigdy sie
|
||||||
|
* nie zmienia: zadanie figuruje jako EGRESS_ACTIVE na zawsze.
|
||||||
|
* `StopEgress` konczy sie wtedy sukcesem i NIC NIE ZMIENIA, bo
|
||||||
|
* nie ma juz czego zatrzymywac.
|
||||||
|
*
|
||||||
|
* Sprawdzone na zywo: wpis wisial po restarcie Egress, a panel
|
||||||
|
* pokazywal go jako osierocone zadanie mimo zerowego zuzycia
|
||||||
|
* procesora i mimo udanego zatrzymania.
|
||||||
|
*
|
||||||
|
* Dlatego liczy sie takze WIEK. Zadanie starsze niz okno sensownego
|
||||||
|
* nagrania jest martwe niezaleznie od tego, co twierdzi status:
|
||||||
|
* najdluzszy webinar trwa kilka godzin, a montaz pliku minuty.
|
||||||
|
*/
|
||||||
|
long staleBefore = System.currentTimeMillis() - STALE_EGRESS_AFTER.toMillis();
|
||||||
|
|
||||||
return response.body().stream()
|
return response.body().stream()
|
||||||
.filter(info -> switch (info.getStatus()) {
|
.filter(info -> switch (info.getStatus()) {
|
||||||
case EGRESS_STARTING, EGRESS_ACTIVE, EGRESS_ENDING -> true;
|
case EGRESS_STARTING, EGRESS_ACTIVE, EGRESS_ENDING -> true;
|
||||||
default -> false;
|
default -> false;
|
||||||
})
|
})
|
||||||
|
/*
|
||||||
|
* `startedAt` jest w NANOSEKUNDACH od epoki: proto podaje
|
||||||
|
* sam `int64` bez jednostki, ale webhooki LiveKit niosa
|
||||||
|
* tam wartosci 19-cyfrowe, a `duration` w `FileInfo` jest
|
||||||
|
* liczone tak samo. Stad dzielenie przez milion.
|
||||||
|
*
|
||||||
|
* Zero oznacza zadanie, ktore nie zdazylo wystartowac:
|
||||||
|
* traktujemy je jak biezace, zeby swiezo zlecone nagranie
|
||||||
|
* nie znikalo z ekranu, zanim ruszy.
|
||||||
|
*/
|
||||||
|
.filter(info -> info.getStartedAt() == 0L
|
||||||
|
|| info.getStartedAt() / 1_000_000L > staleBefore)
|
||||||
.map(LivekitEgress.EgressInfo::getEgressId)
|
.map(LivekitEgress.EgressInfo::getEgressId)
|
||||||
.toList();
|
.toList();
|
||||||
} catch (ValidationException e) {
|
} catch (ValidationException e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user