| 817 | } |
| 818 | |
| 819 | std::pair<int32_t, int32_t> ReplicatedMergeTreeQueue::pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, Coordination::WatchCallbackPtr watch_callback, PullLogsReason reason) |
| 820 | { |
| 821 | std::lock_guard lock(pull_logs_to_queue_mutex); |
| 822 | |
| 823 | if (reason != LOAD && reason != FIX_METADATA_VERSION) |
| 824 | { |
| 825 | /// It's totally ok to load queue on readonly replica (that's what RestartingThread does on initialization). |
| 826 | /// It's ok if replica became readonly due to connection loss after we got current zookeeper (in this case zookeeper must be expired). |
| 827 | /// And it's ok if replica became readonly after shutdown. |
| 828 | /// In other cases it's likely that someone called pullLogsToQueue(...) when queue is not initialized yet by RestartingThread. |
| 829 | bool not_completely_initialized = storage.is_readonly && !zookeeper->expired() && !storage.shutdown_prepared_called; |
| 830 | if (not_completely_initialized) |
| 831 | throw Exception(ErrorCodes::LOGICAL_ERROR, "Tried to pull logs to queue (reason: {}) on readonly replica {}, it's a bug", |
| 832 | reason, storage.getStorageID().getNameForLogs()); |
| 833 | } |
| 834 | |
| 835 | if (pull_log_blocker.isCancelled()) |
| 836 | throw Exception(ErrorCodes::ABORTED, "Log pulling is cancelled"); |
| 837 | |
| 838 | String index_str = zookeeper->get(fs::path(replica_path) / "log_pointer"); |
| 839 | UInt64 index = 0; |
| 840 | |
| 841 | /// The version of "/log" is modified when new entries to merge/mutate/drop appear. |
| 842 | Coordination::Stat stat; |
| 843 | zookeeper->get(fs::path(zookeeper_path) / "log", &stat); |
| 844 | |
| 845 | Strings log_entries = zookeeper->getChildrenWatch( |
| 846 | fs::path(zookeeper_path) / "log", |
| 847 | nullptr, |
| 848 | Coordination::WatchCallbackPtrOrEventPtr{watch_callback, ProfileEvents::ZooKeeperWatchTriggeredReplicatedMergeTreeLog}); |
| 849 | |
| 850 | /// We update mutations after we have loaded the list of log entries, but before we insert them |
| 851 | /// in the queue. |
| 852 | /// With this we ensure that if you read the log state L1 and then the state of mutations M1, |
| 853 | /// then L1 "happened-before" M1. |
| 854 | int32_t mutations_version = updateMutations(zookeeper); |
| 855 | |
| 856 | if (index_str.empty()) |
| 857 | { |
| 858 | /// If we do not already have a pointer to the log, put a pointer to the first entry in it. |
| 859 | index = log_entries.empty() ? 0 : parse<UInt64>(std::min_element(log_entries.begin(), log_entries.end())->substr(strlen("log-"))); |
| 860 | |
| 861 | zookeeper->set(fs::path(replica_path) / "log_pointer", toString(index)); |
| 862 | } |
| 863 | else |
| 864 | { |
| 865 | index = parse<UInt64>(index_str); |
| 866 | } |
| 867 | |
| 868 | String min_log_entry = "log-" + padIndex(index); |
| 869 | |
| 870 | /// Multiple log entries that must be copied to the queue. |
| 871 | |
| 872 | std::erase_if(log_entries, [&min_log_entry](const String & entry) { return entry < min_log_entry; }); |
| 873 | |
| 874 | if (!log_entries.empty()) |
| 875 | { |
| 876 | ::sort(log_entries.begin(), log_entries.end()); |
no test coverage detected