private void RestoreChangelog(ChangelogMetadata changelogMetadata)
        {
            var numRecords = changelogMetadata.BufferedLimit;

            if (numRecords > 0)
            {
                var records = changelogMetadata.BufferedRecords.Take(numRecords);
                changelogMetadata.StateManager.Restore(changelogMetadata.StoreMetadata, records);

                if (numRecords >= changelogMetadata.BufferedRecords.Count)
                {
                    changelogMetadata.BufferedRecords.Clear();
                }

                long currentOffset = changelogMetadata.StoreMetadata.Offset.Value;
                changelogMetadata.CurrentOffset = currentOffset;
                log.LogDebug($"Restored {numRecords} records from " +
                             $"changelog {changelogMetadata.StoreMetadata.Store.Name} " +
                             $"to store {changelogMetadata.StoreMetadata.ChangelogTopicPartition}, " +
                             $"end offset is {(changelogMetadata.RestoreEndOffset.HasValue ? changelogMetadata.RestoreEndOffset.Value : "unknown")}, " +
                             $"current offset is {currentOffset}");

                changelogMetadata.BufferedLimit  = 0;
                changelogMetadata.TotalRestored += numRecords;

                // TODO : call trigger batchRestored

                var restorationRecordSensor = TaskMetrics.RestorationRecordsSensor(
                    threadId,
                    changelogMetadata.StateManager.taskId,
                    metricsRegistry);
                restorationRecordSensor.Record(Math.Max(changelogMetadata.RestoreEndOffset.Value - currentOffset, 0));
            }
            else if (changelogMetadata.StoreMetadata.Offset.HasValue)
            {
                changelogMetadata.CurrentOffset = changelogMetadata.StoreMetadata.Offset.Value;
            }


            if (HasRestoredToEnd(changelogMetadata))
            {
                log.LogInformation($"Finished restoring changelog {changelogMetadata.StoreMetadata.Store.Name} " +
                                   $"to store {changelogMetadata.StoreMetadata.ChangelogTopicPartition} " +
                                   $"with a total number of {changelogMetadata.TotalRestored} records");

                changelogMetadata.ChangelogState = ChangelogState.COMPLETED;

                if (!restoreConsumer.Assignment.Contains(changelogMetadata.StoreMetadata.ChangelogTopicPartition))
                {
                    throw new IllegalStateException($"The current assignment {string.Join(",", restoreConsumer.Assignment.Select(t => $"{t.Topic}-{t.Partition}"))} " +
                                                    $"does not contain the partition {changelogMetadata.StoreMetadata.ChangelogTopicPartition} for pausing.");
                }

                restoreConsumer.Pause(changelogMetadata.StoreMetadata.ChangelogTopicPartition.ToSingle());

                log.LogDebug($"Paused partition {changelogMetadata.StoreMetadata.ChangelogTopicPartition} from the restore consumer");

                // TODO : call trigger restoredEnd
            }
        }
        // internal for testing
        internal bool HasRestoredToEnd(ChangelogMetadata changelogMetadata)
        {
            long?endOffset = changelogMetadata.RestoreEndOffset;

            if (endOffset == null || endOffset == Offset.Unset || endOffset == 0)
            {
                return(true);
            }
            if (!changelogMetadata.BufferedRecords.Any())
            {
                var offset = restoreConsumer.Position(changelogMetadata.StoreMetadata.ChangelogTopicPartition);
                return(offset != Offset.Unset && offset >= endOffset);
            }

            return(changelogMetadata.CurrentOffset >= endOffset);
        }
        public void TestHasRestoredEnd()
        {
            ChangelogMetadata metadata = new ChangelogMetadata
            {
                RestoreEndOffset = null
            };

            Assert.IsTrue(storeChangelogReader.HasRestoredToEnd(metadata));

            metadata = new ChangelogMetadata
            {
                RestoreEndOffset = 0
            };
            Assert.IsTrue(storeChangelogReader.HasRestoredToEnd(metadata));

            metadata = new ChangelogMetadata
            {
                RestoreEndOffset = Offset.Unset
            };
            Assert.IsTrue(storeChangelogReader.HasRestoredToEnd(metadata));

            restoreConsumer.Commit(new List <TopicPartitionOffset>()
            {
                new TopicPartitionOffset(new TopicPartition(changelogTopic, 0), 10)
            });

            metadata = new ChangelogMetadata
            {
                RestoreEndOffset = 10,
                CurrentOffset    = 12,
                BufferedRecords  = new List <ConsumeResult <byte[], byte[]> >(),
                StoreMetadata    = new ProcessorStateManager.StateStoreMetadata()
                {
                    ChangelogTopicPartition = new TopicPartition(changelogTopic, 0)
                }
            };
            Assert.IsTrue(storeChangelogReader.HasRestoredToEnd(metadata));
        }
        public void Register(TopicPartition topicPartition, ProcessorStateManager processorStateManager)
        {
            var storeMetadata = processorStateManager.GetStoreMetadata(topicPartition);

            if (storeMetadata == null)
            {
                throw new StreamsException($"Cannot find the corresponding state store metadata for changelog {topicPartition}");
            }

            var changelogMetadata = new ChangelogMetadata
            {
                StoreMetadata    = storeMetadata,
                StateManager     = processorStateManager,
                ChangelogState   = ChangelogState.REGISTERED,
                RestoreEndOffset = null,
                BeginOffset      = null,
                CurrentOffset    = null,
                TotalRestored    = 0,
                BufferedLimit    = 0,
                BufferedRecords  = new List <ConsumeResult <byte[], byte[]> >()
            };

            changelogs.Add(topicPartition, changelogMetadata);
        }