#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using DT = HeuristicLab.Services.Hive.DataTransfer;
namespace HeuristicLab.Services.Hive.DataAccess {
public class OptimizedHiveDao : IOptimizedHiveDao {
private HiveDataContext Db { get; set; }
public OptimizedHiveDao(HiveDataContext db) {
Db = db;
}
#region Task Methods
public Task GetTaskById(Guid taskId) {
return GetTaskByIdQuery(Db, taskId).SingleOrDefault();
}
private static readonly Func> GetTaskByIdQuery = CompiledQuery.Compile((HiveDataContext db, Guid taskId) =>
from t in db.Tasks
where t.TaskId == taskId
select t
);
public Task GetTaskByDto(DT.Task taskDto) {
var task = GetTaskById(taskDto.Id);
DT.Convert.ToEntity(taskDto, task);
return task;
}
public Tuple GetTaskByIdAndLastStateLogSlaveId(Guid taskId) {
return GetTaskByIdAndLastStateLogSlaveIdQuery(Db, taskId).SingleOrDefault();
}
private static readonly Func>> GetTaskByIdAndLastStateLogSlaveIdQuery = CompiledQuery.Compile((HiveDataContext db, Guid taskId) =>
from t in db.Tasks
let lastStateLog = t.StateLogs.OrderByDescending(sl => sl.DateTime).FirstOrDefault()
where t.TaskId == taskId
select new Tuple(t, lastStateLog != null ? lastStateLog.SlaveId : null)
);
private const string GetWaitingTasksQueryString = @"
WITH pr AS (
SELECT ResourceId, ParentResourceId
FROM [Resource]
WHERE ResourceId = {0}
UNION ALL
SELECT r.ResourceId, r.ParentResourceId
FROM [Resource] r JOIN pr ON r.ResourceId = pr.ParentResourceId
)
SELECT DISTINCT t.TaskId, t.JobId, t.Priority
FROM pr JOIN AssignedResources ar ON ar.ResourceId = pr.ResourceId
JOIN Task t ON t.TaskId = ar.TaskId
WHERE NOT (t.IsParentTask = 1 AND t.FinishWhenChildJobsFinished = 1)
AND t.TaskState = {1}
AND t.CoresNeeded <= {2}
AND t.MemoryNeeded <= {3}
";
public IEnumerable GetWaitingTasks(Slave slave) {
//Originally we checked here if there are parent tasks which should be calculated (with GetParentTasks(resourceIds, count, false);).
//Because there is at the moment no case where this makes sense (there don't exist parent tasks which need to be calculated),
//we skip this step because it's wasted runtime
return Db.ExecuteQuery(GetWaitingTasksQueryString, slave.ResourceId, Enum.GetName(typeof(TaskState), TaskState.Waiting), slave.FreeCores, slave.FreeMemory);
}
public IQueryable GetLightweightTasks(Guid jobId) {
return GetLightweightTasksQuery(Db, jobId);
}
private static readonly Func> GetLightweightTasksQuery = CompiledQuery.Compile((HiveDataContext db, Guid jobId) =>
from task in db.Tasks
where task.JobId == jobId
select new DT.LightweightTask {
Id = task.TaskId,
ExecutionTime = TimeSpan.FromMilliseconds(task.ExecutionTimeMs),
ParentTaskId = task.ParentTaskId,
StateLog = task.StateLogs.OrderBy(sl => sl.DateTime).Select(sl => ConvertStateLog(sl)).ToList(),
State = ConvertTaskState(task.State),
Command = ConvertCommand(task.Command),
LastTaskDataUpdate = task.JobData.LastUpdate
}
);
private static readonly Func ConvertStateLog = sl => DT.Convert.ToDto(sl);
private static readonly Func ConvertTaskState = ts => DT.Convert.ToDto(ts);
private static readonly Func ConvertCommand = c => DT.Convert.ToDto(c);
public void UpdateTask(Task task) {
Db.SubmitChanges();
}
public Task UpdateTaskState(Guid taskId, TaskState taskState, Guid? slaveId, Guid? userId, string exception) {
Db.StateLogs.InsertOnSubmit(new StateLog {
TaskId = taskId,
State = taskState,
SlaveId = slaveId,
UserId = userId,
Exception = exception,
DateTime = DateTime.Now
});
var task = GetTaskById(taskId);
task.State = taskState;
Db.SubmitChanges();
return task;
}
public Guid AddTask(Task task) {
Db.Tasks.InsertOnSubmit(task);
Db.SubmitChanges();
return task.TaskId;
}
public void AssignJobToResource(Guid taskId, IEnumerable resourceIds) {
Db.AssignedResources.InsertAllOnSubmit(resourceIds.Select(resourceId => new AssignedResource { TaskId = taskId, ResourceId = resourceId }));
Db.SubmitChanges();
}
private const string TaskIsAllowedToBeCalculatedBySlaveQueryString = @"
WITH pr AS (
SELECT ResourceId, ParentResourceId
FROM [Resource]
WHERE ResourceId = {0}
UNION ALL
SELECT r.ResourceId, r.ParentResourceId
FROM [Resource] r JOIN pr ON r.ResourceId = pr.ParentResourceId
)
SELECT COUNT(ar.TaskId)
FROM pr JOIN AssignedResources ar ON pr.ResourceId = ar.ResourceId
WHERE ar.TaskId = {1}
";
public bool TaskIsAllowedToBeCalculatedBySlave(Guid taskId, Guid slaveId) {
return Db.ExecuteQuery(TaskIsAllowedToBeCalculatedBySlaveQueryString, slaveId, taskId).First() > 0;
}
#endregion
#region TaskData Methods
public TaskData GetTaskDataById(Guid id) {
return GetTaskDataByIdQuery(Db, id).SingleOrDefault();
}
private static readonly Func> GetTaskDataByIdQuery = CompiledQuery.Compile((HiveDataContext db, Guid id) =>
from t in db.TaskDatas
where t.TaskId == id
select t
);
public TaskData GetTaskDataByDto(DT.TaskData dto) {
var taskData = GetTaskDataById(dto.TaskId);
DT.Convert.ToEntity(dto, taskData);
return taskData;
}
public void UpdateTaskData(TaskData taskData) {
Db.SubmitChanges();
}
#endregion
#region Plugin Methods
public Plugin GetPluginById(Guid pluginId) {
return GetPluginByIdQuery(Db, pluginId).SingleOrDefault();
}
private static readonly Func> GetPluginByIdQuery = CompiledQuery.Compile((HiveDataContext db, Guid pluginId) =>
from p in db.Plugins
where p.PluginId == pluginId
select p
);
#endregion
#region Slave Methods
public Slave GetSlaveById(Guid id) {
return GetSlaveByIdQuery(Db, id).SingleOrDefault();
}
private static readonly Func> GetSlaveByIdQuery = CompiledQuery.Compile((HiveDataContext db, Guid slaveId) =>
from s in db.Resources.OfType()
where s.ResourceId == slaveId
select s
);
public void UpdateSlave(Slave slave) {
Db.SubmitChanges();
}
private const string DowntimeQueryString = @"
WITH pr AS (
SELECT ResourceId, ParentResourceId
FROM [Resource]
WHERE ResourceId = {0}
UNION ALL
SELECT r.ResourceId, r.ParentResourceId
FROM [Resource] r JOIN pr ON r.ResourceId = pr.ParentResourceId
)
SELECT COUNT(dt.DowntimeId)
FROM pr JOIN [Downtime] dt ON pr.ResourceId = dt.ResourceId
WHERE {1} BETWEEN dt.StartDate AND dt.EndDate
AND dt.DowntimeType = {2}
";
public bool SlaveHasToShutdownComputer(Guid slaveId) {
return Db.ExecuteQuery(DowntimeQueryString, slaveId, DateTime.Now, DowntimeType.Shutdown.ToString()).FirstOrDefault() > 0;
}
public bool SlaveIsAllowedToCalculate(Guid slaveId) {
return Db.ExecuteQuery(DowntimeQueryString, slaveId, DateTime.Now, DowntimeType.Offline.ToString()).FirstOrDefault() == 0;
}
#endregion
#region Resource Methods
public IEnumerable GetAssignedResourceIds(Guid taskId) {
return GetAssignedResourceIdsQuery(Db, taskId);
}
private static readonly Func> GetAssignedResourceIdsQuery = CompiledQuery.Compile((HiveDataContext db, Guid taskId) =>
from ar in db.AssignedResources
where ar.TaskId == taskId
select ar.ResourceId
);
#endregion
#region Website Methods
private const string GetAllResourceIdsString = @"SELECT ResourceId FROM [Resource]";
public IEnumerable GetAllResourceIds() {
return Db.ExecuteQuery(GetAllResourceIdsString);
}
private const string GetNumberOfWaitingTasksString = @"SELECT COUNT(TaskId)
FROM [Task]
WHERE TaskState LIKE 'Waiting'";
public int GetNumberOfWaitingTasks() {
return Db.ExecuteQuery(GetNumberOfWaitingTasksString).Single();
}
private class UserTasks {
public Guid OwnerUserId;
public int Count;
}
private const string GetCalculatingTasksByUserString = @"SELECT Job.OwnerUserId, COUNT(Task.TaskId) as Count
FROM Task, Job
WHERE TaskState LIKE 'Calculating' AND Task.JobId = Job.JobId
GROUP BY Job.OwnerUserId";
public Dictionary GetCalculatingTasksByUser() {
var result = Db.ExecuteQuery(GetCalculatingTasksByUserString);
Dictionary lst = new Dictionary();
foreach (var userTask in result) {
lst.Add(userTask.OwnerUserId, userTask.Count);
}
return lst;
}
private const string GetWaitingTasksByUserString = @"SELECT Job.OwnerUserId, COUNT(Task.TaskId) as Count
FROM Task, Job
WHERE TaskState LIKE 'Waiting' AND Task.JobId = Job.JobId
GROUP BY Job.OwnerUserId";
public Dictionary GetWaitingTasksByUser() {
var result = Db.ExecuteQuery(GetWaitingTasksByUserString);
Dictionary lst = new Dictionary();
foreach (var userTask in result) {
lst.Add(userTask.OwnerUserId, userTask.Count);
}
return lst;
}
#endregion
}
}