/// <inheritdoc /> public IEnumerable <int> Handle(FindErrorMessagesToDeleteQuery <int> query) { if (query.Cancellation.IsCancellationRequested) { return(Enumerable.Empty <int>()); } using (var db = _connectionInformation.GetDatabase()) { //before executing a query, double check that we aren't stopping //otherwise, there is a chance that the tables no longer exist in memory mode if (query.Cancellation.IsCancellationRequested) { return(Enumerable.Empty <int>()); } var col = db.Database.GetCollection <Schema.MetaDataErrorsTable>(_tableNameHelper.MetaDataErrorsName); var date = DateTime.UtcNow.Subtract(_configuration.MessageAge); var results = col.Query() .Where(x => x.LastExceptionDate < date) .ToList(); var data = new List <int>(results.Count); foreach (var record in results) { data.Add(record.QueueId); } return(data); } }
/// <inheritdoc /> public long Handle(ResetHeartBeatCommand <int> inputCommand) { using (var db = _connectionInformation.GetDatabase()) { db.Database.BeginTrans(); try { var col = db.Database.GetCollection <Schema.MetaDataTable>(_tableNameHelper.MetaDataName); var results = col.Query() .Where(x => x.Status == QueueStatuses.Processing) .Where(x => x.HeartBeat != null && x.HeartBeat.Value == inputCommand.MessageReset.HeartBeat) .Where(x => x.QueueId == inputCommand.MessageReset.QueueId) .Limit(1) .ToList(); if (results.Count == 1) { results[0].Status = QueueStatuses.Waiting; results[0].HeartBeat = null; col.Update(results[0]); } db.Database.Commit(); return(results.Count); } catch { db.Database.Rollback(); throw; } } }
/// <inheritdoc /> public IEnumerable <int> Handle(FindExpiredMessagesToDeleteQuery <int> query) { if (query.Cancellation.IsCancellationRequested) { return(Enumerable.Empty <int>()); } using (var db = _connectionInformation.GetDatabase()) { //before executing a query, double check that we aren't stopping if (query.Cancellation.IsCancellationRequested) { return(Enumerable.Empty <int>()); } var col = db.Database.GetCollection <Schema.MetaDataTable>(_tableNameHelper.MetaDataName); var results = col.Query() .Where(x => x.ExpirationTime < DateTime.UtcNow) .ToList(); var data = new List <int>(results.Count); foreach (var record in results) { data.Add(record.QueueId); } return(data); } }
/// <inheritdoc /> public QueueCreationResult Handle(CreateQueueTablesAndSaveConfigurationCommand <ITable> command) { //create database and enforce UTC date de-serialization using (var db = _connectionInformation.GetDatabase()) { db.Database.Pragma("UTC_DATE", true); //create all tables foreach (var table in command.Tables) { table.Create(_connectionInformation, _options.Value, _tableNameHelper); } //save configuration foreach (var table in command.Tables) { if (table is ConfigurationTable configTable) { var col = db.Database.GetCollection <ConfigurationTable>(_tableNameHelper.ConfigurationName); configTable.Configuration = _serializer.ConvertToBytes(_options.Value); col.Insert(configTable); break; } } return(new QueueCreationResult(QueueCreationStatus.Success)); } }
/// <inheritdoc /> public long Handle(DeleteMessageCommand <int> command) { using (var db = _connectionInformation.GetDatabase()) { db.Database.BeginTrans(); try { var col = db.Database.GetCollection(_tableNameHelper.QueueName); var result = col.Delete(command.QueueId); //note - continue in case we have orphaned records, regardless of result. var meta = db.Database.GetCollection <Schema.MetaDataTable>(_tableNameHelper.MetaDataName); meta.DeleteMany(x => x.QueueId == command.QueueId); var status = db.Database.GetCollection <Schema.StatusTable>(_tableNameHelper.StatusName); status.DeleteMany(x => x.QueueId == command.QueueId); var errorTrack = db.Database.GetCollection <Schema.ErrorTrackingTable>(_tableNameHelper.ErrorTrackingName); errorTrack.DeleteMany(x => x.QueueId == command.QueueId); var metaErrors = db.Database.GetCollection <Schema.MetaDataErrorsTable>(_tableNameHelper.MetaDataErrorsName); metaErrors.DeleteMany(x => x.QueueId == command.QueueId); db.Database.Commit(); return(result ? 1 : 0); } catch { db.Database.Rollback(); throw; } } }
/// <inheritdoc /> public DateTime?Handle(SendHeartBeatCommand <int> command) { using (var db = _connectionInformation.GetDatabase()) { db.Database.BeginTrans(); try { var col = db.Database.GetCollection <Schema.MetaDataTable>(_tableNameHelper.MetaDataName); var results = col.Query() .Where(x => x.QueueId == command.QueueId) .Limit(1) .ToList(); DateTime?date = null; if (results.Count == 1) { var record = results[0]; date = DateTime.UtcNow; record.HeartBeat = date; col.Update(record); } db.Database.Commit(); return(date); } catch { db.Database.Rollback(); throw; } } }
/// <inheritdoc /> public bool Create(LiteDbConnectionManager connection, LiteDbMessageQueueTransportOptions options, TableNameHelper helper) { using (var db = connection.GetDatabase()) { var col = db.Database.GetCollection <MetaDataTable>(helper.MetaDataName); col.EnsureIndex(x => x.Id); col.EnsureIndex(x => x.QueueId, true); if (options.EnableStatus) { col.EnsureIndex(x => x.Status); } if (options.EnableMessageExpiration) { col.EnsureIndex(x => x.ExpirationTime); } if (options.EnableHeartBeat) { col.EnsureIndex(x => x.HeartBeat); } if (options.EnableRoute) { col.EnsureIndex(x => x.Route); } return(true); } }
/// <inheritdoc /> public bool Create(LiteDbConnectionManager connection, LiteDbMessageQueueTransportOptions options, TableNameHelper helper) { var db = connection.GetDatabase(); { var col = db.Database.GetCollection <QueueTable>(helper.QueueName); //indexed by Id col.EnsureIndex(x => x.Id); return(true); } }
/// <inheritdoc /> public bool Create(LiteDbConnectionManager connection, LiteDbMessageQueueTransportOptions options, TableNameHelper helper) { using (var db = connection.GetDatabase()) { var col = db.Database.GetCollection <ErrorTrackingTable>(helper.ErrorTrackingName); col.EnsureIndex(x => x.Id); col.EnsureIndex(x => x.QueueId, false); //multiple exceptions per record are possible return(true); } }
/// <inheritdoc /> public bool Create(LiteDbConnectionManager connection, LiteDbMessageQueueTransportOptions options, TableNameHelper helper) { using (var db = connection.GetDatabase()) { var col = db.Database.GetCollection <MetaDataErrorsTable>(helper.MetaDataErrorsName); col.EnsureIndex(x => x.Id); col.EnsureIndex(x => x.QueueId, true); return(true); } }
/// <inheritdoc /> public QueueRemoveResult Handle(DeleteQueueTablesCommand inputCommand) { using (var db = _connectionInformation.GetDatabase()) { var dbs = _tableNameHelper.Tables; var delete = db.Database.GetCollectionNames().Where(database => dbs.Contains(database)).ToList(); foreach (var toDelete in delete) { db.Database.DropCollection(toDelete); } return(new QueueRemoveResult(QueueRemoveStatus.Success)); } }
/// <inheritdoc /> public IEnumerable <MessageToReset <int> > Handle(FindMessagesToResetByHeartBeatQuery <int> query) { if (query.Cancellation.IsCancellationRequested) { return(Enumerable.Empty <MessageToReset <int> >()); } using (var db = _connectionInformation.GetDatabase()) { //before executing a query, double check that we aren't stopping //otherwise, there is a chance that the tables no longer exist in memory mode if (query.Cancellation.IsCancellationRequested) { return(Enumerable.Empty <MessageToReset <int> >()); } var col = db.Database.GetCollection <Schema.MetaDataTable>(_tableNameHelper.MetaDataName); var date = DateTime.UtcNow.Subtract(_configuration.Time); var results = col.Query() .Where(x => x.Status == QueueStatuses.Processing) .Where(x => x.HeartBeat.HasValue && x.HeartBeat.Value < date) .ToList(); var data = new List <MessageToReset <int> >(results.Count); var queue = db.Database.GetCollection <Schema.QueueTable>(_tableNameHelper.QueueName); foreach (var record in results) { if (record.HeartBeat.HasValue) { var queueRecord = queue.FindById(record.QueueId); if (queueRecord != null) { var headers = _serialization.InternalSerializer.ConvertBytesTo <IDictionary <string, object> >( queueRecord .Headers); var reset = new MessageToReset <int>(record.QueueId, record.HeartBeat.Value, new ReadOnlyDictionary <string, object>(headers)); data.Add(reset); } } } return(data); } }
/// <inheritdoc /> public DateTimeOffset Handle(GetJobLastKnownEventQuery query) { using (var db = _connectionInformation.GetDatabase()) { var col = db.Database.GetCollection <Schema.JobsTable>(_tableNameHelper.JobTableName); var results = col.Query() .Where(x => x.JobName == query.JobName) .Limit(1) .ToList(); if (results != null && results.Count == 1) { return(results[0].JobEventTime); } return(default);
/// <inheritdoc /> public TTransportOptions Handle(GetQueueOptionsQuery <TTransportOptions> query) { using (var db = _connectionInformation.GetDatabase()) { if (!_tableExists.Handle(new GetTableExistsQuery(db.Database, _tableNameHelper.ConfigurationName))) { return(null); } var col = db.Database.GetCollection <ConfigurationTable>(_tableNameHelper.ConfigurationName); var result = col.FindOne(global::LiteDB.Query.All()); return(result?.Configuration != null ? _serializer.ConvertBytesTo <TTransportOptions>(result.Configuration) : null); } }
/// <inheritdoc /> public int Handle(GetJobIdQuery <int> query) { using (var db = _connectionInformation.GetDatabase()) { var col = db.Database.GetCollection <Schema.StatusTable>(_tableNameHelper.StatusName); var results = col.Query() .Where(x => x.JobName == query.JobName) .Limit(1) .ToList(); if (results != null && results.Count == 1) { return(results[0].QueueId); } return(default);
/// <inheritdoc /> public IDictionary <string, object> Handle(GetHeaderQuery <int> query) { using (var db = _connectionInformation.GetDatabase()) { var queue = db.Database.GetCollection <Schema.QueueTable>(_tableNameHelper.QueueName); var queueRecord = queue.FindById(query.Id); if (queueRecord != null) { var headers = _serialization.InternalSerializer.ConvertBytesTo <IDictionary <string, object> >(queueRecord .Headers); return(headers); } } return(null); }
/// <inheritdoc /> public Dictionary <string, int> Handle(GetMessageErrorsQuery <int> query) { var returnData = new Dictionary <string, int>(); using (var db = _connectionInformation.GetDatabase()) { var col = db.Database.GetCollection <Schema.ErrorTrackingTable>(_tableNameHelper.ErrorTrackingName); var results = col.Query() .Where(x => x.QueueId.Equals(query.QueueId)) .ToList(); foreach (var record in results) { returnData.Add(record.ExceptionType, record.RetryCount); } return(returnData); } }
/// <inheritdoc /> public int Handle(GetErrorRetryCountQuery <int> query) { using (var db = _connectionInformation.GetDatabase()) { var col = db.Database.GetCollection <Schema.ErrorTrackingTable>(_tableNameHelper.ErrorTrackingName); var results = col.Query() .Where(x => x.QueueId.Equals(query.QueueId)) .Where(x => x.ExceptionType == query.ExceptionType) .Limit(1) .ToList(); if (results != null && results.Count == 1) { var record = results[0]; return(record.RetryCount); } } return(0); }
/// <inheritdoc /> public void Handle(SetErrorCountCommand <int> command) { using (var db = _connectionInformation.GetDatabase()) { db.Database.BeginTrans(); try { var meta = db.Database.GetCollection <Schema.ErrorTrackingTable>(_tableNameHelper.ErrorTrackingName); var results = meta.Query() .Where(x => x.QueueId == command.QueueId) .Where(x => x.ExceptionType == command.ExceptionType) .Limit(1) .ToList(); if (results != null && results.Count == 1) { //update results[0].RetryCount = results[0].RetryCount + 1; meta.Update(results[0]); } else { var record = new Schema.ErrorTrackingTable() { QueueId = command.QueueId, ExceptionType = command.ExceptionType, RetryCount = 1 }; meta.Insert(record); } db.Database.Commit(); } catch { db.Database.Rollback(); throw; } } }
/// <summary> /// Handles the specified query. /// </summary> /// <param name="query">The query.</param> /// <returns></returns> public IReceivedMessageInternal Handle(ReceiveMessageQuery query) { if (!_databaseExists.Exists()) { return(null); } //ensure created if (!_options.IsValueCreated) { _options.Value.ValidConfiguration(); } using (var db = _connectionInformation.GetDatabase()) { lock (Reader) //we have to enforce a single de-queue action per process, as BeginTrans does not block in direct or memory mode { db.Database.BeginTrans(); //will block in shared mode, but not direct or memory try { var record = DequeueRecord(query, db.Database); if (record != null) { return(_messageDeQueue.HandleMessage(record.Item1, record.Item2.QueueId, record.Item2.CorrelationId)); } } finally { db.Database.Commit(); } } } return(null); }
/// <inheritdoc /> public void Handle(MoveRecordToErrorQueueCommand <int> command) { using (var db = _connectionInformation.GetDatabase()) { bool delete = false; db.Database.BeginTrans(); try { var meta = db.Database.GetCollection <MetaDataTable>(_tableNameHelper.MetaDataName); var results = meta.Query() .Where(x => x.QueueId == command.QueueId) .ToList(); if (results != null && results.Count == 1) { //move record to error table var errorRecord = new MetaDataErrorsTable() { LastExceptionDate = DateTime.UtcNow, LastException = command.Exception.ToString(), QueueId = results[0].QueueId, Status = QueueStatuses.Error, CorrelationId = results[0].CorrelationId, HeartBeat = results[0].HeartBeat, ExpirationTime = results[0].ExpirationTime, QueueProcessTime = results[0].QueueProcessTime, QueuedDateTime = results[0].QueuedDateTime, Route = results[0].Route }; var errorCol = db.Database.GetCollection <MetaDataErrorsTable>(_tableNameHelper.MetaDataErrorsName); errorCol.Insert(errorRecord); //delete record from metadata table delete = meta.Delete(results[0].Id); //update status table if (_options.Value.EnableStatusTable) { var colStatus = db.Database.GetCollection <StatusTable>(_tableNameHelper.StatusName); var resultsStatus = colStatus.Query() .Where(x => x.QueueId == command.QueueId) .ToList(); if (resultsStatus != null && resultsStatus.Count == 1) { resultsStatus[0].Status = QueueStatuses.Error; colStatus.Update(resultsStatus[0]); } } } if (delete) { db.Database.Commit(); } else { db.Database.Rollback(); } } catch { db.Database.Rollback(); throw; } } }
/// <summary> /// Handles the specified rollback command. /// </summary> /// <param name="rollBackCommand">The rollBackCommand.</param> public void Handle(RollbackMessageCommand <int> rollBackCommand) { if (!_databaseExists.Exists()) { return; } using (var db = _connectionInformation.GetDatabase()) { db.Database.BeginTrans(); try { var col = db.Database.GetCollection <Schema.MetaDataTable>(_tableNameHelper.MetaDataName); var results2 = col.Query() .Where(x => x.QueueId.Equals(rollBackCommand.QueueId)) .Limit(1) .ToList(); if (results2 != null && results2.Count == 1) { var record = results2[0]; if (record != null) { var process = true; if (rollBackCommand.LastHeartBeat.HasValue) { //heartbeat must match if (TrimMilliseconds(record.HeartBeat) != TrimMilliseconds(rollBackCommand.LastHeartBeat)) { process = false; } } if (process) { if (_options.Value.EnableDelayedProcessing && rollBackCommand.IncreaseQueueDelay.HasValue) { var dtUtcDate = _getUtcDateQuery.Create().GetCurrentUtcDate(); var dtUtcDateIncreased = dtUtcDate.Add(rollBackCommand.IncreaseQueueDelay.Value); //move to future record.QueueProcessTime = dtUtcDateIncreased; } if (_options.Value.EnableHeartBeat) { record.HeartBeat = null; } record.Status = QueueStatuses.Waiting; col.Update(record); if (_options.Value.EnableStatusTable) { var statusCol = db.Database.GetCollection <Schema.StatusTable>(_tableNameHelper.StatusName); var results = statusCol.Query() .Where(x => x.QueueId.Equals(record.QueueId)) .Limit(1) .ToList(); if (results.Count == 1) { var statusRecord = results[0]; statusRecord.Status = record.Status; statusCol.Update(statusRecord); } } } } } db.Database.Commit(); } catch { db.Database.Rollback(); throw; } } }
/// <summary> /// Handles the specified command. /// </summary> /// <param name="commandSend">The command.</param> /// <returns></returns> /// <exception cref="DotNetWorkQueueException">Failed to insert record - the ID of the new record returned by SQLite was 0</exception> public async Task <int> HandleAsync(SendMessageCommand commandSend) { return(await Task.Run(() => //hack until litedb adds async methods { if (!_databaseExists.Exists()) { return 0; } if (!_messageExpirationEnabled.HasValue) { _messageExpirationEnabled = _options.Value.EnableMessageExpiration; } TimeSpan?expiration = null; if (_messageExpirationEnabled.Value) { expiration = MessageExpiration.GetExpiration(commandSend, data => data.GetExpiration()); } var jobName = _jobSchedulerMetaData.GetJobName(commandSend.MessageData); var scheduledTime = DateTimeOffset.MinValue; var eventTime = DateTimeOffset.MinValue; if (!string.IsNullOrWhiteSpace(jobName)) { scheduledTime = _jobSchedulerMetaData.GetScheduledTime(commandSend.MessageData); eventTime = _jobSchedulerMetaData.GetEventTime(commandSend.MessageData); } int id = 0; using (var db = _connectionInformation.GetDatabase()) { lock (Locker) //we need to block due to jobs { try { db.Database.BeginTrans(); //only blocks on shared connections if (string.IsNullOrWhiteSpace(jobName) || _jobExistsHandler.Handle( new DoesJobExistQuery(jobName, scheduledTime, db.Database)) == QueueStatuses.NotQueued) { var serialization = _serializer.Serializer.MessageToBytes( new MessageBody { Body = commandSend.MessageToSend.Body }, commandSend.MessageToSend.Headers); //create queue var queueData = new LiteDb.Schema.QueueTable() { Body = serialization.Output }; commandSend.MessageToSend.SetHeader(_headers.StandardHeaders.MessageInterceptorGraph, serialization.Graph); queueData.Headers = _serializer.InternalSerializer.ConvertToBytes(commandSend.MessageToSend.Headers); var col = db.Database.GetCollection <QueueTable>(_tableNameHelper.QueueName); id = col.Insert(queueData).AsInt32; //create metadata var metaData = new LiteDb.Schema.MetaDataTable { QueueId = id, CorrelationId = (Guid)commandSend.MessageData.CorrelationId.Id.Value, QueuedDateTime = DateTime.UtcNow }; if (!string.IsNullOrWhiteSpace(jobName)) { metaData.QueueProcessTime = scheduledTime.UtcDateTime; } else if (_options.Value.EnableDelayedProcessing) { var delay = commandSend.MessageData.GetDelay(); if (delay.HasValue) { metaData.QueueProcessTime = DateTime.UtcNow.Add(delay.Value); } } if (_options.Value.EnableMessageExpiration && expiration.HasValue) { metaData.ExpirationTime = DateTime.UtcNow.Add(expiration.Value); } if (_options.Value.EnableStatus) { metaData.Status = QueueStatuses.Waiting; } if (_options.Value.EnableRoute) { metaData.Route = commandSend.MessageData.Route; } var colMeta = db.Database.GetCollection <MetaDataTable>(_tableNameHelper.MetaDataName); colMeta.Insert(metaData); //create status table record if (_options.Value.EnableStatusTable || !string.IsNullOrWhiteSpace(jobName)) { var statusData = new LiteDb.Schema.StatusTable() { Status = metaData.Status, CorrelationId = metaData.CorrelationId, QueueId = id }; if (!string.IsNullOrWhiteSpace(jobName)) { statusData.JobName = jobName; } var colStatus = db.Database.GetCollection <StatusTable>(_tableNameHelper.StatusName); colStatus.Insert(statusData); } //job name if (!string.IsNullOrWhiteSpace(jobName)) { _sendJobStatus.Handle(new SetJobLastKnownEventCommand(jobName, eventTime, scheduledTime, db.Database)); } } else { throw new DotNetWorkQueueException( "Failed to insert record - the job has already been queued or processed"); } db.Database.Commit(); } catch { db.Database.Rollback(); throw; } } } return id; })); }