Skip to content

Commit

Permalink
Merge pull request #313 from VEuPathDB/issue-304
Browse files Browse the repository at this point in the history
Logging improvements around plugin executions.
  • Loading branch information
Foxcapades authored Jun 26, 2024
2 parents 010bb65 + c4406eb commit b0ec504
Show file tree
Hide file tree
Showing 12 changed files with 839 additions and 211 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vdi.component.reinstaller
import kotlinx.coroutines.sync.Mutex
import org.slf4j.LoggerFactory
import org.veupathdb.lib.s3.s34k.S3Api
import org.veupathdb.lib.s3.s34k.errors.S34KError
import org.veupathdb.vdi.lib.common.compression.Zip
import org.veupathdb.vdi.lib.common.field.ProjectID
import org.veupathdb.vdi.lib.common.fs.TempFiles
Expand All @@ -14,11 +15,13 @@ import vdi.component.db.app.model.InstallStatus
import vdi.component.db.app.model.InstallType
import vdi.component.db.app.withTransaction
import vdi.component.metrics.Metrics
import vdi.component.plugin.client.PluginHandlerClient
import vdi.component.plugin.client.PluginException
import vdi.component.plugin.client.PluginRequestException
import vdi.component.plugin.client.response.ind.*
import vdi.component.plugin.client.response.uni.UninstallBadRequestResponse
import vdi.component.plugin.client.response.uni.UninstallResponseType
import vdi.component.plugin.client.response.uni.UninstallUnexpectedErrorResponse
import vdi.component.plugin.mapping.PluginHandler
import vdi.component.plugin.mapping.PluginHandlers
import vdi.component.s3.DatasetDirectory
import vdi.component.s3.DatasetManager
Expand Down Expand Up @@ -95,6 +98,9 @@ object DatasetReinstaller {
for (dataset in datasets) {
try {
processDataset(dataset, projectID, manager)
} catch (e: PluginException) {
Metrics.Reinstaller.failedDatasetReinstall.inc()
log.error("failed to process dataset ${dataset.owner}/${dataset.datasetID} reinstallation for project $projectID via plugin ${e.plugin}", e)
} catch (e: Throwable) {
Metrics.Reinstaller.failedDatasetReinstall.inc()
log.error("failed to process dataset ${dataset.owner}/${dataset.datasetID} reinstallation for project $projectID", e)
Expand All @@ -115,31 +121,44 @@ object DatasetReinstaller {
if (!handler.appliesToProject(projectID))
throw IllegalStateException("dataset type ${dataset.typeName} does not apply to project $projectID")

uninstallDataset(dataset, projectID, handler.client)
reinstallDataset(dataset, projectID, handler.client, manager)
uninstallDataset(dataset, projectID, handler)
reinstallDataset(dataset, projectID, handler, manager)
}

private suspend fun uninstallDataset(dataset: DatasetRecord, projectID: ProjectID, client: PluginHandlerClient) {
private suspend fun uninstallDataset(dataset: DatasetRecord, projectID: ProjectID, handler: PluginHandler) {
log.debug("attempting to uninstall dataset {}/{} from project {}", dataset.owner, dataset.datasetID, projectID)

val uninstallResult = client.postUninstall(dataset.datasetID, projectID)
val uninstallResult = try {
handler.client.postUninstall(dataset.datasetID, projectID)
} catch (e: Throwable) {
throw PluginRequestException.uninstall(handler.displayName, projectID, dataset.owner, dataset.datasetID, cause = e)
}

when (uninstallResult.type) {
UninstallResponseType.Success
-> { /* do nothing */ }
UninstallResponseType.Success -> { /* do nothing */ }

UninstallResponseType.BadRequest
-> throw IllegalStateException("uninstall failed with 400 error: " + (uninstallResult as UninstallBadRequestResponse).message)
UninstallResponseType.BadRequest -> throw PluginException.uninstall(
handler.displayName,
projectID,
dataset.owner,
dataset.datasetID,
(uninstallResult as UninstallBadRequestResponse).message,
)

UninstallResponseType.UnexpectedError
-> throw IllegalStateException("uninstall failed with 500 error: " + (uninstallResult as UninstallUnexpectedErrorResponse).message)
UninstallResponseType.UnexpectedError -> throw PluginException.uninstall(
handler.displayName,
projectID,
dataset.owner,
dataset.datasetID,
(uninstallResult as UninstallUnexpectedErrorResponse).message,
)
}
}

private suspend fun reinstallDataset(
dataset: DatasetRecord,
projectID: ProjectID,
client: PluginHandlerClient,
handler: PluginHandler,
manager: DatasetManager,
) {
log.debug("attempting to reinstall dataset {}/{} into project {}", dataset.owner, dataset.datasetID, projectID)
Expand All @@ -151,23 +170,29 @@ object DatasetReinstaller {
return
}

val response = withInstallBundle(directory) { client.postInstallData(dataset.datasetID, projectID, it) }
val response = try {
withInstallBundle(directory) { handler.client.postInstallData(dataset.datasetID, projectID, it) }
} catch (e: S34KError) { // don't mix up minio errors with request errors
throw PluginException.installData(handler.displayName, projectID, dataset.owner, dataset.datasetID, cause = e)
} catch (e: Throwable) {
throw PluginRequestException.installData(handler.displayName, projectID, dataset.owner, dataset.datasetID, cause = e)
}

when (response.type) {
InstallDataResponseType.Success
-> handleInstallSuccess(response as InstallDataSuccessResponse, dataset, projectID)
-> handleInstallSuccess(handler, response as InstallDataSuccessResponse, dataset, projectID)

InstallDataResponseType.BadRequest
-> handleInstallBadRequest(response as InstallDataBadRequestResponse, dataset, projectID)
-> handleInstallBadRequest(handler, response as InstallDataBadRequestResponse, dataset, projectID)

InstallDataResponseType.ValidationFailure
-> handleInstallValidationFailure(response as InstallDataValidationFailureResponse, dataset, projectID)
-> handleInstallValidationFailure(handler, response as InstallDataValidationFailureResponse, dataset, projectID)

InstallDataResponseType.MissingDependencies
-> handleInstallMissingDependency(response as InstallDataMissingDependenciesResponse, dataset, projectID)
-> handleInstallMissingDependency(handler, response as InstallDataMissingDependenciesResponse, dataset, projectID)

InstallDataResponseType.UnexpectedError
-> handleInstallUnexpectedError(response as InstallDataUnexpectedErrorResponse, dataset, projectID)
-> handleInstallUnexpectedError(handler, response as InstallDataUnexpectedErrorResponse, dataset, projectID)
}
}

Expand All @@ -192,8 +217,19 @@ object DatasetReinstaller {
return ok
}

private fun handleInstallSuccess(res: InstallDataSuccessResponse, dataset: DatasetRecord, projectID: ProjectID) {
log.info("dataset {}/{} was reinstalled successfully into project {}", dataset.owner, dataset.datasetID, projectID)
private fun handleInstallSuccess(
handler: PluginHandler,
res: InstallDataSuccessResponse,
dataset: DatasetRecord,
projectID: ProjectID,
) {
log.info(
"dataset {}/{} was reinstalled successfully into project {} via plugin {}",
dataset.owner,
dataset.datasetID,
projectID,
handler.displayName,
)

appDB.withTransaction(projectID) {
it.updateDatasetInstallMessage(DatasetInstallMessage(
Expand All @@ -206,11 +242,19 @@ object DatasetReinstaller {
}

private fun handleInstallBadRequest(
handler: PluginHandler,
response: InstallDataBadRequestResponse,
dataset: DatasetRecord,
projectID: ProjectID
) {
log.error("dataset {}/{} reinstall into {} failed due to bad request exception from handler server: {}", dataset.owner, dataset.datasetID, projectID, response.message)
log.error(
"dataset {}/{} reinstall into {} failed due to bad request exception from plugin {}: {}",
dataset.owner,
dataset.datasetID,
projectID,
handler.displayName,
response.message,
)

appDB.withTransaction(projectID) {
it.updateDatasetInstallMessage(DatasetInstallMessage(
Expand All @@ -221,15 +265,22 @@ object DatasetReinstaller {
))
}

throw Exception(response.message)
throw PluginException.installData(handler.displayName, projectID, dataset.owner, dataset.datasetID, response.message)
}

private fun handleInstallValidationFailure(
handler: PluginHandler,
response: InstallDataValidationFailureResponse,
dataset: DatasetRecord,
projectID: ProjectID
) {
log.info("dataset {}/{} reinstall into {} failed due to validation error", dataset.owner, dataset.datasetID, projectID)
log.info(
"dataset {}/{} reinstall into {} failed due to validation error in plugin {}",
dataset.owner,
dataset.datasetID,
projectID,
handler.displayName,
)

appDB.withTransaction(projectID) {
it.updateDatasetInstallMessage(DatasetInstallMessage(
Expand All @@ -242,11 +293,18 @@ object DatasetReinstaller {
}

private fun handleInstallMissingDependency(
handler: PluginHandler,
response: InstallDataMissingDependenciesResponse,
dataset: DatasetRecord,
projectID: ProjectID,
) {
log.info("dataset {}/{} reinstall into {} was rejected for missing dependencies", dataset.owner, dataset.datasetID, projectID)
log.info(
"dataset {}/{} reinstall into {} was rejected for missing dependencies in plugin {}",
dataset.owner,
dataset.datasetID,
projectID,
handler.displayName
)

appDB.withTransaction(projectID) {
it.updateDatasetInstallMessage(DatasetInstallMessage(
Expand All @@ -259,11 +317,18 @@ object DatasetReinstaller {
}

private fun handleInstallUnexpectedError(
handler: PluginHandler,
response: InstallDataUnexpectedErrorResponse,
dataset: DatasetRecord,
projectID: ProjectID,
) {
log.error("dataset {}/{} reinstall into {} failed with a 500 from the handler server", dataset.owner, dataset.datasetID, projectID)
log.error(
"dataset {}/{} reinstall into {} failed with a 500 from plugin {}",
dataset.owner,
dataset.datasetID,
projectID,
handler.displayName,
)

appDB.withTransaction(projectID) {
it.updateDatasetInstallMessage(DatasetInstallMessage(
Expand All @@ -274,7 +339,7 @@ object DatasetReinstaller {
))
}

throw Exception(response.message)
throw PluginException.installData(handler.displayName, projectID, dataset.owner, dataset.datasetID, response.message)
}

private suspend fun <T> withInstallBundle(s3Dir: DatasetDirectory, fn: suspend (upload: InputStream) -> T) =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package vdi.component.plugin.client

import org.slf4j.Logger
import org.veupathdb.vdi.lib.common.field.DatasetID
import org.veupathdb.vdi.lib.common.field.ProjectID
import org.veupathdb.vdi.lib.common.field.UserID

open class PluginException : Exception {
val action: String
val plugin: String
val projectID: ProjectID
val userID: UserID
val datasetID: DatasetID

override val message: String
get() = super.message!!

protected constructor(
action: String,
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String,
) : super(message) {
this.action = action
this.plugin = plugin
this.projectID = projectID
this.userID = userID
this.datasetID = datasetID
}

protected constructor(
action: String,
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String,
cause: Throwable,
) : super(message, cause) {
this.action = action
this.plugin = plugin
this.projectID = projectID
this.userID = userID
this.datasetID = datasetID
}

fun log(logFn: (String, Throwable) -> Unit) {
logFn(message, this)
}

companion object {
@JvmStatic
fun import(
plugin: String,
userID: UserID,
datasetID: DatasetID,
message: String? = null,
cause: Throwable? = null,
) = new("import", plugin, "N/A", userID, datasetID, message, cause)

@JvmStatic
fun installData(
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String? = null,
cause: Throwable? = null,
) = new("install-data", plugin, projectID, userID, datasetID, message, cause)

@JvmStatic
fun installMeta(
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String? = null,
cause: Throwable? = null,
) = new("install-meta", plugin, projectID, userID, datasetID, message, cause)

@JvmStatic
fun uninstall(
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String? = null,
cause: Throwable? = null,
) = new("uninstall", plugin, projectID, userID, datasetID, message, cause)

private fun new(
action: String,
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String?,
cause: Throwable?,
) =
makeMessage(action, plugin, projectID, userID, datasetID, message, cause)
.let {
if (cause == null)
PluginException(action, plugin, projectID, userID, datasetID, it)
else
PluginException(action, plugin, projectID, userID, datasetID, it, cause)
}

private fun makeMessage(
action: String,
plugin: String,
projectID: ProjectID,
userID: UserID,
datasetID: DatasetID,
message: String? = null,
cause: Throwable? = null,
) =
"$action failed for dataset $userID/$datasetID in plugin $plugin targeting $projectID" +
if (message != null)
": $message"
else if (cause != null)
": ${cause.message}"
else
" for unknown reasons"
}
}

Loading

0 comments on commit b0ec504

Please sign in to comment.