modelMapper->findLatest($strategy::getTypeName()); } catch (DoesNotExistException $e) { $this->logger->debug("No models found. Can't load latest"); throw new ModelNotFoundException('No models found', 0, $e); } return $this->load($latestModel->getId()); } private function getCacheKey(int $id): string { return "suspicious_login_model_$id"; } private function getCached(int $id): ?string { if (!$this->cacheFactory->isLocalCacheAvailable()) { return null; } $cache = $this->cacheFactory->createLocal(); return $cache->get( $this->getCacheKey($id) ); } private function cache(int $id, string $serialized): void { if (!$this->cacheFactory->isLocalCacheAvailable()) { return; } $cache = $this->cacheFactory->createLocal(); $cache->set($this->getCacheKey($id), $serialized); } /** * @return Estimator * @throws RuntimeException * @throws ModelNotFoundException */ public function load(int $id): Estimator { $cached = $this->getCached($id); if ($cached !== null) { $this->logger->debug("using cached model $id"); $serialized = $cached; } else { $this->logger->debug("loading model $id from app data"); try { $modelsFolder = $this->appData->getFolder(self::APPDATA_MODELS_FOLDER); $modelFile = $modelsFolder->getFile((string)$id); } catch (NotFoundException $e) { $this->logger->error("Could not load classifier model $id: " . $e->getMessage()); throw new ModelNotFoundException("Could not load model $id", 0, $e); } $serialized = $modelFile->getContent(); $this->cache($id, $serialized); } $this->logger->debug('seralized model size: ' . strlen($serialized)); // Inefficient, but we can't get the real path from app data as it might // not be a local file $tmpFile = $this->tempManager->getTemporaryFile(); file_put_contents($tmpFile, $serialized); try { $learner = PersistentModel::load(new Filesystem($tmpFile)); } catch (RuntimeException $e) { $this->logger->error("Could not deserialize persisted model $id: " . $e->getMessage()); throw $e; } return $learner; } /** * @param Learner $estimator (Must implement Persistable) * @todo encapsulate in transaction to prevent inconsistencies */ public function persist(Learner $estimator, Model $model) { if (!($estimator instanceof Persistable)) { throw new RuntimeException('Estimator is not persistable'); } $model->setType(get_class($estimator)); $model->setAppVersion($this->appManager->getAppVersion(Application::APP_ID)); $this->modelMapper->insert($model); try { $modelsFolder = $this->appData->getFolder(self::APPDATA_MODELS_FOLDER); } catch (NotFoundException $e) { $this->logger->info('App data models folder does not exist. Creating it'); $modelsFolder = $this->appData->newFolder(self::APPDATA_MODELS_FOLDER); } try { $modelFile = $modelsFolder->newFile((string)$model->getId()); // Inefficient, but we can't get the real path from app data as it might // not be a local file $tmpFile = $this->tempManager->getTemporaryFile(); $persistentModel = new PersistentModel($estimator, new Filesystem($tmpFile)); $persistentModel->save(); $modelFile->putContent(file_get_contents($tmpFile)); } catch (Throwable $e) { $this->logger->error('Could not save persisted estimator to storage, reverting', [ 'exception' => $e, ]); $this->modelMapper->delete($model); throw $e; } } }