/// <summary> /// Retrieves a model by id /// </summary> /// <param name="modelId">The model id to retrieve</param> /// <param name="cancellationToken">The cancellation token assigned for the operation.</param> /// <returns>The found model or <value>null</value> if not found</returns> public async Task <Model> GetModelAsync(Guid modelId, CancellationToken cancellationToken) { ThrowIfDisposed(); try { Trace.TraceInformation($"Getting model '{modelId}' entity from the table"); ModelTableEntity modelEntity = await _modelsTable.GetEntityAsync <ModelTableEntity>( modelId.ToString(), cancellationToken, nameof(ModelTableEntity.Description), nameof(ModelTableEntity.CreationTime), nameof(ModelTableEntity.ModelStatus), nameof(ModelTableEntity.StatusMessage), nameof(ModelTableEntity.ModelParameters), nameof(ModelTableEntity.ModelStatistics)); // convert and return return(ConvertEntityToModel(modelEntity)); } catch (StorageException storageException) { var exception = new Exception($"Exception while trying to get model ({modelId}) entity from table", storageException); Trace.TraceError(exception.ToString()); throw exception; } }
/// <summary> /// Retrieves a model by id synchronously. /// </summary> /// <param name="modelId">The model id to retrieve</param> /// <returns>The found model or <value>null</value> if not found</returns> public Model GetModel(Guid modelId) { ThrowIfDisposed(); try { Trace.TraceVerbose($"Getting model '{modelId}' entity from the table"); ModelTableEntity modelEntity = _modelsTable.GetEntity <ModelTableEntity>( modelId.ToString(), nameof(ModelTableEntity.Description), nameof(ModelTableEntity.CreationTime), nameof(ModelTableEntity.ModelStatus), nameof(ModelTableEntity.StatusMessage), nameof(ModelTableEntity.ModelParameters), nameof(ModelTableEntity.ModelStatistics)); // convert and return return(ConvertEntityToModel(modelEntity)); } catch (StorageException storageException) { var exception = new Exception($"Exception while trying to get model ({modelId}) entity from table", storageException); Trace.TraceError(exception.ToString()); throw exception; } }
/// <summary> /// Converts the entity to a <see cref="Model"/> instance. /// </summary> private static Model ConvertEntityToModel(ModelTableEntity entity) { if (entity == null) { return(null); } // create a model from the entity properties var model = new Model { Id = Guid.Parse(entity.RowKey), Description = entity.Description, CreationTime = entity.CreationTime, Status = default(ModelStatus), StatusMessage = entity.StatusMessage, }; // try to parse the model status ModelStatus status; if (!string.IsNullOrWhiteSpace(entity.ModelStatus) && Enum.TryParse(entity.ModelStatus, out status)) { model.Status = status; } try { // deserialize the model parameters if (!string.IsNullOrWhiteSpace(entity.ModelParameters)) { model.Parameters = JsonConvert.DeserializeObject <ModelTrainingParameters>(entity.ModelParameters); } } catch (JsonSerializationException ex) { Trace.TraceError($"Failed deserializing {nameof(ModelTrainingParameters)} for model {entity.RowKey}. Exception: {ex}"); } try { // deserialize the model statistics if (!string.IsNullOrWhiteSpace(entity.ModelStatistics)) { model.Statistics = JsonConvert.DeserializeObject <ModelStatistics>(entity.ModelStatistics); } } catch (JsonSerializationException ex) { Trace.TraceError($"Failed deserializing {nameof(ModelStatistics)} for model {entity.RowKey}. Exception: {ex}"); } // return the model return(model); }
/// <summary> /// Retrieves a model by id /// </summary> /// <param name="modelId">The model id to retrieve</param> /// <param name="cancellationToken">The cancellation token assigned for the operation.</param> /// <returns>The found model or <value>null</value> if not found</returns> public async Task <ModelStatus?> GetModelStatusAsync(Guid modelId, CancellationToken cancellationToken) { ThrowIfDisposed(); try { Trace.TraceInformation($"Trying to get the model '{modelId}' status from the cache"); ModelStatus?modelStatus = _cache.Get(modelId.ToString()) as ModelStatus?; if (modelStatus.HasValue) { return(modelStatus); } Trace.TraceInformation($"Getting model '{modelId}' status from the table"); ModelTableEntity entity = await _modelsTable.GetEntityAsync <ModelTableEntity>( modelId.ToString(), cancellationToken, nameof(ModelTableEntity.ModelStatus)); ModelStatus status; if (!string.IsNullOrWhiteSpace(entity?.ModelStatus) && Enum.TryParse(entity.ModelStatus, out status)) { modelStatus = status; // only update the cache if the model state is 'stable' if (modelStatus == ModelStatus.Completed || modelStatus == ModelStatus.Failed) { _cache.Set(modelId.ToString(), modelStatus, new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(1) }); } } Trace.TraceInformation($"Found model '{modelId}' status: {modelStatus}"); return(modelStatus); } catch (StorageException storageException) { var exception = new Exception($"Exception while trying to get model ({modelId}) status from table", storageException); Trace.TraceError(exception.ToString()); throw exception; } }
/// <summary> /// Updates a model by overriding the provided non-null properties. /// </summary> /// <param name="modelId">The id of the model to update</param> /// <param name="cancellationToken">The cancellation token assigned for the operation.</param> /// <param name="status">A new status to update</param> /// <param name="statusMessage">A status message</param> /// <param name="statistics">Optional new model statistics to update</param> public async Task UpdateModelAsync(Guid modelId, CancellationToken cancellationToken, ModelStatus?status = null, string statusMessage = null, ModelStatistics statistics = null) { ThrowIfDisposed(); try { // create a model entity var modelEntity = new ModelTableEntity(modelId, null, statistics) { ETag = "*" }; // set the model status (if provided) if (status.HasValue) { modelEntity.ModelStatus = status.Value.ToString(); } // set the model status message (if provided) if (statusMessage != null) { if (statusMessage.Length > MaxAllowedStatusMessageLength) { statusMessage = statusMessage.Substring(0, MaxAllowedStatusMessageLength) + "... [trimmed]"; } modelEntity.StatusMessage = statusMessage; } Trace.TraceInformation($"Updating model '{modelId}' properties in the table"); // override only the non-null properties of the update model entity to the table await _modelsTable.MergeEntityAsync(modelEntity, cancellationToken); } catch (StorageException storageException) { var exception = new Exception($"Exception while trying to update model entity ({modelId}) to the table", storageException); Trace.TraceError(exception.ToString()); throw exception; } }
/// <summary> /// Creates a new model /// </summary> /// <param name="modelParameters">The new model parameters</param> /// <param name="description">The new model description</param> /// <param name="cancellationToken">The cancellation token assigned for the operation.</param> /// <returns>The newly created <see cref="Model"/> or <value>null</value> if failed to create</returns> public async Task <Model> CreateModelAsync(ModelTrainingParameters modelParameters, string description, CancellationToken cancellationToken) { ThrowIfDisposed(); if (modelParameters == null) { throw new ArgumentNullException(nameof(modelParameters)); } // allocate a new model id var modelId = Guid.NewGuid(); // create a new model entity var newModelEntity = new ModelTableEntity(modelId, modelParameters) { Description = description, ModelStatus = ModelStatus.Created.ToString(), CreationTime = DateTime.UtcNow }; try { Trace.TraceInformation($"Creating a new model ({modelId}) in the table"); if (!await _modelsTable.InsertEntityAsync(newModelEntity, cancellationToken)) { Trace.TraceError($"Failed to create table entry for model {modelId}"); return(null); } } catch (StorageException storageException) { var exception = new Exception( $"Exception while trying to create a new model entity ({modelId}) in the table", storageException); Trace.TraceError(exception.ToString()); throw exception; } // convert the entity to model Model newModel = ConvertEntityToModel(newModelEntity); return(newModel); }