public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken) { if (queues is null || !queues.Any()) { throw new ArgumentException("Queue array must be non-empty.", nameof(queues)); } var token = Guid.NewGuid().ToString(); // I'm not sure if this is used correctly... var expiration = _options.InvisibilityTimeout; // the connection variable is mutable, it job is claimed then this connection // is passed to job itself, and variable is set to null so it does not get // disposed here in such case var connection = _storage.CreateAndOpenConnection(); try { while (true) { cancellationToken.ThrowIfCancellationRequested(); using (ResourceLock.AcquireOne( connection, _options.TablesPrefix, LockableResource.Queue)) { var updated = ClaimJob(connection, queues, expiration, token); if (updated != 0) { var fetchedJob = FetchJobByToken(connection, token); return(new MySqlFetchedJob( _storage, _options, Interlocked.Exchange(ref connection, null), fetchedJob)); } } cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval); cancellationToken.ThrowIfCancellationRequested(); } } catch (MySqlException ex) { Logger.ErrorException(ex.Message, ex); throw; } finally { if (connection != null) { _storage.ReleaseConnection(connection); } } }
public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken) { if (queues == null) { throw new ArgumentNullException("queues"); } if (queues.Length == 0) { throw new ArgumentException("Queue array must be non-empty.", "queues"); } FetchedJob fetchedJob = null; MySqlConnection connection = null; do { cancellationToken.ThrowIfCancellationRequested(); connection = _storage.CreateAndOpenConnection(); try { using (new MySqlDistributedLock(_storage, "JobQueue", TimeSpan.FromSeconds(30), _options)) { string token = Guid.NewGuid().ToString(); int nUpdated = connection.Execute( $"update `{_options.TablesPrefix}JobQueue` set FetchedAt = UTC_TIMESTAMP(), FetchToken = @fetchToken " + "where (FetchedAt is null or FetchedAt < DATE_ADD(UTC_TIMESTAMP(), INTERVAL @timeout SECOND)) " + " and Queue in @queues " + "LIMIT 1;", new { queues = queues, timeout = _options.InvisibilityTimeout.Negate().TotalSeconds, fetchToken = token }); if (nUpdated != 0) { fetchedJob = connection .Query <FetchedJob>( "select Id, JobId, Queue " + $"from `{_options.TablesPrefix}JobQueue` " + "where FetchToken = @fetchToken;", new { fetchToken = token }) .SingleOrDefault(); } } } catch (MySqlException ex) { Logger.ErrorException(ex.Message, ex); _storage.ReleaseConnection(connection); throw; } if (fetchedJob == null) { _storage.ReleaseConnection(connection); cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval); cancellationToken.ThrowIfCancellationRequested(); } } while (fetchedJob == null); return(new MySqlFetchedJob(_storage, connection, fetchedJob, _options)); }
public MySqlDistributedLock(MySqlStorage storage, MySqlStorageOptions options, string resource, TimeSpan timeout) : this(storage.CreateAndOpenConnection(), options, resource, timeout) { _storage = storage; }
public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken) { if (queues == null) { throw new ArgumentNullException("queues"); } if (queues.Length == 0) { throw new ArgumentException("Queue array must be non-empty.", "queues"); } FetchedJob fetchedJob = null; MySqlConnection connection = null; do { cancellationToken.ThrowIfCancellationRequested(); connection = _storage.CreateAndOpenConnection(); try { using (new MySqlDistributedLock(_storage, "JobQueue", TimeSpan.FromSeconds(30))) { fetchedJob = connection .Query <FetchedJob>( "select Id, JobId, Queue " + "from JobQueue " + "where (FetchedAt is null or FetchedAt < DATE_ADD(UTC_TIMESTAMP(), INTERVAL @timeout SECOND)) " + " and Queue in @queues " + "limit 1;", new { queues = queues, timeout = _options.InvisibilityTimeout.Negate().TotalSeconds }) .SingleOrDefault(); if (fetchedJob != null) { connection .Execute( "update JobQueue set FetchedAt = @fetchedAt where Id = @id", new { fetchedAt = DateTime.UtcNow, id = fetchedJob.Id }); } } } catch (MySqlException ex) { Logger.ErrorException(ex.Message, ex); _storage.ReleaseConnection(connection); throw; } if (fetchedJob == null) { _storage.ReleaseConnection(connection); cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval); cancellationToken.ThrowIfCancellationRequested(); } } while (fetchedJob == null); return(new MySqlFetchedJob(_storage, connection, fetchedJob)); }
public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken) { if (queues == null) { throw new ArgumentNullException("queues"); } if (queues.Length == 0) { throw new ArgumentException("Queue array must be non-empty.", "queues"); } FetchedJob fetchedJob = null; MySqlConnection connection = null; do { cancellationToken.ThrowIfCancellationRequested(); try { connection = _storage.CreateAndOpenConnection(); //var joinedQueues = string.Join(",", queues.Select(q => "'" + q.Replace("'", "''") + "'")); /*var resource = ("JobQueue:" + joinedQueues); * if (resource.Length > 100) * resource = resource.Substring(0, 100); * * using (new MySqlDistributedLock(_storage, resource, TimeSpan.FromSeconds(70)))*/ { string token = Guid.NewGuid().ToString(); int nUpdated; while (!_semaphoreSlim.WaitOne(TimeSpan.FromSeconds(5))) { cancellationToken.ThrowIfCancellationRequested(); } //_semaphoreSlim.Wait(cancellationToken); try { nUpdated = MySqlStorageConnection.AttemptActionReturnObject(() => connection.Execute( "update JobQueue set FetchedAt = DATE_ADD(UTC_TIMESTAMP(), INTERVAL @timeout SECOND), FetchToken = @fetchToken " + "where (FetchedAt is null or FetchedAt < UTC_TIMESTAMP()) " + " and Queue in @queues " + // "ORDER BY FIELD(Queue, " + joinedQueues + "), JobId " + "ORDER BY Priority DESC, JobId " + "LIMIT 1;", new { queues = queues, timeout = 45, //_options.InvisibilityTimeout.Negate().TotalSeconds, fetchToken = token }, commandTimeout: 15), 3); } finally { _semaphoreSlim.Release(); } if (nUpdated != 0) { fetchedJob = MySqlStorageConnection.AttemptActionReturnObject(() => connection .Query <FetchedJob>( "select Id, JobId, Queue " + "from JobQueue " + "where FetchToken = @fetchToken;", new { fetchToken = token }, commandTimeout: 15) .SingleOrDefault(), 3); if (fetchedJob != null) { nUpdated = MySqlStorageConnection.AttemptActionReturnObject(() => connection.Execute( "update JobQueue set FetchedAt = DATE_ADD(UTC_TIMESTAMP(), INTERVAL @timeout SECOND), FetchToken = @fetchToken " + "where FetchToken = @fetchToken;", new { timeout = _options.InvisibilityTimeout.TotalSeconds, fetchToken = token }, commandTimeout: 15), 5); if (nUpdated == 0) { fetchedJob = null; } } } } } catch (MySqlException ex) { Logger.ErrorException(ex.Message, ex); throw; } finally { _storage.ReleaseConnection(connection); } if (fetchedJob == null) { cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval); cancellationToken.ThrowIfCancellationRequested(); } } while (fetchedJob == null); return(new MySqlFetchedJob(_storage, fetchedJob)); }
public MySqlDistributedLock(MySqlStorage storage, string resource, TimeSpan timeout) : this(storage.CreateAndOpenConnection(), resource, timeout) { _storage = storage; }