Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Hive.Server.Core/ClientCommunicator.cs @ 1248

Last change on this file since 1248 was 1170, checked in by msteinbi, 16 years ago

Implementing Lifecycle Management (#453)

File size: 14.0 KB
RevLine 
[1121]1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
[741]23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
[751]26using HeuristicLab.Hive.Contracts.BusinessObjects;
[780]27using HeuristicLab.Hive.Contracts.Interfaces;
28using HeuristicLab.Hive.Contracts;
[823]29using HeuristicLab.Core;
[842]30using HeuristicLab.Hive.Server.Core.InternalInterfaces.DataAccess;
31using System.Resources;
32using System.Reflection;
[1001]33using HeuristicLab.Hive.JobBase;
[1141]34using HeuristicLab.Hive.Server.Core.InternalInterfaces;
[1154]35using System.Threading;
[741]36
37namespace HeuristicLab.Hive.Server.Core {
[780]38  /// <summary>
39  /// The ClientCommunicator manages the whole communication with the client
40  /// </summary>
41  public class ClientCommunicator: IClientCommunicator {
[1154]42    private static Dictionary<Guid, DateTime> lastHeartbeats =
[1099]43      new Dictionary<Guid,DateTime>();
[783]44
[1154]45    private static ReaderWriterLockSlim heartbeatLock =
[1158]46      new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
[1154]47
48    private static Mutex jobLock =
49      new Mutex();
50
[842]51    IClientAdapter clientAdapter;
[970]52    IJobAdapter jobAdapter;
[1004]53    IJobResultsAdapter jobResultAdapter;
[1088]54    ILifecycleManager lifecycleManager;
[1141]55    IInternalJobManager jobManager;
[842]56
[1121]57    /// <summary>
58    /// Initialization of the Adapters to the database
59    /// Initialization of Eventhandler for the lifecycle management
60    /// Initialization of lastHearbeats Dictionary
61    /// </summary>
[783]62    public ClientCommunicator() {
[970]63      clientAdapter = ServiceLocator.GetClientAdapter();
64      jobAdapter = ServiceLocator.GetJobAdapter();
[1011]65      jobResultAdapter = ServiceLocator.GetJobResultsAdapter();
[1088]66      lifecycleManager = ServiceLocator.GetLifecycleManager();
[1141]67      jobManager = ServiceLocator.GetJobManager() as
68        IInternalJobManager;
[842]69
[1133]70      lifecycleManager.RegisterHeartbeat(
71        new EventHandler(lifecycleManager_OnServerHeartbeat));
[783]72    }
73
[1121]74    /// <summary>
75    /// Check if online clients send their hearbeats
76    /// if not -> set them offline and check if they where calculating a job
77    /// </summary>
78    /// <param name="sender"></param>
79    /// <param name="e"></param>
[1088]80    void lifecycleManager_OnServerHeartbeat(object sender, EventArgs e) {
81      List<ClientInfo> allClients = new List<ClientInfo>(clientAdapter.GetAll());
82
83      foreach (ClientInfo client in allClients) {
[1096]84        if (client.State != State.offline && client.State != State.nullState) {
[1154]85          heartbeatLock.EnterUpgradeableReadLock();
86
[1096]87          if (!lastHeartbeats.ContainsKey(client.ClientId)) {
88            client.State = State.offline;
89            clientAdapter.Update(client);
[1166]90            foreach (Job job in jobAdapter.GetActiveJobsOf(client)) {
[1160]91              jobManager.ResetJobsDependingOnResults(job);
92            }
[1096]93          } else {
94            DateTime lastHbOfClient = lastHeartbeats[client.ClientId];
[1154]95
[1099]96            TimeSpan dif = DateTime.Now.Subtract(lastHbOfClient);
[1118]97            // check if time between last hearbeat and now is greather than HEARTBEAT_MAX_DIF
98            if (dif.Seconds > ApplicationConstants.HEARTBEAT_MAX_DIF) {
99              // if client calculated jobs, the job must be reset
100              if (client.State == State.calculating) {
101                // check wich job the client was calculating and reset it
[1166]102                foreach (Job job in jobAdapter.GetActiveJobsOf(client)) {
[1160]103                  jobManager.ResetJobsDependingOnResults(job);
[1118]104                }
105              }
106             
107              // client must be set offline
108              client.State = State.offline;
109              clientAdapter.Update(client);
[1154]110
111              heartbeatLock.EnterWriteLock();
[1118]112              lastHeartbeats.Remove(client.ClientId);
[1154]113              heartbeatLock.ExitWriteLock();
[1118]114            }
[1096]115          }
[1154]116
117          heartbeatLock.ExitUpgradeableReadLock();
[1096]118        } else {
[1154]119          heartbeatLock.EnterWriteLock();
[1096]120          if (lastHeartbeats.ContainsKey(client.ClientId))
121            lastHeartbeats.Remove(client.ClientId);
[1154]122          heartbeatLock.ExitWriteLock();
[1096]123        }
[1088]124      }
125    }
126
[741]127    #region IClientCommunicator Members
128
[1121]129    /// <summary>
130    /// Login process for the client
131    /// A hearbeat entry is created as well (login is the first hearbeat)
132    /// </summary>
133    /// <param name="clientInfo"></param>
134    /// <returns></returns>
[791]135    public Response Login(ClientInfo clientInfo) {
[741]136      Response response = new Response();
137
[1154]138      heartbeatLock.EnterWriteLock();
[1096]139      if (lastHeartbeats.ContainsKey(clientInfo.ClientId)) {
140        lastHeartbeats[clientInfo.ClientId] = DateTime.Now;
141      } else {
142        lastHeartbeats.Add(clientInfo.ClientId, DateTime.Now);
143      }
[1154]144      heartbeatLock.ExitWriteLock();
[1096]145
[995]146      ICollection<ClientInfo> allClients = clientAdapter.GetAll();
147      ClientInfo client = clientAdapter.GetById(clientInfo.ClientId);
[1096]148      if (client != null && client.State != State.offline && client.State != State.nullState) {
[1004]149        response.Success = false;
150        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGIN_USER_ALLREADY_ONLINE;
151        return response;
[842]152      }
[1096]153      clientInfo.State = State.idle;
[1004]154      clientAdapter.Update(clientInfo);
155      response.Success = true;
156      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGIN_SUCCESS;
[842]157
[741]158      return response;
159    }
160
[1121]161    /// <summary>
162    /// The client has to send regulary heartbeats
163    /// this hearbeats will be stored in the heartbeats dictionary
164    /// check if there is work for the client and send the client a response if he should pull a job
165    /// </summary>
166    /// <param name="hbData"></param>
167    /// <returns></returns>
[780]168    public ResponseHB SendHeartBeat(HeartBeatData hbData) {
[783]169      ResponseHB response = new ResponseHB();
170
[1096]171      response.ActionRequest = new List<MessageContainer>();
172      if (clientAdapter.GetById(hbData.ClientId).State == State.offline ||
173          clientAdapter.GetById(hbData.ClientId).State == State.nullState) {
174        response.Success = false;
175        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_USER_NOT_LOGGED_IN;
176        response.ActionRequest.Add(new MessageContainer(MessageContainer.MessageType.NoMessage));
177        return response;
178      }
179
[1154]180      heartbeatLock.EnterWriteLock();
[1088]181      if (lastHeartbeats.ContainsKey(hbData.ClientId)) {
182        lastHeartbeats[hbData.ClientId] = DateTime.Now;
183      } else {
184        lastHeartbeats.Add(hbData.ClientId, DateTime.Now);
185      }
[1154]186      heartbeatLock.ExitWriteLock();
[1088]187
[783]188      response.Success = true;
[929]189      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_HARDBEAT_RECEIVED;
[1004]190      List<Job> allOfflineJobs = new List<Job>(jobAdapter.GetJobsByState(State.offline));
191      if (allOfflineJobs.Count > 0 && hbData.freeCores > 0)
[783]192        response.ActionRequest.Add(new MessageContainer(MessageContainer.MessageType.FetchJob));
193      else
194        response.ActionRequest.Add(new MessageContainer(MessageContainer.MessageType.NoMessage));
195
[1124]196      if (hbData.jobProgress != null) {
[1166]197        List<Job> jobsOfClient = new List<Job>(jobAdapter.GetActiveJobsOf(clientAdapter.GetById(hbData.ClientId)));
[1160]198        if (jobsOfClient == null || jobsOfClient.Count == 0) {
199          response.Success = false;
200          response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_IS_NOT_BEEING_CALCULATED;
201          return response;
202        }
203
[1124]204        foreach (KeyValuePair<long, double> jobProgress in hbData.jobProgress) {
205          Job curJob = jobAdapter.GetById(jobProgress.Key);
[1160]206          if (curJob.Client == null || curJob.Client.ClientId != hbData.ClientId) {
207            response.Success = false;
208            response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_IS_NOT_BEEING_CALCULATED;
209          } else {
210            curJob.Percentage = jobProgress.Value;
211            jobAdapter.Update(curJob);
212          }
[1124]213        }
214      }
215
[783]216      return response;
[780]217    }
[1121]218   
219    /// <summary>
220    /// if the client asked to pull a job he calls this method
221    /// the server selects a job and sends it to the client
222    /// </summary>
223    /// <param name="clientId"></param>
224    /// <returns></returns>
[780]225    public ResponseJob PullJob(Guid clientId) {
[783]226      ResponseJob response = new ResponseJob();
[1154]227     
228      /// Critical section ///
229      jobLock.WaitOne();
230
231      LinkedList<Job> allOfflineJobs = new LinkedList<Job>(jobAdapter.GetJobsByState(State.offline));
232      if (allOfflineJobs != null && allOfflineJobs.Count > 0) {
233        Job job2Calculate = allOfflineJobs.First.Value;
234        job2Calculate.State = State.calculating;
235        job2Calculate.Client = clientAdapter.GetById(clientId);
236        job2Calculate.Client.State = State.calculating;
237
[1170]238        job2Calculate.DateCalculated = DateTime.Now;
[1154]239        response.Job = job2Calculate;
240        jobAdapter.Update(job2Calculate);
241        response.Success = true;
242        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_PULLED;
[1160]243      } else {
244        response.Success = true;
245        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_NO_JOBS_LEFT;
[805]246      }
[1154]247
248      jobLock.ReleaseMutex();
[1160]249      /// End Critical section ///
[1154]250
[941]251      return response;
[780]252    }
253
[1121]254    /// <summary>
255    /// the client can send job results during calculating
256    /// and will send a final job result when he finished calculating
257    /// these job results will be stored in the database
258    /// </summary>
259    /// <param name="clientId"></param>
260    /// <param name="jobId"></param>
261    /// <param name="result"></param>
262    /// <param name="exception"></param>
263    /// <param name="finished"></param>
264    /// <returns></returns>
[1103]265    public ResponseResultReceived SendJobResult(Guid clientId,
266      long jobId,
267      byte[] result,
[1133]268      double percentage,
[1103]269      Exception exception, 
270      bool finished) {
[838]271      ResponseResultReceived response = new ResponseResultReceived();
[1103]272      ClientInfo client =
273        clientAdapter.GetById(clientId);
274
275      Job job =
276        jobAdapter.GetById(jobId);
[1133]277
278      if (job.Client == null)    {
279        response.Success = false;
280        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOB_IS_NOT_BEEING_CALCULATED;
281        return response;
282      }
283      if (job.Client.ClientId != clientId) {
284        response.Success = false;
285        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_WRONG_CLIENT_FOR_JOB;
286        return response;
287      }
[1004]288      if (job == null) {
289        response.Success = false;
[1133]290        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_NO_JOB_WITH_THIS_ID;
[1004]291        return response;
292      }
293      if (job.State != State.calculating) {
294        response.Success = false;
295        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_WRONG_JOB_STATE;
296        return response;
297      }
[1133]298      job.SerializedJob = result;
299      job.Percentage = percentage;
300
[1004]301      if (finished) {
302        job.State = State.finished;
303        jobAdapter.Update(job);
304
[1154]305        client.State = State.idle;
306        clientAdapter.Update(client);
307
[1004]308        List<JobResult> jobResults = new List<JobResult>(jobResultAdapter.GetResultsOf(job));
309        foreach (JobResult currentResult in jobResults)
310          jobResultAdapter.Delete(currentResult);
311      }
312
[1103]313      JobResult jobResult =
314        new JobResult();
315      jobResult.Client = client;
316      jobResult.Job = job;
317      jobResult.Result = result;
[1133]318      jobResult.Percentage = percentage;
[1103]319      jobResult.Exception = exception;
[1170]320      jobResult.DateFinished = DateTime.Now;
[1103]321
[1133]322      jobResultAdapter.Update(jobResult);
323      jobAdapter.Update(job);
[1103]324
[783]325      response.Success = true;
[929]326      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_JOBRESULT_RECEIVED;
[1103]327      response.JobId = jobId;
[1137]328      response.finished = finished;
[783]329
330      return response;
[780]331    }
[1096]332
[1121]333    /// <summary>
334    /// when a client logs out the state will be set
335    /// and the entry in the last hearbeats dictionary will be removed
336    /// </summary>
337    /// <param name="clientId"></param>
[1154]338    /// <returns></returns>                       
[780]339    public Response Logout(Guid clientId) {
[786]340      Response response = new Response();
[1096]341
[1154]342      heartbeatLock.EnterWriteLock();
[1096]343      if (lastHeartbeats.ContainsKey(clientId))
344        lastHeartbeats.Remove(clientId);
[1154]345      heartbeatLock.ExitWriteLock();
[1096]346
[995]347      ClientInfo client = clientAdapter.GetById(clientId);
[902]348      if (client == null) {
[783]349        response.Success = false;
[929]350        response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGOUT_CLIENT_NOT_REGISTERED;
[902]351        return response;
[783]352      }
[1127]353      List<Job> allJobs = new List<Job>(jobAdapter.GetAll());
354      if (client.State == State.calculating) {
355        // check wich job the client was calculating and reset it
356        foreach (Job job in allJobs) {
357          if (job.Client.ClientId == client.ClientId) {
[1141]358            jobManager.ResetJobsDependingOnResults(job);
[1127]359          }
360        }
361      }
362
[902]363      client.State = State.offline;
[995]364      clientAdapter.Update(client);
[902]365
366      response.Success = true;
[929]367      response.StatusMessage = ApplicationConstants.RESPONSE_COMMUNICATOR_LOGOUT_SUCCESS;
[902]368     
[783]369      return response;
[780]370    }
371
[741]372    #endregion
373  }
374}
Note: See TracBrowser for help on using the repository browser.