/// <summary>
        /// Gets the latest videos added to the site.
        /// </summary>
        public async Task<LatestVideos> GetLastestVideos(GetLatestVideos getVideos)
        {
            // We may need multiple queries to fill the quota
            var results = new List<VideoPreview>();

            // Generate a list of all the possibly bucket dates by truncating now to the day, then subtracting days from that day
            // going back as many days as we're allowed to query back
            DateTimeOffset nowToTheDay = DateTimeOffset.UtcNow.Truncate(TimeSpan.TicksPerDay);
            var bucketDates = Enumerable.Range(0, MaxDaysInPastForLatestVideos + 1)
                                        .Select(day => nowToTheDay.Subtract(TimeSpan.FromDays(day)));

            // If we're going to include paging parameters for the first query, filter out any bucket dates that are more recent
            // than the first video on the page
            bool pageFirstQuery = getVideos.FirstVideoOnPageDate.HasValue && getVideos.FirstVideoOnPageVideoId.HasValue;
            if (pageFirstQuery)
            {
                DateTimeOffset maxBucketToTheDay = getVideos.FirstVideoOnPageDate.Value.Truncate(TimeSpan.TicksPerDay);
                bucketDates = bucketDates.Where(bucketToTheDay => bucketToTheDay <= maxBucketToTheDay);
            }

            DateTimeOffset[] bucketDatesArray = bucketDates.ToArray();

            // TODO: Run queries in parallel instead of sequentially?
            for (var i = 0; i < bucketDatesArray.Length; i++)
            {
                int recordsStillNeeded = getVideos.PageSize - results.Count;

                string bucket = bucketDatesArray[i].ToString("yyyyMMdd");

                // If we're processing a paged request, use the appropriate statement
                PreparedStatement preparedStatement;
                IStatement boundStatement;
                if (pageFirstQuery && i == 0)
                {
                    preparedStatement = await _statementCache.NoContext.GetOrAddAsync(
                        "SELECT * FROM latest_videos WHERE yyyymmdd = ? AND (added_date, videoid) <= (?, ?) LIMIT ?");
                    boundStatement = preparedStatement.Bind(bucket, getVideos.FirstVideoOnPageDate.Value, getVideos.FirstVideoOnPageVideoId.Value,
                                                            recordsStillNeeded);
                }
                else
                {
                    preparedStatement = await _statementCache.NoContext.GetOrAddAsync("SELECT * FROM latest_videos WHERE yyyymmdd = ? LIMIT ?");
                    boundStatement = preparedStatement.Bind(bucket, recordsStillNeeded);
                }

                RowSet rows = await _session.ExecuteAsync(boundStatement).ConfigureAwait(false);

                results.AddRange(rows.Select(MapRowToVideoPreview));

                // If we've got all the records we need, we can quit querying
                if (results.Count >= getVideos.PageSize)
                    break;
            }

            return new LatestVideos
            {
                Videos = results
            };
        }
        /// <summary>
        /// Gets the latest videos added to the site.
        /// </summary>
        public async Task<LatestVideos> GetLastestVideos(GetLatestVideos getVideos)
        {
            string[] buckets;
            int bucketIndex;
            string rowPagingState;

            // See if we have paging state from a previous page of videos
            if (TryParsePagingState(getVideos.PagingState, out buckets, out bucketIndex, out rowPagingState) == false)
            {
                // Generate a list of all the possibly bucket dates by truncating now to the day, then subtracting days from that day
                // going back as many days as we're allowed to query back
                DateTimeOffset nowToTheDay = DateTimeOffset.UtcNow.Truncate(TimeSpan.TicksPerDay);
                buckets = Enumerable.Range(0, MaxDaysInPastForLatestVideos + 1)
                                    .Select(day => nowToTheDay.Subtract(TimeSpan.FromDays(day)).ToString("yyyyMMdd"))
                                    .ToArray();
                bucketIndex = 0;
                rowPagingState = null;
            }
            
            // We may need multiple queries to fill the quota so build up a list of results
            var results = new List<VideoPreview>();
            string nextPageState = null;

            // TODO: Run queries in parallel?
            while (bucketIndex < buckets.Length)
            {
                int recordsStillNeeded = getVideos.PageSize - results.Count;
                string bucket = buckets[bucketIndex];

                // Get a page of records but don't automatically load more pages when enumerating the RowSet
                PreparedStatement preparedStatement = await _statementCache.NoContext.GetOrAddAsync("SELECT * FROM latest_videos WHERE yyyymmdd = ?");
                IStatement boundStatement = preparedStatement.Bind(bucket)
                                                             .SetAutoPage(false)
                                                             .SetPageSize(recordsStillNeeded);

                // Start from where we left off in this bucket
                if (string.IsNullOrEmpty(rowPagingState) == false)
                    boundStatement.SetPagingState(Convert.FromBase64String(rowPagingState));

                RowSet rows = await _session.ExecuteAsync(boundStatement).ConfigureAwait(false);
                results.AddRange(rows.Select(MapRowToVideoPreview));

                // See if we can stop querying
                if (results.Count == getVideos.PageSize)
                {
                    // Are there more rows in the current bucket?
                    if (rows.PagingState != null && rows.PagingState.Length > 0)
                    {
                        // Start from where we left off in this bucket if we get the next page
                        nextPageState = CreatePagingState(buckets, bucketIndex, Convert.ToBase64String(rows.PagingState));
                    }
                    else if (bucketIndex != buckets.Length - 1)
                    {
                        // Start from the beginning of the next bucket since we're out of rows in this one
                        nextPageState = CreatePagingState(buckets, bucketIndex + 1, string.Empty);
                    }

                    break;
                }

                bucketIndex++;
            }
            
            return new LatestVideos
            {
                Videos = results,
                PagingState = nextPageState
            };
        }