public override long GetCounter(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            long             value    = 0;
            FirebaseResponse response = Client.Get($"counters/raw/{key}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Counter> collections = response.ResultAs <Dictionary <string, Counter> >();
                value += collections.Sum(c => c.Value.Value);
            }

            response = Client.Get($"counters/aggregrated/{key}");
            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Counter> collections = response.ResultAs <Dictionary <string, Counter> >();
                value += collections.Sum(c => c.Value.Value);
            }

            return(value);
        }
        public override StateData GetStateData(string jobId)
        {
            if (jobId == null)
            {
                throw new ArgumentNullException(nameof(jobId));
            }

            FirebaseResponse response = Client.Get($"jobs/{jobId}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Entities.Job job = response.ResultAs <Entities.Job>();
                response = Client.Get($"states/{jobId}/{job.StateId}");
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    State data = response.ResultAs <State>();
                    return(new StateData
                    {
                        Name = data.Name,
                        Reason = data.Reason,
                        Data = data.Data.Trasnform()
                    });
                }
            }

            return(null);
        }
        public override string GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (toScore < fromScore)
            {
                throw new ArgumentException("The `toScore` value must be higher or equal to the `fromScore` value.");
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");

            builder.OrderBy("key");
            FirebaseResponse response = Client.Get("sets", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Set> sets = response.ResultAs <Dictionary <string, Set> >();
                return(sets.Select(s => s.Value)
                       .OrderBy(s => s.Score)
                       .Where(s => s.Score >= fromScore && s.Score <= toScore)
                       .Select(s => s.Value).FirstOrDefault());
            }

            return(string.Empty);
        }
        public override int RemoveTimedOutServers(TimeSpan timeOut)
        {
            if (timeOut.Duration() != timeOut)
            {
                throw new ArgumentException("The `timeOut` value must be positive.", nameof(timeOut));
            }

            FirebaseResponse response = Client.Get("servers");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Entities.Server> servers = response.ResultAs <Dictionary <string, Entities.Server> >();

                // get the firebase reference key for each timeout server
                DateTime lastHeartbeat = DateTime.UtcNow.Add(timeOut.Negate());
                string[] references    = servers.Where(s => s.Value.LastHeartbeat < lastHeartbeat)
                                         .Select(s => s.Key)
                                         .ToArray();

                // remove all timeout server.
                Array.ForEach(references, reference => Client.Delete($"servers/{reference}"));
                return(references.Length);
            }

            return(default(int));
        }
        public void RemoveFromList(string key, string value)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentNullException(nameof(value));
            }

            QueueCommand(() =>
            {
                QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");
                builder.OrderBy("key");
                FirebaseResponse response = connection.Client.Get("lists", builder);
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    Dictionary <string, List> lists = response.ResultAs <Dictionary <string, List> >();
                    string reference = lists.Where(l => l.Value.Value == value).Select(k => k.Key).FirstOrDefault();
                    if (string.IsNullOrEmpty(reference))
                    {
                        response = connection.Client.Delete($"lists/{reference}");
                        if (response.StatusCode != HttpStatusCode.OK)
                        {
                            throw new HttpRequestException(response.Body);
                        }
                    }
                }
            });
        }
        public override void Heartbeat(string serverId)
        {
            if (serverId == null)
            {
                throw new ArgumentNullException(nameof(serverId));
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{serverId}""");

            builder.OrderBy("server_id");
            FirebaseResponse response = Client.Get("servers", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Entities.Server> servers = response.ResultAs <Dictionary <string, Entities.Server> >();
                string reference = servers.Where(s => s.Value.ServerId == serverId).Select(s => s.Key).FirstOrDefault();
                if (!string.IsNullOrEmpty(reference))
                {
                    response = Client.Set($"servers/{reference}/last_heartbeat", DateTime.UtcNow);
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        throw new HttpRequestException(response.Body);
                    }
                }
            }
        }
        public JobDetailsDto JobDetails(string jobId)
        {
            if (string.IsNullOrEmpty(jobId))
            {
                throw new ArgumentNullException(nameof(jobId));
            }

            List <StateHistoryDto> states = new List <StateHistoryDto>();

            FirebaseResponse response = connection.Client.Get($"jobs/{jobId}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Entities.Job   job            = response.ResultAs <Entities.Job>();
                InvocationData invocationData = job.InvocationData;
                invocationData.Arguments = job.Arguments;

                response = connection.Client.Get($"states/{jobId}");
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    Dictionary <string, State> collections = response.ResultAs <Dictionary <string, State> >();
                    states = collections.Select(s => new StateHistoryDto
                    {
                        Data      = s.Value.Data.Trasnform(),
                        CreatedAt = s.Value.CreatedOn,
                        Reason    = s.Value.Reason,
                        StateName = s.Value.Name
                    }).ToList();
                }

                return(new JobDetailsDto
                {
                    Job = invocationData.Deserialize(),
                    CreatedAt = job.CreatedOn,
                    ExpireAt = job.ExpireOn,
                    Properties = job.Parameters.ToDictionary(p => p.Name, p => p.Value),
                    History = states
                });
            }

            return(null);
        }
Beispiel #8
0
        public int GetEnqueuedCount(string queue)
        {
            FirebaseResponse response = connection.Client.Get($"queue/{queue}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, string> collection = response.ResultAs <Dictionary <string, string> >();
                return(collection.Count);
            }
            return(default(int));
        }
Beispiel #9
0
        public IEnumerable <string> GetEnqueuedJobIds(string queue, int from, int perPage)
        {
            FirebaseResponse response = connection.Client.Get($"queue/{queue}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, string> collection = response.ResultAs <Dictionary <string, string> >();
                return(collection.Skip(from).Take(perPage).Select(c => c.Value));
            }
            return(null);
        }
Beispiel #10
0
        public void Execute(CancellationToken cancellationToken)
        {
            Logger.Debug("Aggregating records in 'Counter' table.");

            using (new FirebaseDistributedLock(distributedLockKey, defaultLockTimeout, connection.Client))
            {
                FirebaseResponse response = connection.Client.Get("counters/raw");
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    Dictionary <string, Dictionary <string, Counter> > counters = response.ResultAs <Dictionary <string, Dictionary <string, Counter> > >();
                    Array.ForEach(counters.Keys.ToArray(), key =>
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        Dictionary <string, Counter> data;
                        if (counters.TryGetValue(key, out data))
                        {
                            data = data.ToDictionary(k => k.Key, v => v.Value);

                            int value          = data.Sum(c => c.Value.Value);
                            DateTime?expireOn  = data.Max(c => c.Value.ExpireOn);
                            Counter aggregated = new Counter
                            {
                                Value    = value,
                                ExpireOn = expireOn
                            };

                            FirebaseResponse counterResponse = connection.Client.Get($"counters/aggregrated/{key}");
                            if (counterResponse.StatusCode == HttpStatusCode.OK && !counterResponse.IsNull())
                            {
                                Counter counter = counterResponse.ResultAs <Counter>();
                                if (counter != null)
                                {
                                    aggregated.Value += counter.Value;
                                }
                            }

                            // update the aggregrated counter
                            FirebaseResponse aggResponse = connection.Client.Set($"counters/aggregrated/{key}", aggregated);
                            if (aggResponse.StatusCode == HttpStatusCode.OK)
                            {
                                // delete all the counter references for the key
                                Array.ForEach(data.Keys.ToArray(), reference => connection.Client.Delete($"counters/raw/{key}/{reference}"));
                            }
                        }
                    });
                }
            }

            Logger.Trace("Records from the 'Counter' table aggregated.");
            cancellationToken.WaitHandle.WaitOne(checkInterval);
        }
 private void Relase()
 {
     lock (syncLock)
     {
         QueryBuilder builder = QueryBuilder.New($@"equalTo=""{lockReference}""");
         builder.OrderBy("$key");
         FirebaseResponse response = client.Get("locks", builder);
         if (response.StatusCode == System.Net.HttpStatusCode.OK && !response.IsNull())
         {
             client.Delete($"locks/{lockReference}");
         }
     }
 }
        private List <KeyValuePair <string, T> > GetJobsOnState <T>(string stateName, int from, int count, Func <State, Common.Job, T> selector)
        {
            List <KeyValuePair <string, T> > jobs = new List <KeyValuePair <string, T> >();

            FirebaseResponse response = connection.Client.Get("jobs");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Entities.Job> collections = response.ResultAs <Dictionary <string, Entities.Job> >();
                string[] references = collections.Where(s => s.Value.StateName.Equals(stateName, StringComparison.InvariantCulture))
                                      .Skip(from).Take(count)
                                      .Select(k => k.Key)
                                      .ToArray();

                if (references.Any())
                {
                    // get all states
                    Dictionary <string, State> states        = new Dictionary <string, State>();
                    FirebaseResponse           stateResponse = connection.Client.Get("states");
                    if (stateResponse.StatusCode == HttpStatusCode.OK && !stateResponse.IsNull())
                    {
                        Dictionary <string, Dictionary <string, State> > stateCollections = stateResponse.ResultAs <Dictionary <string, Dictionary <string, State> > >();
                        states = stateCollections.Where(s => references.Contains(s.Key))
                                 .SelectMany(s => s.Value)
                                 .ToDictionary(k => k.Key, v => v.Value);
                    }

                    Array.ForEach(references, reference =>
                    {
                        Entities.Job job;
                        if (collections.TryGetValue(reference, out job))
                        {
                            State state;
                            if (states.TryGetValue(job.StateId, out state))
                            {
                                state.Data = state.Data.Trasnform();

                                InvocationData invocationData = job.InvocationData;
                                invocationData.Arguments      = job.Arguments;

                                T data = selector(state, invocationData.Deserialize());
                                jobs.Add(new KeyValuePair <string, T>(reference, data));
                            }
                        }
                    });
                }
            }

            return(jobs);
        }
        private long GetNumberOfJobsByStateName(string state)
        {
            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{state}""");

            builder.OrderBy("state_name");
            FirebaseResponse response = connection.Client.Get("jobs", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Entities.Job> jobs = response.ResultAs <Dictionary <string, Entities.Job> >();
                return(jobs.LongCount());
            }

            return(default(long));
        }
        public void SetRangeInHash(string key, IEnumerable <KeyValuePair <string, string> > keyValuePairs)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (keyValuePairs == null)
            {
                throw new ArgumentNullException(nameof(keyValuePairs));
            }

            QueueCommand(() =>
            {
                List <Hash> hashes = keyValuePairs.Select(k => new Hash
                {
                    Field = k.Key,
                    Value = k.Value
                }).ToList();

                key = key.ToValidKey();
                FirebaseResponse response = connection.Client.Get($"hashes/{key}");
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    // update
                    Dictionary <string, Hash> collection = response.ResultAs <Dictionary <string, Hash> >();
                    string[] references = collection.Where(h => hashes.Any(k => k.Field == h.Value.Field))
                                          .Select(h => h.Key)
                                          .ToArray();
                    Parallel.ForEach(references, reference =>
                    {
                        Hash hash;
                        if (collection.TryGetValue(reference, out hash) && hashes.Any(k => k.Field == hash.Field))
                        {
                            string value = hashes.Where(k => k.Field == hash.Field).Select(k => k.Value).Single();
                            connection.Client.Set($"hashes/{key}/{reference}/value", value);

                            // remove the hash from the list
                            hashes.RemoveAll(x => x.Field == hash.Field);
                        }
                    });
                }

                // new
                Parallel.ForEach(hashes, hash => connection.Client.Push($"hashes/{key}", hash));
            });
        }
        public override Dictionary <string, string> GetAllEntriesFromHash(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            key = key.ToValidKey();
            FirebaseResponse response = Client.Get($"hashes/{key}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Hash> hashes = response.ResultAs <Dictionary <string, Hash> >();
                return(hashes.Select(h => h.Value).ToDictionary(h => h.Field, h => h.Value));
            }

            return(new Dictionary <string, string>());
        }
        private List <KeyValuePair <string, T> > GetJobsOnQueue <T>(string queue, int from, int count, Func <string, Common.Job, T> selector)
        {
            if (string.IsNullOrEmpty(queue))
            {
                throw new ArgumentNullException(nameof(queue));
            }

            List <KeyValuePair <string, T> > jobs = new List <KeyValuePair <string, T> >();

            FirebaseResponse response = connection.Client.Get($"queue/{queue}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, string> collection = response.ResultAs <Dictionary <string, string> >();
                string[] references = collection.Skip(from).Take(count).Select(k => k.Value).ToArray();

                if (references.Any())
                {
                    // get all jobs
                    Dictionary <string, Entities.Job> jobCollection = new Dictionary <string, Entities.Job>();
                    FirebaseResponse stateResponse = connection.Client.Get("jobs");
                    if (stateResponse.StatusCode == HttpStatusCode.OK && !stateResponse.IsNull())
                    {
                        jobCollection = stateResponse.ResultAs <Dictionary <string, Entities.Job> >();
                    }

                    Array.ForEach(references, reference =>
                    {
                        Entities.Job job;
                        if (jobCollection.TryGetValue(reference, out job))
                        {
                            InvocationData invocationData = job.InvocationData;
                            invocationData.Arguments      = job.Arguments;

                            T data = selector(job.StateName, invocationData.Deserialize());
                            jobs.Add(new KeyValuePair <string, T>(reference, data));
                        }
                    });
                }
            }

            return(jobs);
        }
        public override long GetHashCount(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            key = key.ToValidKey();
            QueryBuilder     builder  = QueryBuilder.New().Shallow(true);
            FirebaseResponse response = Client.Get($"hashes/{key}", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                string[] hashes = response.ResultAs <string[]>();
                return(hashes.LongCount());
            }

            return(default(long));
        }
        public override long GetListCount(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");

            builder.OrderBy("key");
            FirebaseResponse response = Client.Get("lists", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, List> lists = response.ResultAs <Dictionary <string, List> >();
                return(lists.LongCount());
            }

            return(default(long));
        }
        private Dictionary <DateTime, long> GetTimelineStats(Dictionary <string, DateTime> keys)
        {
            Dictionary <DateTime, long> result = keys.ToDictionary(k => k.Value, v => default(long));

            FirebaseResponse response = connection.Client.Get("counters/aggregrated");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Counter> collections = response.ResultAs <Dictionary <string, Counter> >();
                Dictionary <string, int>     data        = collections.Where(k => keys.ContainsKey(k.Key)).ToDictionary(k => k.Key, k => k.Value.Value);

                foreach (string key in keys.Keys)
                {
                    DateTime date = keys.Where(k => k.Key == key).Select(k => k.Value).First();
                    result[date] = data.ContainsKey(key) ? data[key] : 0;
                }
            }

            return(result);
        }
        public override List <string> GetRangeFromSet(string key, int startingFrom, int endingAt)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");

            builder.OrderBy("key");
            FirebaseResponse response = Client.Get("sets", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Set> collections = response.ResultAs <Dictionary <string, Set> >();
                return(collections.Skip(startingFrom).Take(endingAt).Select(c => c.Value).Select(s => s.Value).ToList());
            }

            return(new List <string>());
        }
        public override List <string> GetAllItemsFromList(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");

            builder.OrderBy("key");
            FirebaseResponse response = Client.Get("lists", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, List> lists = response.ResultAs <Dictionary <string, List> >();
                return(lists.Select(l => l.Value).Select(l => l.Value).ToList());
            }

            return(new List <string>());
        }
        public IList <ServerDto> Servers()
        {
            List <ServerDto> servers = new List <ServerDto>();

            FirebaseResponse response = connection.Client.Get("servers");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Entities.Server> collections = response.ResultAs <Dictionary <string, Entities.Server> >();
                servers = collections?.Select(s => new ServerDto
                {
                    Name         = s.Value.ServerId,
                    Heartbeat    = s.Value.LastHeartbeat,
                    Queues       = s.Value.Queues,
                    StartedAt    = s.Value.CreatedOn,
                    WorkersCount = s.Value.Workers
                }).ToList();
            }

            return(servers);
        }
        public override string GetJobParameter(string id, string name)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            FirebaseResponse response = Client.Get($"jobs/{id}/parameter");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Parameter[] parameters = response.ResultAs <Parameter[]>();
                return(parameters.Where(p => p.Name == name)
                       .Select(p => p.Value).FirstOrDefault());
            }

            return(null);
        }
        public override TimeSpan GetHashTtl(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            key = key.ToValidKey();
            FirebaseResponse response = Client.Get($"hashes/{key}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Hash> hashes = response.ResultAs <Dictionary <string, Hash> >();
                DateTime?expireOn = hashes.Select(h => h.Value).Min(h => h.ExpireOn);
                if (expireOn.HasValue)
                {
                    return(expireOn.Value - DateTime.UtcNow);
                }
            }

            return(TimeSpan.FromSeconds(-1));
        }
        public override string GetValueFromHash(string key, string name)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            key = key.ToValidKey();
            FirebaseResponse response = Client.Get($"hashes/{key}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Hash> hashes = response.ResultAs <Dictionary <string, Hash> >();
                return(hashes.Select(h => h.Value).Where(h => h.Field == name).Select(v => v.Value).FirstOrDefault());
            }

            return(string.Empty);
        }
        public override void RemoveServer(string serverId)
        {
            if (serverId == null)
            {
                throw new ArgumentNullException(nameof(serverId));
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{serverId}""");

            builder.OrderBy("server_id");
            FirebaseResponse response = Client.Get("servers", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, Entities.Server> servers = response.ResultAs <Dictionary <string, Entities.Server> >();
                string reference = servers.Where(s => s.Value.ServerId == serverId).Select(s => s.Key).Single();
                if (!string.IsNullOrEmpty(reference))
                {
                    Client.Delete($"servers/{reference}");
                }
            }
        }
        public override JobData GetJobData(string jobId)
        {
            if (jobId == null)
            {
                throw new ArgumentNullException(nameof(jobId));
            }

            FirebaseResponse response = Client.Get($"jobs/{jobId}");

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Entities.Job   data           = response.ResultAs <Entities.Job>();
                InvocationData invocationData = data.InvocationData;
                invocationData.Arguments = data.Arguments;

                Common.Job       job           = null;
                JobLoadException loadException = null;

                try
                {
                    job = invocationData.Deserialize();
                }
                catch (JobLoadException ex)
                {
                    loadException = ex;
                }

                return(new JobData
                {
                    Job = job,
                    State = data.StateName,
                    CreatedAt = data.CreatedOn,
                    LoadException = loadException
                });
            }

            return(null);
        }
        public void TrimList(string key, int keepStartingFrom, int keepEndingAt)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException(nameof(key));
            }

            QueueCommand(() =>
            {
                QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");
                builder.OrderBy("key");
                FirebaseResponse response = connection.Client.Get("lists", builder);
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    Dictionary <string, List> lists = response.ResultAs <Dictionary <string, List> >();
                    string[] listsReferences        = lists.Skip(keepStartingFrom).Take(keepEndingAt)
                                                      .Select(l => l.Key)
                                                      .ToArray();

                    Parallel.ForEach(listsReferences, reference => connection.Client.Delete($"lists/{reference}"));
                }
            });
        }
        public void SetJobState(string jobId, IState state)
        {
            if (string.IsNullOrEmpty(jobId))
            {
                throw new ArgumentNullException(nameof(jobId));
            }
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }

            QueueCommand(() =>
            {
                FirebaseResponse response = connection.Client.Get($"jobs/{jobId}");
                if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
                {
                    Job job    = response.ResultAs <Job>();
                    State data = new State
                    {
                        Name      = state.Name,
                        Reason    = state.Reason,
                        CreatedOn = DateTime.UtcNow,
                        Data      = state.SerializeData()
                    };

                    response = connection.Client.Push($"states/{jobId}", data);
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        string reference = ((PushResponse)response).Result.name;
                        job.StateId      = reference;
                        job.StateName    = state.Name;

                        connection.Client.Set($"jobs/{jobId}", job);
                    }
                }
            });
        }
        public override TimeSpan GetListTtl(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            QueryBuilder builder = QueryBuilder.New($@"equalTo=""{key}""");

            builder.OrderBy("key");
            FirebaseResponse response = Client.Get("lists", builder);

            if (response.StatusCode == HttpStatusCode.OK && !response.IsNull())
            {
                Dictionary <string, List> lists = response.ResultAs <Dictionary <string, List> >();
                DateTime?expireOn = lists.Select(l => l.Value).Min(l => l.ExpireOn);
                if (expireOn.HasValue)
                {
                    return(expireOn.Value - DateTime.UtcNow);
                }
            }

            return(TimeSpan.FromSeconds(-1));
        }