Improve group hangling
- try starting model's group after failing to restart that specific model - fix recording hijacking of same-priority model
This commit is contained in:
parent
164907b691
commit
994bb99b1f
|
@ -78,7 +78,7 @@ public class OnlineMonitor extends Thread {
|
||||||
boolean skipCheckForMarkedAsLater = model.isMarkedForLaterRecording();
|
boolean skipCheckForMarkedAsLater = model.isMarkedForLaterRecording();
|
||||||
if (skipCheckForSuspended || skipCheckForMarkedAsLater) {
|
if (skipCheckForSuspended || skipCheckForMarkedAsLater) {
|
||||||
// force set offline in case model was suspended while online
|
// force set offline in case model was suspended while online
|
||||||
// TODO: check if this is thread safe
|
// this should be thread safe as we don't access the same model from multiple threads
|
||||||
setModelStateNotified(model, Model.State.OFFLINE);
|
setModelStateNotified(model, Model.State.OFFLINE);
|
||||||
} else {
|
} else {
|
||||||
futures.add(updateModel(model));
|
futures.add(updateModel(model));
|
||||||
|
|
|
@ -8,11 +8,14 @@ import ctbrec.recorder.download.RecordingProcess;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.common.collect.Comparators;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -33,7 +36,7 @@ public class RecordingPreconditions {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
|
void check(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException, ExecutionException {
|
||||||
LOG.debug("Checking preconditions for model {}", model);
|
LOG.debug("Checking preconditions for model {}", model);
|
||||||
ensureRecorderIsActive();
|
ensureRecorderIsActive();
|
||||||
ensureNotInTimeoutPeriod();
|
ensureNotInTimeoutPeriod();
|
||||||
|
@ -44,6 +47,7 @@ public class RecordingPreconditions {
|
||||||
ensureNoRecordingRunningForModel(model);
|
ensureNoRecordingRunningForModel(model);
|
||||||
ensureModelShouldBeRecorded(model);
|
ensureModelShouldBeRecorded(model);
|
||||||
ensureEnoughSpaceForRecording();
|
ensureEnoughSpaceForRecording();
|
||||||
|
//ensureAllModelsFromGroupAreOnlineChecked(model);
|
||||||
ensureNoOtherFromModelGroupIsRecording(model);
|
ensureNoOtherFromModelGroupIsRecording(model);
|
||||||
ensureModelIsOnline(model);
|
ensureModelIsOnline(model);
|
||||||
ensureDownloadSlotAvailable(model);
|
ensureDownloadSlotAvailable(model);
|
||||||
|
@ -169,17 +173,39 @@ public class RecordingPreconditions {
|
||||||
return concurrentRecordings == 0 || concurrentRecordings > 0 && recorder.getRecordingProcesses().size() < concurrentRecordings;
|
return concurrentRecordings == 0 || concurrentRecordings > 0 && recorder.getRecordingProcesses().size() < concurrentRecordings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureAllModelsFromGroupAreOnlineChecked(Model model) throws IOException, ExecutionException {
|
||||||
|
Optional<ModelGroup> modelGroup = recorder.getModelGroup(model);
|
||||||
|
if (modelGroup.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String modelUrl : modelGroup.get().getModelUrls()) {
|
||||||
|
Optional<Model> otherModel = getModelForUrl(modelUrl);
|
||||||
|
if (otherModel.isPresent()) {
|
||||||
|
if (otherModel.get().getOnlineState(true) == Model.State.UNCHECKED) {
|
||||||
|
throw new PreconditionNotMetException(otherModel.get() + "'s online state has not been checked yet");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.warn("Model for url {} was not found", modelUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ensureNoOtherFromModelGroupIsRecording(Model model) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
|
private void ensureNoOtherFromModelGroupIsRecording(Model model) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
|
||||||
Optional<ModelGroup> modelGroup = recorder.getModelGroup(model);
|
Optional<ModelGroup> modelGroup = recorder.getModelGroup(model);
|
||||||
if (modelGroup.isEmpty()) {
|
if (modelGroup.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go through each model in group in descendind priority order, checking this model last amongst same-prio models
|
||||||
|
// this is to make sure that we only stop lower priority recordings in favor of this one. I.e. if same-prio model is recordnig, let it run
|
||||||
for (var groupModel : modelGroup.get().getModelUrls().stream()
|
for (var groupModel : modelGroup.get().getModelUrls().stream()
|
||||||
.map(modelUrl -> getModelForUrl(modelUrl))
|
.map(modelUrl -> getModelForUrl(modelUrl))
|
||||||
.filter(x -> x.isPresent())
|
.filter(x -> x.isPresent())
|
||||||
.map(x -> x.get())
|
.map(x -> x.get())
|
||||||
.sorted((l, r) -> Integer.compare(r.getPriority(), l.getPriority())) // high to low
|
.sorted(Comparator
|
||||||
|
.comparing((Model m) -> m.getPriority()).reversed() // high to low
|
||||||
|
.thenComparing((Model m) -> m.getUrl().equals(model.getUrl()))) // this model last (false -> true)
|
||||||
.toList()) {
|
.toList()) {
|
||||||
if (model.getUrl().equals(groupModel.getUrl())) {
|
if (model.getUrl().equals(groupModel.getUrl())) {
|
||||||
// no other model with higher prio is online, start recording
|
// no other model with higher prio is online, start recording
|
||||||
|
@ -187,15 +213,11 @@ public class RecordingPreconditions {
|
||||||
stopModelsWithLowerPrio(modelGroup.get());
|
stopModelsWithLowerPrio(modelGroup.get());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
Optional<Model> otherModel = getModelForUrl(groupModel.getUrl());
|
Model otherModel = groupModel;
|
||||||
if (otherModel.isPresent()) {
|
if (!otherModel.isSuspended() && otherModelIsRecorded(otherModel)) {
|
||||||
if (!otherModel.get().isSuspended() && otherModelIsRecorded(otherModel.get())) {
|
throw new PreconditionNotMetException(otherModel + " from the same group is already recorded");
|
||||||
throw new PreconditionNotMetException(otherModel.get() + " from the same group is already recorded");
|
} else if (otherModel.getPriority() > model.getPriority() && otherModelCanBeRecorded(otherModel)) {
|
||||||
} else if (otherModelCanBeRecorded(otherModel.get())) {
|
throw new PreconditionNotMetException(otherModel + " from the same group can be recorded");
|
||||||
throw new PreconditionNotMetException(otherModel.get() + " from the same group can be recorded");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG.warn("Couldn't check if model from same group has higer prio for {}", groupModel.getUrl());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -747,13 +747,14 @@ public class SimplifiedLocalRecorder implements Recorder {
|
||||||
boolean online = model.isOnline(IGNORE_CACHE);
|
boolean online = model.isOnline(IGNORE_CACHE);
|
||||||
if (online) {
|
if (online) {
|
||||||
log.info("Restarting recording for model {}", model);
|
log.info("Restarting recording for model {}", model);
|
||||||
|
online = startRecordingProcessSync(model);
|
||||||
try {
|
}
|
||||||
recorderLock.lock();
|
|
||||||
startRecordingProcessAsync(model);
|
// if the specific model is offline or starting it failed, try to restart it's group
|
||||||
} finally {
|
// FIXME: this might spam unnecessary start attempts
|
||||||
recorderLock.unlock();
|
if (!online) {
|
||||||
}
|
log.info("Restarting recording for group of offline model {}", model);
|
||||||
|
startGroupRecordingOfModel(model);
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
@ -789,7 +790,7 @@ public class SimplifiedLocalRecorder implements Recorder {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startRecordingProcessSync(Model model) {
|
private boolean startRecordingProcessSync(Model model) {
|
||||||
recorderLock.lock();
|
recorderLock.lock();
|
||||||
try {
|
try {
|
||||||
preconditions.check(model);
|
preconditions.check(model);
|
||||||
|
@ -800,6 +801,7 @@ public class SimplifiedLocalRecorder implements Recorder {
|
||||||
rec.getModel().setLastRecorded(rec.getStartDate());
|
rec.getModel().setLastRecorded(rec.getStartDate());
|
||||||
recordingManager.saveRecording(rec);
|
recordingManager.saveRecording(rec);
|
||||||
recordingLoopPool.submit(() -> {singleRecordingLoop(rec);});
|
recordingLoopPool.submit(() -> {singleRecordingLoop(rec);});
|
||||||
|
return true;
|
||||||
} catch (RecordUntilExpiredException e) {
|
} catch (RecordUntilExpiredException e) {
|
||||||
log.info("Precondition not met to record {}: {}", model, e.getLocalizedMessage());
|
log.info("Precondition not met to record {}: {}", model, e.getLocalizedMessage());
|
||||||
executeRecordUntilSubsequentAction(model);
|
executeRecordUntilSubsequentAction(model);
|
||||||
|
@ -810,6 +812,7 @@ public class SimplifiedLocalRecorder implements Recorder {
|
||||||
} finally {
|
} finally {
|
||||||
recorderLock.unlock();
|
recorderLock.unlock();
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startRecordingProcessAsync(Model model) {
|
private void startRecordingProcessAsync(Model model) {
|
||||||
|
@ -845,30 +848,43 @@ public class SimplifiedLocalRecorder implements Recorder {
|
||||||
log.error("Recording {} not found. Can't rerun post-processing", recording);
|
log.error("Recording {} not found. Can't rerun post-processing", recording);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startGroupRecordingOfModel(Model model) {
|
private boolean startGroupRecordingOfModel(Model model) {
|
||||||
var opt = getModelGroup(model);
|
var opt = getModelGroup(model);
|
||||||
if (!opt.isPresent()) return;
|
if (!opt.isPresent())
|
||||||
|
return false;
|
||||||
|
|
||||||
var group = opt.get();
|
var group = opt.get();
|
||||||
List<Model> groupModels = group.getModelUrls().stream()
|
|
||||||
.map(url -> {
|
|
||||||
// FIXME: replace loop with hashmap lookup
|
|
||||||
for (var m : models) {
|
|
||||||
if (m.getUrl().equals(url))
|
|
||||||
return Optional.of(m);
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
})
|
|
||||||
.filter(Optional::isPresent)
|
|
||||||
.map(x -> (Model)x.get())
|
|
||||||
.sorted((l, r) -> Integer.compare(r.getPriority(), l.getPriority()))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
recordingLoopPool.submit(() -> {
|
recordingLoopPool.submit(() -> {
|
||||||
|
List<Model> groupModels = group.getModelUrls().stream()
|
||||||
|
.map(url -> {
|
||||||
|
// FIXME: replace loop with hashmap lookup
|
||||||
|
for (var m : models) {
|
||||||
|
if (m.getUrl().equals(url))
|
||||||
|
return Optional.of(m);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
})
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(x -> (Model)x.get())
|
||||||
|
.filter(x -> {
|
||||||
|
try {
|
||||||
|
// this call is IO blocking
|
||||||
|
return x.isOnline();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sorted((l, r) -> Integer.compare(r.getPriority(), l.getPriority()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
for (var groupModel : groupModels) {
|
for (var groupModel : groupModels) {
|
||||||
startRecordingProcessSync(groupModel);
|
if (startRecordingProcessSync(groupModel))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue