diff --git a/CLAUDE.md b/CLAUDE.md index 7f4fc42f..7bf0ef57 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,6 +13,7 @@ an optional read-only Neo4j backend — selected by the *type* of the `backend=` | Java | `CLDK.java(...)` (needs the `cldk[java]` extra) | `JCodeanalyzer` (the `codeanalyzer-java` 3.2.0 wheel's jar on its bundled JVM, subprocess, `-a 1..4` — no jar in this repo, no JDK download) | `JNeo4jBackend` (graphs emitted by ≥ 3.2.0, probed at attach) | `cldk/models/java/` (schema v2 mirror) + `projections.py` | | Python | `CLDK.python(...)` | `PyCodeanalyzer` (in-process `codeanalyzer-python`) | `PyNeo4jBackend` | re-exported from `codeanalyzer-python` | | TypeScript (+ JavaScript modules) | `CLDK.typescript(...)` | `TSCodeanalyzer` (`codeanalyzer-typescript` 1.6.0 binary from the wheel, subprocess; `-a 1..4`, but `--emit neo4j` takes no `-a` and is always full depth) | `TSNeo4jBackend` (graphs emitted by ≥ 1.5.2; older refused at attach) | `cldk/models/typescript/` (schema v2 mirror) | +| Go | `CLDK.go(...)` | `GoCodeanalyzer` (`cango` binary, subprocess, `-a 1..2` only — no L3/L4; `--analysis-schema 2` mandatory or it emits legacy v1; binary via `$CODEANALYZER_GO_BIN` or the `codeanalyzer-go` wheel — **no release cut yet, so the version pin is deferred**) | `GoNeo4jBackend` (lazy Cypher over the `GO_`-prefixed graph; **not** an eager rebuild — Go graphs reach enterprise scale) | `cldk/models/go/` (schema v2 mirror; **no `projections.py`** — out of SDK3 scope) | **Java, since leg 3a (#310):** the models are an `extra="forbid"` mirror of canonical schema v2, so a 1.x `analysis.json` (and a pre-3.0.1 Neo4j graph) is refused, not parsed; `get_call_graph()` keys diff --git a/README.md b/README.md index 37e952bd..0da03996 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ The three steps below take a Python project from disk to a question about its co analysis_level=AnalysisLevel.call_graph, ) # CLDK.java(...) and CLDK.typescript(...) take the same arguments. + # CLDK.go(...) too — see the Go Quick Start below. ``` 2. Find the callable at a line, and read its source. @@ -141,6 +142,64 @@ analysis = CLDK.python( The reference's [Attach](./docs/agent-api-reference.md#attach) section and the per-language sections after it list the graph versions each backend accepts. They also show how to migrate an older graph. +### Go Quick Start + +Go is CLDK's first **type-centric, class-less** facade. A Go `type` is a `struct` or an +`interface` (never a class), and package-level **functions** are kept distinct from receiver +**methods** — so the Go facade is `get_types` / `get_functions` / `get_methods_of`, not +`get_classes`. Point the facade at a Go **module** (a directory with a `go.mod`): + +```python +from cldk import CLDK +from cldk.analysis import AnalysisLevel + +analysis = CLDK.go( + project_path="/path/to/go/module", + analysis_level=AnalysisLevel.call_graph, # level 1 (symbols) or 2 (+ call graph); Go has no L3/L4 +) + +# Walk the types (structs + interfaces) and their receiver methods. Types, methods and fields are +# addressed by the type's durable `can://` id (a Go type has no unique short name across packages). +for type_id, go_type in analysis.get_types().items(): + print(f"{go_type.kind} {type_id}") + for sig, method in analysis.get_methods_of(type_id).items(): + print(f" method {sig}") + print(analysis.get_source(method.id)) # the method's source, sliced from its module + +# Package-level functions are separate from methods — no "module-as-class" hack: +for sig, fn in analysis.get_functions().items(): + print(f"func {sig}") + +# Who calls whom (full edges with provenance and weight), keyed on the callable id: +call_graph = analysis.get_call_graph() # a networkx.DiGraph +for func_id in analysis.get_functions(): + for edge in analysis.get_callees(func_id): + print(f"{edge.src} -> {edge.dst} ({', '.join(edge.prov)}, weight={edge.weight})") +``` + +Query a pre-populated Go graph **read-only** over Neo4j the same way as the other languages — the +backend is chosen by the config type, and the queries lazily hit the `GO_`-prefixed graph (so it +scales to large enterprise modules without materializing the whole program): + +```python +from cldk import CLDK +from cldk.analysis.commons.backend_config import Neo4jConnectionConfig + +analysis = CLDK.go( + backend=Neo4jConnectionConfig( + uri="bolt://localhost:7687", + application_name="my-go-app", # the --app-name the graph was emitted with + ), +) +types = analysis.get_types() +``` + +> **Go analyzer:** Go is analyzed by the `cango` binary (`codeanalyzer-go`). It emits analysis +> levels 1–2 only; slicing/CFG/CDG/DDG are not produced, so those accessors are absent rather than +> present-and-erroring. Until the analyzer's first release is published, resolve the binary with +> `$CODEANALYZER_GO_BIN` pointing at a local build. + + ## Read Next - [docs/agent-api-reference.md](./docs/agent-api-reference.md) is the full reference. It opens with the four moves most questions decompose into, then lists every accessor with its cost. diff --git a/cldk/analysis/commons/backend_config.py b/cldk/analysis/commons/backend_config.py index 911b3582..f2a3b83b 100644 --- a/cldk/analysis/commons/backend_config.py +++ b/cldk/analysis/commons/backend_config.py @@ -41,7 +41,7 @@ # The canonical sub-directory name each language's artifacts live under inside the shared cache # root. Keyed so that a polyglot repository analyzed under more than one language does not have its # backends overwrite a single shared ``analysis.json``. -_CACHE_KEYS = {"java": "java", "python": "python", "typescript": "typescript"} +_CACHE_KEYS = {"java": "java", "python": "python", "typescript": "typescript", "go": "go"} @dataclass @@ -118,6 +118,10 @@ class Neo4jConnectionConfig: JavaBackend = Union[CodeAnalyzerConfig, Neo4jConnectionConfig] PyBackend = Union[PyCodeAnalyzerConfig, Neo4jConnectionConfig] TSBackend = Union[TSCodeAnalyzerConfig, CodeAnalyzerConfig, Neo4jConnectionConfig] +# Go's cango has no backend-only knob today, so it reuses the base CodeAnalyzerConfig rather than a +# GoCodeAnalyzerConfig subclass -- the same shape as JavaBackend. Add a subclass only if cango later +# grows a knob. +GoBackend = Union[CodeAnalyzerConfig, Neo4jConnectionConfig] def cache_subdir(cache_dir: Union[str, Path, None], project_dir: Union[str, Path, None], language: str) -> Path | None: @@ -127,7 +131,7 @@ def cache_subdir(cache_dir: Union[str, Path, None], project_dir: Union[str, Path cache_dir: The cache root from the backend config. When ``None``, defaults to ``/.codeanalyzer``. project_dir: The project directory, used to derive the default root. - language: The canonical language key (``"java"``, ``"python"``, ``"typescript"``). + language: The canonical language key (``"java"``, ``"python"``, ``"typescript"``, ``"go"``). Returns: ``/`` as an absolute path, or ``None`` if no root can be determined diff --git a/cldk/analysis/go/__init__.py b/cldk/analysis/go/__init__.py new file mode 100644 index 00000000..98989829 --- /dev/null +++ b/cldk/analysis/go/__init__.py @@ -0,0 +1,26 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Go analysis package. + +Exports the public :class:`~cldk.analysis.go.go_analysis.GoAnalysis` facade; the +:class:`~cldk.analysis.go.backend.GoAnalysisBackend` ABC and its two concrete backends live +alongside it. +""" + +from cldk.analysis.go.go_analysis import GoAnalysis + +__all__ = ["GoAnalysis"] diff --git a/cldk/analysis/go/backend.py b/cldk/analysis/go/backend.py new file mode 100644 index 00000000..476ed680 --- /dev/null +++ b/cldk/analysis/go/backend.py @@ -0,0 +1,116 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""The abstract base every Go analysis backend implements. + +:class:`GoAnalysisBackend` parameterises the shared, cross-language +:class:`~cldk.analysis.commons.backend.AnalysisBackend` with Go's pydantic models and sets the +Go graph vocabulary prefixes (``N="Go"`` / ``P="GO_"``). The local +:class:`~cldk.analysis.go.codeanalyzer.codeanalyzer.GoCodeanalyzer` and the read-only +``GoNeo4jBackend`` both subclass it, which is what guarantees the two backends answer the facade +identically — a ``test_go_backend_contract`` asserts every abstract method is implemented on both. + +**Go is the SDK's first class-less, type-centric facade.** The *facade* vocabulary the user sees +is ``get_types`` / ``get_functions`` / ``get_methods_of`` — never ``class``. The class-named slots +on this backend (``get_all_classes`` / ``get_class`` / ``get_all_methods_in_class`` / ``get_method``) +are **inherited from the generic base ABC** and filled only so the backend can instantiate (Python +refuses an abstract class with an unimplemented ``@abstractmethod``); they map ``TypeT=GoType`` +(structs + interfaces) and are never user-facing. The base has no package-level-function slot, so +:meth:`get_functions` is the one type-centric accessor added here. + +**The artifact/dependency/config layer** (the inherited ``get_artifacts`` / ``get_dependencies`` / +``get_config_keys`` / ``get_config_uses`` / ``get_unresolved_config_reads``) is not emitted by a +local L1/L2 ``cango`` run: those app-scope keys are absent from ``analysis.json``. Following the +Java precedent — ``[]`` is reserved for "the pass ran and found nothing" — the local backend +**raises** :data:`ARTIFACT_LAYER_UNAVAILABLE` rather than returning an ambiguous empty; the Neo4j +backend answers them for real from its ``Artifact`` / ``Package`` / ``ConfigKey`` nodes. +""" + +from __future__ import annotations + +from abc import abstractmethod +from typing import ClassVar, Dict, List + +from cldk.analysis.commons.backend import AnalysisBackend +from cldk.models.go import ( + GoApplication, + GoCallable, + GoCallGraphEdge, + GoField, + GoModule, + GoParam, + GoType, +) + +#: Raised by the local backend for the artifact/dependency/config accessors: a local L1/L2 cango +#: analysis does not emit that layer, so an empty result would be indistinguishable from a proved +#: absence. Both the message and the raise mirror codeanalyzer-java's unavailable-overlay pattern. +#: ``{app}`` is the application anchor name, never a can:// id. +ARTIFACT_LAYER_UNAVAILABLE = ( + "the artifact/dependency/config layer is not emitted by a local codeanalyzer-go L1/L2 analysis " + "of {app!r}; query the Neo4j backend, which carries it" +) + + +class GoAnalysisBackend(AnalysisBackend[GoApplication, GoModule, GoType, GoCallable, GoField, GoParam]): + """Abstract base every Go analysis backend implements (see the module docstring). + + Inherited abstract (from :class:`~cldk.analysis.commons.backend.AnalysisBackend`): + ``get_application_view``, ``get_symbol_table``, ``get_call_graph``, ``get_all_classes``, + ``get_class``, ``get_all_methods_in_class``, ``get_method``, ``get_all_fields``, + ``get_method_parameters``, ``get_artifacts``, ``get_dependencies``, ``get_config_keys``, + ``get_config_uses``, ``get_unresolved_config_reads``. + """ + + P: ClassVar[str] = "GO_" + N: ClassVar[str] = "Go" + + # -----[ type-centric leaf accessors the base ABC does not cover ]----- + @abstractmethod + def get_functions(self) -> Dict[str, GoCallable]: + """Every package-level function, keyed by signature. + + Go keeps package-level functions distinct from receiver methods (no module-as-class hack), + and the generic base ABC has no slot for them — ``get_all_methods_in_class`` covers only a + type's methods. This is the facade's ``get_functions``. + """ + + # -----[ call graph (keyed on the callable id, SDK2) ]----- + @abstractmethod + def get_callers(self, callable_id: str) -> List[GoCallGraphEdge]: + """Every call-graph edge whose ``dst`` is ``callable_id`` — the callables that call it. + + Returns the full :class:`~cldk.models.go.GoCallGraphEdge` (``src``/``dst``/``prov``/ + ``weight``), losslessly: the caller ids are ``[e.src for e in result]``, and ``prov``/ + ``weight`` stay available. Both backends supply the same shape (the Neo4j ``GO_CALLS`` + relationship carries ``weight``/``prov`` as properties). + """ + + @abstractmethod + def get_callees(self, callable_id: str) -> List[GoCallGraphEdge]: + """Every call-graph edge whose ``src`` is ``callable_id`` — the callables it calls. + + Full edges, as :meth:`get_callers`; the callee ids are ``[e.dst for e in result]``. + """ + + # -----[ source + whole-program ]----- + @abstractmethod + def get_source(self, node_id: str) -> str: + """The source text of the node ``node_id`` — ``module.source[node.span.bytes]``.""" + + @abstractmethod + def get_call_graph_json(self) -> str: + """The application's call graph as a JSON string (Tier A).""" diff --git a/cldk/analysis/go/codeanalyzer/__init__.py b/cldk/analysis/go/codeanalyzer/__init__.py new file mode 100644 index 00000000..e47c0248 --- /dev/null +++ b/cldk/analysis/go/codeanalyzer/__init__.py @@ -0,0 +1,21 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""The local (in-process subprocess) Go analysis backend.""" + +from cldk.analysis.go.codeanalyzer.codeanalyzer import GoCodeanalyzer + +__all__ = ["GoCodeanalyzer"] diff --git a/cldk/analysis/go/codeanalyzer/codeanalyzer.py b/cldk/analysis/go/codeanalyzer/codeanalyzer.py new file mode 100644 index 00000000..fd14e975 --- /dev/null +++ b/cldk/analysis/go/codeanalyzer/codeanalyzer.py @@ -0,0 +1,326 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""The local Go analysis backend: run ``cango`` as a subprocess, read the ``analysis.json`` v2 +envelope, validate it into :class:`~cldk.models.go.GoAnalysis`, and answer every +:class:`~cldk.analysis.go.backend.GoAnalysisBackend` method from the in-memory tree. + +The binary ships (post-release) with the ``codeanalyzer-go`` PyPI dependency; ``$CODEANALYZER_GO_BIN`` +is the out-of-band override used today, before the wheel is pinned. +""" + +from __future__ import annotations + +import json +import os +import shlex +import subprocess +from pathlib import Path +from subprocess import CompletedProcess +from typing import Dict, List, Union + +import networkx as nx + +from cldk.analysis.commons.backend_config import cache_subdir +from cldk.analysis.commons.levels import analyzer_level +from cldk.analysis.go.backend import ARTIFACT_LAYER_UNAVAILABLE, GoAnalysisBackend +from cldk.models.go import ( + GoAnalysis, + GoApplication, + GoCallable, + GoCallGraphEdge, + GoField, + GoModule, + GoParam, + GoType, +) +from cldk.models.python import PyArtifact, PyConfigKey, PyConfigRead, PyConfigUseEdge, PyDependency +from cldk.utils.exceptions.exceptions import CodeanalyzerExecutionException + +class GoCodeanalyzer(GoAnalysisBackend): + """Run ``cango`` and answer queries over the parsed ``analysis.json``. + + Attributes: + analysis: The whole ``analysis.json`` v2 envelope (``schema_version``, ``max_level``, + ``analyzer``, ``application``). + application: The application root — symbol table + call graph. + application_name: The ``--app-name`` anchor the analyzer stamped into every id, read back + off the ``can://`` root id. Spelled the same as the Neo4j backend's + ``application_name`` so a raised message names the application identically whichever + backend raised it. + """ + + def __init__( + self, + project_dir: Union[str, Path], + cache_dir: Union[str, Path, None], + analysis_level: str, + eager_analysis: bool, + target_files: List[str] | None, + ) -> None: + self.project_dir = project_dir + self.cache_dir = cache_dir + self.analysis_level = analysis_level + self.eager_analysis = eager_analysis + self.target_files = target_files + # analyzer_level maps the AnalysisLevel enum/name/value to its integer, raising on an + # unknown level rather than silently defaulting to 1 (the bug a hand-rolled dict caused). + # cango has no L3/L4, so _argv caps the result at 2. + self.analysis: GoAnalysis = self._init_codeanalyzer(analyzer_level(analysis_level)) + self.application: GoApplication = self.analysis.application + self.application_name: str = self.application.id.removeprefix("can://") + self._call_graph: nx.DiGraph | None = None + self._index() + + # -----[ binary resolution ]----- + def _get_codeanalyzer_exec(self) -> List[str]: + """Resolve the ``cango`` executable command. + + ``$CODEANALYZER_GO_BIN`` is the out-of-band override (a locally built binary); otherwise the + binary ships inside the ``codeanalyzer-go`` PyPI package (platform wheel), mirroring the TS + backend. The wheel arm lights up once the analyzer release is pinned. + """ + env_bin = os.environ.get("CODEANALYZER_GO_BIN") + if env_bin: + return shlex.split(env_bin) + try: + import codeanalyzer_go # type: ignore[import-not-found] + + return [str(codeanalyzer_go.bin_path())] + except (ModuleNotFoundError, FileNotFoundError, AttributeError) as e: + raise CodeanalyzerExecutionException( + "codeanalyzer-go binary not found: $CODEANALYZER_GO_BIN is unset and the " + f"`codeanalyzer-go` wheel is not importable or carries no binary for this platform ({e}). " + "Install it with `pip install codeanalyzer-go`, or set $CODEANALYZER_GO_BIN." + ) from e + + def _argv(self, analysis_level: int, output_dir: Path | None) -> List[str]: + """The cango command line: ``-i --app-name -a <1|2> + [-o --cache-dir ] --skip-tests [--eager] [-t ]...``. + + . The level is capped at 2 — cango produces no L3/L4. + """ + project = Path(self.project_dir) + args = self._get_codeanalyzer_exec() + [ + "-i", + str(project), + "--app-name", + project.name, + "-a", + str(min(analysis_level, 2)), + ] + if output_dir is not None: + args += ["-o", str(output_dir), "--cache-dir", str(output_dir)] + args += ["--skip-tests"] + if self.eager_analysis: + args += ["--eager"] + for tf in self.target_files or []: + args += ["-t", str(tf).strip()] + return args + + @staticmethod + def _cached_level(analysis_json_file: Path) -> int | None: + """The ``max_level`` of a cached ``analysis.json``, or ``None`` if it is absent, unreadable, + or malformed — in which case the caller re-runs rather than trusting it.""" + try: + data = json.loads(analysis_json_file.read_text(encoding="utf-8")) + level = data.get("max_level") + return int(level) if isinstance(level, int) else None + except (OSError, ValueError, json.JSONDecodeError): + return None + + def _init_codeanalyzer(self, analysis_level: int) -> GoAnalysis: + """Run cango and return the validated v2 envelope. + + With no cache directory the output is read from the subprocess stdout pipe; with one, the + analysis.json is persisted under ``/go`` and reused unless ``eager_analysis``, + ``target_files``, or a cache **shallower than the requested level** force a re-run. A cache + at or above the requested level is reused as-is: the schema is additive (``L1 ⊆ L2``), so a + deeper cache already contains everything a shallower request needs. + """ + output_dir = cache_subdir(self.cache_dir, self.project_dir, "go") + if output_dir is None: + args = self._argv(analysis_level, None) + try: + proc: CompletedProcess[str] = subprocess.run(args, capture_output=True, text=True, check=True) + except subprocess.CalledProcessError as e: + raise CodeanalyzerExecutionException(f"cango failed: {e.stderr or e}") from e + return GoAnalysis.model_validate_json(proc.stdout) + + output_dir.mkdir(parents=True, exist_ok=True) + analysis_json_file = output_dir / "analysis.json" + # cango caps at 2, so a request above 2 is satisfied by a level-2 cache (never re-runs for a + # level it cannot produce). Compare the requested level, capped, against the cached level. + requested = min(analysis_level, 2) + cached_level = self._cached_level(analysis_json_file) if analysis_json_file.exists() else None + stale = cached_level is None or cached_level < requested + needs_run = self.eager_analysis or bool(self.target_files) or stale + if needs_run: + args = self._argv(analysis_level, output_dir) + try: + subprocess.run(args, capture_output=True, text=True, check=True) + except subprocess.CalledProcessError as e: + raise CodeanalyzerExecutionException(f"cango failed: {e.stderr or e}") from e + if not analysis_json_file.exists(): + raise CodeanalyzerExecutionException("cango did not generate analysis.json.") + return GoAnalysis.model_validate_json(analysis_json_file.read_text(encoding="utf-8")) + + # -----[ indexing ]----- + def _index(self) -> None: + """Flatten the symbol table into lookups and an id → (module, node) index for source + slicing. Types are keyed by their durable ``id`` (a Go ``type`` carries no ``signature``, + and its ``types{}`` map key is a bare short name that is not unique across packages); + callables by their ``signature``. Go's call-graph endpoints are full ``can://`` ids that + are themselves the callable ids, so no id → key indirection is needed (unlike TS).""" + self._types: Dict[str, GoType] = {} # type id -> type + self._functions: Dict[str, GoCallable] = {} # signature -> function + self._methods_by_type: Dict[str, Dict[str, GoCallable]] = {} # type id -> {method sig -> method} + self._callables: Dict[str, GoCallable] = {} # id -> callable + self._file_of: Dict[str, str] = {} # type id / callable signature -> module file path + #: durable node id -> (owning module, node) for get_source slicing. + self._source_of: Dict[str, tuple] = {} + #: owning module id -> its source encoded UTF-8 once, for a non-ASCII module (byte-offset + #: slicing); ASCII modules are absent and indexed directly. Encoded lazily in get_source. + self._module_bytes: Dict[str, bytes] = {} + + for fp, mod in self.application.symbol_table.items(): + for fn in mod.functions.values(): + self._functions[fn.signature] = fn + self._add_callable(fn, mod, fp) + for ty in mod.types.values(): + self._types[ty.id] = ty + self._file_of[ty.id] = fp + self._source_of[ty.id] = (mod, ty) + methods: Dict[str, GoCallable] = {} + for m in ty.callables.values(): + self._add_callable(m, mod, fp) + methods[m.signature] = m + self._methods_by_type[ty.id] = methods + for fld in ty.fields.values(): + self._source_of[fld.id] = (mod, fld) + + def _add_callable(self, c: GoCallable, mod: GoModule, fp: str) -> None: + self._callables[c.id] = c + self._file_of[c.signature] = fp + self._source_of[c.id] = (mod, c) + for inner in c.callables.values(): + self._add_callable(inner, mod, fp) + + # -----[ application / whole-program ]----- + def get_application_view(self) -> GoApplication: + return self.application + + def get_symbol_table(self) -> Dict[str, GoModule]: + return self.application.symbol_table + + # -----[ call graph ]----- + def get_call_graph(self) -> nx.DiGraph: + """The application's call graph as a NetworkX DiGraph: nodes keyed by ``can://`` callable + id, edges carrying ``weight`` and ``provenance`` (as the sibling backends do).""" + if self._call_graph is not None: + return self._call_graph + graph = nx.DiGraph() + for edge in self.application.call_graph: + graph.add_node(edge.src, id=edge.src) + graph.add_node(edge.dst, id=edge.dst) + graph.add_edge(edge.src, edge.dst, type="CALL_DEP", weight=edge.weight, provenance=tuple(edge.prov)) + self._call_graph = graph + return graph + + def get_call_graph_json(self) -> str: + return self.application.model_dump_json() + + def get_callers(self, callable_id: str) -> List[GoCallGraphEdge]: + """Every call-graph edge whose ``dst`` is ``callable_id`` — the callables that call it.""" + return [e for e in self.application.call_graph if e.dst == callable_id] + + def get_callees(self, callable_id: str) -> List[GoCallGraphEdge]: + """Every call-graph edge whose ``src`` is ``callable_id`` — the callables it calls.""" + return [e for e in self.application.call_graph if e.src == callable_id] + + # -----[ types (class-named base slots; Go has no class — see backend module docstring) ]----- + def get_all_classes(self) -> Dict[str, GoType]: + """Every Go type (struct + interface), keyed by its durable ``can://`` id. The base ABC's + class-named slot; the facade exposes this as ``get_types``.""" + return self._types + + def get_class(self, qualified_class_name: str) -> GoType | None: + return self._types.get(qualified_class_name) + + def get_all_methods_in_class(self, qualified_class_name: str) -> Dict[str, GoCallable]: + """The receiver methods of a type, keyed by signature. The facade exposes this as + ``get_methods_of``.""" + return self._methods_by_type.get(qualified_class_name, {}) + + def get_method(self, qualified_class_name: str, qualified_method_name: str) -> GoCallable | None: + return self._methods_by_type.get(qualified_class_name, {}).get(qualified_method_name) + + def get_all_fields(self, qualified_class_name: str) -> List[GoField]: + ty = self._types.get(qualified_class_name) + return list(ty.fields.values()) if ty else [] + + def get_method_parameters(self, qualified_class_name: str, qualified_method_name: str) -> List[GoParam]: + m = self.get_method(qualified_class_name, qualified_method_name) + return list(m.parameters) if m else [] + + # -----[ functions + source ]----- + def get_functions(self) -> Dict[str, GoCallable]: + return self._functions + + def get_source(self, node_id: str) -> str: + """The source text of ``node_id`` — ``module.source`` sliced by ``node.span.bytes`` — for a + durable-id node (type, callable, field). + + ``span.bytes`` are **UTF-8 byte** offsets, so a non-ASCII module is sliced on the encoded + bytes and decoded back (an ASCII module is indexed directly, paying nothing), the same way + the TS ``_Spanned.code`` property does. Slicing the ``str`` by character index would drift + on any file with a multibyte character before the span. + """ + entry = self._source_of.get(node_id) + if entry is None: + raise CodeanalyzerExecutionException( + f"no source for {node_id!r}: not a type, callable or field of application " + f"{self.application_name!r}" + ) + mod, node = entry + if node.span is None: + raise CodeanalyzerExecutionException(f"node {node_id!r} carries no span, so it has no source text") + b0, b1 = node.span.bytes + if mod.source.isascii(): + return mod.source[b0:b1] + encoded = self._module_bytes.get(mod.id) + if encoded is None: + encoded = mod.source.encode("utf-8") + self._module_bytes[mod.id] = encoded + return encoded[b0:b1].decode("utf-8") + + # -----[ artifact / dependency / config layer — not emitted by a local L1/L2 analysis ]----- + def get_artifacts(self) -> Dict[str, PyArtifact]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_dependencies( + self, *, direct_only: bool = False, ecosystem: str | None = None, declared_in: str | None = None + ) -> List[PyDependency]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_config_keys(self) -> Dict[str, PyConfigKey]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_config_uses(self, key: str | None = None) -> List[PyConfigUseEdge]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_unresolved_config_reads(self) -> List[PyConfigRead]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) diff --git a/cldk/analysis/go/go_analysis.py b/cldk/analysis/go/go_analysis.py new file mode 100644 index 00000000..5575be9c --- /dev/null +++ b/cldk/analysis/go/go_analysis.py @@ -0,0 +1,155 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Go analysis facade. + +Thin, read-only query layer over the canonical ``GoApplication`` a Go backend produces. Mirrors +the lifecycle vocabulary of the sibling facades (there is no shared base class — the facades match +by convention) and delegates all indexing and query work to its backend. + +**Go is the SDK's first class-less, type-centric facade.** The public surface speaks ``get_types`` +/ ``get_functions`` / ``get_methods_of`` — never ``class``. These forward to the backend's +inherited class-named slots (``get_all_classes`` / ``get_all_methods_in_class``), which exist only +to satisfy the shared ABC; the lie never reaches a caller. Package-level **functions** are kept +distinct from receiver **methods** (no module-as-class hack). Types, their methods and their fields +are addressed by the type's durable ``can://`` id (a Go type has no unique short name across +packages); callers/callees by the callable's ``can://`` id. +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Dict, List + +import networkx as nx + +from cldk.analysis.commons.backend_config import CodeAnalyzerConfig, GoBackend, Neo4jConnectionConfig +from cldk.analysis.go.backend import GoAnalysisBackend +from cldk.analysis.go.codeanalyzer import GoCodeanalyzer +from cldk.analysis.go.neo4j import GoNeo4jBackend +from cldk.models.go import GoApplication, GoCallable, GoCallGraphEdge, GoField, GoModule, GoParam, GoType + + +class GoAnalysis: + """Analysis facade for Go projects. + + Delegates every query to a backend. Two interchangeable backends exist, both exposing the same + method surface: + + * :class:`~cldk.analysis.go.codeanalyzer.codeanalyzer.GoCodeanalyzer` (default) — runs ``cango`` + and walks the in-memory ``GoApplication`` parsed from ``analysis.json``; + * :class:`~cldk.analysis.go.neo4j.GoNeo4jBackend` — answers the *same* queries with lazy Cypher + over the ``GO_``-prefixed graph a ``cango --emit neo4j`` run populated. Selected by passing a + :class:`~cldk.analysis.commons.backend_config.Neo4jConnectionConfig`. + """ + + def __init__( + self, + project_dir: str | Path | None, + analysis_level: str, + target_files: List[str] | None, + eager_analysis: bool, + backend: GoBackend | None = None, + ) -> None: + self.project_dir = project_dir + self.analysis_level = analysis_level + self.target_files = target_files + self.eager_analysis = eager_analysis + # The backend is selected by the *type* of the config: Neo4jConnectionConfig picks the + # read-only Cypher backend, CodeAnalyzerConfig (the default) the in-process cango. + self.backend_config: GoBackend = backend if backend is not None else CodeAnalyzerConfig() + self.backend: GoAnalysisBackend + if isinstance(self.backend_config, Neo4jConnectionConfig): + cfg = self.backend_config + application_name = cfg.application_name or (Path(project_dir).name if project_dir else None) + self.backend = GoNeo4jBackend( + neo4j_uri=cfg.uri, + neo4j_username=cfg.username, + neo4j_password=cfg.password, + neo4j_database=cfg.database, + application_name=application_name, + ) + else: + self.backend = GoCodeanalyzer( + project_dir=project_dir, + cache_dir=self.backend_config.cache_dir, + analysis_level=analysis_level, + eager_analysis=eager_analysis, + target_files=target_files, + ) + self.application: GoApplication = self.backend.get_application_view() + + # -----[ Tier A: lifecycle / whole-program ]----- + def get_application_view(self) -> GoApplication: + """The whole application view (symbol table + call graph).""" + return self.backend.get_application_view() + + def get_symbol_table(self) -> Dict[str, GoModule]: + """The per-file symbol table, keyed by module file path.""" + return self.backend.get_symbol_table() + + def get_call_graph(self) -> nx.DiGraph: + """NetworkX DiGraph of the application's call edges, nodes keyed by ``can://`` callable id, + edges carrying ``weight`` and ``provenance``.""" + return self.backend.get_call_graph() + + def get_call_graph_json(self) -> str: + """The application's call graph as a JSON string.""" + return self.backend.get_call_graph_json() + + def get_callers(self, callable_id: str) -> List[GoCallGraphEdge]: + """The call-graph edges whose ``dst`` is ``callable_id`` — the callables that call it. Full + edges (``src``/``dst``/``prov``/``weight``); caller ids are ``[e.src for e in result]``.""" + return self.backend.get_callers(callable_id) + + def get_callees(self, callable_id: str) -> List[GoCallGraphEdge]: + """The call-graph edges whose ``src`` is ``callable_id`` — the callables it calls.""" + return self.backend.get_callees(callable_id) + + # -----[ type-centric navigation (SDK1: structs + interfaces, never "class") ]----- + def get_types(self) -> Dict[str, GoType]: + """Every Go type (struct + interface), keyed by its durable ``can://`` id.""" + return self.backend.get_all_classes() + + def get_type(self, type_id: str) -> GoType | None: + """A single type by its ``can://`` id.""" + return self.backend.get_class(type_id) + + def get_functions(self) -> Dict[str, GoCallable]: + """Every package-level function, keyed by signature (kept distinct from receiver methods).""" + return self.backend.get_functions() + + def get_methods_of(self, type_id: str) -> Dict[str, GoCallable]: + """The receiver methods of the type ``type_id``, keyed by signature.""" + return self.backend.get_all_methods_in_class(type_id) + + def get_method(self, type_id: str, method_signature: str) -> GoCallable | None: + """A single receiver method of ``type_id`` by its signature.""" + return self.backend.get_method(type_id, method_signature) + + def get_fields(self, type_id: str) -> List[GoField]: + """The fields of the type ``type_id``.""" + return self.backend.get_all_fields(type_id) + + def get_method_parameters(self, type_id: str, method_signature: str) -> List[GoParam]: + """The parameters of a receiver method of ``type_id``.""" + return self.backend.get_method_parameters(type_id, method_signature) + + # -----[ source ]----- + def get_source(self, node_id: str) -> str: + """The source text of ``node_id`` (a type, callable or field) — its span sliced from the + owning module's source.""" + return self.backend.get_source(node_id) diff --git a/cldk/analysis/go/neo4j/__init__.py b/cldk/analysis/go/neo4j/__init__.py new file mode 100644 index 00000000..d3b58164 --- /dev/null +++ b/cldk/analysis/go/neo4j/__init__.py @@ -0,0 +1,21 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""The read-only Neo4j Go analysis backend.""" + +from cldk.analysis.go.neo4j.neo4j_backend import GoNeo4jBackend + +__all__ = ["GoNeo4jBackend"] diff --git a/cldk/analysis/go/neo4j/config.py b/cldk/analysis/go/neo4j/config.py new file mode 100644 index 00000000..a4fda92b --- /dev/null +++ b/cldk/analysis/go/neo4j/config.py @@ -0,0 +1,27 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Connection settings for the read-only Neo4j-backed Go analysis backend. + +The definition lives in :mod:`cldk.analysis.commons.backend_config`; it is re-exported here for +symmetry with the sibling language backends. +""" + +from __future__ import annotations + +from cldk.analysis.commons.backend_config import Neo4jConnectionConfig + +__all__ = ["Neo4jConnectionConfig"] diff --git a/cldk/analysis/go/neo4j/neo4j_backend.py b/cldk/analysis/go/neo4j/neo4j_backend.py new file mode 100644 index 00000000..4a5068f3 --- /dev/null +++ b/cldk/analysis/go/neo4j/neo4j_backend.py @@ -0,0 +1,343 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""The read-only Neo4j Go analysis backend. + +A drop-in alternative to :class:`~cldk.analysis.go.codeanalyzer.codeanalyzer.GoCodeanalyzer`: the +same :class:`~cldk.analysis.go.backend.GoAnalysisBackend` query surface, answered over the +``GO_``-prefixed graph a ``cango --emit neo4j`` run populated out of band. + +**Lazy per-subtree fetch, not an eager rebuild.** Go graphs reach enterprise scale (a 525k-node, +156k-edge graph was measured on a real app), so this backend never materialises the whole +application: each accessor issues one *indexed* Cypher query touching only what it needs +(``get_callers(id)`` is a single ``GO_CALLS`` lookup, ``get_class(id)`` one node). Parity with the +local backend is therefore proven by test (the two answer the same question by different paths), +not guaranteed by sharing a rebuilt ``GoApplication``. + +Every query is scoped to one application by the id prefix ``can:///`` (and the ``:GoCanNode`` +marker label the projector stamps), so a shared graph holding several applications or a +sibling-language subgraph is never scanned into an answer. + +The artifact/dependency/config six **raise** here, exactly as on the local backend: the Go neo4j +projector declares ``:Artifact`` / ``:Package`` / ``:ConfigKey`` in its schema but does not yet +emit them, so the layer is reserved-but-empty on this projection too. When the projector starts +emitting it, those six flip to querying the graph for real. +""" + +from __future__ import annotations + +import json +from functools import cached_property +from typing import Any, Dict, List + +import networkx as nx + +from cldk.analysis.go.backend import ARTIFACT_LAYER_UNAVAILABLE, GoAnalysisBackend +from cldk.analysis.go.neo4j import reconstruct as R +from cldk.models.go import GoApplication, GoCallable, GoCallGraphEdge, GoField, GoModule, GoParam, GoType +from cldk.models.python import PyArtifact, PyConfigKey, PyConfigRead, PyConfigUseEdge, PyDependency +from cldk.utils.exceptions.exceptions import CodeanalyzerExecutionException + +#: Relationship types a Go graph must carry to be a codeanalyzer-go projection at all. +_REQUIRED_RELS = {"GO_HAS_MODULE", "GO_DECLARES", "GO_HAS_METHOD", "GO_CALLS"} + + +class GoNeo4jBackend(GoAnalysisBackend): + """Answer the Go query surface over a ``GO_``-prefixed Neo4j graph (see the module docstring).""" + + def __init__( + self, + neo4j_uri: str, + neo4j_username: str, + neo4j_password: str, + neo4j_database: str | None = None, + application_name: str | None = None, + ) -> None: + try: + from neo4j import GraphDatabase + except ModuleNotFoundError as e: # pragma: no cover - import guard + raise CodeanalyzerExecutionException( + "The Neo4j backend requires the 'neo4j' driver. Install it with `pip install neo4j` " + "(or `pip install cldk[neo4j]`)." + ) from e + self._init_with_driver( + GraphDatabase.driver(neo4j_uri, auth=(neo4j_username, neo4j_password)), + application_name=application_name, + neo4j_database=neo4j_database, + ) + + @classmethod + def _from_driver(cls, driver: Any, *, application_name: str | None = None, neo4j_database: str | None = None) -> "GoNeo4jBackend": + """Construct from an already-built driver — the seam tests inject a fake/real driver here.""" + self = cls.__new__(cls) + self._init_with_driver(driver, application_name=application_name, neo4j_database=neo4j_database) + return self + + def _init_with_driver(self, driver: Any, *, application_name: str | None, neo4j_database: str | None) -> None: + if not application_name: + raise CodeanalyzerExecutionException("application_name is required to scope queries to an application.") + self.application_name = application_name + self._database = neo4j_database + self._driver = driver + self._session_obj: Any | None = None + self._call_graph: nx.DiGraph | None = None + self._probe_schema() + + # -----[ scope + execution ]----- + @property + def _app_id(self) -> str: + """``can://`` — the ``:GoApplication`` anchor id and the root of every id below it.""" + return f"can://{self.application_name}" + + @property + def _scope_prefix(self) -> str: + """``can:///`` — the trailing slash keeps ``app`` from matching ``app-b``.""" + return f"{self._app_id}/" + + def _run(self, query: str, **params: Any) -> List[Dict[str, Any]]: + """Run one read statement over a reused session; drop the session on failure.""" + if self._session_obj is None: + self._session_obj = self._driver.session(database=self._database) + try: + return [record.data() for record in self._session_obj.run(query, **params)] + except Exception: + self._session_obj = None + raise + + def close(self) -> None: + if self._session_obj is not None: + self._session_obj = None + self._driver.close() + + def __enter__(self) -> "GoNeo4jBackend": + return self + + def __exit__(self, *exc: Any) -> None: + self.close() + + def _probe_schema(self) -> None: + """Refuse a graph that is not a codeanalyzer-go projection or holds no node for this app.""" + found = {r["relationshipType"] for r in self._run("CALL db.relationshipTypes()")} + missing = _REQUIRED_RELS - found + if missing: + raise CodeanalyzerExecutionException( + f"the graph is not a codeanalyzer-go projection: missing relationship types {sorted(missing)}" + ) + rows = self._run("OPTIONAL MATCH (a:GoApplication {id: $app_id}) RETURN count(a) AS n", app_id=self._app_id) + if not (rows and rows[0].get("n")): + raise CodeanalyzerExecutionException( + f"the graph holds no :GoApplication node for {self.application_name!r} (id {self._app_id!r})" + ) + + # -----[ application / whole-program ]----- + def get_application_view(self) -> GoApplication: + """The whole application view. Whole-program accessors touch everything by nature; this + builds the symbol table from the containment relationships and the call graph from + ``GO_CALLS``, cached.""" + return self._application + + @cached_property + def _application(self) -> GoApplication: + return GoApplication(id=self._app_id, symbol_table=self._build_symbol_table(), call_graph=self._call_edges()) + + def get_symbol_table(self) -> Dict[str, GoModule]: + return self.get_application_view().symbol_table + + def _module_file_key(self, module_id: str) -> str: + """``can:///`` → ```` (the symbol-table key the local backend uses).""" + return module_id[len(self._scope_prefix):] + + def _build_symbol_table(self) -> Dict[str, GoModule]: + """Rebuild every module with its types, functions, methods and fields from the containment + relationships. This is the expensive whole-program path; point accessors below never call + it.""" + modules: Dict[str, GoModule] = {} + mod_rows = self._run( + "MATCH (:GoApplication {id: $app_id})-[:GO_HAS_MODULE]->(m:GoModule) RETURN properties(m) AS p ORDER BY m.id", + app_id=self._app_id, + ) + for mr in mod_rows: + mod = R.module(mr["p"]) + # package-level functions + for fr in self._run( + "MATCH (m:GoModule {id: $mid})-[:GO_DECLARES]->(c:GoCallable) RETURN properties(c) AS p ORDER BY c.signature", + mid=mod.id, + ): + fn = R.callable_(fr["p"]) + mod.functions[fn.signature] = fn + # types + their methods and fields + for tr in self._run( + "MATCH (m:GoModule {id: $mid})-[:GO_DECLARES]->(t:GoType) RETURN properties(t) AS p ORDER BY t.id", + mid=mod.id, + ): + ty = R.go_type(tr["p"]) + for mrow in self._run( + "MATCH (t:GoType {id: $tid})-[:GO_HAS_METHOD]->(c:GoCallable) RETURN properties(c) AS p ORDER BY c.signature", + tid=ty.id, + ): + m = R.callable_(mrow["p"]) + ty.callables[m.signature] = m + for frow in self._run( + "MATCH (t:GoType {id: $tid})-[:GO_HAS_FIELD]->(f:GoField) RETURN properties(f) AS p ORDER BY f.id", + tid=ty.id, + ): + fld = R.field(frow["p"]) + ty.fields[fld.id.split("/")[-1]] = fld + # keyed by the short name, as in the JSON types{} map + mod.types[ty.id.split("/")[-1]] = ty + modules[self._module_file_key(mod.id)] = mod + return modules + + # -----[ call graph ]----- + def _call_edges(self) -> List[GoCallGraphEdge]: + rows = self._run( + "MATCH (a:GoCallable)-[r:GO_CALLS]->(b) WHERE a.id STARTS WITH $pre " + "RETURN a.id AS src, b.id AS dst, properties(r) AS p ORDER BY a.id, b.id", + pre=self._scope_prefix, + ) + return [ + GoCallGraphEdge(src=r["src"], dst=r["dst"], prov=list(r["p"].get("prov") or []), weight=r["p"].get("weight", 1)) + for r in rows + ] + + def get_call_graph(self) -> nx.DiGraph: + if self._call_graph is not None: + return self._call_graph + graph = nx.DiGraph() + for e in self._call_edges(): + graph.add_node(e.src, id=e.src) + graph.add_node(e.dst, id=e.dst) + graph.add_edge(e.src, e.dst, type="CALL_DEP", weight=e.weight, provenance=tuple(e.prov)) + self._call_graph = graph + return graph + + def get_call_graph_json(self) -> str: + return self.get_application_view().model_dump_json() + + def get_callers(self, callable_id: str) -> List[GoCallGraphEdge]: + """Indexed point query: ``(a)-[:GO_CALLS]->(callable_id)``. No whole-graph materialisation.""" + rows = self._run( + "MATCH (a:GoCallable)-[r:GO_CALLS]->(b:GoCallable {id: $id}) RETURN a.id AS src, properties(r) AS p ORDER BY a.id", + id=callable_id, + ) + return [GoCallGraphEdge(src=r["src"], dst=callable_id, prov=list(r["p"].get("prov") or []), weight=r["p"].get("weight", 1)) for r in rows] + + def get_callees(self, callable_id: str) -> List[GoCallGraphEdge]: + rows = self._run( + "MATCH (a:GoCallable {id: $id})-[r:GO_CALLS]->(b) RETURN b.id AS dst, properties(r) AS p ORDER BY b.id", + id=callable_id, + ) + return [GoCallGraphEdge(src=callable_id, dst=r["dst"], prov=list(r["p"].get("prov") or []), weight=r["p"].get("weight", 1)) for r in rows] + + # -----[ types (class-named base slots; Go has no class) ]----- + def get_all_classes(self) -> Dict[str, GoType]: + """Every Go type (struct + interface), keyed by its durable ``can://`` id. One indexed scan + of ``:GoType`` nodes under the app prefix; methods/fields are not attached here (that is the + whole-program rebuild) — use :meth:`get_all_methods_in_class` / :meth:`get_all_fields`.""" + rows = self._run( + "MATCH (t:GoType) WHERE t.id STARTS WITH $pre RETURN properties(t) AS p ORDER BY t.id", + pre=self._scope_prefix, + ) + return {r["p"]["id"]: R.go_type(r["p"]) for r in rows} + + def get_class(self, qualified_class_name: str) -> GoType | None: + rows = self._run("MATCH (t:GoType {id: $id}) RETURN properties(t) AS p", id=qualified_class_name) + return R.go_type(rows[0]["p"]) if rows else None + + def get_all_methods_in_class(self, qualified_class_name: str) -> Dict[str, GoCallable]: + rows = self._run( + "MATCH (t:GoType {id: $id})-[:GO_HAS_METHOD]->(c:GoCallable) RETURN properties(c) AS p ORDER BY c.signature", + id=qualified_class_name, + ) + return {r["p"]["signature"]: R.callable_(r["p"]) for r in rows} + + def get_method(self, qualified_class_name: str, qualified_method_name: str) -> GoCallable | None: + rows = self._run( + "MATCH (t:GoType {id: $id})-[:GO_HAS_METHOD]->(c:GoCallable {signature: $sig}) RETURN properties(c) AS p", + id=qualified_class_name, + sig=qualified_method_name, + ) + return R.callable_(rows[0]["p"]) if rows else None + + def get_all_fields(self, qualified_class_name: str) -> List[GoField]: + rows = self._run( + "MATCH (t:GoType {id: $id})-[:GO_HAS_FIELD]->(f:GoField) RETURN properties(f) AS p ORDER BY f.id", + id=qualified_class_name, + ) + return [R.field(r["p"]) for r in rows] + + def get_method_parameters(self, qualified_class_name: str, qualified_method_name: str) -> List[GoParam]: + # :GoCallable does not project parameters; a reconstructed method carries none. Returning + # [] keeps parity with "the graph does not carry this", not an invented answer. + m = self.get_method(qualified_class_name, qualified_method_name) + return list(m.parameters) if m else [] + + # -----[ functions + source ]----- + def get_functions(self) -> Dict[str, GoCallable]: + """Every package-level function (``:GoModule``-[:GO_DECLARES]->(:GoCallable)``), keyed by + signature.""" + rows = self._run( + "MATCH (m:GoModule)-[:GO_DECLARES]->(c:GoCallable) WHERE m.id STARTS WITH $pre " + "RETURN properties(c) AS p ORDER BY c.signature", + pre=self._scope_prefix, + ) + return {r["p"]["signature"]: R.callable_(r["p"]) for r in rows} + + def get_source(self, node_id: str) -> str: + """The source text of ``node_id`` — ``module.source`` sliced by the node's ``span_json`` + byte offsets. The owning ``:GoModule`` carries ``source``; the node's module is the longest + ``:GoModule.id`` the node id starts with. Byte offsets are UTF-8, as in the local backend.""" + node_rows = self._run( + "MATCH (n:GoCanNode {id: $id}) RETURN n.span_json AS span", + id=node_id, + ) + if not node_rows: + raise CodeanalyzerExecutionException( + f"no node {node_id!r} in the graph for application {self.application_name!r}" + ) + span_json = node_rows[0]["span"] + if not span_json: + raise CodeanalyzerExecutionException(f"node {node_id!r} carries no span, so it has no source text") + # the owning module: the :GoModule whose id is a prefix of this node's id + mod_rows = self._run( + "MATCH (m:GoModule) WHERE $id STARTS WITH m.id + '/' RETURN m.id AS id, m.source AS source ORDER BY size(m.id) DESC LIMIT 1", + id=node_id, + ) + if not mod_rows: + raise CodeanalyzerExecutionException(f"no owning module for {node_id!r}") + source = mod_rows[0]["source"] or "" + b0, b1 = json.loads(span_json)["bytes"] + if source.isascii(): + return source[b0:b1] + return source.encode("utf-8")[b0:b1].decode("utf-8") + + # -----[ artifact / dependency / config layer — reserved-but-unemitted on this projection ]----- + def get_artifacts(self) -> Dict[str, PyArtifact]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_dependencies( + self, *, direct_only: bool = False, ecosystem: str | None = None, declared_in: str | None = None + ) -> List[PyDependency]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_config_keys(self) -> Dict[str, PyConfigKey]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_config_uses(self, key: str | None = None) -> List[PyConfigUseEdge]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) + + def get_unresolved_config_reads(self) -> List[PyConfigRead]: + raise CodeanalyzerExecutionException(ARTIFACT_LAYER_UNAVAILABLE.format(app=self.application_name)) diff --git a/cldk/analysis/go/neo4j/reconstruct.py b/cldk/analysis/go/neo4j/reconstruct.py new file mode 100644 index 00000000..19e0c2fa --- /dev/null +++ b/cldk/analysis/go/neo4j/reconstruct.py @@ -0,0 +1,102 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Rebuild :mod:`cldk.models.go` objects from the ``codeanalyzer-go`` Neo4j node property maps. + +Pure functions: each takes the flat property dict the ``--emit neo4j`` projection wrote for one +node (``internal/neo4j/schema.go`` is the authority for what each label carries) and returns the +same pydantic object the local backend parses from ``analysis.json``. :class:`GoNeo4jBackend` +fetches the rows and these turn them back into models; the per-node shape lives here. + +Two props are **JSON strings** on the wire, not nested maps (the projector flattens them so a Bolt +property stays scalar): ``span_json`` (every spanned node) and ``metrics_json`` (callables). They +are parsed here. The graph does **not** carry ``base_types`` on a ``:GoType`` (embeds are the +``GO_EMBEDS`` relationship, not a node property) nor ``parameters`` on a ``:GoCallable`` (not +projected), so a reconstructed type/callable has those at their empty default — the lazy backend +answers the few accessors that need them from the relationships instead. +""" + +from __future__ import annotations + +import json +from typing import Any, Dict, Mapping, Optional + +from cldk.models.go import GoBodyNode, GoCallable, GoField, GoMetrics, GoModule, GoSpan, GoType + +Props = Mapping[str, Any] + + +def _span(props: Props) -> Optional[GoSpan]: + """Parse the ``span_json`` string into a :class:`GoSpan`; ``None`` when absent.""" + raw = props.get("span_json") + if not raw: + return None + d = json.loads(raw) + return GoSpan(start=tuple(d["start"]), end=tuple(d["end"]), bytes=tuple(d["bytes"])) + + +def _metrics(props: Props) -> Optional[GoMetrics]: + raw = props.get("metrics_json") + if not raw: + return None + return GoMetrics(**json.loads(raw)) + + +def module(props: Props) -> GoModule: + """A :class:`GoModule` from a ``:GoModule`` node (carries ``source``, so ``get_source`` works + on this backend). ``imports``/``types``/``functions`` are filled by the caller from the + containment relationships; a bare module reconstruct leaves them empty.""" + return GoModule( + id=props["id"], + span=_span(props), + package=props.get("package", ""), + source=props.get("source", ""), + content_hash=props.get("content_hash"), + ) + + +def go_type(props: Props) -> GoType: + """A :class:`GoType` from a ``:GoType`` node. ``callables``/``fields`` are filled by the caller + from ``GO_HAS_METHOD``/``GO_HAS_FIELD``; ``base_types`` from ``GO_EMBEDS``.""" + return GoType(id=props["id"], kind=props["kind"], span=_span(props)) + + +def callable_(props: Props) -> GoCallable: + """A :class:`GoCallable` from a ``:GoCallable`` node. ``parameters``/``body``/``callables`` are + not projected on the node; the caller fills ``body`` from ``GO_HAS_BODY_NODE`` where needed.""" + return GoCallable( + id=props["id"], + kind=props.get("kind", "function"), + signature=props["signature"], + return_type=props.get("return_type"), + error_channel=list(props.get("error_channel") or []), + metrics=_metrics(props), + source_file=props.get("source_file"), + span=_span(props), + ) + + +def field(props: Props) -> GoField: + return GoField(id=props["id"], type=props.get("type"), span=_span(props)) + + +def body_node(props: Props) -> GoBodyNode: + return GoBodyNode( + kind=props.get("kind", "call"), + callee=props.get("callee"), + is_goroutine=bool(props.get("is_goroutine", False)), + span=_span(props), + ) diff --git a/cldk/core.py b/cldk/core.py index 01459f64..6d4534ee 100644 --- a/cldk/core.py +++ b/cldk/core.py @@ -47,6 +47,7 @@ from cldk.analysis.java import JavaAnalysis from cldk.analysis.commons.backend_config import ( CodeAnalyzerConfig, + GoBackend, JavaBackend, Neo4jConnectionConfig, PyBackend, @@ -55,6 +56,7 @@ TSCodeAnalyzerConfig, ) from cldk.analysis.commons.treesitter import TreesitterJava +from cldk.analysis.go import GoAnalysis from cldk.analysis.python.python_analysis import PythonAnalysis from cldk.analysis.typescript import TypeScriptAnalysis from cldk.utils.exceptions import CldkInitializationException @@ -220,6 +222,39 @@ def typescript( backend=backend, ) + @staticmethod + def go( + project_path: str | Path | None = None, + *, + analysis_level: str = AnalysisLevel.symbol_table, + target_files: List[str] | None = None, + eager: bool = False, + backend: GoBackend | None = None, + ) -> GoAnalysis: + """Create a Go analysis facade. + + Go is the SDK's first class-less, type-centric facade: the surface is + ``get_types`` / ``get_functions`` / ``get_methods_of``, never ``get_classes``. + + Args: + project_path: Path to the Go module directory (needs a ``go.mod``). Optional only when + ``backend`` is a :class:`Neo4jConnectionConfig` (the graph is populated out of band + over Bolt). When provided, the path is validated — it must exist and be a directory. + analysis_level: Analysis depth (see :class:`~cldk.analysis.AnalysisLevel`); ``cango`` + produces levels 1 (symbol table) and 2 (+ resolver call graph) only. + target_files: Restrict analysis to these files. + eager: Force regeneration of cached analysis. + backend: Backend configuration. Defaults to :class:`CodeAnalyzerConfig` (runs ``cango``); + pass a :class:`Neo4jConnectionConfig` to use the read-only Neo4j backend. + """ + return GoAnalysis( + project_dir=_normalize_project_path(project_path), + analysis_level=analysis_level, + target_files=target_files, + eager_analysis=eager, + backend=backend, + ) + def analysis( self, project_path: str | Path | None = None, @@ -232,11 +267,11 @@ def analysis( cache_dir: str | Path | None = None, use_ray: bool = False, neo4j_config: "Neo4jConnectionConfig | None" = None, - ) -> JavaAnalysis | PythonAnalysis | TypeScriptAnalysis: + ) -> JavaAnalysis | PythonAnalysis | TypeScriptAnalysis | GoAnalysis: """Deprecated entry point. Use the per-language factory methods instead. ``CLDK(language).analysis(...)`` is retained as a thin compatibility shim that forwards to - :meth:`java` / :meth:`python` / :meth:`typescript` with an appropriate + :meth:`java` / :meth:`python` / :meth:`typescript` / :meth:`go` with an appropriate ``backend=`` configuration object. The former ``analysis_json_path`` is folded into the unified ``cache_dir`` (it is used as @@ -249,7 +284,7 @@ def analysis( """ warnings.warn( "CLDK(language).analysis(...) is deprecated; use the per-language factory methods " - "CLDK.java()/CLDK.python()/CLDK.typescript() with a backend= object.", + "CLDK.java()/CLDK.python()/CLDK.typescript()/CLDK.go() with a backend= object.", DeprecationWarning, stacklevel=2, ) @@ -297,6 +332,17 @@ def analysis( eager=eager, backend=backend, ) + elif self.language == "go": + if source_code is not None: + raise CldkInitializationException("source_code mode is not supported for Go; please pass project_path.") + backend = neo4j_config if neo4j_config is not None else CodeAnalyzerConfig(cache_dir=cache_root) + return CLDK.go( + project_path=project_path, + analysis_level=analysis_level, + target_files=target_files, + eager=eager, + backend=backend, + ) else: raise NotImplementedError(f"Analysis support for {self.language} is not implemented yet.") diff --git a/cldk/models/go/__init__.py b/cldk/models/go/__init__.py new file mode 100644 index 00000000..c989e80d --- /dev/null +++ b/cldk/models/go/__init__.py @@ -0,0 +1,56 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Go model package — a pydantic mirror of the ``codeanalyzer-go`` (``cango``) ``analysis.json`` +(schema v2, L1/L2). + +Go is the SDK's first class-less, type-centric facade: :class:`GoType` is one model discriminated +by a ``kind`` field (``struct`` | ``interface``), never a class; package-level ``functions`` are +kept distinct from receiver ``methods``. No ``projections.py`` yet — the overview models the +Java/TS siblings ship are out of this train's SDK3 scope and add purely additively later. +""" + +from .models import ( + GoAnalysis, + GoAnalyzer, + GoApplication, + GoBodyNode, + GoCallable, + GoCallGraphEdge, + GoField, + GoImport, + GoMetrics, + GoModule, + GoParam, + GoSpan, + GoType, +) + +__all__ = [ + "GoAnalysis", + "GoAnalyzer", + "GoApplication", + "GoBodyNode", + "GoCallable", + "GoCallGraphEdge", + "GoField", + "GoImport", + "GoMetrics", + "GoModule", + "GoParam", + "GoSpan", + "GoType", +] diff --git a/cldk/models/go/models.py b/cldk/models/go/models.py new file mode 100644 index 00000000..c0f3d80a --- /dev/null +++ b/cldk/models/go/models.py @@ -0,0 +1,262 @@ +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Go schema models — a pydantic mirror of the ``codeanalyzer-go`` (``cango``) ``analysis.json``, +schema v2, at full-depth L1/L2 (the scope the analyzer emits today). + +The wire is one additive containment tree: ``GoAnalysis{analyzer, application}`` → +``GoApplication{symbol_table{module → types{}/functions{}}, call_graph}`` → +``GoType{callables{}/fields{}}`` → ``GoCallable{body{}}``. Every node above the callable leaf +carries a ``can://`` ``id`` and a :class:`GoSpan`; a module carries its full ``source`` once and +every node's text is a slice of it (``module.source[span.bytes]``). + +Go is the SDK's first **class-less, type-centric** facade: a ``type`` is a ``struct`` or an +``interface`` (never a class), and package-level ``functions`` are kept distinct from receiver +``methods``. :class:`GoType` is therefore a **single model with a ``kind`` field**, not a +discriminated ``Union`` of per-kind subclasses like :class:`~cldk.models.typescript.TSType` — the +two Go kinds carry the same wire shape, so the union would be ceremony (CLDK design note, issue +python-sdk#1 / spec §4 SDK1). + +**Go-native fields** carried as first-class typed fields the way TypeScript carries ``is_async`` / +``is_tsx`` (schema v2 "typed field" additions, recorded per-language): ``GoParam.is_variadic`` +(``...T``), ``GoBodyNode.is_goroutine`` (a ``go f()`` spawn), ``GoCallable.error_channel`` (the +generalized ``(T, error)`` return idiom), ``GoCallable.source_file``. + +``extra="ignore"`` (matching where TypeScript landed in #386): an additive ``cango`` release is +consumable without an SDK edit, at the cost of the drift detector — an unmodelled field is dropped +rather than failing loudly. :class:`_NullSafeBase` coerces a JSON ``null`` collection to its empty +default **before** validation: Go's zero-value idiom marshals a ``nil`` slice/map to ``null``, and +although ``cango`` emits ``[]``/``{}`` today, this keeps the SDK robust to a future emitter that +does not. +""" + +from __future__ import annotations + +from typing import Dict, List, Optional, Tuple + +from pydantic import BaseModel, ConfigDict, model_validator +from typing_extensions import Literal + + +class _Base(BaseModel): + #: ``ignore``, not ``forbid`` (per TypeScript #386): an additive analyzer release is consumable + #: without an SDK edit. The cost is the drift detector — a field the SDK does not model is + #: dropped silently rather than refusing to parse; a declared field is the only way a value is + #: reachable. + model_config = ConfigDict(extra="ignore") + + +class _NullSafeBase(_Base): + """Coerce a JSON ``null`` into the field's empty collection default before validation. + + Go marshals a ``nil`` slice/map to JSON ``null``, which Pydantic v2 ``List[T]`` / ``Dict[K, V]`` + rejects. ``cango`` emits ``[]`` / ``{}`` on every sample measured, but a future emitter default + could regress; this makes that invisible to the SDK rather than a hard parse error. + """ + + @model_validator(mode="before") + @classmethod + def _coerce_null_collections(cls, data): + if not isinstance(data, dict): + return data + for name, info in cls.model_fields.items(): + if data.get(name) is not None: + continue + # A collection default reaches us either as ``info.default`` (the ergonomic + # ``= []`` / ``= {}`` field style) or as ``info.default_factory`` (``Field( + # default_factory=list)``). Honor both so ``null`` coerces to the empty default + # regardless of how the field was declared. + if info.default_factory is not None: + sentinel = info.default_factory() + else: + sentinel = info.default + if isinstance(sentinel, (list, dict)): + data[name] = sentinel + return data + + +# ---------------------------------------------------------------------------------------------- +# Span — the one universal attribute +# ---------------------------------------------------------------------------------------------- + + +class GoSpan(_Base): + """``start``/``end`` are ``[line, column]`` (1-based); ``bytes`` are ``[from, to]`` offsets into + the owning module's ``source``.""" + + start: Tuple[int, int] + end: Tuple[int, int] + bytes: Tuple[int, int] + + +# ---------------------------------------------------------------------------------------------- +# Leaf models +# ---------------------------------------------------------------------------------------------- + + +class GoImport(_Base): + """One import binding on a module.""" + + name: str + path: str + alias: Optional[str] = None + span: Optional[GoSpan] = None + + +class GoParam(_Base): + """A function / method parameter. ``is_variadic`` is Go's ``...T``.""" + + name: str + type: Optional[str] = None + span: Optional[GoSpan] = None + is_variadic: bool = False + + +class GoField(_Base): + """A struct field (its map key is the field name).""" + + id: str + kind: Literal["field"] = "field" + span: Optional[GoSpan] = None + type: Optional[str] = None + + +class GoMetrics(_Base): + """Per-callable metrics. Only ``cyclomatic`` is emitted today; extensible map in spirit.""" + + cyclomatic: int = 0 + + +class GoBodyNode(_NullSafeBase): + """One entry of a callable's ``body{}`` map, keyed by local id (``L:C`` or ``@tag``). + + At L1/L2 the only ``kind`` is ``call`` (a call site). ``callee`` is the one sanctioned ``null`` + on the wire — unresolved at L1, refined to a ``can://`` id at L2. ``is_goroutine`` marks a + ``go f()`` spawn. + """ + + kind: str + span: Optional[GoSpan] = None + callee: Optional[str] = None + is_goroutine: bool = False + + +class GoCallGraphEdge(_Base): + """A wire call-graph edge: ``can://`` endpoints, open provenance tokens (``go/types``, …).""" + + src: str + dst: str + prov: List[str] = [] + weight: int = 1 + + +# ---------------------------------------------------------------------------------------------- +# Callable +# ---------------------------------------------------------------------------------------------- + + +class GoCallable(_NullSafeBase): + """A package-level ``function`` or a receiver ``method`` (kept distinct — no module-as-class + hack). ``error_channel`` generalizes the ``(T, error)`` return idiom; ``callables`` holds + nested func literals / closures when non-empty.""" + + id: str + kind: str = "function" # function | method + span: Optional[GoSpan] = None + signature: str + parameters: List[GoParam] = [] + return_type: Optional[str] = None + error_channel: List[str] = [] + metrics: Optional[GoMetrics] = None + source_file: Optional[str] = None + body: Dict[str, GoBodyNode] = {} + callables: Dict[str, "GoCallable"] = {} + + def __hash__(self) -> int: + return hash(self.signature) + + +# ---------------------------------------------------------------------------------------------- +# Type — one model with a ``kind`` field (struct | interface); never a class. +# ---------------------------------------------------------------------------------------------- + + +class GoType(_NullSafeBase): + """A Go ``struct`` or ``interface``. The two kinds carry the same wire shape, so this is one + model discriminated by ``kind`` rather than a ``Union`` of subclasses. ``base_types`` are + embedded types; ``fields`` are the struct's data fields (empty for an interface).""" + + id: str + kind: Literal["struct", "interface"] + span: Optional[GoSpan] = None + base_types: List[str] = [] + callables: Dict[str, GoCallable] = {} + fields: Dict[str, GoField] = {} + + +# ---------------------------------------------------------------------------------------------- +# Module +# ---------------------------------------------------------------------------------------------- + + +class GoModule(_NullSafeBase): + """A compilation unit (one ``.go`` file). The symbol-table key is its repo-relative path; + ``source`` is the whole file's text, carried once, that every node's text slices from.""" + + id: str + kind: Literal["module"] = "module" + span: Optional[GoSpan] = None + package: str + source: str + imports: List[GoImport] = [] + types: Dict[str, GoType] = {} + functions: Dict[str, GoCallable] = {} + content_hash: Optional[str] = None + + +# ---------------------------------------------------------------------------------------------- +# Application + envelope +# ---------------------------------------------------------------------------------------------- + + +class GoApplication(_NullSafeBase): + """The application root: the containment tree (``symbol_table``) plus the one app-scope overlay + the analyzer emits at L2 (``call_graph``).""" + + id: str + kind: Literal["application"] = "application" + symbol_table: Dict[str, GoModule] + call_graph: List[GoCallGraphEdge] = [] + + +class GoAnalyzer(_Base): + name: str + version: str + + +class GoAnalysis(_Base): + """The envelope ``analysis.json`` IS.""" + + schema_version: str + language: str + max_level: int + k_limit: Optional[int] = None # L3+ (absent at L1/L2) + analyzer: GoAnalyzer + application: GoApplication + + +# Resolve the forward reference in the self-recursive callable. +GoCallable.model_rebuild() diff --git a/examples/go_quickstart.py b/examples/go_quickstart.py new file mode 100644 index 00000000..b892c303 --- /dev/null +++ b/examples/go_quickstart.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +################################################################################ +# Copyright IBM Corporation 2026 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Runnable Go Quick Start — the README's Go snippet against a real Go module. + +Go is CLDK's first type-centric, class-less facade: a type is a struct or interface, and +package-level functions are kept distinct from receiver methods, so the surface is +``get_types`` / ``get_functions`` / ``get_methods_of`` — never ``get_classes``. + +Both `--backend` choices below run the same in-process `GoCodeanalyzer` over `CLDK.go(...)`; +they differ only in *where the cango binary comes from*, which is the one axis +`GoCodeanalyzer._get_codeanalyzer_exec` chooses on: + +* ``cldk`` — the ``cango`` bundled in the pinned ``codeanalyzer-go`` wheel (resolved by + ``codeanalyzer_go.bin_path()``). Nothing to install or point at; this is the default. +* ``local`` — a hand-built ``cango`` named by ``$CODEANALYZER_GO_BIN``. That env var *shadows* + the wheel when set, so the ``cldk`` run unsets it for the duration to exercise the wheel honestly. + +Usage: + + # cldk backend — the bundled wheel's cango; point at a Go MODULE (a directory with a go.mod): + python examples/go_quickstart.py /path/to/go/module # --backend cldk (default) + + # local backend — your own cango build (env var shadows the wheel while set): + export CODEANALYZER_GO_BIN=/path/to/cango + python examples/go_quickstart.py --backend local /path/to/go/module + + # run both, back to back: + python examples/go_quickstart.py --backend both /path/to/go/module + + # also run the read-only Neo4j backend against a graph a `cango --emit neo4j` run populated: + export CLDK_GO_NEO4J_URI=bolt://localhost:7687 + export CLDK_GO_NEO4J_USER=neo4j # optional, defaults to neo4j + export CLDK_GO_NEO4J_PASSWORD=... + export CLDK_GO_NEO4J_APP=go/my-app # the --app-name the graph was emitted with + python examples/go_quickstart.py /path/to/go/module + +The Neo4j section is skipped (with a note) when those env vars are not set. +""" + +from __future__ import annotations + +import argparse +import os + +from cldk import CLDK +from cldk.analysis import AnalysisLevel +from cldk.analysis.commons.backend_config import Neo4jConnectionConfig + +LIMIT = 10 # keep the console output readable on a large module + +#: The analysis levels cango produces — 1 (symbol table) and 2 (symbol table + call graph) only. +#: Go has no L3/L4 (no CFG/CDG/DDG), so the other two AnalysisLevel members are intentionally not +#: offered here. Mapped from the CLI's integer choice to the enum. +_LEVELS = {1: AnalysisLevel.symbol_table, 2: AnalysisLevel.call_graph} + + +def _short(node_id: str) -> str: + """The last path segment of a can:// id, for readable printing.""" + return node_id.rstrip("/").split("/")[-1] + + +def _run(project_path: str, level: AnalysisLevel, eager: bool, *, banner: str) -> None: + """The in-process backend: run cango over the module and query the parsed analysis.json. + + The caller has already selected the cango binary source (wheel vs. ``$CODEANALYZER_GO_BIN``) + before entering here — this body is identical whichever one resolved, because both land on the + same ``GoCodeanalyzer``. + """ + print("=" * 72) + print(f"{banner} — {project_path} [level: {level.value}{', eager' if eager else ''}]") + print("=" * 72) + + analysis = CLDK.go(project_path=project_path, analysis_level=level, eager=eager) + + types = analysis.get_types() + functions = analysis.get_functions() + print(f"\n{len(types)} type(s), {len(functions)} package-level function(s)\n") + + # Walk the types (structs + interfaces) and their receiver methods, addressed by the type id. + for type_id, go_type in list(types.items())[:LIMIT]: + print(f"{go_type.kind} {_short(type_id)}") + for sig, method in analysis.get_methods_of(type_id).items(): + params = ", ".join(f"{p.name} {p.type}" for p in method.parameters) + print(f" method {_short(sig)}({params}) -> {method.return_type or ''}") + if len(types) > LIMIT: + print(f" ... and {len(types) - LIMIT} more type(s)") + + # Package-level functions are separate from methods — no "module-as-class" hack. + print() + for sig in list(functions)[:LIMIT]: + print(f"func {_short(sig)}") + if len(functions) > LIMIT: + print(f"... and {len(functions) - LIMIT} more function(s)") + + # Source of one callable, sliced from its module's source by the node's span. + a_callable = next(iter(functions.values()), None) or next( + (m for tid in types for m in analysis.get_methods_of(tid).values()), None + ) + if a_callable is not None: + print(f"\n--- source of {_short(a_callable.signature)} ---") + print(analysis.get_source(a_callable.id)) + + # Call graph: who calls whom, with provenance and weight, keyed on the callable id. Only + # populated at level 2 — at level 1 cango emits the symbol table alone, so the graph is empty + # by construction, not because the project has no calls. + if level is AnalysisLevel.symbol_table: + print("\ncall graph: not computed at level 1 (symbol_table); re-run with --level 2") + return + cg = analysis.get_call_graph() + print(f"\ncall graph: {cg.number_of_nodes()} node(s), {cg.number_of_edges()} edge(s)") + shown = 0 + for func_id in functions: + for edge in analysis.get_callees(func_id): + print(f" {_short(edge.src)} -> {_short(edge.dst)} ({', '.join(edge.prov)}, weight={edge.weight})") + shown += 1 + if shown >= LIMIT: + break + if shown >= LIMIT: + break + + +def run_cldk(project_path: str, level: AnalysisLevel, eager: bool) -> None: + """Analyze on the fly using the ``cango`` bundled in the pinned ``codeanalyzer-go`` wheel. + + ``$CODEANALYZER_GO_BIN`` shadows the wheel in ``_get_codeanalyzer_exec`` whenever it is set, so + we pop it for the duration of this call — otherwise a stray env var would silently make the + "cldk" run actually use someone's hand-built binary. It is restored on the way out. + """ + saved = os.environ.pop("CODEANALYZER_GO_BIN", None) + try: + _run(project_path, level, eager, banner="CLDK backend (bundled codeanalyzer-go wheel)") + finally: + if saved is not None: + os.environ["CODEANALYZER_GO_BIN"] = saved + + +def run_local(project_path: str, level: AnalysisLevel, eager: bool) -> None: + """Analyze with a hand-built ``cango`` named by ``$CODEANALYZER_GO_BIN`` (set it first).""" + if not os.environ.get("CODEANALYZER_GO_BIN"): + print( + "\n(--backend local needs $CODEANALYZER_GO_BIN pointing at a cango build; " + "unset, so this would fall back to the wheel. Use --backend cldk, or set the env var.)" + ) + return + _run(project_path, level, eager, banner="LOCAL backend ($CODEANALYZER_GO_BIN cango)") + + +def run_neo4j() -> bool: + """The read-only Neo4j backend, if the env is set. Returns True when it ran.""" + uri = os.environ.get("CLDK_GO_NEO4J_URI") + password = os.environ.get("CLDK_GO_NEO4J_PASSWORD") + app = os.environ.get("CLDK_GO_NEO4J_APP") + if not (uri and password and app): + print("\n(Neo4j backend skipped — set CLDK_GO_NEO4J_URI / _PASSWORD / _APP to run it.)") + return False + + print("\n" + "=" * 72) + print(f"NEO4J backend (read-only) — {uri}, app {app}") + print("=" * 72) + + analysis = CLDK.go( + backend=Neo4jConnectionConfig( + uri=uri, + username=os.environ.get("CLDK_GO_NEO4J_USER", "neo4j"), + password=password, + application_name=app, # the graph is populated out of band by `cango --emit neo4j` + ), + ) + types = analysis.get_types() + functions = analysis.get_functions() + cg = analysis.get_call_graph() + print(f"\n{len(types)} type(s), {len(functions)} function(s), {cg.number_of_edges()} call edge(s)") + for type_id in list(types)[:LIMIT]: + print(f" type {_short(type_id)}: {len(analysis.get_methods_of(type_id))} method(s)") + # The backend owns a driver; close it when done. + close = getattr(analysis.backend, "close", None) + if close: + close() + return True + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Run the Go Quick Start against a Go module.", + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument("project_path", help="path to a Go module (a directory containing go.mod)") + parser.add_argument( + "-b", + "--backend", + choices=("cldk", "local", "both"), + default="cldk", + help="which cango to analyze with: cldk = the bundled codeanalyzer-go wheel (default); " + "local = your own build named by $CODEANALYZER_GO_BIN; both = run them in turn. Both " + "choices use the same in-process GoCodeanalyzer — only the binary source differs.", + ) + parser.add_argument( + "-a", + "--level", + type=int, + choices=(1, 2), + default=2, + help="analysis level for the local backend: 1 = symbol table, 2 = symbol table + call graph " + "(default). cango produces only 1 and 2; Go has no L3/L4. The Neo4j graph is always " + "full-depth, so this flag affects the local run only.", + ) + parser.add_argument( + "-e", + "--eager", + action="store_true", + help="force a fresh cango run, ignoring any cached analysis.json under " + "/.codeanalyzer/go. Use this if a cache from an earlier, shallower level (e.g. a " + "prior -a 1 run) is being reused for a deeper request — the cache is reused by existence, " + "not by level.", + ) + args = parser.parse_args() + + level = _LEVELS[args.level] + if args.backend in ("cldk", "both"): + run_cldk(args.project_path, level, args.eager) + if args.backend in ("local", "both"): + run_local(args.project_path, level, args.eager) + run_neo4j() + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/pyproject.toml b/pyproject.toml index 45b2a114..80a6b436 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ dependencies = [ "tree-sitter-javascript==0.23.1", "codeanalyzer-python==1.5.3", "codeanalyzer-typescript==1.6.0", + "codeanalyzer-go==0.1.0rc1", ] [project.optional-dependencies] @@ -89,6 +90,7 @@ include = [ codeanalyzer-java = "3.3.3" codeanalyzer-python = "1.5.3" codeanalyzer-typescript = "1.6.0" +codeanalyzer-go = "0.1.0rc1" ######################################## # Tool configurations diff --git a/tests/analysis/go/__init__.py b/tests/analysis/go/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/analysis/go/test_go_analysis.py b/tests/analysis/go/test_go_analysis.py new file mode 100644 index 00000000..1f7a7e33 --- /dev/null +++ b/tests/analysis/go/test_go_analysis.py @@ -0,0 +1,273 @@ +"""Tests for the Go analysis facade with the cango subprocess mocked — no binary, no Neo4j. + +One corpus, driven through the mocked subprocess (which writes the payload into the cache dir exactly +as cango would at ``-o``): ``tests/resources/go/analysis.json``, the committed real +``can://go/litestream`` corpus (55 modules, 959 edges). + +* The deterministic level/cache/capture regression tests mutate its ``max_level`` and call graph to + simulate L1-vs-L2 generations. +* ``go_analysis`` runs the basic accessor smoke tests over it, and ``litestream_facade`` runs the + whole accessor surface, so callers/callees/source are proven against genuine cango output. +""" + +import json +from pathlib import Path +from unittest.mock import MagicMock + +import networkx as nx +import pytest + +from cldk import CLDK +from cldk.analysis import AnalysisLevel +from cldk.analysis.commons.backend_config import CodeAnalyzerConfig +from cldk.models.go import GoType +from cldk.utils.exceptions.exceptions import CodeanalyzerExecutionException + +#: the committed real corpus, via the parents[2]/"resources" convention the TS/Java tests use +GO_ANALYSIS = Path(__file__).resolve().parents[2] / "resources" / "go" / "analysis.json" + +# real, stable ids from the litestream corpus (abs/replica_client.go) +LS_TYPE_ID = "can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient" +LS_METHOD_ID = f"{LS_TYPE_ID}/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll" +LS_FUNC_ID = "can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient" +LS_FUNC_SIG = "github.com/benbjohnson/litestream/abs.NewReplicaClient" +LS_FUNC_CALLER_ID = "can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClientFromURL" + + +def _fake_run_writing_output(payload: str): + def _run(cmd, *args, **kwargs): + if "-o" in cmd: + out = Path(cmd[cmd.index("-o") + 1]) + out.mkdir(parents=True, exist_ok=True) + (out / "analysis.json").write_text(payload, encoding="utf-8") + return MagicMock(stdout=payload, returncode=0) + return _run + + +@pytest.fixture +def go_analysis(tmp_path, monkeypatch): + payload = GO_ANALYSIS.read_text(encoding="utf-8") + monkeypatch.setenv("CODEANALYZER_GO_BIN", "cango") + monkeypatch.setattr("subprocess.run", _fake_run_writing_output(payload)) + return CLDK.go( + project_path=str(tmp_path), + analysis_level=AnalysisLevel.call_graph, + eager=True, + backend=CodeAnalyzerConfig(cache_dir=str(tmp_path)), + ) + + +# -----[ regression: a cache shallower than the request is not reused ]----- +def _level_aware_run(call_count): + """A subprocess.run mock that writes an analysis.json whose max_level matches the `-a` flag it + was given, and counts how many times cango was invoked.""" + base = json.loads(GO_ANALYSIS.read_text(encoding="utf-8")) + + def _run(cmd, *args, **kwargs): + call_count.append(1) + a = int(cmd[cmd.index("-a") + 1]) if "-a" in cmd else 2 + payload = dict(base) + payload["max_level"] = a + if a < 2: # a level-1 payload carries no call graph + payload["application"] = {**base["application"], "call_graph": []} + text = json.dumps(payload) + if "-o" in cmd: + out = Path(cmd[cmd.index("-o") + 1]) + out.mkdir(parents=True, exist_ok=True) + (out / "analysis.json").write_text(text, encoding="utf-8") + return MagicMock(stdout=text, returncode=0) + + return _run + + +def test_shallow_cache_is_not_reused_for_a_deeper_request(tmp_path, monkeypatch): + """A level-1 cache must NOT satisfy a level-2 request: the backend re-runs cango. (The reuse + check used to test only file existence, serving the stale shallower result.)""" + calls = [] + monkeypatch.setenv("CODEANALYZER_GO_BIN", "cango") + monkeypatch.setattr("subprocess.run", _level_aware_run(calls)) + + # First: a level-1 run writes a level-1 cache. + CLDK.go(project_path=str(tmp_path), analysis_level=AnalysisLevel.symbol_table, backend=CodeAnalyzerConfig(cache_dir=str(tmp_path))) + assert len(calls) == 1 + + # Then: a level-2 request over that level-1 cache must re-run (not reuse). + a2 = CLDK.go(project_path=str(tmp_path), analysis_level=AnalysisLevel.call_graph, backend=CodeAnalyzerConfig(cache_dir=str(tmp_path))) + assert len(calls) == 2, "level-2 request should have re-run cango over the stale level-1 cache" + assert a2.backend.analysis.max_level == 2 + + +def test_deeper_cache_is_reused_for_a_shallower_request(tmp_path, monkeypatch): + """A level-2 cache satisfies a level-1 request without re-running — the schema is additive + (L1 subset of L2), so the deeper cache already contains everything.""" + calls = [] + monkeypatch.setenv("CODEANALYZER_GO_BIN", "cango") + monkeypatch.setattr("subprocess.run", _level_aware_run(calls)) + + CLDK.go(project_path=str(tmp_path), analysis_level=AnalysisLevel.call_graph, backend=CodeAnalyzerConfig(cache_dir=str(tmp_path))) + assert len(calls) == 1 + CLDK.go(project_path=str(tmp_path), analysis_level=AnalysisLevel.symbol_table, backend=CodeAnalyzerConfig(cache_dir=str(tmp_path))) + assert len(calls) == 1, "level-1 request should reuse the deeper level-2 cache, not re-run" + + +# -----[ regression: the requested analysis level actually reaches cango ]----- +@pytest.mark.parametrize( + "level, expected_a", + [(AnalysisLevel.symbol_table, "1"), (AnalysisLevel.call_graph, "2")], +) +def test_requested_level_is_passed_to_cango(level, expected_a, tmp_path, monkeypatch): + """A hand-rolled name->int dict silently defaulted every level to 1 (the enum's value is + 'call graph', not 'call_graph'), so `-a 2` never reached cango and no project ever got a call + graph. Assert the integer `-a` flag matches the requested level.""" + captured = {} + + def _capture(cmd, *args, **kwargs): + if "-a" in cmd: + captured["a"] = cmd[cmd.index("-a") + 1] + out = Path(cmd[cmd.index("-o") + 1]) if "-o" in cmd else None + if out: + out.mkdir(parents=True, exist_ok=True) + (out / "analysis.json").write_text(GO_ANALYSIS.read_text(encoding="utf-8"), encoding="utf-8") + return MagicMock(stdout=GO_ANALYSIS.read_text(encoding="utf-8"), returncode=0) + + monkeypatch.setenv("CODEANALYZER_GO_BIN", "cango") + monkeypatch.setattr("subprocess.run", _capture) + CLDK.go(project_path=str(tmp_path), analysis_level=level, eager=True, backend=CodeAnalyzerConfig(cache_dir=str(tmp_path))) + assert captured.get("a") == expected_a + + +# -----[ the public surface is type-centric, never class ]----- +def test_public_surface_has_no_class_named_method(): + """SDK1: Go is class-less. No public facade method mentions 'class'.""" + public = [m for m in dir(CLDK.go) if not m.startswith("_")] # the facade methods on the type + from cldk.analysis.go import GoAnalysis + + public = [m for m in dir(GoAnalysis) if not m.startswith("_")] + assert not [m for m in public if "class" in m.lower()] + for expected in ("get_types", "get_type", "get_functions", "get_methods_of", "get_method", "get_fields"): + assert expected in public + + +# -----[ accessors over the fixture ]----- +def test_symbol_table_not_empty(go_analysis): + assert go_analysis.get_symbol_table() + + +def test_get_types_returns_the_struct(go_analysis): + types = go_analysis.get_types() + assert types, "no types" + assert all(isinstance(t, GoType) for t in types.values()) + assert any(t.kind == "struct" for t in types.values()) + + +def test_get_functions_distinct_from_methods(go_analysis): + funcs = go_analysis.get_functions() + assert funcs, "no package-level functions" + # the package function is not among any type's methods + method_ids = {m.id for tid in go_analysis.get_types() for m in go_analysis.get_methods_of(tid).values()} + assert all(f.id not in method_ids for f in funcs.values()) + + +def test_methods_and_get_method_and_params(go_analysis): + tid = next(iter(go_analysis.get_types())) + methods = go_analysis.get_methods_of(tid) + assert methods, "the struct has no methods" + sig = next(iter(methods)) + assert go_analysis.get_method(tid, sig).signature == sig + # params list is well-formed (may be empty) + assert isinstance(go_analysis.get_method_parameters(tid, sig), list) + + +def test_call_graph_is_dangling_free(go_analysis): + g = go_analysis.get_call_graph() + assert isinstance(g, nx.DiGraph) + for src, dst in g.edges(): + assert "id" in g.nodes[src] and "id" in g.nodes[dst] + + +def test_callers_callees_keyed_on_id(go_analysis): + # the one call edge: its src has a callee, its dst has a caller + edges = go_analysis.get_application_view().call_graph + assert edges + e = edges[0] + assert any(x.dst == e.dst for x in go_analysis.get_callees(e.src)) + assert any(x.src == e.src for x in go_analysis.get_callers(e.dst)) + + +def test_get_source_slices_the_method(go_analysis): + tid = next(iter(go_analysis.get_types())) + sig = next(iter(go_analysis.get_methods_of(tid))) + src = go_analysis.get_source(go_analysis.get_method(tid, sig).id) + assert "func" in src + + +def test_artifact_six_raise_on_local(go_analysis): + for name in ("get_artifacts", "get_dependencies", "get_config_keys", "get_config_uses", "get_unresolved_config_reads"): + with pytest.raises(CodeanalyzerExecutionException): + getattr(go_analysis.backend, name)() + + +# -----[ the whole accessor surface over the real litestream corpus ]----- +@pytest.fixture(scope="module") +def litestream_facade(tmp_path_factory): + """The facade over the committed real corpus, through the same mocked subprocess as + ``go_analysis`` — no binary, but 55 modules and 959 edges instead of a one-edge toy.""" + import subprocess + from unittest.mock import patch + + payload = GO_ANALYSIS.read_text(encoding="utf-8") + proj = tmp_path_factory.mktemp("litestream-facade") + with patch.dict("os.environ", {"CODEANALYZER_GO_BIN": "cango"}), patch.object( + subprocess, "run", _fake_run_writing_output(payload) + ): + yield CLDK.go( + project_path=str(proj), + analysis_level=AnalysisLevel.call_graph, + eager=True, + backend=CodeAnalyzerConfig(cache_dir=str(proj)), + ) + + +def test_real_corpus_scale(litestream_facade): + assert len(litestream_facade.get_symbol_table()) == 55 + assert len(litestream_facade.get_application_view().call_graph) == 959 + + +def test_real_corpus_function_accessor(litestream_facade): + funcs = litestream_facade.get_functions() + assert LS_FUNC_SIG in funcs + assert funcs[LS_FUNC_SIG].id == LS_FUNC_ID + + +def test_real_corpus_type_and_method(litestream_facade): + ty = litestream_facade.get_type(LS_TYPE_ID) + assert ty is not None and ty.kind == "struct" + methods = litestream_facade.get_methods_of(LS_TYPE_ID) + assert any(m.id == LS_METHOD_ID for m in methods.values()) + assert any(f.type == "string" for f in litestream_facade.get_fields(LS_TYPE_ID)) + + +def test_real_corpus_callees_of_delete_all(litestream_facade): + """DeleteAll calls exactly two callees in this corpus and is called by none (measured).""" + callees = litestream_facade.get_callees(LS_METHOD_ID) + assert len(callees) == 2 + assert all(e.src == LS_METHOD_ID for e in callees) + assert litestream_facade.get_callers(LS_METHOD_ID) == [] + + +def test_real_corpus_callers_of_constructor(litestream_facade): + callers = litestream_facade.get_callers(LS_FUNC_ID) + assert LS_FUNC_CALLER_ID in {e.src for e in callers} + + +def test_real_corpus_source_slices_the_method(litestream_facade): + src = litestream_facade.get_source(LS_METHOD_ID) + assert src.startswith("func") and "DeleteAll" in src + + +def test_real_corpus_call_graph_dangling_free(litestream_facade): + """Every one of the 959 edges lands on a node that carries its id (no dangling endpoints).""" + g = litestream_facade.get_call_graph() + assert g.number_of_edges() == 959 + for src, dst in g.edges(): + assert "id" in g.nodes[src] and "id" in g.nodes[dst] diff --git a/tests/analysis/go/test_go_backend_contract.py b/tests/analysis/go/test_go_backend_contract.py new file mode 100644 index 00000000..f3707d69 --- /dev/null +++ b/tests/analysis/go/test_go_backend_contract.py @@ -0,0 +1,69 @@ +"""The Go backend contract: both backends implement the same ABC, that ABC parameterises the +generic cross-language :class:`AnalysisBackend`, and the Go-specific accessors are present on +both. No binary and no live Neo4j needed.""" + +import inspect + +import pytest + +from cldk.analysis.commons.backend import AnalysisBackend +from cldk.analysis.go.backend import GoAnalysisBackend +from cldk.analysis.go.codeanalyzer.codeanalyzer import GoCodeanalyzer +from cldk.analysis.go.neo4j.neo4j_backend import GoNeo4jBackend + +BACKENDS = [GoCodeanalyzer, GoNeo4jBackend] +GENERIC_METHODS = sorted(AnalysisBackend.__abstractmethods__) +#: The accessors the Go contract adds on top of the generic ABC (SDK1/SDK2/SDK3 leaf surface). +GO_METHODS = ["get_functions", "get_callers", "get_callees", "get_source", "get_call_graph_json"] + + +def test_go_contract_parameterises_the_generic_abc(): + assert issubclass(GoAnalysisBackend, AnalysisBackend) + assert GoAnalysisBackend.N == "Go" + assert GoAnalysisBackend.P == "GO_" + + +def test_generic_methods_are_all_abstract_on_the_go_contract(): + """Inheriting the generic ABC must not quietly satisfy any of its methods with a stub.""" + assert set(GENERIC_METHODS) <= GoAnalysisBackend.__abstractmethods__ + + +def test_go_adds_its_own_abstract_methods(): + for name in GO_METHODS: + m = getattr(GoAnalysisBackend, name) + assert getattr(m, "__isabstractmethod__", False), f"{name} should be abstract on the contract" + + +@pytest.mark.parametrize("backend", BACKENDS) +def test_backends_subclass_the_contract(backend): + assert issubclass(backend, GoAnalysisBackend) + + +def test_contract_is_abstract(): + with pytest.raises(TypeError): + GoAnalysisBackend() + + +@pytest.mark.parametrize("backend", BACKENDS) +def test_backends_fully_implement_the_contract(backend): + """No abstract methods left unimplemented — generic and Go-specific alike. This is the gate + 'GoAnalysisBackend instantiates' reduced to the class level.""" + assert backend.__abstractmethods__ == frozenset() + + +@pytest.mark.parametrize("backend", BACKENDS) +@pytest.mark.parametrize("name", GENERIC_METHODS + GO_METHODS) +def test_backend_implements_every_method(backend, name): + impl = getattr(backend, name, None) + assert impl is not None and not getattr(impl, "__isabstractmethod__", False), f"{backend.__name__}.{name} is not implemented" + + +@pytest.mark.parametrize("backend", BACKENDS) +def test_signatures_match_the_contract(backend): + """Every abstract method's parameters and defaults are preserved by each backend. Return + annotations are not compared: the generic ABC's are type variables the Go backends narrow.""" + for name, base_method in inspect.getmembers(GoAnalysisBackend, predicate=inspect.isfunction): + if getattr(base_method, "__isabstractmethod__", False): + base_sig = inspect.signature(base_method).replace(return_annotation=inspect.Signature.empty) + impl_sig = inspect.signature(getattr(backend, name)).replace(return_annotation=inspect.Signature.empty) + assert impl_sig == base_sig, f"{backend.__name__}.{name} signature drifted: {impl_sig} != {base_sig}" diff --git a/tests/analysis/go/test_go_e2e.py b/tests/analysis/go/test_go_e2e.py new file mode 100644 index 00000000..68cffc4b --- /dev/null +++ b/tests/analysis/go/test_go_e2e.py @@ -0,0 +1,126 @@ +"""End-to-end: run the real cango binary on a throwaway Go module. Skips cleanly when no binary is +resolvable (``$CODEANALYZER_GO_BIN`` or a ``codeanalyzer-go`` wheel), so CI without the analyzer is +unaffected.""" + +import os +import shlex +import shutil +from pathlib import Path + +import pytest + +from cldk import CLDK +from cldk.analysis import AnalysisLevel +from cldk.analysis.commons.backend_config import CodeAnalyzerConfig + + +def _binary() -> str | None: + env = os.environ.get("CODEANALYZER_GO_BIN") + if env: + exe = shlex.split(env)[0] + return env if Path(exe).exists() or shutil.which(exe) else None + try: + import codeanalyzer_go # type: ignore[import-not-found] + + p = Path(codeanalyzer_go.bin_path()) + return str(p) if p.exists() else None + except (ModuleNotFoundError, FileNotFoundError, AttributeError): + return None + + +pytestmark = pytest.mark.skipif(_binary() is None, reason="no codeanalyzer-go (cango) binary resolvable") + +_MAIN_GO = """package main + +import "fmt" + +type Greeter struct{ name string } + +func (g Greeter) Hello() string { return fmt.Sprintf("hi %s", g.name) } + +func main() { g := Greeter{"x"}; fmt.Println(g.Hello()) } +""" + + +@pytest.fixture(scope="module") +def go_module(tmp_path_factory): + """A minimal real Go module (needs a go.mod — cango uses go/packages).""" + d = tmp_path_factory.mktemp("go-e2e") + (d / "main.go").write_text(_MAIN_GO, encoding="utf-8") + (d / "go.mod").write_text("module goe2e\n\ngo 1.21\n", encoding="utf-8") + return d + + +@pytest.fixture(scope="module") +def analysis(go_module, tmp_path_factory): + cache = tmp_path_factory.mktemp("go-e2e-cache") + return CLDK.go( + project_path=str(go_module), + analysis_level=AnalysisLevel.call_graph, + eager=True, + backend=CodeAnalyzerConfig(cache_dir=str(cache)), + ) + + +def test_symbol_table_not_empty(analysis): + assert analysis.get_symbol_table() + + +def test_types_and_functions(analysis): + types = analysis.get_types() + assert any(t.kind == "struct" for t in types.values()), "expected the Greeter struct" + assert analysis.get_functions(), "expected the package-level main func" + + +def test_methods_and_source(analysis): + tid = next(t_id for t_id, t in analysis.get_types().items() if t.kind == "struct") + methods = analysis.get_methods_of(tid) + assert methods, "the struct has a method" + sig = next(iter(methods)) + assert "func" in analysis.get_source(analysis.get_method(tid, sig).id) + + +def test_call_graph_dangling_free(analysis): + g = analysis.get_call_graph() + for src, dst in g.edges(): + assert "id" in g.nodes[src] and "id" in g.nodes[dst] + + +# -----[ live run on the committed real application (litestream.zip) ]----- +import zipfile # noqa: E402 + +_LITESTREAM_ZIP = Path(__file__).resolve().parents[2] / "resources" / "go" / "litestream.zip" + + +@pytest.fixture(scope="module") +def litestream_project(tmp_path_factory): + """Unzip the committed litestream app; the archive roots at ``litestream/`` (carries go.mod).""" + if not _LITESTREAM_ZIP.exists(): + pytest.skip("litestream.zip resource not present") + dest = tmp_path_factory.mktemp("litestream-src") + with zipfile.ZipFile(_LITESTREAM_ZIP) as zf: + zf.extractall(dest) + root = dest / "litestream" + assert (root / "go.mod").exists(), "expected go.mod at the archive's litestream/ root" + return root + + +@pytest.fixture(scope="module") +def litestream_analysis(litestream_project, tmp_path_factory): + cache = tmp_path_factory.mktemp("litestream-cache") + return CLDK.go( + project_path=str(litestream_project), + analysis_level=AnalysisLevel.call_graph, + eager=True, + backend=CodeAnalyzerConfig(cache_dir=str(cache)), + ) + + +def test_litestream_app_analyzes(litestream_analysis): + """The real binary on the real app: a non-trivial symbol table and call graph, app id intact.""" + assert litestream_analysis.get_application_view().id == "can://go/litestream" + assert len(litestream_analysis.get_symbol_table()) > 1 + g = litestream_analysis.get_call_graph() + assert g.number_of_edges() > 0 + for src, dst in g.edges(): + assert "id" in g.nodes[src] and "id" in g.nodes[dst] diff --git a/tests/analysis/go/test_go_neo4j_live.py b/tests/analysis/go/test_go_neo4j_live.py new file mode 100644 index 00000000..71bceb4b --- /dev/null +++ b/tests/analysis/go/test_go_neo4j_live.py @@ -0,0 +1,93 @@ +"""Live Neo4j parity: GoNeo4jBackend must answer identically to GoCodeanalyzer on the same +application. Skips unless a Go graph is reachable AND its matching analysis.json is given. + +Set, pointing at a read-only graph a ``cango --emit neo4j`` run populated: + + CLDK_TEST_GO_NEO4J_URI (e.g. bolt://localhost:7687) + CLDK_TEST_GO_NEO4J_USER (default: neo4j) + CLDK_TEST_GO_NEO4J_PASSWORD + CLDK_TEST_GO_NEO4J_APP (the --app-name the graph was loaded with, e.g. go/cobra) + CLDK_TEST_GO_ANALYSIS_JSON (path to the SAME application's analysis.json, for the diff) + +With the first four set but no analysis.json, only the standalone (non-parity) checks run. +""" + +import json +import os +import shutil +from pathlib import Path + +import pytest + +from cldk.analysis.commons.backend_config import Neo4jConnectionConfig +from cldk.analysis.go.codeanalyzer import GoCodeanalyzer +from cldk.analysis.go.neo4j import GoNeo4jBackend + +URI = os.environ.get("CLDK_TEST_GO_NEO4J_URI") +USER = os.environ.get("CLDK_TEST_GO_NEO4J_USER", "neo4j") +PASSWORD = os.environ.get("CLDK_TEST_GO_NEO4J_PASSWORD") +APP = os.environ.get("CLDK_TEST_GO_NEO4J_APP") +ANALYSIS_JSON = os.environ.get("CLDK_TEST_GO_ANALYSIS_JSON") + +pytestmark = pytest.mark.skipif( + not (URI and PASSWORD and APP), + reason="set CLDK_TEST_GO_NEO4J_URI / _PASSWORD / _APP to a read-only codeanalyzer-go graph", +) + + +@pytest.fixture(scope="module") +def neo4j_backend(): + b = GoNeo4jBackend(neo4j_uri=URI, neo4j_username=USER, neo4j_password=PASSWORD, application_name=APP) + yield b + b.close() + + +@pytest.fixture(scope="module") +def local_backend(tmp_path_factory): + if not (ANALYSIS_JSON and Path(ANALYSIS_JSON).exists()): + pytest.skip("set CLDK_TEST_GO_ANALYSIS_JSON to the same application's analysis.json for the parity diff") + cache = tmp_path_factory.mktemp("go-parity") + (cache / "go").mkdir() + shutil.copy(ANALYSIS_JSON, cache / "go" / "analysis.json") + return GoCodeanalyzer(project_dir="/unused", cache_dir=str(cache), analysis_level="call_graph", eager_analysis=False, target_files=None) + + +# -----[ standalone: the graph answers at all ]----- +def test_neo4j_backend_populated(neo4j_backend): + assert neo4j_backend.get_all_classes() or neo4j_backend.get_functions() + assert neo4j_backend.get_call_graph().number_of_edges() >= 0 + + +def test_artifact_six_raise_on_neo4j_too(neo4j_backend): + from cldk.utils.exceptions.exceptions import CodeanalyzerExecutionException + + for name in ("get_artifacts", "get_dependencies", "get_config_keys", "get_config_uses", "get_unresolved_config_reads"): + with pytest.raises(CodeanalyzerExecutionException): + getattr(neo4j_backend, name)() + + +# -----[ parity: the two backends agree on the same application ]----- +def test_type_id_sets_match(local_backend, neo4j_backend): + assert set(local_backend.get_all_classes()) == set(neo4j_backend.get_all_classes()) + + +def test_function_signature_sets_match(local_backend, neo4j_backend): + assert set(local_backend.get_functions()) == set(neo4j_backend.get_functions()) + + +def test_call_graph_edge_counts_match(local_backend, neo4j_backend): + assert local_backend.get_call_graph().number_of_edges() == neo4j_backend.get_call_graph().number_of_edges() + + +def test_symbol_table_module_counts_match(local_backend, neo4j_backend): + assert len(local_backend.get_symbol_table()) == len(neo4j_backend.get_symbol_table()) + + +def test_callees_match_on_a_real_callable(local_backend, neo4j_backend): + edges = local_backend.get_application_view().call_graph + if not edges: + pytest.skip("no call edges in this application") + cid = edges[0].src + lo = sorted((e.src, e.dst, tuple(e.prov), e.weight) for e in local_backend.get_callees(cid)) + ne = sorted((e.src, e.dst, tuple(e.prov), e.weight) for e in neo4j_backend.get_callees(cid)) + assert lo == ne diff --git a/tests/models/go/__init__.py b/tests/models/go/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/models/go/test_go_models.py b/tests/models/go/test_go_models.py new file mode 100644 index 00000000..ecad147e --- /dev/null +++ b/tests/models/go/test_go_models.py @@ -0,0 +1,84 @@ +"""Go model tests — the ``extra="forbid"`` pydantic mirror of canonical schema v2. + +Like the sibling ``tests/models/`` suites, the file corpus comes from ``tests/resources/go`` +(no local toy JSON): the committed real ``can://go/litestream`` project (55 modules, 959 edges) +proves the mirror parses genuine cango output at scale. The remaining tests construct models inline +to pin the Go-specific shape — the single ``GoType`` with a ``kind`` field (no discriminated union), +the null-safe collection coercion for Go's nil-slice idiom, and the native first-class fields. +""" + +from pathlib import Path + +import pytest + +from cldk.models.go import GoAnalysis, GoModule, GoType + +#: the committed real corpus, via the parents[2]/"resources" convention the TS/Java tests use +GO_ANALYSIS = Path(__file__).resolve().parents[2] / "resources" / "go" / "analysis.json" + +# real, stable ids from the litestream corpus (abs/replica_client.go) +LS_MODULE = "abs/replica_client.go" +LS_TYPE = "ReplicaClient" +LS_METHOD_SIG = "github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll" +LS_FUNC_SIG = "github.com/benbjohnson/litestream/abs.NewReplicaClient" + + +# -----[ real corpus: the mirror parses genuine cango output at scale ]----- +@pytest.fixture(scope="module") +def litestream() -> GoAnalysis: + return GoAnalysis.model_validate_json(GO_ANALYSIS.read_text(encoding="utf-8")) + + +def test_real_corpus_envelope(litestream): + assert litestream.schema_version == "2.0.0" + assert litestream.language == "go" + assert litestream.max_level == 2 + assert litestream.application.id == "can://go/litestream" + assert len(litestream.application.symbol_table) == 55 + assert len(litestream.application.call_graph) == 959 + + +def test_real_corpus_struct_round_trips(litestream): + """A real struct with its method and field survives the round trip with its native shape.""" + mod = litestream.application.symbol_table[LS_MODULE] + assert mod.package == "abs" and mod.source # the module carries its source text + ty = mod.types[LS_TYPE] + assert ty.kind == "struct" + assert LS_METHOD_SIG in ty.callables + assert ty.callables[LS_METHOD_SIG].kind == "method" + assert ty.fields["AccountKey"].type == "string" + assert LS_FUNC_SIG in mod.functions # a package-level constructor function + + +def test_real_corpus_call_edges_are_well_formed(litestream): + """Every edge names a src and dst id under the application; prov/weight parse.""" + pre = "can://go/litestream/" + for e in litestream.application.call_graph: + assert e.src.startswith(pre) and e.dst.startswith(pre) + assert isinstance(e.prov, list) and isinstance(e.weight, int) + + +# -----[ the Go-specific model shape, pinned inline ]----- +def test_gotype_is_a_single_model_with_a_kind_field(): + t = GoType(id="can://go/x/f.go/T", kind="struct") + assert t.kind == "struct" + assert t.base_types == [] and t.callables == {} and t.fields == {} + + +def test_null_safe_base_coerces_null_collections(): + """A JSON ``null`` list/map (Go marshals a nil slice this way) becomes the empty default rather + than raising, honoring the ``= []`` field style via the broadened validator.""" + t = GoType(id="x", kind="interface", base_types=None, callables=None, fields=None) + assert t.base_types == [] and t.callables == {} and t.fields == {} + m = GoModule(id="m", package="p", source="", imports=None, types=None, functions=None) + assert m.imports == [] and m.types == {} and m.functions == {} + + +def test_go_native_fields_present(): + """The Go-specific first-class fields exist with their defaults.""" + from cldk.models.go import GoBodyNode, GoCallable, GoParam + + assert GoParam(name="x").is_variadic is False + assert GoBodyNode(kind="call").is_goroutine is False + c = GoCallable(id="c", signature="s") + assert c.error_channel == [] and c.source_file is None diff --git a/tests/resources/go/analysis.json b/tests/resources/go/analysis.json new file mode 100644 index 00000000..5f47977e --- /dev/null +++ b/tests/resources/go/analysis.json @@ -0,0 +1 @@ +{"schema_version":"2.0.0","language":"go","max_level":2,"analyzer":{"name":"codeanalyzer-go","version":"0.1.0"},"application":{"id":"can://go/litestream","kind":"application","symbol_table":{"abs/replica_client.go":{"id":"can://go/litestream/abs/replica_client.go","kind":"module","span":{"start":[1,1],"end":[500,1],"bytes":[0,14566]},"package":"abs","source":"package abs\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/to\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azidentity\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror\"\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"abs\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"abs\"\n\n// MetadataKeyTimestamp is the metadata key for storing LTX file timestamps in Azure Blob Storage.\n// Azure metadata keys cannot contain hyphens, so we use litestreamtimestamp (C# identifier rules).\nconst MetadataKeyTimestamp = \"litestreamtimestamp\"\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files to Azure Blob Storage.\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\tclient *azblob.Client\n\tlogger *slog.Logger\n\n\t// Azure credentials\n\tAccountName string\n\tAccountKey string\n\tSASToken string // SAS token for container-level access\n\tEndpoint string\n\n\t// Azure Blob Storage container information\n\tBucket string\n\tPath string\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\n// URL format: abs://[account-name@]container/path\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tclient := NewReplicaClient()\n\n\t// Extract account name from userinfo if present (abs://account@container/path)\n\tif userinfo != nil {\n\t\tclient.AccountName = userinfo.Username()\n\t}\n\n\tclient.Bucket = host\n\tclient.Path = urlPath\n\tclient.Endpoint = query.Get(\"endpoint\")\n\n\tif client.Bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for abs replica URL\")\n\t}\n\n\treturn client, nil\n}\n\n// Type returns \"abs\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to Azure. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) (err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.client != nil {\n\t\treturn nil\n\t}\n\n\t// Validate required configuration\n\tif c.Bucket == \"\" {\n\t\treturn fmt.Errorf(\"abs: container name is required\")\n\t}\n\n\t// Construct \u0026 parse endpoint unless already set.\n\tendpoint := c.Endpoint\n\tif endpoint == \"\" {\n\t\tif c.AccountName == \"\" {\n\t\t\treturn fmt.Errorf(\"abs: account name is required when endpoint is not specified\")\n\t\t}\n\t\tendpoint = fmt.Sprintf(\"https://%s.blob.core.windows.net\", c.AccountName)\n\t}\n\n\t// Configure client options with retry policy\n\tclientOptions := \u0026azblob.ClientOptions{\n\t\tClientOptions: azcore.ClientOptions{\n\t\t\tRetry: policy.RetryOptions{\n\t\t\t\tMaxRetries: 10,\n\t\t\t\tRetryDelay: time.Second,\n\t\t\t\tMaxRetryDelay: 30 * time.Second,\n\t\t\t\tTryTimeout: 15 * time.Minute, // Reasonable timeout for blob operations\n\t\t\t\tStatusCodes: []int{\n\t\t\t\t\thttp.StatusRequestTimeout,\n\t\t\t\t\thttp.StatusTooManyRequests,\n\t\t\t\t\thttp.StatusInternalServerError,\n\t\t\t\t\thttp.StatusBadGateway,\n\t\t\t\t\thttp.StatusServiceUnavailable,\n\t\t\t\t\thttp.StatusGatewayTimeout,\n\t\t\t\t},\n\t\t\t},\n\t\t\tTelemetry: policy.TelemetryOptions{\n\t\t\t\tApplicationID: \"litestream\",\n\t\t\t},\n\t\t},\n\t}\n\n\t// Check for SAS token first (highest priority for explicit credentials)\n\tsasToken := c.SASToken\n\tif sasToken == \"\" {\n\t\tsasToken = os.Getenv(\"LITESTREAM_AZURE_SAS_TOKEN\")\n\t}\n\n\t// Check if we have explicit credentials or should use default credential chain\n\taccountKey := c.AccountKey\n\tif accountKey == \"\" {\n\t\taccountKey = os.Getenv(\"LITESTREAM_AZURE_ACCOUNT_KEY\")\n\t}\n\n\t// Create Azure Blob Storage client with appropriate authentication\n\t// Priority: SAS token \u003e Shared key \u003e Default credential chain\n\tvar client *azblob.Client\n\tif sasToken != \"\" {\n\t\t// SAS token authentication - append token to endpoint URL\n\t\tif accountKey != \"\" {\n\t\t\tslog.Warn(\"both SAS token and account key configured, using SAS token\")\n\t\t} else {\n\t\t\tslog.Debug(\"using SAS token authentication\")\n\t\t}\n\t\t// Strip leading \"?\" if present to avoid double \"?\"\n\t\tendpointWithSAS := fmt.Sprintf(\"%s?%s\", endpoint, strings.TrimPrefix(sasToken, \"?\"))\n\t\tclient, err = azblob.NewClientWithNoCredential(endpointWithSAS, clientOptions)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot create azure blob client with SAS token: %w\", err)\n\t\t}\n\t} else if accountKey != \"\" \u0026\u0026 c.AccountName != \"\" {\n\t\t// Use shared key authentication (existing behavior)\n\t\tslog.Debug(\"using shared key authentication\")\n\t\tcredential, err := azblob.NewSharedKeyCredential(c.AccountName, accountKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot create shared key credential: %w\", err)\n\t\t}\n\t\tclient, err = azblob.NewClientWithSharedKeyCredential(endpoint, credential, clientOptions)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot create azure blob client with shared key: %w\", err)\n\t\t}\n\t} else {\n\t\t// Use default credential chain (similar to AWS SDK default credential chain)\n\t\t// This includes:\n\t\t// - Environment variables (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)\n\t\t// - Managed Identity (for Azure VMs, App Service, etc.)\n\t\t// - Azure CLI credentials\n\t\t// - Visual Studio Code credentials\n\t\tslog.Debug(\"using default credential chain (managed identity, Azure CLI, environment variables, etc.)\")\n\t\tcredential, err := azidentity.NewDefaultAzureCredential(nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot create default azure credential: %w\", err)\n\t\t}\n\t\tclient, err = azblob.NewClient(endpoint, credential, clientOptions)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot create azure blob client with default credential: %w\", err)\n\t\t}\n\t}\n\n\tc.client = client\n\treturn nil\n}\n\n// LTXFiles returns an iterator over all available LTX files.\n// Azure always uses accurate timestamps from metadata since they're included in LIST operations at zero cost.\n// The useMetadata parameter is ignored.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\treturn newLTXFileIterator(ctx, c, level, seek), nil\n}\n\n// WriteLTXFile writes an LTX file to remote storage.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(rd, \u0026buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\trc := internal.NewReadCounter(io.MultiReader(\u0026buf, rd))\n\n\t// Upload blob with proper content type, access tier, and metadata\n\t// Azure metadata keys cannot contain hyphens, so use litestreamtimestamp\n\t_, err = c.client.UploadStream(ctx, c.Bucket, key, rc, \u0026azblob.UploadStreamOptions{\n\t\tHTTPHeaders: \u0026blob.HTTPHeaders{\n\t\t\tBlobContentType: to.Ptr(\"application/octet-stream\"),\n\t\t},\n\t\tAccessTier: to.Ptr(blob.AccessTierHot), // Use Hot tier as default\n\t\tMetadata: map[string]*string{\n\t\t\tMetadataKeyTimestamp: to.Ptr(timestamp.Format(time.RFC3339Nano)),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"abs: cannot upload ltx file %q: %w\", key, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(rc.N()))\n\n\treturn \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: rc.N(),\n\t\tCreatedAt: timestamp,\n\t}, nil\n}\n\n// OpenLTXFile returns a reader for an LTX file.\n// Returns os.ErrNotExist if no matching min/max TXID is not found.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\tresp, err := c.client.DownloadStream(ctx, c.Bucket, key, \u0026azblob.DownloadStreamOptions{\n\t\tRange: blob.HTTPRange{\n\t\t\tOffset: offset,\n\t\t\tCount: size,\n\t\t},\n\t})\n\n\tif isNotExists(err) {\n\t\treturn nil, os.ErrNotExist\n\t} else if err != nil {\n\t\treturn nil, fmt.Errorf(\"abs: cannot start new reader for %q: %w\", key, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\tif resp.ContentLength != nil {\n\t\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Add(float64(*resp.ContentLength))\n\t}\n\n\treturn resp.Body, nil\n}\n\n// DeleteLTXFiles deletes LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, info := range a {\n\t\tkey := litestream.LTXFilePath(c.Path, info.Level, info.MinTXID, info.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"key\", key)\n\n\t\t_, err := c.client.DeleteBlob(ctx, c.Bucket, key, nil)\n\t\tif isNotExists(err) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot delete ltx file %q: %w\", key, err)\n\t\t}\n\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n\n// DeleteAll deletes all LTX files.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\t// List all blobs with the configured path prefix\n\tprefix := \"/\"\n\tif c.Path != \"\" {\n\t\tprefix = strings.TrimSuffix(c.Path, \"/\") + \"/\"\n\t}\n\n\tpager := c.client.NewListBlobsFlatPager(c.Bucket, \u0026azblob.ListBlobsFlatOptions{\n\t\tPrefix: \u0026prefix,\n\t\tInclude: azblob.ListBlobsInclude{Metadata: true},\n\t})\n\n\tfor pager.More() {\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"LIST\").Inc()\n\n\t\tresp, err := pager.NextPage(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"abs: cannot list blobs: %w\", err)\n\t\t}\n\n\t\tfor _, item := range resp.Segment.BlobItems {\n\t\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\n\t\t\t_, err := c.client.DeleteBlob(ctx, c.Bucket, *item.Name, nil)\n\t\t\tif isNotExists(err) {\n\t\t\t\tcontinue\n\t\t\t} else if err != nil {\n\t\t\t\treturn fmt.Errorf(\"abs: cannot delete blob %q: %w\", *item.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype ltxFileIterator struct {\n\tctx context.Context\n\tcancel context.CancelFunc\n\tclient *ReplicaClient\n\tlevel int\n\tseek ltx.TXID\n\n\tpager *runtime.Pager[azblob.ListBlobsFlatResponse]\n\tpageItems []*ltx.FileInfo\n\tpageIndex int\n\n\tclosed bool\n\terr error\n\tinfo *ltx.FileInfo\n}\n\nfunc newLTXFileIterator(ctx context.Context, client *ReplicaClient, level int, seek ltx.TXID) *ltxFileIterator {\n\tctx, cancel := context.WithCancel(ctx)\n\n\titr := \u0026ltxFileIterator{\n\t\tctx: ctx,\n\t\tcancel: cancel,\n\t\tclient: client,\n\t\tlevel: level,\n\t\tseek: seek,\n\t}\n\n\t// Create paginator for listing blobs with level prefix\n\tdir := litestream.LTXLevelDir(client.Path, level)\n\tprefix := dir + \"/\"\n\n\titr.pager = client.client.NewListBlobsFlatPager(client.Bucket, \u0026azblob.ListBlobsFlatOptions{\n\t\tPrefix: \u0026prefix,\n\t\tInclude: azblob.ListBlobsInclude{Metadata: true},\n\t})\n\n\treturn itr\n}\n\nfunc (itr *ltxFileIterator) Close() (err error) {\n\titr.closed = true\n\titr.cancel()\n\treturn itr.err\n}\n\nfunc (itr *ltxFileIterator) Next() bool {\n\tif itr.closed || itr.err != nil {\n\t\treturn false\n\t}\n\n\t// Process blobs until we find a valid LTX file\n\tfor {\n\t\t// Load next page if needed\n\t\tif itr.pageItems == nil || itr.pageIndex \u003e= len(itr.pageItems) {\n\t\t\tif !itr.loadNextPage() {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\t// Process current item from page\n\t\tif itr.pageIndex \u003c len(itr.pageItems) {\n\t\t\titr.info = itr.pageItems[itr.pageIndex]\n\t\t\titr.pageIndex++\n\t\t\treturn true\n\t\t}\n\t}\n}\n\n// loadNextPage loads the next page of blobs and extracts valid LTX files\nfunc (itr *ltxFileIterator) loadNextPage() bool {\n\tif !itr.pager.More() {\n\t\treturn false\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"LIST\").Inc()\n\n\tresp, err := itr.pager.NextPage(itr.ctx)\n\tif err != nil {\n\t\titr.err = fmt.Errorf(\"abs: cannot list blobs: %w\", err)\n\t\treturn false\n\t}\n\n\t// Extract blob items directly from the response\n\titr.pageItems = nil\n\titr.pageIndex = 0\n\n\tfor _, item := range resp.Segment.BlobItems {\n\t\tkey := path.Base(*item.Name)\n\t\tminTXID, maxTXID, err := ltx.ParseFilename(key)\n\t\tif err != nil {\n\t\t\tcontinue // Skip non-LTX files\n\t\t}\n\n\t\t// Build file info\n\t\tinfo := \u0026ltx.FileInfo{\n\t\t\tLevel: itr.level,\n\t\t\tMinTXID: minTXID,\n\t\t\tMaxTXID: maxTXID,\n\t\t\tSize: *item.Properties.ContentLength,\n\t\t}\n\n\t\t// Skip if below seek TXID\n\t\tif info.MinTXID \u003c itr.seek {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip if wrong level\n\t\tif info.Level != itr.level {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Always use accurate timestamp from metadata since it's zero-cost\n\t\t// Azure includes metadata in LIST operations, so no extra API call needed\n\t\tinfo.CreatedAt = item.Properties.CreationTime.UTC()\n\t\tif item.Metadata != nil {\n\t\t\tif ts, ok := item.Metadata[MetadataKeyTimestamp]; ok \u0026\u0026 ts != nil {\n\t\t\t\tif parsed, err := time.Parse(time.RFC3339Nano, *ts); err == nil {\n\t\t\t\t\tinfo.CreatedAt = parsed\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\titr.pageItems = append(itr.pageItems, info)\n\t}\n\n\treturn len(itr.pageItems) \u003e 0 || itr.pager.More()\n}\n\nfunc (itr *ltxFileIterator) Err() error { return itr.err }\n\nfunc (itr *ltxFileIterator) Item() *ltx.FileInfo {\n\treturn itr.info\n}\n\nfunc isNotExists(err error) bool {\n\tvar respErr *azcore.ResponseError\n\tif errors.As(err, \u0026respErr) {\n\t\treturn respErr.ErrorCode == string(bloberror.BlobNotFound) || respErr.ErrorCode == string(bloberror.ContainerNotFound)\n\t}\n\treturn false\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[22,30]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[31,41]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[42,51]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[52,58]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[59,64]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[65,76]}},{"name":"http","path":"net/http","span":{"start":[10,1],"end":[10,12],"bytes":[77,88]}},{"name":"url","path":"net/url","span":{"start":[11,1],"end":[11,11],"bytes":[89,99]}},{"name":"os","path":"os","span":{"start":[12,1],"end":[12,6],"bytes":[100,105]}},{"name":"path","path":"path","span":{"start":[13,1],"end":[13,8],"bytes":[106,113]}},{"name":"strings","path":"strings","span":{"start":[14,1],"end":[14,11],"bytes":[114,124]}},{"name":"sync","path":"sync","span":{"start":[15,1],"end":[15,8],"bytes":[125,132]}},{"name":"time","path":"time","span":{"start":[16,1],"end":[16,8],"bytes":[133,140]}},{"name":"azcore","path":"github.com/Azure/azure-sdk-for-go/sdk/azcore","span":{"start":[18,1],"end":[18,48],"bytes":[142,189]}},{"name":"policy","path":"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy","span":{"start":[19,1],"end":[19,55],"bytes":[190,244]}},{"name":"runtime","path":"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime","span":{"start":[20,1],"end":[20,56],"bytes":[245,300]}},{"name":"to","path":"github.com/Azure/azure-sdk-for-go/sdk/azcore/to","span":{"start":[21,1],"end":[21,51],"bytes":[301,351]}},{"name":"azidentity","path":"github.com/Azure/azure-sdk-for-go/sdk/azidentity","span":{"start":[22,1],"end":[22,52],"bytes":[352,403]}},{"name":"azblob","path":"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob","span":{"start":[23,1],"end":[23,56],"bytes":[404,459]}},{"name":"blob","path":"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob","span":{"start":[24,1],"end":[24,61],"bytes":[460,520]}},{"name":"bloberror","path":"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror","span":{"start":[25,1],"end":[25,66],"bytes":[521,586]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[26,1],"end":[26,27],"bytes":[587,613]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[28,1],"end":[28,37],"bytes":[615,651]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[29,1],"end":[29,46],"bytes":[652,697]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient","kind":"struct","span":{"start":[46,1],"end":[60,2],"bytes":[1263,1574]},"callables":{"github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll","kind":"method","span":{"start":[320,1],"end":[357,2],"bytes":[10261,11259]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[320,1],"end":[320,63],"bytes":[10261,10323]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"321:12":{"kind":"call","span":{"start":[321,12],"end":[321,23],"bytes":[10335,10346]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init"},"328:12":{"kind":"call","span":{"start":[328,12],"end":[328,43],"bytes":[10474,10505]},"callee":null},"331:11":{"kind":"call","span":{"start":[331,11],"end":[334,4],"bytes":[10526,10672]},"callee":null},"336:6":{"kind":"call","span":{"start":[336,6],"end":[336,18],"bytes":[10679,10691]},"callee":null},"337:3":{"kind":"call","span":{"start":[337,3],"end":[337,79],"bytes":[10696,10772]},"callee":null},"339:16":{"kind":"call","span":{"start":[339,16],"end":[339,35],"bytes":[10795,10814]},"callee":null},"341:11":{"kind":"call","span":{"start":[341,11],"end":[341,56],"bytes":[10843,10888]},"callee":null},"345:4":{"kind":"call","span":{"start":[345,4],"end":[345,82],"bytes":[10945,11023]},"callee":null},"347:14":{"kind":"call","span":{"start":[347,14],"end":[347,65],"bytes":[11044,11095]},"callee":null},"348:7":{"kind":"call","span":{"start":[348,7],"end":[348,23],"bytes":[11102,11118]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists"},"351:12":{"kind":"call","span":{"start":[351,12],"end":[351,73],"bytes":[11171,11232]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[296,1],"end":[317,2],"bytes":[9557,10223]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[296,1],"end":[296,87],"bytes":[9557,9643]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[296,1],"end":[296,87],"bytes":[9557,9643]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"297:12":{"kind":"call","span":{"start":[297,12],"end":[297,23],"bytes":[9655,9666]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init"},"302:10":{"kind":"call","span":{"start":[302,10],"end":[302,80],"bytes":[9733,9803]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"304:3":{"kind":"call","span":{"start":[304,3],"end":[304,121],"bytes":[9807,9925]},"callee":null},"306:13":{"kind":"call","span":{"start":[306,13],"end":[306,57],"bytes":[9939,9983]},"callee":null},"307:6":{"kind":"call","span":{"start":[307,6],"end":[307,22],"bytes":[9989,10005]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists"},"310:11":{"kind":"call","span":{"start":[310,11],"end":[310,69],"bytes":[10055,10113]},"callee":null},"313:3":{"kind":"call","span":{"start":[313,3],"end":[313,81],"bytes":[10121,10199]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ReplicaClient.Init":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init","kind":"method","span":{"start":[101,1],"end":[205,2],"bytes":[2766,6407]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[101,1],"end":[101,64],"bytes":[2766,2829]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":15},"body":{"102:2":{"kind":"call","span":{"start":[102,2],"end":[102,13],"bytes":[2831,2842]},"callee":null},"103:8":{"kind":"call","span":{"start":[103,8],"end":[103,21],"bytes":[2850,2863]},"callee":null},"111:10":{"kind":"call","span":{"start":[111,10],"end":[111,55],"bytes":[2970,3015]},"callee":null},"118:11":{"kind":"call","span":{"start":[118,11],"end":[118,85],"bytes":[3153,3227]},"callee":null},"120:14":{"kind":"call","span":{"start":[120,14],"end":[120,76],"bytes":[3245,3307]},"callee":null},"149:14":{"kind":"call","span":{"start":[149,14],"end":[149,53],"bytes":[4095,4134]},"callee":null},"155:16":{"kind":"call","span":{"start":[155,16],"end":[155,57],"bytes":[4286,4327]},"callee":null},"164:4":{"kind":"call","span":{"start":[164,4],"end":[164,75],"bytes":[4601,4672]},"callee":null},"166:4":{"kind":"call","span":{"start":[166,4],"end":[166,48],"bytes":[4687,4731]},"callee":null},"169:22":{"kind":"call","span":{"start":[169,22],"end":[169,87],"bytes":[4811,4876]},"callee":null},"169:53":{"kind":"call","span":{"start":[169,53],"end":[169,86],"bytes":[4842,4875]},"callee":null},"170:17":{"kind":"call","span":{"start":[170,17],"end":[170,81],"bytes":[4893,4957]},"callee":null},"172:11":{"kind":"call","span":{"start":[172,11],"end":[172,85],"bytes":[4986,5060]},"callee":null},"176:3":{"kind":"call","span":{"start":[176,3],"end":[176,48],"bytes":[5175,5220]},"callee":null},"177:22":{"kind":"call","span":{"start":[177,22],"end":[177,78],"bytes":[5242,5298]},"callee":null},"179:11":{"kind":"call","span":{"start":[179,11],"end":[179,74],"bytes":[5327,5390]},"callee":null},"181:17":{"kind":"call","span":{"start":[181,17],"end":[181,93],"bytes":[5411,5487]},"callee":null},"183:11":{"kind":"call","span":{"start":[183,11],"end":[183,86],"bytes":[5516,5591]},"callee":null},"192:3":{"kind":"call","span":{"start":[192,3],"end":[192,106],"bytes":[5919,6022]},"callee":null},"193:22":{"kind":"call","span":{"start":[193,22],"end":[193,63],"bytes":[6044,6085]},"callee":null},"195:11":{"kind":"call","span":{"start":[195,11],"end":[195,77],"bytes":[6114,6180]},"callee":null},"197:17":{"kind":"call","span":{"start":[197,17],"end":[197,70],"bytes":[6201,6254]},"callee":null},"199:11":{"kind":"call","span":{"start":[199,11],"end":[199,94],"bytes":[6283,6366]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ReplicaClient.LTXFiles":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.LTXFiles","kind":"method","span":{"start":[210,1],"end":[215,2],"bytes":[6623,6861]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[210,1],"end":[210,126],"bytes":[6623,6748]}},{"name":"level","type":"int","span":{"start":[210,1],"end":[210,126],"bytes":[6623,6748]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[210,1],"end":[210,126],"bytes":[6623,6748]}},{"name":"useMetadata","type":"bool","span":{"start":[210,1],"end":[210,126],"bytes":[6623,6748]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"211:12":{"kind":"call","span":{"start":[211,12],"end":[211,23],"bytes":[6760,6771]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init"},"214:9":{"kind":"call","span":{"start":[214,9],"end":[214,48],"bytes":[6815,6854]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.newLTXFileIterator"}}},"github.com/benbjohnson/litestream/abs.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[268,1],"end":[293,2],"bytes":[8682,9518]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[268,1],"end":[268,140],"bytes":[8682,8821]}},{"name":"level","type":"int","span":{"start":[268,1],"end":[268,140],"bytes":[8682,8821]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[268,1],"end":[268,140],"bytes":[8682,8821]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[268,1],"end":[268,140],"bytes":[8682,8821]}},{"name":"offset","type":"int64","span":{"start":[268,1],"end":[268,140],"bytes":[8682,8821]}},{"name":"size","type":"int64","span":{"start":[268,1],"end":[268,140],"bytes":[8682,8821]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"269:12":{"kind":"call","span":{"start":[269,12],"end":[269,23],"bytes":[8833,8844]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init"},"273:9":{"kind":"call","span":{"start":[273,9],"end":[273,64],"bytes":[8889,8944]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"274:15":{"kind":"call","span":{"start":[274,15],"end":[279,4],"bytes":[8959,9103]},"callee":null},"281:5":{"kind":"call","span":{"start":[281,5],"end":[281,21],"bytes":[9109,9125]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists"},"284:15":{"kind":"call","span":{"start":[284,15],"end":[284,78],"bytes":[9195,9258]},"callee":null},"287:2":{"kind":"call","span":{"start":[287,2],"end":[287,77],"bytes":[9264,9339]},"callee":null},"289:3":{"kind":"call","span":{"start":[289,3],"end":[289,78],"bytes":[9380,9455]},"callee":null},"289:83":{"kind":"call","span":{"start":[289,83],"end":[289,111],"bytes":[9460,9488]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ReplicaClient.SetLogger":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.SetLogger","kind":"method","span":{"start":[69,1],"end":[71,2],"bytes":[1763,1869]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[69,1],"end":[69,57],"bytes":[1763,1819]}}],"metrics":{"cyclomatic":1},"body":{"70:13":{"kind":"call","span":{"start":[70,13],"end":[70,48],"bytes":[1832,1867]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ReplicaClient.Type":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Type","kind":"method","span":{"start":[96,1],"end":[98,2],"bytes":[2622,2689]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[218,1],"end":[264,2],"bytes":[6917,8563]},"signature":"github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[218,1],"end":[218,144],"bytes":[6917,7060]}},{"name":"level","type":"int","span":{"start":[218,1],"end":[218,144],"bytes":[6917,7060]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[218,1],"end":[218,144],"bytes":[6917,7060]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[218,1],"end":[218,144],"bytes":[6917,7060]}},{"name":"rd","type":"io.Reader","span":{"start":[218,1],"end":[218,144],"bytes":[6917,7060]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"219:12":{"kind":"call","span":{"start":[219,12],"end":[219,23],"bytes":[7072,7083]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init"},"223:9":{"kind":"call","span":{"start":[223,9],"end":[223,64],"bytes":[7128,7183]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"227:15":{"kind":"call","span":{"start":[227,15],"end":[227,37],"bytes":[7294,7316]},"callee":null},"230:17":{"kind":"call","span":{"start":[230,17],"end":[230,42],"bytes":[7372,7397]},"callee":null},"232:15":{"kind":"call","span":{"start":[232,15],"end":[232,71],"bytes":[7429,7485]},"callee":null},"234:15":{"kind":"call","span":{"start":[234,15],"end":[234,44],"bytes":[7503,7532]},"callee":null},"237:32":{"kind":"call","span":{"start":[237,32],"end":[237,56],"bytes":[7617,7641]},"callee":null},"237:8":{"kind":"call","span":{"start":[237,8],"end":[237,57],"bytes":[7593,7642]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter"},"241:11":{"kind":"call","span":{"start":[241,11],"end":[249,4],"bytes":[7797,8145]},"callee":null},"243:21":{"kind":"call","span":{"start":[243,21],"end":[243,55],"bytes":[7926,7960]},"callee":null},"245:15":{"kind":"call","span":{"start":[245,15],"end":[245,41],"bytes":[7981,8007]},"callee":null},"247:26":{"kind":"call","span":{"start":[247,26],"end":[247,68],"bytes":[8093,8135]},"callee":null},"247:33":{"kind":"call","span":{"start":[247,33],"end":[247,67],"bytes":[8100,8134]},"callee":null},"251:15":{"kind":"call","span":{"start":[251,15],"end":[251,73],"bytes":[8177,8235]},"callee":null},"254:2":{"kind":"call","span":{"start":[254,2],"end":[254,77],"bytes":[8241,8316]},"callee":null},"255:2":{"kind":"call","span":{"start":[255,2],"end":[255,77],"bytes":[8324,8399]},"callee":null},"255:82":{"kind":"call","span":{"start":[255,82],"end":[255,97],"bytes":[8404,8419]},"callee":null},"255:90":{"kind":"call","span":{"start":[255,90],"end":[255,96],"bytes":[8412,8418]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N"},"261:14":{"kind":"call","span":{"start":[261,14],"end":[261,20],"bytes":[8522,8528]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N"}}}},"fields":{"AccountKey":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/AccountKey","kind":"field","type":"string","span":{"start":[53,1],"end":[53,20],"bytes":[1397,1416]}},"AccountName":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/AccountName","kind":"field","type":"string","span":{"start":[52,1],"end":[52,20],"bytes":[1377,1396]}},"Bucket":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/Bucket","kind":"field","type":"string","span":{"start":[58,1],"end":[58,15],"bytes":[1543,1557]}},"Endpoint":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/Endpoint","kind":"field","type":"string","span":{"start":[55,1],"end":[55,20],"bytes":[1477,1496]}},"Path":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[59,1],"end":[59,15],"bytes":[1558,1572]}},"SASToken":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/SASToken","kind":"field","type":"string","span":{"start":[54,1],"end":[54,60],"bytes":[1417,1476]}},"client":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/client","kind":"field","type":"*github.com/Azure/azure-sdk-for-go/sdk/storage/azblob.Client","span":{"start":[48,1],"end":[48,23],"bytes":[1310,1332]}},"logger":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[49,1],"end":[49,21],"bytes":[1333,1353]}},"mu":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[47,1],"end":[47,19],"bytes":[1291,1309]}}}},"ltxFileIterator":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator","kind":"struct","span":{"start":[359,1],"end":[373,2],"bytes":[11261,11544]},"callables":{"github.com/benbjohnson/litestream/abs.ltxFileIterator.Close":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.Close","kind":"method","span":{"start":[398,1],"end":[402,2],"bytes":[12130,12230]},"signature":"github.com/benbjohnson/litestream/abs.ltxFileIterator.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"400:2":{"kind":"call","span":{"start":[400,2],"end":[400,14],"bytes":[12200,12212]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ltxFileIterator.Err":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.Err","kind":"method","span":{"start":[487,1],"end":[487,59],"bytes":[14194,14252]},"signature":"github.com/benbjohnson/litestream/abs.ltxFileIterator.Err","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/abs.ltxFileIterator.Item":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.Item","kind":"method","span":{"start":[489,1],"end":[491,2],"bytes":[14254,14323]},"signature":"github.com/benbjohnson/litestream/abs.ltxFileIterator.Item","parameters":[],"return_type":"*github.com/superfly/ltx.FileInfo","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/abs.ltxFileIterator.Next":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.Next","kind":"method","span":{"start":[404,1],"end":[425,2],"bytes":[12232,12699]},"signature":"github.com/benbjohnson/litestream/abs.ltxFileIterator.Next","parameters":[],"return_type":"bool","metrics":{"cyclomatic":6},"body":{"412:47":{"kind":"call","span":{"start":[412,47],"end":[412,65],"bytes":[12460,12478]},"callee":null},"413:8":{"kind":"call","span":{"start":[413,8],"end":[413,26],"bytes":[12488,12506]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.loadNextPage"},"419:22":{"kind":"call","span":{"start":[419,22],"end":[419,40],"bytes":[12593,12611]},"callee":null}}},"github.com/benbjohnson/litestream/abs.ltxFileIterator.loadNextPage":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.loadNextPage","kind":"method","span":{"start":[428,1],"end":[485,2],"bytes":[12775,14192]},"signature":"github.com/benbjohnson/litestream/abs.ltxFileIterator.loadNextPage","parameters":[],"return_type":"bool","metrics":{"cyclomatic":10},"body":{"429:6":{"kind":"call","span":{"start":[429,6],"end":[429,22],"bytes":[12830,12846]},"callee":null},"433:2":{"kind":"call","span":{"start":[433,2],"end":[433,78],"bytes":[12869,12945]},"callee":null},"435:15":{"kind":"call","span":{"start":[435,15],"end":[435,42],"bytes":[12967,12994]},"callee":null},"437:13":{"kind":"call","span":{"start":[437,13],"end":[437,58],"bytes":[13024,13069]},"callee":null},"446:10":{"kind":"call","span":{"start":[446,10],"end":[446,31],"bytes":[13236,13257]},"callee":null},"447:28":{"kind":"call","span":{"start":[447,28],"end":[447,50],"bytes":[13285,13307]},"callee":null},"472:20":{"kind":"call","span":{"start":[472,20],"end":[472,54],"bytes":[13841,13875]},"callee":null},"475:23":{"kind":"call","span":{"start":[475,23],"end":[475,56],"bytes":[13997,14030]},"callee":null},"481:19":{"kind":"call","span":{"start":[481,19],"end":[481,46],"bytes":[14108,14135]},"callee":null},"484:35":{"kind":"call","span":{"start":[484,35],"end":[484,51],"bytes":[14174,14190]},"callee":null},"484:9":{"kind":"call","span":{"start":[484,9],"end":[484,27],"bytes":[14148,14166]},"callee":null}}}},"fields":{"cancel":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/cancel","kind":"field","type":"context.CancelFunc","span":{"start":[361,1],"end":[361,27],"bytes":[11315,11341]}},"client":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/client","kind":"field","type":"*github.com/benbjohnson/litestream/abs.ReplicaClient","span":{"start":[362,1],"end":[362,23],"bytes":[11342,11364]}},"closed":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/closed","kind":"field","type":"bool","span":{"start":[370,1],"end":[370,13],"bytes":[11494,11506]}},"ctx":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/ctx","kind":"field","type":"context.Context","span":{"start":[360,1],"end":[360,24],"bytes":[11291,11314]}},"err":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/err","kind":"field","type":"error","span":{"start":[371,1],"end":[371,14],"bytes":[11507,11520]}},"info":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/info","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[372,1],"end":[372,22],"bytes":[11521,11542]}},"level":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/level","kind":"field","type":"int","span":{"start":[363,1],"end":[363,12],"bytes":[11365,11376]}},"pageIndex":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/pageIndex","kind":"field","type":"int","span":{"start":[368,1],"end":[368,15],"bytes":[11478,11492]}},"pageItems":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/pageItems","kind":"field","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[367,1],"end":[367,27],"bytes":[11451,11477]}},"pager":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/pager","kind":"field","type":"*github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime.Pager[github.com/Azure/azure-sdk-for-go/sdk/storage/azblob.ListBlobsFlatResponse]","span":{"start":[366,1],"end":[366,56],"bytes":[11395,11450]}},"seek":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/seek","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[364,1],"end":[364,17],"bytes":[11377,11393]}}}}},"functions":{"github.com/benbjohnson/litestream/abs.NewReplicaClient":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient","kind":"function","span":{"start":[63,1],"end":[67,2],"bytes":[1637,1761]},"signature":"github.com/benbjohnson/litestream/abs.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/abs.ReplicaClient","metrics":{"cyclomatic":1},"body":{"65:11":{"kind":"call","span":{"start":[65,11],"end":[65,25],"bytes":[1712,1726]},"callee":null}}},"github.com/benbjohnson/litestream/abs.NewReplicaClientFromURL":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClientFromURL","kind":"function","span":{"start":[76,1],"end":[93,2],"bytes":[2058,2578]},"signature":"github.com/benbjohnson/litestream/abs.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[76,1],"end":[76,137],"bytes":[2058,2194]}},{"name":"host","type":"string","span":{"start":[76,1],"end":[76,137],"bytes":[2058,2194]}},{"name":"urlPath","type":"string","span":{"start":[76,1],"end":[76,137],"bytes":[2058,2194]}},{"name":"query","type":"net/url.Values","span":{"start":[76,1],"end":[76,137],"bytes":[2058,2194]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[76,1],"end":[76,137],"bytes":[2058,2194]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"77:12":{"kind":"call","span":{"start":[77,12],"end":[77,30],"bytes":[2206,2224]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient"},"81:24":{"kind":"call","span":{"start":[81,24],"end":[81,43],"bytes":[2352,2371]},"callee":null},"86:20":{"kind":"call","span":{"start":[86,20],"end":[86,41],"bytes":[2440,2461]},"callee":null},"89:15":{"kind":"call","span":{"start":[89,15],"end":[89,64],"bytes":[2503,2552]},"callee":null}}},"github.com/benbjohnson/litestream/abs.init":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.init","kind":"function","span":{"start":[32,1],"end":[34,2],"bytes":[701,789]},"signature":"github.com/benbjohnson/litestream/abs.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"33:2":{"kind":"call","span":{"start":[33,2],"end":[33,73],"bytes":[716,787]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}},"github.com/benbjohnson/litestream/abs.isNotExists":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists","kind":"function","span":{"start":[493,1],"end":[499,2],"bytes":[14325,14565]},"signature":"github.com/benbjohnson/litestream/abs.isNotExists","parameters":[{"name":"err","type":"error","span":{"start":[493,1],"end":[493,35],"bytes":[14325,14359]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"495:5":{"kind":"call","span":{"start":[495,5],"end":[495,29],"bytes":[14399,14423]},"callee":null},"496:31":{"kind":"call","span":{"start":[496,31],"end":[496,61],"bytes":[14456,14486]},"callee":null},"496:86":{"kind":"call","span":{"start":[496,86],"end":[496,121],"bytes":[14511,14546]},"callee":null}}},"github.com/benbjohnson/litestream/abs.newLTXFileIterator":{"id":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.newLTXFileIterator","kind":"function","span":{"start":[375,1],"end":[396,2],"bytes":[11546,12128]},"signature":"github.com/benbjohnson/litestream/abs.newLTXFileIterator","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[375,1],"end":[375,113],"bytes":[11546,11658]}},{"name":"client","type":"*github.com/benbjohnson/litestream/abs.ReplicaClient","span":{"start":[375,1],"end":[375,113],"bytes":[11546,11658]}},{"name":"level","type":"int","span":{"start":[375,1],"end":[375,113],"bytes":[11546,11658]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[375,1],"end":[375,113],"bytes":[11546,11658]}}],"return_type":"*github.com/benbjohnson/litestream/abs.ltxFileIterator","metrics":{"cyclomatic":1},"body":{"376:17":{"kind":"call","span":{"start":[376,17],"end":[376,40],"bytes":[11675,11698]},"callee":null},"387:9":{"kind":"call","span":{"start":[387,9],"end":[387,51],"bytes":[11879,11921]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"390:14":{"kind":"call","span":{"start":[390,14],"end":[393,4],"bytes":[11957,12113]},"callee":null}}}},"content_hash":"c7db1d1f59a3eba397f1aaf250de94ac365294a2e7006e25b32337d5bd5f5c63"},"cmd/litestream-test/load.go":{"id":"can://go/litestream/cmd/litestream-test/load.go","kind":"module","span":{"start":[1,1],"end":[334,1],"bytes":[0,8040]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\tcryptorand \"crypto/rand\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"math/rand\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"syscall\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\ntype LoadCommand struct {\n\tMain *Main\n\n\tDB string\n\tWriteRate int\n\tDuration time.Duration\n\tPattern string\n\tPayloadSize int\n\tReadRatio float64\n\tWorkers int\n}\n\ntype LoadStats struct {\n\twrites int64\n\treads int64\n\terrors int64\n\tstartTime time.Time\n\tlastReport time.Time\n\tmu sync.Mutex\n}\n\nfunc (c *LoadCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-test load\", flag.ExitOnError)\n\tfs.StringVar(\u0026c.DB, \"db\", \"\", \"Database path (required)\")\n\tfs.IntVar(\u0026c.WriteRate, \"write-rate\", 100, \"Writes per second\")\n\tfs.DurationVar(\u0026c.Duration, \"duration\", 1*time.Minute, \"How long to run\")\n\tfs.StringVar(\u0026c.Pattern, \"pattern\", \"constant\", \"Write pattern (constant, burst, random, wave)\")\n\tfs.IntVar(\u0026c.PayloadSize, \"payload-size\", 1024, \"Size of each write operation in bytes\")\n\tfs.Float64Var(\u0026c.ReadRatio, \"read-ratio\", 0.2, \"Read/write ratio (0.0-1.0)\")\n\tfs.IntVar(\u0026c.Workers, \"workers\", 1, \"Number of concurrent workers\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif c.DB == \"\" {\n\t\treturn fmt.Errorf(\"database path required\")\n\t}\n\n\tif _, err := os.Stat(c.DB); err != nil {\n\t\treturn fmt.Errorf(\"database does not exist: %w\", err)\n\t}\n\n\tslog.Info(\"Starting load generation\",\n\t\t\"db\", c.DB,\n\t\t\"write_rate\", c.WriteRate,\n\t\t\"duration\", c.Duration,\n\t\t\"pattern\", c.Pattern,\n\t\t\"payload_size\", c.PayloadSize,\n\t\t\"read_ratio\", c.ReadRatio,\n\t\t\"workers\", c.Workers,\n\t)\n\n\treturn c.generateLoad(ctx)\n}\n\nfunc (c *LoadCommand) generateLoad(ctx context.Context) error {\n\tdb, err := sql.Open(\"sqlite3\", c.DB+\"?_journal_mode=WAL\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open database: %w\", err)\n\t}\n\tdefer db.Close()\n\n\tdb.SetMaxOpenConns(c.Workers + 1)\n\tdb.SetMaxIdleConns(c.Workers)\n\n\tif err := c.ensureTestTable(db); err != nil {\n\t\treturn fmt.Errorf(\"ensure test table: %w\", err)\n\t}\n\n\tctx, cancel := context.WithTimeout(ctx, c.Duration)\n\tdefer cancel()\n\n\tsigChan := make(chan os.Signal, 1)\n\tsignal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)\n\tgo func() {\n\t\t\u003c-sigChan\n\t\tslog.Info(\"Received interrupt signal, stopping load generation\")\n\t\tcancel()\n\t}()\n\n\tstats := \u0026LoadStats{\n\t\tstartTime: time.Now(),\n\t\tlastReport: time.Now(),\n\t}\n\n\tvar wg sync.WaitGroup\n\tfor i := 0; i \u003c c.Workers; i++ {\n\t\twg.Add(1)\n\t\tgo func(workerID int) {\n\t\t\tdefer wg.Done()\n\t\t\tc.worker(ctx, db, workerID, stats)\n\t\t}(i)\n\t}\n\n\tgo c.reportStats(ctx, stats)\n\n\twg.Wait()\n\n\tc.finalReport(stats)\n\treturn nil\n}\n\nfunc (c *LoadCommand) worker(ctx context.Context, db *sql.DB, workerID int, stats *LoadStats) {\n\tticker := time.NewTicker(time.Second / time.Duration(c.WriteRate/c.Workers))\n\tdefer ticker.Stop()\n\n\tdata := make([]byte, c.PayloadSize)\n\tcryptorand.Read(data)\n\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\treturn\n\t\tcase \u003c-ticker.C:\n\t\t\t// calculateRate() returns a modulation factor (not a multiplier\n\t\t\t// on the number of ops). The ticker already fires at the base\n\t\t\t// write-rate, so rate is used only to gate whether this tick\n\t\t\t// produces an operation: \u003c=0 means skip, \u003c1 means probabilistic,\n\t\t\t// \u003e=1 means always fire.\n\t\t\trate := c.calculateRate(stats)\n\t\t\tif rate \u003c= 0 || (rate \u003c 1.0 \u0026\u0026 rand.Float64() \u003e rate) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif rand.Float64() \u003c c.ReadRatio {\n\t\t\t\tif err := c.performRead(db); err != nil {\n\t\t\t\t\tatomic.AddInt64(\u0026stats.errors, 1)\n\t\t\t\t\tslog.Error(\"Read failed\", \"error\", err)\n\t\t\t\t} else {\n\t\t\t\t\tatomic.AddInt64(\u0026stats.reads, 1)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := c.performWrite(db, data); err != nil {\n\t\t\t\t\tatomic.AddInt64(\u0026stats.errors, 1)\n\t\t\t\t\tslog.Error(\"Write failed\", \"error\", err)\n\t\t\t\t} else {\n\t\t\t\t\tatomic.AddInt64(\u0026stats.writes, 1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (c *LoadCommand) calculateRate(stats *LoadStats) float64 {\n\telapsed := time.Since(stats.startTime).Seconds()\n\n\tswitch c.Pattern {\n\tcase \"burst\":\n\t\tif int(elapsed)%10 \u003c 3 {\n\t\t\treturn 2.0\n\t\t}\n\t\treturn 0.0\n\tcase \"random\":\n\t\treturn rand.Float64() * 2.0\n\tcase \"wave\":\n\t\treturn 1.0 + 0.4*sinApprox(elapsed/10.0)\n\tdefault:\n\t\treturn 1.0\n\t}\n}\n\nfunc sinApprox(x float64) float64 {\n\tconst twoPi = 2 * 3.14159265359\n\tx = x - float64(int(x/twoPi))*twoPi\n\n\tif x \u003c 3.14159265359 {\n\t\treturn 4 * x * (3.14159265359 - x) / (3.14159265359 * 3.14159265359)\n\t}\n\tx = x - 3.14159265359\n\treturn -4 * x * (3.14159265359 - x) / (3.14159265359 * 3.14159265359)\n}\n\nfunc (c *LoadCommand) ensureTestTable(db *sql.DB) error {\n\tcreateSQL := `\n\t\tCREATE TABLE IF NOT EXISTS load_test (\n\t\t\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\t\t\tdata BLOB,\n\t\t\ttext_field TEXT,\n\t\t\tint_field INTEGER,\n\t\t\ttimestamp INTEGER\n\t\t)\n\t`\n\t_, err := db.Exec(createSQL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create table: %w\", err)\n\t}\n\n\t_, err = db.Exec(\"CREATE INDEX IF NOT EXISTS idx_load_test_timestamp ON load_test(timestamp)\")\n\treturn err\n}\n\nfunc (c *LoadCommand) performWrite(db *sql.DB, data []byte) error {\n\ttextField := fmt.Sprintf(\"load_%d\", time.Now().UnixNano())\n\tintField := rand.Int63()\n\ttimestamp := time.Now().Unix()\n\n\t_, err := db.Exec(`\n\t\tINSERT INTO load_test (data, text_field, int_field, timestamp)\n\t\tVALUES (?, ?, ?, ?)\n\t`, data, textField, intField, timestamp)\n\n\treturn err\n}\n\nfunc (c *LoadCommand) performRead(db *sql.DB) error {\n\tvar count int\n\tquery := `SELECT COUNT(*) FROM load_test WHERE timestamp \u003e ?`\n\ttimestamp := time.Now().Add(-1 * time.Hour).Unix()\n\n\treturn db.QueryRow(query, timestamp).Scan(\u0026count)\n}\n\nfunc (c *LoadCommand) reportStats(ctx context.Context, stats *LoadStats) {\n\tticker := time.NewTicker(5 * time.Second)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\treturn\n\t\tcase \u003c-ticker.C:\n\t\t\tstats.mu.Lock()\n\t\t\telapsed := time.Since(stats.lastReport).Seconds()\n\t\t\twrites := atomic.LoadInt64(\u0026stats.writes)\n\t\t\treads := atomic.LoadInt64(\u0026stats.reads)\n\t\t\terrors := atomic.LoadInt64(\u0026stats.errors)\n\n\t\t\twriteRate := float64(writes) / elapsed\n\t\t\treadRate := float64(reads) / elapsed\n\n\t\t\tslog.Info(\"Load statistics\",\n\t\t\t\t\"writes_per_sec\", fmt.Sprintf(\"%.1f\", writeRate),\n\t\t\t\t\"reads_per_sec\", fmt.Sprintf(\"%.1f\", readRate),\n\t\t\t\t\"total_writes\", writes,\n\t\t\t\t\"total_reads\", reads,\n\t\t\t\t\"errors\", errors,\n\t\t\t\t\"elapsed\", time.Since(stats.startTime).Round(time.Second),\n\t\t\t)\n\n\t\t\tatomic.StoreInt64(\u0026stats.writes, 0)\n\t\t\tatomic.StoreInt64(\u0026stats.reads, 0)\n\t\t\tstats.lastReport = time.Now()\n\t\t\tstats.mu.Unlock()\n\t\t}\n\t}\n}\n\nfunc (c *LoadCommand) finalReport(stats *LoadStats) {\n\ttotalTime := time.Since(stats.startTime)\n\twrites := atomic.LoadInt64(\u0026stats.writes)\n\treads := atomic.LoadInt64(\u0026stats.reads)\n\terrors := atomic.LoadInt64(\u0026stats.errors)\n\n\tslog.Info(\"Load generation complete\",\n\t\t\"duration\", totalTime.Round(time.Second),\n\t\t\"total_writes\", writes,\n\t\t\"total_reads\", reads,\n\t\t\"total_errors\", errors,\n\t\t\"avg_writes_per_sec\", fmt.Sprintf(\"%.1f\", float64(writes)/totalTime.Seconds()),\n\t\t\"avg_reads_per_sec\", fmt.Sprintf(\"%.1f\", float64(reads)/totalTime.Seconds()),\n\t)\n}\n\nfunc (c *LoadCommand) Usage() {\n\tfmt.Fprintln(c.Main.Stdout, `\nGenerate continuous load on a SQLite database for testing.\n\nUsage:\n\n\tlitestream-test load [options]\n\nOptions:\n\n\t-db PATH\n\t Database path (required)\n\n\t-write-rate RATE\n\t Target writes per second\n\t Default: 100\n\n\t-duration DURATION\n\t How long to run (e.g., \"10m\", \"1h\")\n\t Default: 1m\n\n\t-pattern PATTERN\n\t Write pattern: constant, burst, random, wave\n\t Default: constant\n\n\t-payload-size SIZE\n\t Size of each write operation in bytes\n\t Default: 1024\n\n\t-read-ratio RATIO\n\t Read/write ratio (0.0-1.0)\n\t Default: 0.2\n\n\t-workers COUNT\n\t Number of concurrent workers\n\t Default: 1\n\nExamples:\n\n\t# Generate constant load for 10 minutes\n\tlitestream-test load -db /tmp/test.db -write-rate 100 -duration 10m\n\n\t# Generate burst pattern load\n\tlitestream-test load -db /tmp/test.db -pattern burst -duration 1h\n\n\t# Heavy write load with multiple workers\n\tlitestream-test load -db /tmp/test.db -write-rate 1000 -workers 4 -read-ratio 0.1\n`[1:])\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"rand","path":"crypto/rand","alias":"cryptorand","span":{"start":[5,1],"end":[5,26],"bytes":[34,59]}},{"name":"sql","path":"database/sql","span":{"start":[6,1],"end":[6,16],"bytes":[60,75]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[76,83]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[84,90]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[91,102]}},{"name":"rand","path":"math/rand","span":{"start":[10,1],"end":[10,13],"bytes":[103,115]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[116,121]}},{"name":"signal","path":"os/signal","span":{"start":[12,1],"end":[12,13],"bytes":[122,134]}},{"name":"sync","path":"sync","span":{"start":[13,1],"end":[13,8],"bytes":[135,142]}},{"name":"atomic","path":"sync/atomic","span":{"start":[14,1],"end":[14,15],"bytes":[143,157]}},{"name":"syscall","path":"syscall","span":{"start":[15,1],"end":[15,11],"bytes":[158,168]}},{"name":"time","path":"time","span":{"start":[16,1],"end":[16,8],"bytes":[169,176]}},{"name":"go-sqlite3","path":"github.com/mattn/go-sqlite3","alias":"_","span":{"start":[18,1],"end":[18,33],"bytes":[178,210]}}],"types":{"LoadCommand":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand","kind":"struct","span":{"start":[21,1],"end":[31,2],"bytes":[214,393]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Run":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Run","kind":"method","span":{"start":[42,1],"end":[75,2],"bytes":[543,1709]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[42,1],"end":[42,70],"bytes":[543,612]}},{"name":"args","type":"[]string","span":{"start":[42,1],"end":[42,70],"bytes":[543,612]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"43:8":{"kind":"call","span":{"start":[43,8],"end":[43,65],"bytes":[620,677]},"callee":null},"44:2":{"kind":"call","span":{"start":[44,2],"end":[44,59],"bytes":[679,736]},"callee":null},"45:2":{"kind":"call","span":{"start":[45,2],"end":[45,65],"bytes":[738,801]},"callee":null},"46:2":{"kind":"call","span":{"start":[46,2],"end":[46,75],"bytes":[803,876]},"callee":null},"47:2":{"kind":"call","span":{"start":[47,2],"end":[47,98],"bytes":[878,974]},"callee":null},"48:2":{"kind":"call","span":{"start":[48,2],"end":[48,90],"bytes":[976,1064]},"callee":null},"49:2":{"kind":"call","span":{"start":[49,2],"end":[49,78],"bytes":[1066,1142]},"callee":null},"50:2":{"kind":"call","span":{"start":[50,2],"end":[50,69],"bytes":[1144,1211]},"callee":null},"52:12":{"kind":"call","span":{"start":[52,12],"end":[52,26],"bytes":[1243,1257]},"callee":null},"57:10":{"kind":"call","span":{"start":[57,10],"end":[57,46],"bytes":[1315,1351]},"callee":null},"60:15":{"kind":"call","span":{"start":[60,15],"end":[60,28],"bytes":[1370,1383]},"callee":null},"61:10":{"kind":"call","span":{"start":[61,10],"end":[61,56],"bytes":[1407,1453]},"callee":null},"64:2":{"kind":"call","span":{"start":[64,2],"end":[72,3],"bytes":[1459,1678]},"callee":null},"74:9":{"kind":"call","span":{"start":[74,9],"end":[74,28],"bytes":[1688,1707]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad"}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Usage":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Usage","kind":"method","span":{"start":[285,1],"end":[333,2],"bytes":[7012,8039]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"286:2":{"kind":"call","span":{"start":[286,2],"end":[332,7],"bytes":[7045,8037]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.calculateRate":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.calculateRate","kind":"method","span":{"start":[165,1],"end":[181,2],"bytes":[3859,4197]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.calculateRate","parameters":[{"name":"stats","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats","span":{"start":[165,1],"end":[165,64],"bytes":[3859,3922]}}],"return_type":"float64","metrics":{"cyclomatic":7},"body":{"166:13":{"kind":"call","span":{"start":[166,13],"end":[166,40],"bytes":[3935,3962]},"callee":null},"170:6":{"kind":"call","span":{"start":[170,6],"end":[170,18],"bytes":[4014,4026]},"callee":null},"175:10":{"kind":"call","span":{"start":[175,10],"end":[175,24],"bytes":[4092,4106]},"callee":null},"177:20":{"kind":"call","span":{"start":[177,20],"end":[177,43],"bytes":[4146,4169]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.sinApprox"}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.ensureTestTable":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.ensureTestTable","kind":"method","span":{"start":[194,1],"end":[211,2],"bytes":[4501,4946]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.ensureTestTable","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[194,1],"end":[194,58],"bytes":[4501,4558]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"204:12":{"kind":"call","span":{"start":[204,12],"end":[204,30],"bytes":[4752,4770]},"callee":null},"206:10":{"kind":"call","span":{"start":[206,10],"end":[206,45],"bytes":[4797,4832]},"callee":null},"209:11":{"kind":"call","span":{"start":[209,11],"end":[209,96],"bytes":[4847,4932]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.finalReport":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.finalReport","kind":"method","span":{"start":[269,1],"end":[283,2],"bytes":[6461,7010]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.finalReport","parameters":[{"name":"stats","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats","span":{"start":[269,1],"end":[269,54],"bytes":[6461,6514]}}],"metrics":{"cyclomatic":1},"body":{"270:15":{"kind":"call","span":{"start":[270,15],"end":[270,42],"bytes":[6529,6556]},"callee":null},"271:12":{"kind":"call","span":{"start":[271,12],"end":[271,43],"bytes":[6568,6599]},"callee":null},"272:11":{"kind":"call","span":{"start":[272,11],"end":[272,41],"bytes":[6610,6640]},"callee":null},"273:12":{"kind":"call","span":{"start":[273,12],"end":[273,43],"bytes":[6652,6683]},"callee":null},"275:2":{"kind":"call","span":{"start":[275,2],"end":[282,3],"bytes":[6686,7008]},"callee":null},"276:15":{"kind":"call","span":{"start":[276,15],"end":[276,43],"bytes":[6738,6766]},"callee":null},"280:25":{"kind":"call","span":{"start":[280,25],"end":[280,81],"bytes":[6868,6924]},"callee":null},"280:45":{"kind":"call","span":{"start":[280,45],"end":[280,60],"bytes":[6888,6903]},"callee":null},"280:61":{"kind":"call","span":{"start":[280,61],"end":[280,80],"bytes":[6904,6923]},"callee":null},"281:24":{"kind":"call","span":{"start":[281,24],"end":[281,79],"bytes":[6949,7004]},"callee":null},"281:44":{"kind":"call","span":{"start":[281,44],"end":[281,58],"bytes":[6969,6983]},"callee":null},"281:59":{"kind":"call","span":{"start":[281,59],"end":[281,78],"bytes":[6984,7003]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad","kind":"method","span":{"start":[77,1],"end":[122,2],"bytes":[1711,2676]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[77,1],"end":[77,64],"bytes":[1711,1774]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"103:15":{"kind":"call","span":{"start":[103,15],"end":[103,25],"bytes":[2393,2403]},"callee":null},"104:15":{"kind":"call","span":{"start":[104,15],"end":[104,25],"bytes":[2419,2429]},"callee":null},"109:3":{"kind":"call","span":{"start":[109,3],"end":[109,12],"bytes":[2494,2503]},"callee":null},"110:6":{"kind":"call","span":{"start":[110,6],"end":[113,7],"bytes":[2509,2593]},"callee":null,"is_goroutine":true},"116:5":{"kind":"call","span":{"start":[116,5],"end":[116,30],"bytes":[2602,2627]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.reportStats","is_goroutine":true},"118:2":{"kind":"call","span":{"start":[118,2],"end":[118,11],"bytes":[2630,2639]},"callee":null},"120:2":{"kind":"call","span":{"start":[120,2],"end":[120,22],"bytes":[2642,2662]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.finalReport"},"78:13":{"kind":"call","span":{"start":[78,13],"end":[78,59],"bytes":[1787,1833]},"callee":null},"80:10":{"kind":"call","span":{"start":[80,10],"end":[80,46],"bytes":[1860,1896]},"callee":null},"82:8":{"kind":"call","span":{"start":[82,8],"end":[82,18],"bytes":[1907,1917]},"callee":null},"84:2":{"kind":"call","span":{"start":[84,2],"end":[84,35],"bytes":[1920,1953]},"callee":null},"85:2":{"kind":"call","span":{"start":[85,2],"end":[85,31],"bytes":[1955,1984]},"callee":null},"87:12":{"kind":"call","span":{"start":[87,12],"end":[87,33],"bytes":[1997,2018]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.ensureTestTable"},"88:10":{"kind":"call","span":{"start":[88,10],"end":[88,50],"bytes":[2042,2082]},"callee":null},"91:17":{"kind":"call","span":{"start":[91,17],"end":[91,53],"bytes":[2103,2139]},"callee":null},"92:8":{"kind":"call","span":{"start":[92,8],"end":[92,16],"bytes":[2147,2155]},"callee":null},"94:13":{"kind":"call","span":{"start":[94,13],"end":[94,36],"bytes":[2169,2192]},"callee":null},"95:2":{"kind":"call","span":{"start":[95,2],"end":[95,55],"bytes":[2194,2247]},"callee":null},"96:5":{"kind":"call","span":{"start":[96,5],"end":[100,5],"bytes":[2252,2355]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad.closure_1","kind":"function","span":{"start":[96,1],"end":[100,5],"bytes":[2248,2355]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad.closure_1","parameters":[],"body":{"98:3":{"kind":"call","span":{"start":[98,3],"end":[98,67],"bytes":[2275,2339]},"callee":null},"99:3":{"kind":"call","span":{"start":[99,3],"end":[99,11],"bytes":[2342,2350]},"callee":null}}},"closure_2":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad.closure_2","kind":"function","span":{"start":[110,1],"end":[113,7],"bytes":[2504,2593]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad.closure_2","parameters":[{"name":"workerID","type":"int","span":{"start":[110,1],"end":[110,26],"bytes":[2504,2529]}}],"body":{"111:10":{"kind":"call","span":{"start":[111,10],"end":[111,19],"bytes":[2539,2548]},"callee":null},"112:4":{"kind":"call","span":{"start":[112,4],"end":[112,38],"bytes":[2552,2586]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead","kind":"method","span":{"start":[226,1],"end":[232,2],"bytes":[5301,5538]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[226,1],"end":[226,54],"bytes":[5301,5354]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"229:15":{"kind":"call","span":{"start":[229,15],"end":[229,25],"bytes":[5447,5457]},"callee":null},"231:9":{"kind":"call","span":{"start":[231,9],"end":[231,38],"bytes":[5494,5523]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performWrite":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performWrite","kind":"method","span":{"start":[213,1],"end":[224,2],"bytes":[4948,5299]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performWrite","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[213,1],"end":[213,68],"bytes":[4948,5015]}},{"name":"data","type":"[]byte","span":{"start":[213,1],"end":[213,68],"bytes":[4948,5015]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"214:15":{"kind":"call","span":{"start":[214,15],"end":[214,60],"bytes":[5030,5075]},"callee":null},"214:38":{"kind":"call","span":{"start":[214,38],"end":[214,48],"bytes":[5053,5063]},"callee":null},"215:14":{"kind":"call","span":{"start":[215,14],"end":[215,26],"bytes":[5089,5101]},"callee":null},"216:15":{"kind":"call","span":{"start":[216,15],"end":[216,25],"bytes":[5116,5126]},"callee":null},"218:12":{"kind":"call","span":{"start":[218,12],"end":[221,42],"bytes":[5146,5284]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.reportStats":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.reportStats","kind":"method","span":{"start":[234,1],"end":[267,2],"bytes":[5540,6459]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.reportStats","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[234,1],"end":[234,75],"bytes":[5540,5614]}},{"name":"stats","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats","span":{"start":[234,1],"end":[234,75],"bytes":[5540,5614]}}],"metrics":{"cyclomatic":5},"body":{"235:12":{"kind":"call","span":{"start":[235,12],"end":[235,43],"bytes":[5626,5657]},"callee":null},"236:8":{"kind":"call","span":{"start":[236,8],"end":[236,21],"bytes":[5665,5678]},"callee":null},"240:10":{"kind":"call","span":{"start":[240,10],"end":[240,20],"bytes":[5707,5717]},"callee":null},"243:4":{"kind":"call","span":{"start":[243,4],"end":[243,19],"bytes":[5751,5766]},"callee":null},"244:15":{"kind":"call","span":{"start":[244,15],"end":[244,43],"bytes":[5781,5809]},"callee":null},"245:14":{"kind":"call","span":{"start":[245,14],"end":[245,45],"bytes":[5833,5864]},"callee":null},"246:13":{"kind":"call","span":{"start":[246,13],"end":[246,43],"bytes":[5877,5907]},"callee":null},"247:14":{"kind":"call","span":{"start":[247,14],"end":[247,45],"bytes":[5921,5952]},"callee":null},"249:17":{"kind":"call","span":{"start":[249,17],"end":[249,32],"bytes":[5970,5985]},"callee":null},"250:16":{"kind":"call","span":{"start":[250,16],"end":[250,30],"bytes":[6011,6025]},"callee":null},"252:4":{"kind":"call","span":{"start":[252,4],"end":[259,5],"bytes":[6040,6318]},"callee":null},"253:23":{"kind":"call","span":{"start":[253,23],"end":[253,53],"bytes":[6091,6121]},"callee":null},"254:22":{"kind":"call","span":{"start":[254,22],"end":[254,51],"bytes":[6144,6173]},"callee":null},"258:16":{"kind":"call","span":{"start":[258,16],"end":[258,43],"bytes":[6266,6293]},"callee":null},"261:4":{"kind":"call","span":{"start":[261,4],"end":[261,39],"bytes":[6323,6358]},"callee":null},"262:4":{"kind":"call","span":{"start":[262,4],"end":[262,38],"bytes":[6362,6396]},"callee":null},"263:23":{"kind":"call","span":{"start":[263,23],"end":[263,33],"bytes":[6419,6429]},"callee":null},"264:4":{"kind":"call","span":{"start":[264,4],"end":[264,21],"bytes":[6433,6450]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.worker":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.worker","kind":"method","span":{"start":[124,1],"end":[163,2],"bytes":[2678,3857]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.worker","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[124,1],"end":[124,96],"bytes":[2678,2773]}},{"name":"db","type":"*database/sql.DB","span":{"start":[124,1],"end":[124,96],"bytes":[2678,2773]}},{"name":"workerID","type":"int","span":{"start":[124,1],"end":[124,96],"bytes":[2678,2773]}},{"name":"stats","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats","span":{"start":[124,1],"end":[124,96],"bytes":[2678,2773]}}],"metrics":{"cyclomatic":9},"body":{"125:12":{"kind":"call","span":{"start":[125,12],"end":[125,78],"bytes":[2785,2851]},"callee":null},"125:41":{"kind":"call","span":{"start":[125,41],"end":[125,77],"bytes":[2814,2850]},"callee":null},"126:8":{"kind":"call","span":{"start":[126,8],"end":[126,21],"bytes":[2859,2872]},"callee":null},"128:10":{"kind":"call","span":{"start":[128,10],"end":[128,37],"bytes":[2883,2910]},"callee":null},"129:2":{"kind":"call","span":{"start":[129,2],"end":[129,23],"bytes":[2912,2933]},"callee":null},"133:10":{"kind":"call","span":{"start":[133,10],"end":[133,20],"bytes":[2962,2972]},"callee":null},"141:12":{"kind":"call","span":{"start":[141,12],"end":[141,34],"bytes":[3311,3333]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.calculateRate"},"142:35":{"kind":"call","span":{"start":[142,35],"end":[142,49],"bytes":[3368,3382]},"callee":null},"146:7":{"kind":"call","span":{"start":[146,7],"end":[146,21],"bytes":[3418,3432]},"callee":null},"147:15":{"kind":"call","span":{"start":[147,15],"end":[147,32],"bytes":[3463,3480]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead"},"148:6":{"kind":"call","span":{"start":[148,6],"end":[148,39],"bytes":[3500,3533]},"callee":null},"149:6":{"kind":"call","span":{"start":[149,6],"end":[149,45],"bytes":[3539,3578]},"callee":null},"151:6":{"kind":"call","span":{"start":[151,6],"end":[151,38],"bytes":[3597,3629]},"callee":null},"154:15":{"kind":"call","span":{"start":[154,15],"end":[154,39],"bytes":[3662,3686]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performWrite"},"155:6":{"kind":"call","span":{"start":[155,6],"end":[155,39],"bytes":[3706,3739]},"callee":null},"156:6":{"kind":"call","span":{"start":[156,6],"end":[156,46],"bytes":[3745,3785]},"callee":null},"158:6":{"kind":"call","span":{"start":[158,6],"end":[158,39],"bytes":[3804,3837]},"callee":null}}}},"fields":{"DB":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/DB","kind":"field","type":"string","span":{"start":[24,1],"end":[24,20],"bytes":[253,272]}},"Duration":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/Duration","kind":"field","type":"time.Duration","span":{"start":[26,1],"end":[26,27],"bytes":[290,316]}},"Main":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/Main","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.Main","span":{"start":[22,1],"end":[22,12],"bytes":[240,251]}},"Pattern":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/Pattern","kind":"field","type":"string","span":{"start":[27,1],"end":[27,20],"bytes":[317,336]}},"PayloadSize":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/PayloadSize","kind":"field","type":"int","span":{"start":[28,1],"end":[28,17],"bytes":[337,353]}},"ReadRatio":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/ReadRatio","kind":"field","type":"float64","span":{"start":[29,1],"end":[29,21],"bytes":[354,374]}},"Workers":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/Workers","kind":"field","type":"int","span":{"start":[30,1],"end":[30,17],"bytes":[375,391]}},"WriteRate":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/WriteRate","kind":"field","type":"int","span":{"start":[25,1],"end":[25,17],"bytes":[273,289]}}}},"LoadStats":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats","kind":"struct","span":{"start":[33,1],"end":[40,2],"bytes":[395,541]},"callables":{},"fields":{"errors":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats/errors","kind":"field","type":"int64","span":{"start":[36,1],"end":[36,18],"bytes":[455,472]}},"lastReport":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats/lastReport","kind":"field","type":"time.Time","span":{"start":[38,1],"end":[38,22],"bytes":[495,516]}},"mu":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats/mu","kind":"field","type":"sync.Mutex","span":{"start":[39,1],"end":[39,23],"bytes":[517,539]}},"reads":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats/reads","kind":"field","type":"int64","span":{"start":[35,1],"end":[35,18],"bytes":[437,454]}},"startTime":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats/startTime","kind":"field","type":"time.Time","span":{"start":[37,1],"end":[37,22],"bytes":[473,494]}},"writes":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadStats/writes","kind":"field","type":"int64","span":{"start":[34,1],"end":[34,18],"bytes":[419,436]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream-test.sinApprox":{"id":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.sinApprox","kind":"function","span":{"start":[183,1],"end":[192,2],"bytes":[4199,4499]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.sinApprox","parameters":[{"name":"x","type":"float64","span":{"start":[183,1],"end":[183,36],"bytes":[4199,4234]}}],"return_type":"float64","metrics":{"cyclomatic":2},"body":{"185:10":{"kind":"call","span":{"start":[185,10],"end":[185,31],"bytes":[4277,4298]},"callee":null},"185:18":{"kind":"call","span":{"start":[185,18],"end":[185,30],"bytes":[4285,4297]},"callee":null}}}},"content_hash":"4a3c39cc4c30c25d8696d208ee058c8172951cbd2841a2652d75332912515aa9"},"cmd/litestream-test/main.go":{"id":"can://go/litestream/cmd/litestream-test/main.go","kind":"module","span":{"start":[1,1],"end":[124,1],"bytes":[0,2525]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nvar (\n\tVersion = \"development\"\n\tCommit = \"\"\n)\n\nfunc main() {\n\tm := NewMain()\n\tif err := m.Run(context.Background(), os.Args[1:]); err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n}\n\ntype Main struct {\n\tStdin io.Reader\n\tStdout io.Writer\n\tStderr io.Writer\n}\n\nfunc NewMain() *Main {\n\treturn \u0026Main{\n\t\tStdin: os.Stdin,\n\t\tStdout: os.Stdout,\n\t\tStderr: os.Stderr,\n\t}\n}\n\nfunc (m *Main) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-test\", flag.ExitOnError)\n\tfs.Usage = m.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() == 0 || fs.Arg(0) == \"help\" {\n\t\tm.Usage()\n\t\treturn nil\n\t}\n\n\tswitch fs.Arg(0) {\n\tcase \"populate\":\n\t\treturn (\u0026PopulateCommand{Main: m}).Run(ctx, fs.Args()[1:])\n\tcase \"load\":\n\t\treturn (\u0026LoadCommand{Main: m}).Run(ctx, fs.Args()[1:])\n\tcase \"shrink\":\n\t\treturn (\u0026ShrinkCommand{Main: m}).Run(ctx, fs.Args()[1:])\n\tcase \"validate\":\n\t\treturn (\u0026ValidateCommand{Main: m}).Run(ctx, fs.Args()[1:])\n\tcase \"version\":\n\t\treturn (\u0026VersionCommand{Main: m}).Run(ctx, fs.Args()[1:])\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown command: %s\", fs.Arg(0))\n\t}\n}\n\nfunc (m *Main) Usage() {\n\tfmt.Fprintln(m.Stdout, `\nlitestream-test is a testing harness for Litestream database replication.\n\nUsage:\n\n\tlitestream-test \u003ccommand\u003e [arguments]\n\nCommands:\n\n\tpopulate Quickly populate a database to a target size\n\tload Generate continuous load on a database\n\tshrink Shrink a database by deleting data\n\tvalidate Validate replication integrity\n\tversion Show version information\n\nUse \"litestream-test \u003ccommand\u003e -h\" for more information about a command.\n`[1:])\n}\n\ntype VersionCommand struct {\n\tMain *Main\n}\n\nfunc (c *VersionCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-test version\", flag.ExitOnError)\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Fprintf(c.Main.Stdout, \"litestream-test %s\\n\", Version)\n\tif Commit != \"\" {\n\t\tfmt.Fprintf(c.Main.Stdout, \"commit: %s\\n\", Commit)\n\t}\n\tfmt.Fprintf(c.Main.Stdout, \"go: %s\\n\", strings.TrimPrefix(runtime.Version(), \"go\"))\n\treturn nil\n}\n\nfunc (c *VersionCommand) Usage() {\n\tfmt.Fprintln(c.Main.Stdout, `\nShow version information for litestream-test.\n\nUsage:\n\n\tlitestream-test version\n`[1:])\n}\n\nfunc init() {\n\tlogger := slog.New(slog.NewTextHandler(os.Stderr, \u0026slog.HandlerOptions{\n\t\tLevel: slog.LevelInfo,\n\t}))\n\tslog.SetDefault(logger)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"flag","path":"flag","span":{"start":[5,1],"end":[5,8],"bytes":[34,41]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[42,48]}},{"name":"io","path":"io","span":{"start":[7,1],"end":[7,6],"bytes":[49,54]}},{"name":"slog","path":"log/slog","span":{"start":[8,1],"end":[8,12],"bytes":[55,66]}},{"name":"os","path":"os","span":{"start":[9,1],"end":[9,6],"bytes":[67,72]}},{"name":"runtime","path":"runtime","span":{"start":[10,1],"end":[10,11],"bytes":[73,83]}},{"name":"strings","path":"strings","span":{"start":[11,1],"end":[11,11],"bytes":[84,94]}}],"types":{"Main":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main","kind":"struct","span":{"start":[27,1],"end":[31,2],"bytes":[292,366]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","kind":"method","span":{"start":[41,1],"end":[67,2],"bytes":[474,1216]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[41,1],"end":[41,63],"bytes":[474,536]}},{"name":"args","type":"[]string","span":{"start":[41,1],"end":[41,63],"bytes":[474,536]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"42:8":{"kind":"call","span":{"start":[42,8],"end":[42,60],"bytes":[544,596]},"callee":null},"44:12":{"kind":"call","span":{"start":[44,12],"end":[44,26],"bytes":[628,642]},"callee":null},"48:23":{"kind":"call","span":{"start":[48,23],"end":[48,32],"bytes":[696,705]},"callee":null},"48:5":{"kind":"call","span":{"start":[48,5],"end":[48,14],"bytes":[678,687]},"callee":null},"49:3":{"kind":"call","span":{"start":[49,3],"end":[49,12],"bytes":[720,729]},"callee":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Usage"},"53:9":{"kind":"call","span":{"start":[53,9],"end":[53,18],"bytes":[755,764]},"callee":null},"55:10":{"kind":"call","span":{"start":[55,10],"end":[55,61],"bytes":[794,845]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run"},"55:47":{"kind":"call","span":{"start":[55,47],"end":[55,56],"bytes":[831,840]},"callee":null},"57:10":{"kind":"call","span":{"start":[57,10],"end":[57,57],"bytes":[869,916]},"callee":null},"57:43":{"kind":"call","span":{"start":[57,43],"end":[57,52],"bytes":[902,911]},"callee":null},"59:10":{"kind":"call","span":{"start":[59,10],"end":[59,59],"bytes":[942,991]},"callee":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Run"},"59:45":{"kind":"call","span":{"start":[59,45],"end":[59,54],"bytes":[977,986]},"callee":null},"61:10":{"kind":"call","span":{"start":[61,10],"end":[61,61],"bytes":[1019,1070]},"callee":null},"61:47":{"kind":"call","span":{"start":[61,47],"end":[61,56],"bytes":[1056,1065]},"callee":null},"63:10":{"kind":"call","span":{"start":[63,10],"end":[63,60],"bytes":[1097,1147]},"callee":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Run"},"63:46":{"kind":"call","span":{"start":[63,46],"end":[63,55],"bytes":[1133,1142]},"callee":null},"65:10":{"kind":"call","span":{"start":[65,10],"end":[65,54],"bytes":[1167,1211]},"callee":null},"65:44":{"kind":"call","span":{"start":[65,44],"end":[65,53],"bytes":[1201,1210]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.Main.Usage":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Usage","kind":"method","span":{"start":[69,1],"end":[87,2],"bytes":[1218,1725]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.Main.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"70:2":{"kind":"call","span":{"start":[70,2],"end":[86,7],"bytes":[1244,1723]},"callee":null}}}},"fields":{"Stderr":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/Stderr","kind":"field","type":"io.Writer","span":{"start":[30,1],"end":[30,18],"bytes":[347,364]}},"Stdin":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/Stdin","kind":"field","type":"io.Reader","span":{"start":[28,1],"end":[28,18],"bytes":[311,328]}},"Stdout":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/Stdout","kind":"field","type":"io.Writer","span":{"start":[29,1],"end":[29,18],"bytes":[329,346]}}}},"VersionCommand":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand","kind":"struct","span":{"start":[89,1],"end":[91,2],"bytes":[1727,1769]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Run":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Run","kind":"method","span":{"start":[93,1],"end":[106,2],"bytes":[1771,2223]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[93,1],"end":[93,73],"bytes":[1771,1843]}},{"name":"args","type":"[]string","span":{"start":[93,1],"end":[93,73],"bytes":[1771,1843]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"100:2":{"kind":"call","span":{"start":[100,2],"end":[100,61],"bytes":[1990,2049]},"callee":null},"102:3":{"kind":"call","span":{"start":[102,3],"end":[102,53],"bytes":[2071,2121]},"callee":null},"104:2":{"kind":"call","span":{"start":[104,2],"end":[104,85],"bytes":[2126,2209]},"callee":null},"104:41":{"kind":"call","span":{"start":[104,41],"end":[104,84],"bytes":[2165,2208]},"callee":null},"104:60":{"kind":"call","span":{"start":[104,60],"end":[104,77],"bytes":[2184,2201]},"callee":null},"94:8":{"kind":"call","span":{"start":[94,8],"end":[94,68],"bytes":[1851,1911]},"callee":null},"96:12":{"kind":"call","span":{"start":[96,12],"end":[96,26],"bytes":[1943,1957]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Usage":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Usage","kind":"method","span":{"start":[108,1],"end":[116,2],"bytes":[2225,2379]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"109:2":{"kind":"call","span":{"start":[109,2],"end":[115,7],"bytes":[2261,2377]},"callee":null}}}},"fields":{"Main":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand/Main","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.Main","span":{"start":[90,1],"end":[90,12],"bytes":[1756,1767]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream-test.NewMain":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.NewMain","kind":"function","span":{"start":[33,1],"end":[39,2],"bytes":[368,472]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.NewMain","parameters":[],"return_type":"*github.com/benbjohnson/litestream/cmd/litestream-test.Main","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream-test.init":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.init","kind":"function","span":{"start":[118,1],"end":[123,2],"bytes":[2381,2524]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"119:12":{"kind":"call","span":{"start":[119,12],"end":[121,5],"bytes":[2406,2497]},"callee":null},"119:21":{"kind":"call","span":{"start":[119,21],"end":[121,4],"bytes":[2415,2496]},"callee":null},"122:2":{"kind":"call","span":{"start":[122,2],"end":[122,25],"bytes":[2499,2522]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.main":{"id":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.main","kind":"function","span":{"start":[19,1],"end":[25,2],"bytes":[146,290]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.main","parameters":[],"metrics":{"cyclomatic":2},"body":{"20:7":{"kind":"call","span":{"start":[20,7],"end":[20,16],"bytes":[166,175]},"callee":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.NewMain"},"21:12":{"kind":"call","span":{"start":[21,12],"end":[21,52],"bytes":[187,227]},"callee":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run"},"21:18":{"kind":"call","span":{"start":[21,18],"end":[21,38],"bytes":[193,213]},"callee":null},"22:3":{"kind":"call","span":{"start":[22,3],"end":[22,31],"bytes":[244,272]},"callee":null},"23:3":{"kind":"call","span":{"start":[23,3],"end":[23,13],"bytes":[275,285]},"callee":null}}}},"content_hash":"1aec89087aea47d1cabfc7a216b2c37b7b7cf96292167f8e81e81125e0bc17b9"},"cmd/litestream-test/populate.go":{"id":"can://go/litestream/cmd/litestream-test/populate.go","kind":"module","span":{"start":[1,1],"end":[323,1],"bytes":[0,7805]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\tcryptorand \"crypto/rand\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"math/rand\"\n\t\"os\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\ntype PopulateCommand struct {\n\tMain *Main\n\n\tDB string\n\tTargetSize string\n\tRowSize int\n\tBatchSize int\n\tTableCount int\n\tIndexRatio float64\n\tPageSize int\n}\n\nfunc (c *PopulateCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-test populate\", flag.ExitOnError)\n\tfs.StringVar(\u0026c.DB, \"db\", \"\", \"Database path (required)\")\n\tfs.StringVar(\u0026c.TargetSize, \"target-size\", \"100MB\", \"Target database size (e.g., 1GB, 500MB)\")\n\tfs.IntVar(\u0026c.RowSize, \"row-size\", 1024, \"Average row size in bytes\")\n\tfs.IntVar(\u0026c.BatchSize, \"batch-size\", 1000, \"Rows per transaction\")\n\tfs.IntVar(\u0026c.TableCount, \"table-count\", 1, \"Number of tables to create\")\n\tfs.Float64Var(\u0026c.IndexRatio, \"index-ratio\", 0.2, \"Percentage of columns to index (0.0-1.0)\")\n\tfs.IntVar(\u0026c.PageSize, \"page-size\", 4096, \"SQLite page size in bytes\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif c.DB == \"\" {\n\t\treturn fmt.Errorf(\"database path required\")\n\t}\n\n\ttargetBytes, err := parseSize(c.TargetSize)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid target size: %w\", err)\n\t}\n\n\tslog.Info(\"Starting database population\",\n\t\t\"db\", c.DB,\n\t\t\"target_size\", c.TargetSize,\n\t\t\"row_size\", c.RowSize,\n\t\t\"batch_size\", c.BatchSize,\n\t\t\"table_count\", c.TableCount,\n\t\t\"page_size\", c.PageSize,\n\t)\n\n\tif err := c.populateDatabase(ctx, targetBytes); err != nil {\n\t\treturn fmt.Errorf(\"populate database: %w\", err)\n\t}\n\n\tslog.Info(\"Database population complete\", \"db\", c.DB)\n\treturn nil\n}\n\nfunc (c *PopulateCommand) populateDatabase(ctx context.Context, targetBytes int64) error {\n\tif err := os.Remove(c.DB); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\tslog.Warn(\"Could not remove existing database\", \"error\", err)\n\t}\n\n\tdb, err := sql.Open(\"sqlite3\", c.DB)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open database: %w\", err)\n\t}\n\tdefer db.Close()\n\n\tif _, err := db.Exec(fmt.Sprintf(\"PRAGMA page_size = %d\", c.PageSize)); err != nil {\n\t\treturn fmt.Errorf(\"set page size: %w\", err)\n\t}\n\n\tif _, err := db.Exec(\"PRAGMA journal_mode = WAL\"); err != nil {\n\t\treturn fmt.Errorf(\"set journal mode: %w\", err)\n\t}\n\n\tif _, err := db.Exec(\"PRAGMA synchronous = NORMAL\"); err != nil {\n\t\treturn fmt.Errorf(\"set synchronous: %w\", err)\n\t}\n\n\tfor i := 0; i \u003c c.TableCount; i++ {\n\t\ttableName := fmt.Sprintf(\"test_table_%d\", i)\n\n\t\tcreateSQL := fmt.Sprintf(`\n\t\t\tCREATE TABLE %s (\n\t\t\t\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\t\t\t\tdata BLOB,\n\t\t\t\ttext_field TEXT,\n\t\t\t\tint_field INTEGER,\n\t\t\t\tfloat_field REAL,\n\t\t\t\ttimestamp INTEGER\n\t\t\t)\n\t\t`, tableName)\n\n\t\tif _, err := db.Exec(createSQL); err != nil {\n\t\t\treturn fmt.Errorf(\"create table %s: %w\", tableName, err)\n\t\t}\n\n\t\tif c.IndexRatio \u003e 0 {\n\t\t\tif rand.Float64() \u003c c.IndexRatio {\n\t\t\t\tindexSQL := fmt.Sprintf(\"CREATE INDEX idx_%s_timestamp ON %s(timestamp)\", tableName, tableName)\n\t\t\t\tif _, err := db.Exec(indexSQL); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"create index: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif rand.Float64() \u003c c.IndexRatio {\n\t\t\t\tindexSQL := fmt.Sprintf(\"CREATE INDEX idx_%s_int ON %s(int_field)\", tableName, tableName)\n\t\t\t\tif _, err := db.Exec(indexSQL); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"create index: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttotalRows := int(targetBytes / int64(c.RowSize))\n\trowsPerTable := totalRows / c.TableCount\n\tif rowsPerTable == 0 {\n\t\trowsPerTable = 1\n\t}\n\n\tslog.Info(\"Populating database\",\n\t\t\"target_bytes\", targetBytes,\n\t\t\"total_rows\", totalRows,\n\t\t\"rows_per_table\", rowsPerTable,\n\t)\n\n\tstartTime := time.Now()\n\tfor tableIdx := 0; tableIdx \u003c c.TableCount; tableIdx++ {\n\t\ttableName := fmt.Sprintf(\"test_table_%d\", tableIdx)\n\n\t\tif err := c.populateTable(ctx, db, tableName, rowsPerTable); err != nil {\n\t\t\treturn fmt.Errorf(\"populate table %s: %w\", tableName, err)\n\t\t}\n\n\t\tcurrentSize, _ := getDatabaseSize(c.DB)\n\t\tprogress := float64(currentSize) / float64(targetBytes) * 100\n\t\tslog.Info(\"Progress\",\n\t\t\t\"table\", tableName,\n\t\t\t\"current_size_mb\", currentSize/1024/1024,\n\t\t\t\"progress_percent\", fmt.Sprintf(\"%.1f\", progress),\n\t\t)\n\n\t\tif currentSize \u003e= targetBytes {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tduration := time.Since(startTime)\n\tfinalSize, _ := getDatabaseSize(c.DB)\n\n\tslog.Info(\"Population complete\",\n\t\t\"duration\", duration,\n\t\t\"final_size_mb\", finalSize/1024/1024,\n\t\t\"throughput_mb_per_sec\", fmt.Sprintf(\"%.2f\", float64(finalSize)/1024/1024/duration.Seconds()),\n\t)\n\n\treturn nil\n}\n\nfunc (c *PopulateCommand) populateTable(ctx context.Context, db *sql.DB, tableName string, rowCount int) error {\n\tdata := make([]byte, c.RowSize)\n\n\tfor i := 0; i \u003c rowCount; i += c.BatchSize {\n\t\ttx, err := db.Begin()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"begin transaction: %w\", err)\n\t\t}\n\n\t\tstmt, err := tx.Prepare(fmt.Sprintf(`\n\t\t\tINSERT INTO %s (data, text_field, int_field, float_field, timestamp)\n\t\t\tVALUES (?, ?, ?, ?, ?)\n\t\t`, tableName))\n\t\tif err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn fmt.Errorf(\"prepare statement: %w\", err)\n\t\t}\n\n\t\tbatchEnd := i + c.BatchSize\n\t\tif batchEnd \u003e rowCount {\n\t\t\tbatchEnd = rowCount\n\t\t}\n\n\t\tfor j := i; j \u003c batchEnd; j++ {\n\t\t\tcryptorand.Read(data)\n\t\t\ttextField := fmt.Sprintf(\"row_%d_%d\", i, j)\n\t\t\tintField := rand.Int63()\n\t\t\tfloatField := rand.Float64() * 1000\n\t\t\ttimestamp := time.Now().Unix()\n\n\t\t\tif _, err := stmt.Exec(data, textField, intField, floatField, timestamp); err != nil {\n\t\t\t\tstmt.Close()\n\t\t\t\ttx.Rollback()\n\t\t\t\treturn fmt.Errorf(\"insert row: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tstmt.Close()\n\t\tif err := tx.Commit(); err != nil {\n\t\t\treturn fmt.Errorf(\"commit transaction: %w\", err)\n\t\t}\n\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *PopulateCommand) Usage() {\n\tfmt.Fprintln(c.Main.Stdout, `\nPopulate a SQLite database to a target size for testing.\n\nUsage:\n\n\tlitestream-test populate [options]\n\nOptions:\n\n\t-db PATH\n\t Database path (required)\n\n\t-target-size SIZE\n\t Target database size (e.g., \"1GB\", \"500MB\")\n\t Default: 100MB\n\n\t-row-size SIZE\n\t Average row size in bytes\n\t Default: 1024\n\n\t-batch-size COUNT\n\t Number of rows per transaction\n\t Default: 1000\n\n\t-table-count COUNT\n\t Number of tables to create\n\t Default: 1\n\n\t-index-ratio RATIO\n\t Percentage of columns to index (0.0-1.0)\n\t Default: 0.2\n\n\t-page-size SIZE\n\t SQLite page size in bytes\n\t Default: 4096\n\nExamples:\n\n\t# Create a 1GB database with default settings\n\tlitestream-test populate -db /tmp/test.db -target-size 1GB\n\n\t# Create a 2GB database with larger rows\n\tlitestream-test populate -db /tmp/test.db -target-size 2GB -row-size 4096\n\n\t# Test lock page with different page sizes\n\tlitestream-test populate -db /tmp/test.db -target-size 1.5GB -page-size 8192\n`[1:])\n}\n\nfunc parseSize(s string) (int64, error) {\n\t// Check suffixes in order from longest to shortest to avoid \"B\" matching before \"MB\"\n\tsuffixes := []struct {\n\t\tsuffix string\n\t\tmultiplier int64\n\t}{\n\t\t{\"TB\", 1024 * 1024 * 1024 * 1024},\n\t\t{\"GB\", 1024 * 1024 * 1024},\n\t\t{\"MB\", 1024 * 1024},\n\t\t{\"KB\", 1024},\n\t\t{\"B\", 1},\n\t}\n\n\tfor _, sf := range suffixes {\n\t\tif len(s) \u003e len(sf.suffix) \u0026\u0026 s[len(s)-len(sf.suffix):] == sf.suffix {\n\t\t\tvar value float64\n\t\t\tif _, err := fmt.Sscanf(s[:len(s)-len(sf.suffix)], \"%f\", \u0026value); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\treturn int64(value * float64(sf.multiplier)), nil\n\t\t}\n\t}\n\n\tvar value int64\n\tif _, err := fmt.Sscanf(s, \"%d\", \u0026value); err != nil {\n\t\treturn 0, err\n\t}\n\treturn value, nil\n}\n\nfunc getDatabaseSize(path string) (int64, error) {\n\tinfo, err := os.Stat(path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tsize := info.Size()\n\n\twalPath := path + \"-wal\"\n\tif walInfo, err := os.Stat(walPath); err == nil {\n\t\tsize += walInfo.Size()\n\t}\n\n\tshmPath := path + \"-shm\"\n\tif shmInfo, err := os.Stat(shmPath); err == nil {\n\t\tsize += shmInfo.Size()\n\t}\n\n\treturn size, nil\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"rand","path":"crypto/rand","alias":"cryptorand","span":{"start":[5,1],"end":[5,26],"bytes":[34,59]}},{"name":"sql","path":"database/sql","span":{"start":[6,1],"end":[6,16],"bytes":[60,75]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[76,83]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[84,90]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[91,102]}},{"name":"rand","path":"math/rand","span":{"start":[10,1],"end":[10,13],"bytes":[103,115]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[116,121]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[122,129]}},{"name":"go-sqlite3","path":"github.com/mattn/go-sqlite3","alias":"_","span":{"start":[14,1],"end":[14,33],"bytes":[131,163]}}],"types":{"PopulateCommand":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand","kind":"struct","span":{"start":[17,1],"end":[27,2],"bytes":[167,333]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run","kind":"method","span":{"start":[29,1],"end":[67,2],"bytes":[335,1662]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[29,1],"end":[29,74],"bytes":[335,408]}},{"name":"args","type":"[]string","span":{"start":[29,1],"end":[29,74],"bytes":[335,408]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"30:8":{"kind":"call","span":{"start":[30,8],"end":[30,69],"bytes":[416,477]},"callee":null},"31:2":{"kind":"call","span":{"start":[31,2],"end":[31,59],"bytes":[479,536]},"callee":null},"32:2":{"kind":"call","span":{"start":[32,2],"end":[32,96],"bytes":[538,632]},"callee":null},"33:2":{"kind":"call","span":{"start":[33,2],"end":[33,70],"bytes":[634,702]},"callee":null},"34:2":{"kind":"call","span":{"start":[34,2],"end":[34,69],"bytes":[704,771]},"callee":null},"35:2":{"kind":"call","span":{"start":[35,2],"end":[35,74],"bytes":[773,845]},"callee":null},"36:2":{"kind":"call","span":{"start":[36,2],"end":[36,94],"bytes":[847,939]},"callee":null},"37:2":{"kind":"call","span":{"start":[37,2],"end":[37,72],"bytes":[941,1011]},"callee":null},"39:12":{"kind":"call","span":{"start":[39,12],"end":[39,26],"bytes":[1043,1057]},"callee":null},"44:10":{"kind":"call","span":{"start":[44,10],"end":[44,46],"bytes":[1115,1151]},"callee":null},"47:22":{"kind":"call","span":{"start":[47,22],"end":[47,45],"bytes":[1177,1200]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.parseSize"},"49:10":{"kind":"call","span":{"start":[49,10],"end":[49,52],"bytes":[1227,1269]},"callee":null},"52:2":{"kind":"call","span":{"start":[52,2],"end":[59,3],"bytes":[1275,1476]},"callee":null},"61:12":{"kind":"call","span":{"start":[61,12],"end":[61,48],"bytes":[1489,1525]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase"},"62:10":{"kind":"call","span":{"start":[62,10],"end":[62,50],"bytes":[1549,1589]},"callee":null},"65:2":{"kind":"call","span":{"start":[65,2],"end":[65,55],"bytes":[1595,1648]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Usage":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Usage","kind":"method","span":{"start":[223,1],"end":[271,2],"bytes":[5669,6710]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"224:2":{"kind":"call","span":{"start":[224,2],"end":[270,7],"bytes":[5706,6708]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase","kind":"method","span":{"start":[69,1],"end":[169,2],"bytes":[1664,4461]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[69,1],"end":[69,91],"bytes":[1664,1754]}},{"name":"targetBytes","type":"int64","span":{"start":[69,1],"end":[69,91],"bytes":[1664,1754]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":17},"body":{"106:16":{"kind":"call","span":{"start":[106,16],"end":[106,34],"bytes":[2700,2718]},"callee":null},"107:11":{"kind":"call","span":{"start":[107,11],"end":[107,60],"bytes":[2743,2792]},"callee":null},"111:7":{"kind":"call","span":{"start":[111,7],"end":[111,21],"bytes":[2828,2842]},"callee":null},"112:17":{"kind":"call","span":{"start":[112,17],"end":[112,100],"bytes":[2876,2959]},"callee":null},"113:18":{"kind":"call","span":{"start":[113,18],"end":[113,35],"bytes":[2977,2994]},"callee":null},"114:13":{"kind":"call","span":{"start":[114,13],"end":[114,48],"bytes":[3021,3056]},"callee":null},"117:7":{"kind":"call","span":{"start":[117,7],"end":[117,21],"bytes":[3074,3088]},"callee":null},"118:17":{"kind":"call","span":{"start":[118,17],"end":[118,94],"bytes":[3122,3199]},"callee":null},"119:18":{"kind":"call","span":{"start":[119,18],"end":[119,35],"bytes":[3217,3234]},"callee":null},"120:13":{"kind":"call","span":{"start":[120,13],"end":[120,48],"bytes":[3261,3296]},"callee":null},"126:15":{"kind":"call","span":{"start":[126,15],"end":[126,50],"bytes":[3330,3365]},"callee":null},"126:33":{"kind":"call","span":{"start":[126,33],"end":[126,49],"bytes":[3348,3364]},"callee":null},"132:2":{"kind":"call","span":{"start":[132,2],"end":[136,3],"bytes":[3456,3583]},"callee":null},"138:15":{"kind":"call","span":{"start":[138,15],"end":[138,25],"bytes":[3599,3609]},"callee":null},"140:16":{"kind":"call","span":{"start":[140,16],"end":[140,54],"bytes":[3683,3721]},"callee":null},"142:13":{"kind":"call","span":{"start":[142,13],"end":[142,62],"bytes":[3735,3784]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateTable"},"143:11":{"kind":"call","span":{"start":[143,11],"end":[143,62],"bytes":[3809,3860]},"callee":null},"146:21":{"kind":"call","span":{"start":[146,21],"end":[146,42],"bytes":[3886,3907]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"},"147:15":{"kind":"call","span":{"start":[147,15],"end":[147,35],"bytes":[3922,3942]},"callee":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead"},"147:38":{"kind":"call","span":{"start":[147,38],"end":[147,58],"bytes":[3945,3965]},"callee":null},"148:3":{"kind":"call","span":{"start":[148,3],"end":[152,4],"bytes":[3974,4121]},"callee":null},"151:24":{"kind":"call","span":{"start":[151,24],"end":[151,53],"bytes":[4087,4116]},"callee":null},"159:14":{"kind":"call","span":{"start":[159,14],"end":[159,35],"bytes":[4187,4208]},"callee":null},"160:18":{"kind":"call","span":{"start":[160,18],"end":[160,39],"bytes":[4226,4247]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"},"162:2":{"kind":"call","span":{"start":[162,2],"end":[166,3],"bytes":[4250,4446]},"callee":null},"165:28":{"kind":"call","span":{"start":[165,28],"end":[165,96],"bytes":[4374,4442]},"callee":null},"165:48":{"kind":"call","span":{"start":[165,48],"end":[165,66],"bytes":[4394,4412]},"callee":null},"165:77":{"kind":"call","span":{"start":[165,77],"end":[165,95],"bytes":[4423,4441]},"callee":null},"70:12":{"kind":"call","span":{"start":[70,12],"end":[70,27],"bytes":[1766,1781]},"callee":null},"70:44":{"kind":"call","span":{"start":[70,44],"end":[70,62],"bytes":[1798,1816]},"callee":null},"71:3":{"kind":"call","span":{"start":[71,3],"end":[71,64],"bytes":[1821,1882]},"callee":null},"74:13":{"kind":"call","span":{"start":[74,13],"end":[74,38],"bytes":[1899,1924]},"callee":null},"76:10":{"kind":"call","span":{"start":[76,10],"end":[76,46],"bytes":[1951,1987]},"callee":null},"78:8":{"kind":"call","span":{"start":[78,8],"end":[78,18],"bytes":[1998,2008]},"callee":null},"80:15":{"kind":"call","span":{"start":[80,15],"end":[80,72],"bytes":[2024,2081]},"callee":null},"80:23":{"kind":"call","span":{"start":[80,23],"end":[80,71],"bytes":[2032,2080]},"callee":null},"81:10":{"kind":"call","span":{"start":[81,10],"end":[81,46],"bytes":[2105,2141]},"callee":null},"84:15":{"kind":"call","span":{"start":[84,15],"end":[84,51],"bytes":[2160,2196]},"callee":null},"85:10":{"kind":"call","span":{"start":[85,10],"end":[85,49],"bytes":[2220,2259]},"callee":null},"88:15":{"kind":"call","span":{"start":[88,15],"end":[88,53],"bytes":[2278,2316]},"callee":null},"89:10":{"kind":"call","span":{"start":[89,10],"end":[89,48],"bytes":[2340,2378]},"callee":null},"93:16":{"kind":"call","span":{"start":[93,16],"end":[93,47],"bytes":[2435,2466]},"callee":null},"95:16":{"kind":"call","span":{"start":[95,16],"end":[104,16],"bytes":[2483,2683]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateTable":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateTable","kind":"method","span":{"start":[171,1],"end":[221,2],"bytes":[4463,5667]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateTable","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[171,1],"end":[171,113],"bytes":[4463,4575]}},{"name":"db","type":"*database/sql.DB","span":{"start":[171,1],"end":[171,113],"bytes":[4463,4575]}},{"name":"tableName","type":"string","span":{"start":[171,1],"end":[171,113],"bytes":[4463,4575]}},{"name":"rowCount","type":"int","span":{"start":[171,1],"end":[171,113],"bytes":[4463,4575]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"172:10":{"kind":"call","span":{"start":[172,10],"end":[172,33],"bytes":[4585,4608]},"callee":null},"175:14":{"kind":"call","span":{"start":[175,14],"end":[175,24],"bytes":[4669,4679]},"callee":null},"177:11":{"kind":"call","span":{"start":[177,11],"end":[177,51],"bytes":[4708,4748]},"callee":null},"180:16":{"kind":"call","span":{"start":[180,16],"end":[183,17],"bytes":[4769,4908]},"callee":null},"180:27":{"kind":"call","span":{"start":[180,27],"end":[183,16],"bytes":[4780,4907]},"callee":null},"185:4":{"kind":"call","span":{"start":[185,4],"end":[185,17],"bytes":[4930,4943]},"callee":null},"186:11":{"kind":"call","span":{"start":[186,11],"end":[186,51],"bytes":[4954,4994]},"callee":null},"195:4":{"kind":"call","span":{"start":[195,4],"end":[195,25],"bytes":[5122,5143]},"callee":null},"196:17":{"kind":"call","span":{"start":[196,17],"end":[196,47],"bytes":[5160,5190]},"callee":null},"197:16":{"kind":"call","span":{"start":[197,16],"end":[197,28],"bytes":[5206,5218]},"callee":null},"198:18":{"kind":"call","span":{"start":[198,18],"end":[198,32],"bytes":[5236,5250]},"callee":null},"199:17":{"kind":"call","span":{"start":[199,17],"end":[199,27],"bytes":[5274,5284]},"callee":null},"201:17":{"kind":"call","span":{"start":[201,17],"end":[201,76],"bytes":[5309,5368]},"callee":null},"202:5":{"kind":"call","span":{"start":[202,5],"end":[202,17],"bytes":[5387,5399]},"callee":null},"203:5":{"kind":"call","span":{"start":[203,5],"end":[203,18],"bytes":[5404,5417]},"callee":null},"204:12":{"kind":"call","span":{"start":[204,12],"end":[204,45],"bytes":[5429,5462]},"callee":null},"208:3":{"kind":"call","span":{"start":[208,3],"end":[208,15],"bytes":[5475,5487]},"callee":null},"209:13":{"kind":"call","span":{"start":[209,13],"end":[209,24],"bytes":[5500,5511]},"callee":null},"210:11":{"kind":"call","span":{"start":[210,11],"end":[210,52],"bytes":[5536,5577]},"callee":null},"214:10":{"kind":"call","span":{"start":[214,10],"end":[214,20],"bytes":[5603,5613]},"callee":null},"215:11":{"kind":"call","span":{"start":[215,11],"end":[215,20],"bytes":[5625,5634]},"callee":null}}}},"fields":{"BatchSize":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/BatchSize","kind":"field","type":"int","span":{"start":[23,1],"end":[23,16],"bytes":[264,279]}},"DB":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/DB","kind":"field","type":"string","span":{"start":[20,1],"end":[20,19],"bytes":[210,228]}},"IndexRatio":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/IndexRatio","kind":"field","type":"float64","span":{"start":[25,1],"end":[25,20],"bytes":[296,315]}},"Main":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/Main","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.Main","span":{"start":[18,1],"end":[18,12],"bytes":[197,208]}},"PageSize":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/PageSize","kind":"field","type":"int","span":{"start":[26,1],"end":[26,16],"bytes":[316,331]}},"RowSize":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/RowSize","kind":"field","type":"int","span":{"start":[22,1],"end":[22,16],"bytes":[248,263]}},"TableCount":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/TableCount","kind":"field","type":"int","span":{"start":[24,1],"end":[24,16],"bytes":[280,295]}},"TargetSize":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/TargetSize","kind":"field","type":"string","span":{"start":[21,1],"end":[21,19],"bytes":[229,247]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize","kind":"function","span":{"start":[303,1],"end":[322,2],"bytes":[7435,7804]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize","parameters":[{"name":"path","type":"string","span":{"start":[303,1],"end":[303,51],"bytes":[7435,7485]}}],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"304:15":{"kind":"call","span":{"start":[304,15],"end":[304,28],"bytes":[7500,7513]},"callee":null},"309:10":{"kind":"call","span":{"start":[309,10],"end":[309,21],"bytes":[7560,7571]},"callee":null},"312:21":{"kind":"call","span":{"start":[312,21],"end":[312,37],"bytes":[7619,7635]},"callee":null},"313:11":{"kind":"call","span":{"start":[313,11],"end":[313,25],"bytes":[7660,7674]},"callee":null},"317:21":{"kind":"call","span":{"start":[317,21],"end":[317,37],"bytes":[7725,7741]},"callee":null},"318:11":{"kind":"call","span":{"start":[318,11],"end":[318,25],"bytes":[7766,7780]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.parseSize":{"id":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.parseSize","kind":"function","span":{"start":[273,1],"end":[301,2],"bytes":[6712,7433]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.parseSize","parameters":[{"name":"s","type":"string","span":{"start":[273,1],"end":[273,42],"bytes":[6712,6753]}}],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"287:15":{"kind":"call","span":{"start":[287,15],"end":[287,29],"bytes":[7075,7089]},"callee":null},"287:35":{"kind":"call","span":{"start":[287,35],"end":[287,41],"bytes":[7095,7101]},"callee":null},"287:42":{"kind":"call","span":{"start":[287,42],"end":[287,56],"bytes":[7102,7116]},"callee":null},"287:6":{"kind":"call","span":{"start":[287,6],"end":[287,12],"bytes":[7066,7072]},"callee":null},"289:17":{"kind":"call","span":{"start":[289,17],"end":[289,68],"bytes":[7171,7222]},"callee":null},"289:31":{"kind":"call","span":{"start":[289,31],"end":[289,37],"bytes":[7185,7191]},"callee":null},"289:38":{"kind":"call","span":{"start":[289,38],"end":[289,52],"bytes":[7192,7206]},"callee":null},"292:11":{"kind":"call","span":{"start":[292,11],"end":[292,48],"bytes":[7270,7307]},"callee":null},"292:25":{"kind":"call","span":{"start":[292,25],"end":[292,47],"bytes":[7284,7306]},"callee":null},"297:15":{"kind":"call","span":{"start":[297,15],"end":[297,42],"bytes":[7352,7379]},"callee":null}}}},"content_hash":"a67a7b7cc269d80370f2853a6dbd9a7b82d5fe893644ccea6a7b990ab833b797"},"cmd/litestream-test/shrink.go":{"id":"can://go/litestream/cmd/litestream-test/shrink.go","kind":"module","span":{"start":[1,1],"end":[331,1],"bytes":[0,7667]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\ntype ShrinkCommand struct {\n\tMain *Main\n\n\tDB string\n\tDeletePercentage float64\n\tVacuum bool\n\tCheckpoint bool\n\tCheckpointMode string\n}\n\nfunc (c *ShrinkCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-test shrink\", flag.ExitOnError)\n\tfs.StringVar(\u0026c.DB, \"db\", \"\", \"Database path (required)\")\n\tfs.Float64Var(\u0026c.DeletePercentage, \"delete-percentage\", 50, \"Percentage of data to delete (0-100)\")\n\tfs.BoolVar(\u0026c.Vacuum, \"vacuum\", false, \"Run VACUUM after deletion\")\n\tfs.BoolVar(\u0026c.Checkpoint, \"checkpoint\", false, \"Run checkpoint after deletion\")\n\tfs.StringVar(\u0026c.CheckpointMode, \"checkpoint-mode\", \"PASSIVE\", \"Checkpoint mode (PASSIVE, FULL, RESTART, TRUNCATE)\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif c.DB == \"\" {\n\t\treturn fmt.Errorf(\"database path required\")\n\t}\n\n\tif c.DeletePercentage \u003c 0 || c.DeletePercentage \u003e 100 {\n\t\treturn fmt.Errorf(\"delete percentage must be between 0 and 100\")\n\t}\n\n\tif _, err := os.Stat(c.DB); err != nil {\n\t\treturn fmt.Errorf(\"database does not exist: %w\", err)\n\t}\n\n\tslog.Info(\"Starting database shrink operation\",\n\t\t\"db\", c.DB,\n\t\t\"delete_percentage\", c.DeletePercentage,\n\t\t\"vacuum\", c.Vacuum,\n\t\t\"checkpoint\", c.Checkpoint,\n\t)\n\n\treturn c.shrinkDatabase(ctx)\n}\n\nfunc (c *ShrinkCommand) shrinkDatabase(ctx context.Context) error {\n\tinitialSize, err := getDatabaseSize(c.DB)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get initial size: %w\", err)\n\t}\n\n\tslog.Info(\"Initial database size\",\n\t\t\"size_mb\", initialSize/1024/1024,\n\t)\n\n\tdb, err := sql.Open(\"sqlite3\", c.DB+\"?_journal_mode=WAL\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open database: %w\", err)\n\t}\n\tdefer db.Close()\n\n\ttables, err := c.getTableList(db)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get table list: %w\", err)\n\t}\n\n\tslog.Info(\"Found tables\", \"count\", len(tables))\n\n\ttotalDeleted := int64(0)\n\tfor _, table := range tables {\n\t\tdeleted, err := c.deleteFromTable(db, table)\n\t\tif err != nil {\n\t\t\tslog.Error(\"Failed to delete from table\", \"table\", table, \"error\", err)\n\t\t\tcontinue\n\t\t}\n\t\ttotalDeleted += deleted\n\t\tslog.Info(\"Deleted rows from table\",\n\t\t\t\"table\", table,\n\t\t\t\"rows_deleted\", deleted,\n\t\t)\n\t}\n\n\tslog.Info(\"Deletion complete\", \"total_rows_deleted\", totalDeleted)\n\n\tsizeAfterDelete, err := getDatabaseSize(c.DB)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get size after delete: %w\", err)\n\t}\n\n\tslog.Info(\"Size after deletion\",\n\t\t\"size_mb\", sizeAfterDelete/1024/1024,\n\t\t\"change_mb\", (initialSize-sizeAfterDelete)/1024/1024,\n\t)\n\n\tif c.Checkpoint {\n\t\tif err := c.runCheckpoint(db); err != nil {\n\t\t\treturn fmt.Errorf(\"checkpoint: %w\", err)\n\t\t}\n\n\t\tsizeAfterCheckpoint, _ := getDatabaseSize(c.DB)\n\t\tslog.Info(\"Size after checkpoint\",\n\t\t\t\"size_mb\", sizeAfterCheckpoint/1024/1024,\n\t\t\t\"change_from_delete_mb\", (sizeAfterDelete-sizeAfterCheckpoint)/1024/1024,\n\t\t)\n\t}\n\n\tif c.Vacuum {\n\t\tif err := c.runVacuum(db); err != nil {\n\t\t\treturn fmt.Errorf(\"vacuum: %w\", err)\n\t\t}\n\n\t\tsizeAfterVacuum, _ := getDatabaseSize(c.DB)\n\t\tslog.Info(\"Size after VACUUM\",\n\t\t\t\"size_mb\", sizeAfterVacuum/1024/1024,\n\t\t\t\"total_reduction_mb\", (initialSize-sizeAfterVacuum)/1024/1024,\n\t\t)\n\t}\n\n\tfinalSize, err := getDatabaseSize(c.DB)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get final size: %w\", err)\n\t}\n\n\treductionPercent := float64(initialSize-finalSize) / float64(initialSize) * 100\n\tslog.Info(\"Shrink operation complete\",\n\t\t\"initial_size_mb\", initialSize/1024/1024,\n\t\t\"final_size_mb\", finalSize/1024/1024,\n\t\t\"reduction_percent\", fmt.Sprintf(\"%.1f\", reductionPercent),\n\t)\n\n\treturn nil\n}\n\nfunc (c *ShrinkCommand) getTableList(db *sql.DB) ([]string, error) {\n\trows, err := db.Query(`\n\t\tSELECT name FROM sqlite_master\n\t\tWHERE type='table'\n\t\tAND name NOT LIKE 'sqlite_%'\n\t\tAND name NOT LIKE 'load_test'\n\t`)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar tables []string\n\tfor rows.Next() {\n\t\tvar table string\n\t\tif err := rows.Scan(\u0026table); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttables = append(tables, table)\n\t}\n\n\treturn tables, nil\n}\n\nfunc (c *ShrinkCommand) deleteFromTable(db *sql.DB, table string) (int64, error) {\n\tvar totalRows int\n\tcountQuery := fmt.Sprintf(\"SELECT COUNT(*) FROM %s\", table)\n\tif err := db.QueryRow(countQuery).Scan(\u0026totalRows); err != nil {\n\t\treturn 0, fmt.Errorf(\"count rows: %w\", err)\n\t}\n\n\tif totalRows == 0 {\n\t\treturn 0, nil\n\t}\n\n\trowsToDelete := int(float64(totalRows) * (c.DeletePercentage / 100))\n\tif rowsToDelete == 0 {\n\t\treturn 0, nil\n\t}\n\n\tvar hasID bool\n\tcolumnQuery := fmt.Sprintf(\"PRAGMA table_info(%s)\", table)\n\trows, err := db.Query(columnQuery)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"get table info: %w\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar cid int\n\t\tvar name, dtype string\n\t\tvar notnull, pk int\n\t\tvar dflt sql.NullString\n\t\tif err := rows.Scan(\u0026cid, \u0026name, \u0026dtype, \u0026notnull, \u0026dflt, \u0026pk); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif name == \"id\" || pk == 1 {\n\t\t\thasID = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tvar deleteQuery string\n\tif hasID {\n\t\tdeleteQuery = fmt.Sprintf(`\n\t\t\tDELETE FROM %s\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM %s\n\t\t\t\tORDER BY RANDOM()\n\t\t\t\tLIMIT %d\n\t\t\t)\n\t\t`, table, table, rowsToDelete)\n\t} else {\n\t\tdeleteQuery = fmt.Sprintf(`\n\t\t\tDELETE FROM %s\n\t\t\tWHERE rowid IN (\n\t\t\t\tSELECT rowid FROM %s\n\t\t\t\tORDER BY RANDOM()\n\t\t\t\tLIMIT %d\n\t\t\t)\n\t\t`, table, table, rowsToDelete)\n\t}\n\n\tstartTime := time.Now()\n\tresult, err := db.Exec(deleteQuery)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"delete rows: %w\", err)\n\t}\n\n\trowsDeleted, _ := result.RowsAffected()\n\tduration := time.Since(startTime)\n\n\tslog.Debug(\"Deleted rows from table\",\n\t\t\"table\", table,\n\t\t\"rows_deleted\", rowsDeleted,\n\t\t\"duration\", duration,\n\t)\n\n\treturn rowsDeleted, nil\n}\n\nfunc (c *ShrinkCommand) runCheckpoint(db *sql.DB) error {\n\tslog.Info(\"Running checkpoint\", \"mode\", c.CheckpointMode)\n\n\tstartTime := time.Now()\n\tquery := fmt.Sprintf(\"PRAGMA wal_checkpoint(%s)\", c.CheckpointMode)\n\n\tvar busy, written, total int\n\terr := db.QueryRow(query).Scan(\u0026busy, \u0026written, \u0026total)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"checkpoint failed: %w\", err)\n\t}\n\n\tduration := time.Since(startTime)\n\tslog.Info(\"Checkpoint complete\",\n\t\t\"mode\", c.CheckpointMode,\n\t\t\"busy\", busy,\n\t\t\"pages_written\", written,\n\t\t\"total_pages\", total,\n\t\t\"duration\", duration,\n\t)\n\n\treturn nil\n}\n\nfunc (c *ShrinkCommand) runVacuum(db *sql.DB) error {\n\tslog.Info(\"Running VACUUM (this may take a while)\")\n\n\tstartTime := time.Now()\n\t_, err := db.Exec(\"VACUUM\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"vacuum failed: %w\", err)\n\t}\n\n\tduration := time.Since(startTime)\n\tslog.Info(\"VACUUM complete\", \"duration\", duration)\n\n\treturn nil\n}\n\nfunc (c *ShrinkCommand) Usage() {\n\tfmt.Fprintln(c.Main.Stdout, `\nShrink a database by deleting data and optionally running VACUUM.\n\nUsage:\n\n\tlitestream-test shrink [options]\n\nOptions:\n\n\t-db PATH\n\t Database path (required)\n\n\t-delete-percentage PCT\n\t Percentage of data to delete (0-100)\n\t Default: 50\n\n\t-vacuum\n\t Run VACUUM after deletion\n\t Default: false\n\n\t-checkpoint\n\t Run checkpoint after deletion\n\t Default: false\n\n\t-checkpoint-mode MODE\n\t Checkpoint mode (PASSIVE, FULL, RESTART, TRUNCATE)\n\t Default: PASSIVE\n\nExamples:\n\n\t# Delete 50% of data\n\tlitestream-test shrink -db /tmp/test.db -delete-percentage 50\n\n\t# Delete 75% and run VACUUM\n\tlitestream-test shrink -db /tmp/test.db -delete-percentage 75 -vacuum\n\n\t# Delete 30%, checkpoint, then VACUUM\n\tlitestream-test shrink -db /tmp/test.db -delete-percentage 30 -checkpoint -vacuum\n\n\t# Test with FULL checkpoint mode\n\tlitestream-test shrink -db /tmp/test.db -delete-percentage 50 -checkpoint -checkpoint-mode FULL\n`[1:])\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"sql","path":"database/sql","span":{"start":[5,1],"end":[5,16],"bytes":[34,49]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[50,57]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[58,64]}},{"name":"slog","path":"log/slog","span":{"start":[8,1],"end":[8,12],"bytes":[65,76]}},{"name":"os","path":"os","span":{"start":[9,1],"end":[9,6],"bytes":[77,82]}},{"name":"time","path":"time","span":{"start":[10,1],"end":[10,8],"bytes":[83,90]}},{"name":"go-sqlite3","path":"github.com/mattn/go-sqlite3","alias":"_","span":{"start":[12,1],"end":[12,33],"bytes":[92,124]}}],"types":{"ShrinkCommand":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand","kind":"struct","span":{"start":[15,1],"end":[23,2],"bytes":[128,292]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Run":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Run","kind":"method","span":{"start":[25,1],"end":[57,2],"bytes":[294,1427]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[25,1],"end":[25,72],"bytes":[294,365]}},{"name":"args","type":"[]string","span":{"start":[25,1],"end":[25,72],"bytes":[294,365]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"26:8":{"kind":"call","span":{"start":[26,8],"end":[26,67],"bytes":[373,432]},"callee":null},"27:2":{"kind":"call","span":{"start":[27,2],"end":[27,59],"bytes":[434,491]},"callee":null},"28:2":{"kind":"call","span":{"start":[28,2],"end":[28,101],"bytes":[493,592]},"callee":null},"29:2":{"kind":"call","span":{"start":[29,2],"end":[29,69],"bytes":[594,661]},"callee":null},"30:2":{"kind":"call","span":{"start":[30,2],"end":[30,81],"bytes":[663,742]},"callee":null},"31:2":{"kind":"call","span":{"start":[31,2],"end":[31,117],"bytes":[744,859]},"callee":null},"33:12":{"kind":"call","span":{"start":[33,12],"end":[33,26],"bytes":[891,905]},"callee":null},"38:10":{"kind":"call","span":{"start":[38,10],"end":[38,46],"bytes":[963,999]},"callee":null},"42:10":{"kind":"call","span":{"start":[42,10],"end":[42,67],"bytes":[1070,1127]},"callee":null},"45:15":{"kind":"call","span":{"start":[45,15],"end":[45,28],"bytes":[1146,1159]},"callee":null},"46:10":{"kind":"call","span":{"start":[46,10],"end":[46,56],"bytes":[1183,1229]},"callee":null},"49:2":{"kind":"call","span":{"start":[49,2],"end":[54,3],"bytes":[1235,1394]},"callee":null},"56:9":{"kind":"call","span":{"start":[56,9],"end":[56,30],"bytes":[1404,1425]},"callee":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase"}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Usage":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Usage","kind":"method","span":{"start":[287,1],"end":[330,2],"bytes":[6664,7666]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"288:2":{"kind":"call","span":{"start":[288,2],"end":[329,7],"bytes":[6699,7664]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.deleteFromTable":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.deleteFromTable","kind":"method","span":{"start":[171,1],"end":[246,2],"bytes":[4124,5751]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.deleteFromTable","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[171,1],"end":[171,83],"bytes":[4124,4206]}},{"name":"table","type":"string","span":{"start":[171,1],"end":[171,83],"bytes":[4124,4206]}}],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"173:16":{"kind":"call","span":{"start":[173,16],"end":[173,61],"bytes":[4241,4286]},"callee":null},"174:12":{"kind":"call","span":{"start":[174,12],"end":[174,35],"bytes":[4298,4321]},"callee":null},"175:13":{"kind":"call","span":{"start":[175,13],"end":[175,46],"bytes":[4365,4398]},"callee":null},"182:18":{"kind":"call","span":{"start":[182,18],"end":[182,70],"bytes":[4461,4513]},"callee":null},"182:22":{"kind":"call","span":{"start":[182,22],"end":[182,40],"bytes":[4465,4483]},"callee":null},"188:17":{"kind":"call","span":{"start":[188,17],"end":[188,60],"bytes":[4590,4633]},"callee":null},"189:15":{"kind":"call","span":{"start":[189,15],"end":[189,36],"bytes":[4648,4669]},"callee":null},"191:13":{"kind":"call","span":{"start":[191,13],"end":[191,50],"bytes":[4699,4736]},"callee":null},"193:8":{"kind":"call","span":{"start":[193,8],"end":[193,20],"bytes":[4747,4759]},"callee":null},"195:6":{"kind":"call","span":{"start":[195,6],"end":[195,17],"bytes":[4766,4777]},"callee":null},"200:13":{"kind":"call","span":{"start":[200,13],"end":[200,65],"bytes":[4879,4931]},"callee":null},"211:17":{"kind":"call","span":{"start":[211,17],"end":[218,33],"bytes":[5078,5221]},"callee":null},"220:17":{"kind":"call","span":{"start":[220,17],"end":[227,33],"bytes":[5248,5397]},"callee":null},"230:15":{"kind":"call","span":{"start":[230,15],"end":[230,25],"bytes":[5416,5426]},"callee":null},"231:17":{"kind":"call","span":{"start":[231,17],"end":[231,37],"bytes":[5443,5463]},"callee":null},"233:13":{"kind":"call","span":{"start":[233,13],"end":[233,47],"bytes":[5493,5527]},"callee":null},"236:20":{"kind":"call","span":{"start":[236,20],"end":[236,41],"bytes":[5551,5572]},"callee":null},"237:14":{"kind":"call","span":{"start":[237,14],"end":[237,35],"bytes":[5586,5607]},"callee":null},"239:2":{"kind":"call","span":{"start":[239,2],"end":[243,3],"bytes":[5610,5723]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.getTableList":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.getTableList","kind":"method","span":{"start":[147,1],"end":[169,2],"bytes":[3664,4122]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.getTableList","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[147,1],"end":[147,69],"bytes":[3664,3732]}}],"return_type":"([]string, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"148:15":{"kind":"call","span":{"start":[148,15],"end":[153,4],"bytes":[3747,3878]},"callee":null},"157:8":{"kind":"call","span":{"start":[157,8],"end":[157,20],"bytes":[3924,3936]},"callee":null},"160:6":{"kind":"call","span":{"start":[160,6],"end":[160,17],"bytes":[3964,3975]},"callee":null},"162:13":{"kind":"call","span":{"start":[162,13],"end":[162,30],"bytes":[4009,4026]},"callee":null},"165:12":{"kind":"call","span":{"start":[165,12],"end":[165,33],"bytes":[4075,4096]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runCheckpoint":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runCheckpoint","kind":"method","span":{"start":[248,1],"end":[270,2],"bytes":[5753,6330]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runCheckpoint","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[248,1],"end":[248,58],"bytes":[5753,5810]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"249:2":{"kind":"call","span":{"start":[249,2],"end":[249,59],"bytes":[5812,5869]},"callee":null},"251:15":{"kind":"call","span":{"start":[251,15],"end":[251,25],"bytes":[5885,5895]},"callee":null},"252:11":{"kind":"call","span":{"start":[252,11],"end":[252,69],"bytes":[5906,5964]},"callee":null},"255:9":{"kind":"call","span":{"start":[255,9],"end":[255,27],"bytes":[6004,6022]},"callee":null},"257:10":{"kind":"call","span":{"start":[257,10],"end":[257,50],"bytes":[6079,6119]},"callee":null},"260:14":{"kind":"call","span":{"start":[260,14],"end":[260,35],"bytes":[6137,6158]},"callee":null},"261:2":{"kind":"call","span":{"start":[261,2],"end":[267,3],"bytes":[6160,6315]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runVacuum":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runVacuum","kind":"method","span":{"start":[272,1],"end":[285,2],"bytes":[6332,6662]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runVacuum","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[272,1],"end":[272,54],"bytes":[6332,6385]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"273:2":{"kind":"call","span":{"start":[273,2],"end":[273,53],"bytes":[6387,6438]},"callee":null},"275:15":{"kind":"call","span":{"start":[275,15],"end":[275,25],"bytes":[6454,6464]},"callee":null},"276:12":{"kind":"call","span":{"start":[276,12],"end":[276,29],"bytes":[6476,6493]},"callee":null},"278:10":{"kind":"call","span":{"start":[278,10],"end":[278,46],"bytes":[6520,6556]},"callee":null},"281:14":{"kind":"call","span":{"start":[281,14],"end":[281,35],"bytes":[6574,6595]},"callee":null},"282:2":{"kind":"call","span":{"start":[282,2],"end":[282,52],"bytes":[6597,6647]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","kind":"method","span":{"start":[59,1],"end":[145,2],"bytes":[1429,3662]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[59,1],"end":[59,68],"bytes":[1429,1496]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"100:10":{"kind":"call","span":{"start":[100,10],"end":[100,54],"bytes":[2459,2503]},"callee":null},"103:2":{"kind":"call","span":{"start":[103,2],"end":[106,3],"bytes":[2509,2640]},"callee":null},"109:13":{"kind":"call","span":{"start":[109,13],"end":[109,32],"bytes":[2673,2692]},"callee":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runCheckpoint"},"110:11":{"kind":"call","span":{"start":[110,11],"end":[110,44],"bytes":[2717,2750]},"callee":null},"113:29":{"kind":"call","span":{"start":[113,29],"end":[113,50],"bytes":[2784,2805]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"},"114:3":{"kind":"call","span":{"start":[114,3],"end":[117,4],"bytes":[2808,2968]},"callee":null},"121:13":{"kind":"call","span":{"start":[121,13],"end":[121,28],"bytes":[3000,3015]},"callee":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runVacuum"},"122:11":{"kind":"call","span":{"start":[122,11],"end":[122,40],"bytes":[3040,3069]},"callee":null},"125:25":{"kind":"call","span":{"start":[125,25],"end":[125,46],"bytes":[3099,3120]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"},"126:3":{"kind":"call","span":{"start":[126,3],"end":[129,4],"bytes":[3123,3264]},"callee":null},"132:20":{"kind":"call","span":{"start":[132,20],"end":[132,41],"bytes":[3288,3309]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"},"134:10":{"kind":"call","span":{"start":[134,10],"end":[134,47],"bytes":[3336,3373]},"callee":null},"137:22":{"kind":"call","span":{"start":[137,22],"end":[137,52],"bytes":[3399,3429]},"callee":null},"137:55":{"kind":"call","span":{"start":[137,55],"end":[137,75],"bytes":[3432,3452]},"callee":null},"138:2":{"kind":"call","span":{"start":[138,2],"end":[142,3],"bytes":[3460,3647]},"callee":null},"141:24":{"kind":"call","span":{"start":[141,24],"end":[141,61],"bytes":[3606,3643]},"callee":null},"60:22":{"kind":"call","span":{"start":[60,22],"end":[60,43],"bytes":[1518,1539]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"},"62:10":{"kind":"call","span":{"start":[62,10],"end":[62,49],"bytes":[1566,1605]},"callee":null},"65:2":{"kind":"call","span":{"start":[65,2],"end":[67,3],"bytes":[1611,1684]},"callee":null},"69:13":{"kind":"call","span":{"start":[69,13],"end":[69,59],"bytes":[1698,1744]},"callee":null},"71:10":{"kind":"call","span":{"start":[71,10],"end":[71,46],"bytes":[1771,1807]},"callee":null},"73:8":{"kind":"call","span":{"start":[73,8],"end":[73,18],"bytes":[1818,1828]},"callee":null},"75:17":{"kind":"call","span":{"start":[75,17],"end":[75,35],"bytes":[1846,1864]},"callee":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.getTableList"},"77:10":{"kind":"call","span":{"start":[77,10],"end":[77,47],"bytes":[1891,1928]},"callee":null},"80:2":{"kind":"call","span":{"start":[80,2],"end":[80,49],"bytes":[1934,1981]},"callee":null},"80:37":{"kind":"call","span":{"start":[80,37],"end":[80,48],"bytes":[1969,1980]},"callee":null},"82:18":{"kind":"call","span":{"start":[82,18],"end":[82,26],"bytes":[2000,2008]},"callee":null},"84:19":{"kind":"call","span":{"start":[84,19],"end":[84,47],"bytes":[2059,2087]},"callee":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.deleteFromTable"},"86:4":{"kind":"call","span":{"start":[86,4],"end":[86,75],"bytes":[2109,2180]},"callee":null},"90:3":{"kind":"call","span":{"start":[90,3],"end":[93,4],"bytes":[2225,2312]},"callee":null},"96:2":{"kind":"call","span":{"start":[96,2],"end":[96,68],"bytes":[2318,2384]},"callee":null},"98:26":{"kind":"call","span":{"start":[98,26],"end":[98,47],"bytes":[2411,2432]},"callee":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize"}}}},"fields":{"Checkpoint":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/Checkpoint","kind":"field","type":"bool","span":{"start":[21,1],"end":[21,23],"bytes":[243,265]}},"CheckpointMode":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/CheckpointMode","kind":"field","type":"string","span":{"start":[22,1],"end":[22,25],"bytes":[266,290]}},"DB":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/DB","kind":"field","type":"string","span":{"start":[18,1],"end":[18,25],"bytes":[169,193]}},"DeletePercentage":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/DeletePercentage","kind":"field","type":"float64","span":{"start":[19,1],"end":[19,26],"bytes":[194,219]}},"Main":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/Main","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.Main","span":{"start":[16,1],"end":[16,12],"bytes":[156,167]}},"Vacuum":{"id":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/Vacuum","kind":"field","type":"bool","span":{"start":[20,1],"end":[20,23],"bytes":[220,242]}}}}},"functions":{},"content_hash":"37c5b10667dd19c81741e8154bf9a8dac5dd898270b7f5c126d1375a33fb1b10"},"cmd/litestream-test/validate.go":{"id":"can://go/litestream/cmd/litestream-test/validate.go","kind":"module","span":{"start":[1,1],"end":[505,1],"bytes":[0,12576]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\ntype ValidateCommand struct {\n\tMain *Main\n\n\tSourceDB string\n\tReplicaURL string\n\tRestoredDB string\n\tCheckType string\n\tLTXContinuity bool\n\tConfigPath string\n}\n\ntype ValidationResult struct {\n\tCheckType string\n\tPassed bool\n\tDuration time.Duration\n\tErrorMessage string\n\tDetails map[string]interface{}\n}\n\nfunc (c *ValidateCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-test validate\", flag.ExitOnError)\n\tfs.StringVar(\u0026c.SourceDB, \"source-db\", \"\", \"Original database path\")\n\tfs.StringVar(\u0026c.ReplicaURL, \"replica-url\", \"\", \"Replica URL to validate\")\n\tfs.StringVar(\u0026c.RestoredDB, \"restored-db\", \"\", \"Path for restored database\")\n\tfs.StringVar(\u0026c.CheckType, \"check-type\", \"quick\", \"Type of check (quick, integrity, checksum, full)\")\n\tfs.BoolVar(\u0026c.LTXContinuity, \"ltx-continuity\", false, \"Check LTX file continuity\")\n\tfs.StringVar(\u0026c.ConfigPath, \"config\", \"\", \"Litestream config file path\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif c.SourceDB == \"\" {\n\t\treturn fmt.Errorf(\"source database path required\")\n\t}\n\n\tif c.ReplicaURL == \"\" \u0026\u0026 c.ConfigPath == \"\" {\n\t\treturn fmt.Errorf(\"replica URL or config file required\")\n\t}\n\n\tif c.RestoredDB == \"\" {\n\t\tc.RestoredDB = c.SourceDB + \".restored\"\n\t}\n\n\tslog.Info(\"Starting validation\",\n\t\t\"source_db\", c.SourceDB,\n\t\t\"replica_url\", c.ReplicaURL,\n\t\t\"check_type\", c.CheckType,\n\t\t\"ltx_continuity\", c.LTXContinuity,\n\t)\n\n\tresults := []ValidationResult{}\n\n\tif c.LTXContinuity \u0026\u0026 c.ReplicaURL != \"\" {\n\t\tresult := c.validateLTXContinuity(ctx)\n\t\tresults = append(results, result)\n\t}\n\n\trestoreResult := c.performRestore(ctx)\n\tresults = append(results, restoreResult)\n\n\tif restoreResult.Passed {\n\t\tswitch c.CheckType {\n\t\tcase \"quick\":\n\t\t\tresults = append(results, c.performQuickCheck(ctx))\n\t\tcase \"integrity\":\n\t\t\tresults = append(results, c.performIntegrityCheck(ctx))\n\t\tcase \"checksum\":\n\t\t\tresults = append(results, c.performChecksumCheck(ctx))\n\t\tcase \"full\":\n\t\t\tresults = append(results, c.performQuickCheck(ctx))\n\t\t\tresults = append(results, c.performIntegrityCheck(ctx))\n\t\t\tresults = append(results, c.performChecksumCheck(ctx))\n\t\t\tresults = append(results, c.performDataValidation(ctx))\n\t\t}\n\t}\n\n\treturn c.reportResults(results)\n}\n\nfunc (c *ValidateCommand) performRestore(ctx context.Context) ValidationResult {\n\tstartTime := time.Now()\n\tresult := ValidationResult{\n\t\tCheckType: \"restore\",\n\t\tDetails: make(map[string]interface{}),\n\t}\n\n\tif err := os.Remove(c.RestoredDB); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\tslog.Warn(\"Could not remove existing restored database\", \"error\", err)\n\t}\n\n\tvar cmd *exec.Cmd\n\tif c.ConfigPath != \"\" {\n\t\tcmd = exec.CommandContext(ctx, \"litestream\", \"restore\",\n\t\t\t\"-config\", c.ConfigPath,\n\t\t\t\"-o\", c.RestoredDB,\n\t\t\tc.SourceDB,\n\t\t)\n\t} else {\n\t\tcmd = exec.CommandContext(ctx, \"litestream\", \"restore\",\n\t\t\t\"-o\", c.RestoredDB,\n\t\t\tc.ReplicaURL,\n\t\t)\n\t}\n\n\toutput, err := cmd.CombinedOutput()\n\tresult.Duration = time.Since(startTime)\n\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"restore failed: %v\\nOutput: %s\", err, string(output))\n\t\treturn result\n\t}\n\n\tif _, err := os.Stat(c.RestoredDB); err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"restored database not found: %v\", err)\n\t\treturn result\n\t}\n\n\tresult.Passed = true\n\tresult.Details[\"restored_path\"] = c.RestoredDB\n\n\tif info, err := os.Stat(c.RestoredDB); err == nil {\n\t\tresult.Details[\"restored_size\"] = info.Size()\n\t}\n\n\tslog.Info(\"Restore completed\",\n\t\t\"duration\", result.Duration,\n\t\t\"restored_db\", c.RestoredDB,\n\t)\n\n\treturn result\n}\n\nfunc (c *ValidateCommand) performQuickCheck(ctx context.Context) ValidationResult {\n\tstartTime := time.Now()\n\tresult := ValidationResult{\n\t\tCheckType: \"quick_check\",\n\t\tDetails: make(map[string]interface{}),\n\t}\n\n\tdb, err := sql.Open(\"sqlite3\", c.RestoredDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to open database: %v\", err)\n\t\treturn result\n\t}\n\tdefer db.Close()\n\n\tvar checkResult string\n\terr = db.QueryRow(\"PRAGMA quick_check\").Scan(\u0026checkResult)\n\tresult.Duration = time.Since(startTime)\n\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"quick check failed: %v\", err)\n\t\treturn result\n\t}\n\n\tresult.Passed = checkResult == \"ok\"\n\tresult.Details[\"check_result\"] = checkResult\n\n\tif !result.Passed {\n\t\tresult.ErrorMessage = fmt.Sprintf(\"quick check returned: %s\", checkResult)\n\t}\n\n\tslog.Info(\"Quick check completed\",\n\t\t\"passed\", result.Passed,\n\t\t\"duration\", result.Duration,\n\t)\n\n\treturn result\n}\n\nfunc (c *ValidateCommand) performIntegrityCheck(ctx context.Context) ValidationResult {\n\tstartTime := time.Now()\n\tresult := ValidationResult{\n\t\tCheckType: \"integrity_check\",\n\t\tDetails: make(map[string]interface{}),\n\t}\n\n\tdb, err := sql.Open(\"sqlite3\", c.RestoredDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to open database: %v\", err)\n\t\treturn result\n\t}\n\tdefer db.Close()\n\n\trows, err := db.Query(\"PRAGMA integrity_check\")\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"integrity check failed: %v\", err)\n\t\tresult.Duration = time.Since(startTime)\n\t\treturn result\n\t}\n\tdefer rows.Close()\n\n\tvar results []string\n\tfor rows.Next() {\n\t\tvar line string\n\t\tif err := rows.Scan(\u0026line); err != nil {\n\t\t\tresult.Passed = false\n\t\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to scan result: %v\", err)\n\t\t\tresult.Duration = time.Since(startTime)\n\t\t\treturn result\n\t\t}\n\t\tresults = append(results, line)\n\t}\n\n\tresult.Duration = time.Since(startTime)\n\tresult.Details[\"check_results\"] = results\n\n\tif len(results) == 1 \u0026\u0026 results[0] == \"ok\" {\n\t\tresult.Passed = true\n\t} else {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"integrity check found issues: %v\", results)\n\t}\n\n\tslog.Info(\"Integrity check completed\",\n\t\t\"passed\", result.Passed,\n\t\t\"duration\", result.Duration,\n\t\t\"issues\", len(results)-1,\n\t)\n\n\treturn result\n}\n\nfunc (c *ValidateCommand) performChecksumCheck(ctx context.Context) ValidationResult {\n\tstartTime := time.Now()\n\tresult := ValidationResult{\n\t\tCheckType: \"checksum\",\n\t\tDetails: make(map[string]interface{}),\n\t}\n\n\tsourceChecksum, err := c.calculateDBChecksum(c.SourceDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to calculate source checksum: %v\", err)\n\t\tresult.Duration = time.Since(startTime)\n\t\treturn result\n\t}\n\n\trestoredChecksum, err := c.calculateDBChecksum(c.RestoredDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to calculate restored checksum: %v\", err)\n\t\tresult.Duration = time.Since(startTime)\n\t\treturn result\n\t}\n\n\tresult.Duration = time.Since(startTime)\n\tresult.Details[\"source_checksum\"] = fmt.Sprintf(\"%x\", sourceChecksum)\n\tresult.Details[\"restored_checksum\"] = fmt.Sprintf(\"%x\", restoredChecksum)\n\n\tif string(sourceChecksum) == string(restoredChecksum) {\n\t\tresult.Passed = true\n\t} else {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = \"checksums do not match\"\n\t}\n\n\tslog.Info(\"Checksum check completed\",\n\t\t\"passed\", result.Passed,\n\t\t\"duration\", result.Duration,\n\t\t\"match\", result.Passed,\n\t)\n\n\treturn result\n}\n\nfunc (c *ValidateCommand) performDataValidation(ctx context.Context) ValidationResult {\n\tstartTime := time.Now()\n\tresult := ValidationResult{\n\t\tCheckType: \"data_validation\",\n\t\tDetails: make(map[string]interface{}),\n\t}\n\n\tsourceDB, err := sql.Open(\"sqlite3\", c.SourceDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to open source database: %v\", err)\n\t\treturn result\n\t}\n\tdefer sourceDB.Close()\n\n\trestoredDB, err := sql.Open(\"sqlite3\", c.RestoredDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to open restored database: %v\", err)\n\t\treturn result\n\t}\n\tdefer restoredDB.Close()\n\n\ttables, err := c.getTableList(sourceDB)\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to get table list: %v\", err)\n\t\tresult.Duration = time.Since(startTime)\n\t\treturn result\n\t}\n\n\tresult.Details[\"tables_checked\"] = len(tables)\n\tallMatch := true\n\n\tfor _, table := range tables {\n\t\tsourceCount, err := c.getRowCount(sourceDB, table)\n\t\tif err != nil {\n\t\t\tresult.Passed = false\n\t\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to count rows in source table %s: %v\", table, err)\n\t\t\tresult.Duration = time.Since(startTime)\n\t\t\treturn result\n\t\t}\n\n\t\trestoredCount, err := c.getRowCount(restoredDB, table)\n\t\tif err != nil {\n\t\t\tresult.Passed = false\n\t\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to count rows in restored table %s: %v\", table, err)\n\t\t\tresult.Duration = time.Since(startTime)\n\t\t\treturn result\n\t\t}\n\n\t\tif sourceCount != restoredCount {\n\t\t\tallMatch = false\n\t\t\tresult.Details[fmt.Sprintf(\"table_%s_mismatch\", table)] = fmt.Sprintf(\"source=%d, restored=%d\", sourceCount, restoredCount)\n\t\t}\n\t}\n\n\tresult.Duration = time.Since(startTime)\n\tresult.Passed = allMatch\n\n\tif !allMatch {\n\t\tresult.ErrorMessage = \"row count mismatch between source and restored databases\"\n\t}\n\n\tslog.Info(\"Data validation completed\",\n\t\t\"passed\", result.Passed,\n\t\t\"duration\", result.Duration,\n\t\t\"tables_checked\", len(tables),\n\t)\n\n\treturn result\n}\n\nfunc (c *ValidateCommand) validateLTXContinuity(ctx context.Context) ValidationResult {\n\tstartTime := time.Now()\n\tresult := ValidationResult{\n\t\tCheckType: \"ltx_continuity\",\n\t\tDetails: make(map[string]interface{}),\n\t}\n\n\tcmd := exec.CommandContext(ctx, \"litestream\", \"ltx\", c.ReplicaURL)\n\toutput, err := cmd.Output()\n\tif err != nil {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = fmt.Sprintf(\"failed to list LTX files: %v\", err)\n\t\tresult.Duration = time.Since(startTime)\n\t\treturn result\n\t}\n\n\tlines := strings.Split(string(output), \"\\n\")\n\tif len(lines) \u003c 2 {\n\t\tresult.Passed = false\n\t\tresult.ErrorMessage = \"no LTX files found\"\n\t\tresult.Duration = time.Since(startTime)\n\t\treturn result\n\t}\n\n\tresult.Passed = true\n\tresult.Duration = time.Since(startTime)\n\tresult.Details[\"ltx_files_checked\"] = len(lines) - 2\n\n\tslog.Info(\"LTX continuity check completed\",\n\t\t\"passed\", result.Passed,\n\t\t\"duration\", result.Duration,\n\t)\n\n\treturn result\n}\n\nfunc (c *ValidateCommand) calculateDBChecksum(path string) ([]byte, error) {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\thash := md5.New()\n\tif _, err := io.Copy(hash, file); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn hash.Sum(nil), nil\n}\n\nfunc (c *ValidateCommand) getTableList(db *sql.DB) ([]string, error) {\n\trows, err := db.Query(\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar tables []string\n\tfor rows.Next() {\n\t\tvar table string\n\t\tif err := rows.Scan(\u0026table); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttables = append(tables, table)\n\t}\n\n\treturn tables, nil\n}\n\nfunc (c *ValidateCommand) getRowCount(db *sql.DB, table string) (int, error) {\n\tvar count int\n\tquery := fmt.Sprintf(\"SELECT COUNT(*) FROM %s\", table)\n\terr := db.QueryRow(query).Scan(\u0026count)\n\treturn count, err\n}\n\nfunc (c *ValidateCommand) reportResults(results []ValidationResult) error {\n\tallPassed := true\n\tfor _, result := range results {\n\t\tif !result.Passed {\n\t\t\tallPassed = false\n\t\t\tslog.Error(\"Validation failed\",\n\t\t\t\t\"check_type\", result.CheckType,\n\t\t\t\t\"error\", result.ErrorMessage,\n\t\t\t)\n\t\t}\n\t}\n\n\tif allPassed {\n\t\tslog.Info(\"All validation checks passed\")\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"validation failed\")\n}\n\nfunc (c *ValidateCommand) Usage() {\n\tfmt.Fprintln(c.Main.Stdout, `\nValidate replication integrity by restoring and checking databases.\n\nUsage:\n\n\tlitestream-test validate [options]\n\nOptions:\n\n\t-source-db PATH\n\t Original database path (required)\n\n\t-replica-url URL\n\t Replica URL to validate\n\n\t-restored-db PATH\n\t Path for restored database\n\t Default: source-db.restored\n\n\t-check-type TYPE\n\t Type of check: quick, integrity, checksum, full\n\t Default: quick\n\n\t-ltx-continuity\n\t Check LTX file continuity\n\t Default: false\n\n\t-config PATH\n\t Litestream config file path\n\nExamples:\n\n\t# Quick validation\n\tlitestream-test validate -source-db /tmp/test.db -replica-url s3://bucket/test\n\n\t# Full validation with all checks\n\tlitestream-test validate -source-db /tmp/test.db -replica-url s3://bucket/test -check-type full\n\n\t# Validate with config file\n\tlitestream-test validate -source-db /tmp/test.db -config /etc/litestream.yml -check-type integrity\n`[1:])\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"md5","path":"crypto/md5","span":{"start":[5,1],"end":[5,14],"bytes":[34,47]}},{"name":"sql","path":"database/sql","span":{"start":[6,1],"end":[6,16],"bytes":[48,63]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[64,71]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[72,78]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[79,84]}},{"name":"slog","path":"log/slog","span":{"start":[10,1],"end":[10,12],"bytes":[85,96]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[97,102]}},{"name":"exec","path":"os/exec","span":{"start":[12,1],"end":[12,11],"bytes":[103,113]}},{"name":"strings","path":"strings","span":{"start":[13,1],"end":[13,11],"bytes":[114,124]}},{"name":"time","path":"time","span":{"start":[14,1],"end":[14,8],"bytes":[125,132]}},{"name":"go-sqlite3","path":"github.com/mattn/go-sqlite3","alias":"_","span":{"start":[16,1],"end":[16,33],"bytes":[134,166]}}],"types":{"ValidateCommand":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand","kind":"struct","span":{"start":[19,1],"end":[28,2],"bytes":[170,344]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","kind":"method","span":{"start":[38,1],"end":[97,2],"bytes":[506,2440]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[38,1],"end":[38,74],"bytes":[506,579]}},{"name":"args","type":"[]string","span":{"start":[38,1],"end":[38,74],"bytes":[506,579]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"39:8":{"kind":"call","span":{"start":[39,8],"end":[39,69],"bytes":[587,648]},"callee":null},"40:2":{"kind":"call","span":{"start":[40,2],"end":[40,70],"bytes":[650,718]},"callee":null},"41:2":{"kind":"call","span":{"start":[41,2],"end":[41,75],"bytes":[720,793]},"callee":null},"42:2":{"kind":"call","span":{"start":[42,2],"end":[42,78],"bytes":[795,871]},"callee":null},"43:2":{"kind":"call","span":{"start":[43,2],"end":[43,103],"bytes":[873,974]},"callee":null},"44:2":{"kind":"call","span":{"start":[44,2],"end":[44,84],"bytes":[976,1058]},"callee":null},"45:2":{"kind":"call","span":{"start":[45,2],"end":[45,74],"bytes":[1060,1132]},"callee":null},"47:12":{"kind":"call","span":{"start":[47,12],"end":[47,26],"bytes":[1164,1178]},"callee":null},"52:10":{"kind":"call","span":{"start":[52,10],"end":[52,53],"bytes":[1242,1285]},"callee":null},"56:10":{"kind":"call","span":{"start":[56,10],"end":[56,59],"bytes":[1346,1395]},"callee":null},"63:2":{"kind":"call","span":{"start":[63,2],"end":[68,3],"bytes":[1472,1631]},"callee":null},"73:13":{"kind":"call","span":{"start":[73,13],"end":[73,41],"bytes":[1723,1751]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.validateLTXContinuity"},"74:13":{"kind":"call","span":{"start":[74,13],"end":[74,36],"bytes":[1764,1787]},"callee":null},"77:19":{"kind":"call","span":{"start":[77,19],"end":[77,40],"bytes":[1810,1831]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performRestore"},"78:12":{"kind":"call","span":{"start":[78,12],"end":[78,42],"bytes":[1843,1873]},"callee":null},"83:14":{"kind":"call","span":{"start":[83,14],"end":[83,55],"bytes":[1954,1995]},"callee":null},"83:30":{"kind":"call","span":{"start":[83,30],"end":[83,54],"bytes":[1970,1994]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performQuickCheck"},"85:14":{"kind":"call","span":{"start":[85,14],"end":[85,59],"bytes":[2029,2074]},"callee":null},"85:30":{"kind":"call","span":{"start":[85,30],"end":[85,58],"bytes":[2045,2073]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performIntegrityCheck"},"87:14":{"kind":"call","span":{"start":[87,14],"end":[87,58],"bytes":[2107,2151]},"callee":null},"87:30":{"kind":"call","span":{"start":[87,30],"end":[87,57],"bytes":[2123,2150]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck"},"89:14":{"kind":"call","span":{"start":[89,14],"end":[89,55],"bytes":[2180,2221]},"callee":null},"89:30":{"kind":"call","span":{"start":[89,30],"end":[89,54],"bytes":[2196,2220]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performQuickCheck"},"90:14":{"kind":"call","span":{"start":[90,14],"end":[90,59],"bytes":[2235,2280]},"callee":null},"90:30":{"kind":"call","span":{"start":[90,30],"end":[90,58],"bytes":[2251,2279]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performIntegrityCheck"},"91:14":{"kind":"call","span":{"start":[91,14],"end":[91,58],"bytes":[2294,2338]},"callee":null},"91:30":{"kind":"call","span":{"start":[91,30],"end":[91,57],"bytes":[2310,2337]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck"},"92:14":{"kind":"call","span":{"start":[92,14],"end":[92,59],"bytes":[2352,2397]},"callee":null},"92:30":{"kind":"call","span":{"start":[92,30],"end":[92,58],"bytes":[2368,2396]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation"},"96:9":{"kind":"call","span":{"start":[96,9],"end":[96,33],"bytes":[2414,2438]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.reportResults"}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Usage":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Usage","kind":"method","span":{"start":[462,1],"end":[504,2],"bytes":[11603,12575]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"463:2":{"kind":"call","span":{"start":[463,2],"end":[503,7],"bytes":[11640,12573]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.calculateDBChecksum":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.calculateDBChecksum","kind":"method","span":{"start":[401,1],"end":[414,2],"bytes":[10277,10558]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.calculateDBChecksum","parameters":[{"name":"path","type":"string","span":{"start":[401,1],"end":[401,77],"bytes":[10277,10353]}}],"return_type":"([]byte, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"402:15":{"kind":"call","span":{"start":[402,15],"end":[402,28],"bytes":[10368,10381]},"callee":null},"406:8":{"kind":"call","span":{"start":[406,8],"end":[406,20],"bytes":[10427,10439]},"callee":null},"408:10":{"kind":"call","span":{"start":[408,10],"end":[408,19],"bytes":[10450,10459]},"callee":null},"409:15":{"kind":"call","span":{"start":[409,15],"end":[409,34],"bytes":[10474,10493]},"callee":null},"413:9":{"kind":"call","span":{"start":[413,9],"end":[413,22],"bytes":[10538,10551]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getRowCount":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getRowCount","kind":"method","span":{"start":[435,1],"end":[440,2],"bytes":[10981,11191]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getRowCount","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[435,1],"end":[435,79],"bytes":[10981,11059]}},{"name":"table","type":"string","span":{"start":[435,1],"end":[435,79],"bytes":[10981,11059]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"437:11":{"kind":"call","span":{"start":[437,11],"end":[437,56],"bytes":[11085,11130]},"callee":null},"438:9":{"kind":"call","span":{"start":[438,9],"end":[438,27],"bytes":[11139,11157]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getTableList":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getTableList","kind":"method","span":{"start":[416,1],"end":[433,2],"bytes":[10560,10979]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getTableList","parameters":[{"name":"db","type":"*database/sql.DB","span":{"start":[416,1],"end":[416,71],"bytes":[10560,10630]}}],"return_type":"([]string, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"417:15":{"kind":"call","span":{"start":[417,15],"end":[417,105],"bytes":[10645,10735]},"callee":null},"421:8":{"kind":"call","span":{"start":[421,8],"end":[421,20],"bytes":[10781,10793]},"callee":null},"424:6":{"kind":"call","span":{"start":[424,6],"end":[424,17],"bytes":[10821,10832]},"callee":null},"426:13":{"kind":"call","span":{"start":[426,13],"end":[426,30],"bytes":[10866,10883]},"callee":null},"429:12":{"kind":"call","span":{"start":[429,12],"end":[429,33],"bytes":[10932,10953]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck","kind":"method","span":{"start":[249,1],"end":[290,2],"bytes":[6126,7333]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[249,1],"end":[249,87],"bytes":[6126,6212]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","metrics":{"cyclomatic":4},"body":{"250:15":{"kind":"call","span":{"start":[250,15],"end":[250,25],"bytes":[6227,6237]},"callee":null},"253:14":{"kind":"call","span":{"start":[253,14],"end":[253,42],"bytes":[6305,6333]},"callee":null},"256:25":{"kind":"call","span":{"start":[256,25],"end":[256,58],"bytes":[6363,6396]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.calculateDBChecksum"},"259:25":{"kind":"call","span":{"start":[259,25],"end":[259,84],"bytes":[6462,6521]},"callee":null},"260:21":{"kind":"call","span":{"start":[260,21],"end":[260,42],"bytes":[6542,6563]},"callee":null},"264:27":{"kind":"call","span":{"start":[264,27],"end":[264,62],"bytes":[6610,6645]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.calculateDBChecksum"},"267:25":{"kind":"call","span":{"start":[267,25],"end":[267,86],"bytes":[6711,6772]},"callee":null},"268:21":{"kind":"call","span":{"start":[268,21],"end":[268,42],"bytes":[6793,6814]},"callee":null},"272:20":{"kind":"call","span":{"start":[272,20],"end":[272,41],"bytes":[6854,6875]},"callee":null},"273:38":{"kind":"call","span":{"start":[273,38],"end":[273,71],"bytes":[6913,6946]},"callee":null},"274:40":{"kind":"call","span":{"start":[274,40],"end":[274,75],"bytes":[6986,7021]},"callee":null},"276:31":{"kind":"call","span":{"start":[276,31],"end":[276,55],"bytes":[7053,7077]},"callee":null},"276:5":{"kind":"call","span":{"start":[276,5],"end":[276,27],"bytes":[7027,7049]},"callee":null},"283:2":{"kind":"call","span":{"start":[283,2],"end":[287,3],"bytes":[7191,7315]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation","kind":"method","span":{"start":[292,1],"end":[363,2],"bytes":[7335,9341]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[292,1],"end":[292,88],"bytes":[7335,7422]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","metrics":{"cyclomatic":9},"body":{"293:15":{"kind":"call","span":{"start":[293,15],"end":[293,25],"bytes":[7437,7447]},"callee":null},"296:14":{"kind":"call","span":{"start":[296,14],"end":[296,42],"bytes":[7522,7550]},"callee":null},"299:19":{"kind":"call","span":{"start":[299,19],"end":[299,50],"bytes":[7574,7605]},"callee":null},"302:25":{"kind":"call","span":{"start":[302,25],"end":[302,79],"bytes":[7671,7725]},"callee":null},"305:8":{"kind":"call","span":{"start":[305,8],"end":[305,24],"bytes":[7752,7768]},"callee":null},"307:21":{"kind":"call","span":{"start":[307,21],"end":[307,54],"bytes":[7790,7823]},"callee":null},"310:25":{"kind":"call","span":{"start":[310,25],"end":[310,81],"bytes":[7889,7945]},"callee":null},"313:8":{"kind":"call","span":{"start":[313,8],"end":[313,26],"bytes":[7972,7990]},"callee":null},"315:17":{"kind":"call","span":{"start":[315,17],"end":[315,41],"bytes":[8008,8032]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getTableList"},"318:25":{"kind":"call","span":{"start":[318,25],"end":[318,73],"bytes":[8098,8146]},"callee":null},"319:21":{"kind":"call","span":{"start":[319,21],"end":[319,42],"bytes":[8167,8188]},"callee":null},"323:37":{"kind":"call","span":{"start":[323,37],"end":[323,48],"bytes":[8245,8256]},"callee":null},"327:23":{"kind":"call","span":{"start":[327,23],"end":[327,53],"bytes":[8330,8360]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getRowCount"},"330:26":{"kind":"call","span":{"start":[330,26],"end":[330,96],"bytes":[8429,8499]},"callee":null},"331:22":{"kind":"call","span":{"start":[331,22],"end":[331,43],"bytes":[8521,8542]},"callee":null},"335:25":{"kind":"call","span":{"start":[335,25],"end":[335,57],"bytes":[8589,8621]},"callee":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getRowCount"},"338:26":{"kind":"call","span":{"start":[338,26],"end":[338,98],"bytes":[8690,8762]},"callee":null},"339:22":{"kind":"call","span":{"start":[339,22],"end":[339,43],"bytes":[8784,8805]},"callee":null},"345:19":{"kind":"call","span":{"start":[345,19],"end":[345,58],"bytes":[8902,8941]},"callee":null},"345:62":{"kind":"call","span":{"start":[345,62],"end":[345,127],"bytes":[8945,9010]},"callee":null},"349:20":{"kind":"call","span":{"start":[349,20],"end":[349,41],"bytes":[9038,9059]},"callee":null},"356:2":{"kind":"call","span":{"start":[356,2],"end":[360,3],"bytes":[9191,9323]},"callee":null},"359:21":{"kind":"call","span":{"start":[359,21],"end":[359,32],"bytes":[9308,9319]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performIntegrityCheck":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performIntegrityCheck","kind":"method","span":{"start":[194,1],"end":[247,2],"bytes":[4744,6124]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performIntegrityCheck","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[194,1],"end":[194,88],"bytes":[4744,4831]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","metrics":{"cyclomatic":6},"body":{"195:15":{"kind":"call","span":{"start":[195,15],"end":[195,25],"bytes":[4846,4856]},"callee":null},"198:14":{"kind":"call","span":{"start":[198,14],"end":[198,42],"bytes":[4931,4959]},"callee":null},"201:13":{"kind":"call","span":{"start":[201,13],"end":[201,46],"bytes":[4977,5010]},"callee":null},"204:25":{"kind":"call","span":{"start":[204,25],"end":[204,72],"bytes":[5076,5123]},"callee":null},"207:8":{"kind":"call","span":{"start":[207,8],"end":[207,18],"bytes":[5150,5160]},"callee":null},"209:15":{"kind":"call","span":{"start":[209,15],"end":[209,49],"bytes":[5176,5210]},"callee":null},"212:25":{"kind":"call","span":{"start":[212,25],"end":[212,71],"bytes":[5276,5322]},"callee":null},"213:21":{"kind":"call","span":{"start":[213,21],"end":[213,42],"bytes":[5343,5364]},"callee":null},"216:8":{"kind":"call","span":{"start":[216,8],"end":[216,20],"bytes":[5391,5403]},"callee":null},"219:6":{"kind":"call","span":{"start":[219,6],"end":[219,17],"bytes":[5432,5443]},"callee":null},"221:13":{"kind":"call","span":{"start":[221,13],"end":[221,29],"bytes":[5476,5492]},"callee":null},"223:26":{"kind":"call","span":{"start":[223,26],"end":[223,71],"bytes":[5557,5602]},"callee":null},"224:22":{"kind":"call","span":{"start":[224,22],"end":[224,43],"bytes":[5624,5645]},"callee":null},"227:13":{"kind":"call","span":{"start":[227,13],"end":[227,34],"bytes":[5679,5700]},"callee":null},"230:20":{"kind":"call","span":{"start":[230,20],"end":[230,41],"bytes":[5724,5745]},"callee":null},"233:5":{"kind":"call","span":{"start":[233,5],"end":[233,17],"bytes":[5794,5806]},"callee":null},"237:25":{"kind":"call","span":{"start":[237,25],"end":[237,81],"bytes":[5917,5973]},"callee":null},"240:2":{"kind":"call","span":{"start":[240,2],"end":[244,3],"bytes":[5979,6106]},"callee":null},"243:13":{"kind":"call","span":{"start":[243,13],"end":[243,25],"bytes":[6088,6100]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performQuickCheck":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performQuickCheck","kind":"method","span":{"start":[154,1],"end":[192,2],"bytes":[3778,4742]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performQuickCheck","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[154,1],"end":[154,84],"bytes":[3778,3861]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","metrics":{"cyclomatic":4},"body":{"155:15":{"kind":"call","span":{"start":[155,15],"end":[155,25],"bytes":[3876,3886]},"callee":null},"158:14":{"kind":"call","span":{"start":[158,14],"end":[158,42],"bytes":[3957,3985]},"callee":null},"161:13":{"kind":"call","span":{"start":[161,13],"end":[161,46],"bytes":[4003,4036]},"callee":null},"164:25":{"kind":"call","span":{"start":[164,25],"end":[164,72],"bytes":[4102,4149]},"callee":null},"167:8":{"kind":"call","span":{"start":[167,8],"end":[167,18],"bytes":[4176,4186]},"callee":null},"170:8":{"kind":"call","span":{"start":[170,8],"end":[170,41],"bytes":[4219,4252]},"callee":null},"171:20":{"kind":"call","span":{"start":[171,20],"end":[171,41],"bytes":[4291,4312]},"callee":null},"175:25":{"kind":"call","span":{"start":[175,25],"end":[175,67],"bytes":[4379,4421]},"callee":null},"183:25":{"kind":"call","span":{"start":[183,25],"end":[183,77],"bytes":[4571,4623]},"callee":null},"186:2":{"kind":"call","span":{"start":[186,2],"end":[189,3],"bytes":[4629,4724]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performRestore":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performRestore","kind":"method","span":{"start":[99,1],"end":[152,2],"bytes":[2442,3776]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performRestore","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[99,1],"end":[99,81],"bytes":[2442,2522]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","metrics":{"cyclomatic":6},"body":{"100:15":{"kind":"call","span":{"start":[100,15],"end":[100,25],"bytes":[2537,2547]},"callee":null},"103:14":{"kind":"call","span":{"start":[103,14],"end":[103,42],"bytes":[2614,2642]},"callee":null},"106:12":{"kind":"call","span":{"start":[106,12],"end":[106,35],"bytes":[2659,2682]},"callee":null},"106:52":{"kind":"call","span":{"start":[106,52],"end":[106,70],"bytes":[2699,2717]},"callee":null},"107:3":{"kind":"call","span":{"start":[107,3],"end":[107,73],"bytes":[2722,2792]},"callee":null},"112:9":{"kind":"call","span":{"start":[112,9],"end":[116,4],"bytes":[2849,2968]},"callee":null},"118:9":{"kind":"call","span":{"start":[118,9],"end":[121,4],"bytes":[2987,3080]},"callee":null},"124:17":{"kind":"call","span":{"start":[124,17],"end":[124,37],"bytes":[3101,3121]},"callee":null},"125:20":{"kind":"call","span":{"start":[125,20],"end":[125,41],"bytes":[3141,3162]},"callee":null},"129:25":{"kind":"call","span":{"start":[129,25],"end":[129,91],"bytes":[3229,3295]},"callee":null},"129:76":{"kind":"call","span":{"start":[129,76],"end":[129,90],"bytes":[3280,3294]},"callee":null},"133:15":{"kind":"call","span":{"start":[133,15],"end":[133,36],"bytes":[3330,3351]},"callee":null},"135:25":{"kind":"call","span":{"start":[135,25],"end":[135,76],"bytes":[3414,3465]},"callee":null},"142:18":{"kind":"call","span":{"start":[142,18],"end":[142,39],"bytes":[3574,3595]},"callee":null},"143:37":{"kind":"call","span":{"start":[143,37],"end":[143,48],"bytes":[3646,3657]},"callee":null},"146:2":{"kind":"call","span":{"start":[146,2],"end":[149,3],"bytes":[3663,3758]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.reportResults":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.reportResults","kind":"method","span":{"start":[442,1],"end":[460,2],"bytes":[11193,11601]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.reportResults","parameters":[{"name":"results","type":"[]github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","span":{"start":[442,1],"end":[442,76],"bytes":[11193,11268]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"447:4":{"kind":"call","span":{"start":[447,4],"end":[450,5],"bytes":[11368,11474]},"callee":null},"455:3":{"kind":"call","span":{"start":[455,3],"end":[455,44],"bytes":[11501,11542]},"callee":null},"459:9":{"kind":"call","span":{"start":[459,9],"end":[459,40],"bytes":[11568,11599]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.validateLTXContinuity":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.validateLTXContinuity","kind":"method","span":{"start":[365,1],"end":[399,2],"bytes":[9343,10275]},"signature":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.validateLTXContinuity","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[365,1],"end":[365,88],"bytes":[9343,9430]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","metrics":{"cyclomatic":3},"body":{"366:15":{"kind":"call","span":{"start":[366,15],"end":[366,25],"bytes":[9445,9455]},"callee":null},"369:14":{"kind":"call","span":{"start":[369,14],"end":[369,42],"bytes":[9529,9557]},"callee":null},"372:9":{"kind":"call","span":{"start":[372,9],"end":[372,68],"bytes":[9571,9630]},"callee":null},"373:17":{"kind":"call","span":{"start":[373,17],"end":[373,29],"bytes":[9647,9659]},"callee":null},"376:25":{"kind":"call","span":{"start":[376,25],"end":[376,73],"bytes":[9725,9773]},"callee":null},"377:21":{"kind":"call","span":{"start":[377,21],"end":[377,42],"bytes":[9794,9815]},"callee":null},"381:11":{"kind":"call","span":{"start":[381,11],"end":[381,46],"bytes":[9846,9881]},"callee":null},"381:25":{"kind":"call","span":{"start":[381,25],"end":[381,39],"bytes":[9860,9874]},"callee":null},"382:5":{"kind":"call","span":{"start":[382,5],"end":[382,15],"bytes":[9886,9896]},"callee":null},"385:21":{"kind":"call","span":{"start":[385,21],"end":[385,42],"bytes":[9992,10013]},"callee":null},"390:20":{"kind":"call","span":{"start":[390,20],"end":[390,41],"bytes":[10075,10096]},"callee":null},"391:40":{"kind":"call","span":{"start":[391,40],"end":[391,50],"bytes":[10136,10146]},"callee":null},"393:2":{"kind":"call","span":{"start":[393,2],"end":[396,3],"bytes":[10153,10257]},"callee":null}}}},"fields":{"CheckType":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/CheckType","kind":"field","type":"string","span":{"start":[25,1],"end":[25,22],"bytes":[279,300]}},"ConfigPath":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/ConfigPath","kind":"field","type":"string","span":{"start":[27,1],"end":[27,22],"bytes":[321,342]}},"LTXContinuity":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/LTXContinuity","kind":"field","type":"bool","span":{"start":[26,1],"end":[26,20],"bytes":[301,320]}},"Main":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/Main","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream-test.Main","span":{"start":[20,1],"end":[20,12],"bytes":[200,211]}},"ReplicaURL":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/ReplicaURL","kind":"field","type":"string","span":{"start":[23,1],"end":[23,22],"bytes":[235,256]}},"RestoredDB":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/RestoredDB","kind":"field","type":"string","span":{"start":[24,1],"end":[24,22],"bytes":[257,278]}},"SourceDB":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/SourceDB","kind":"field","type":"string","span":{"start":[22,1],"end":[22,22],"bytes":[213,234]}}}},"ValidationResult":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult","kind":"struct","span":{"start":[30,1],"end":[36,2],"bytes":[346,504]},"callables":{},"fields":{"CheckType":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult/CheckType","kind":"field","type":"string","span":{"start":[31,1],"end":[31,21],"bytes":[377,397]}},"Details":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult/Details","kind":"field","type":"map[string]interface{}","span":{"start":[35,1],"end":[35,37],"bytes":[466,502]}},"Duration":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult/Duration","kind":"field","type":"time.Duration","span":{"start":[33,1],"end":[33,28],"bytes":[417,444]}},"ErrorMessage":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult/ErrorMessage","kind":"field","type":"string","span":{"start":[34,1],"end":[34,21],"bytes":[445,465]}},"Passed":{"id":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidationResult/Passed","kind":"field","type":"bool","span":{"start":[32,1],"end":[32,19],"bytes":[398,416]}}}}},"functions":{},"content_hash":"e60ce14e89afa8decef6dfab01ca6c537d4c7beeda6658ebb90ad8c9cf4766cc"},"cmd/litestream/databases.go":{"id":"can://go/litestream/cmd/litestream/databases.go","kind":"module","span":{"start":[1,1],"end":[105,1],"bytes":[0,2227]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"text/tabwriter\"\n)\n\n// DatabasesCommand is a command for listing managed databases.\ntype DatabasesCommand struct{}\n\n// Run executes the command.\nfunc (c *DatabasesCommand) Run(_ context.Context, args []string) (err error) {\n\tfs := flag.NewFlagSet(\"litestream-databases\", flag.ContinueOnError)\n\tconfigPath, noExpandEnv := registerConfigFlag(fs)\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t} else if fs.NArg() != 0 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\t// Load configuration.\n\tif *configPath == \"\" {\n\t\t*configPath = DefaultConfigPath()\n\t}\n\tconfig, err := ReadConfigFile(*configPath, !*noExpandEnv)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdatabases := make([]DatabaseInfo, 0, len(config.DBs))\n\tfor _, dbConfig := range config.DBs {\n\t\tdb, err := NewDBFromConfig(dbConfig)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdatabases = append(databases, DatabaseInfo{\n\t\t\tPath: db.Path(),\n\t\t\tReplica: db.Replica.Client.Type(),\n\t\t})\n\t}\n\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(databases, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)\n\tdefer w.Flush()\n\n\tfmt.Fprintln(w, \"path\\treplica\")\n\tfor _, db := range databases {\n\t\tfmt.Fprintf(w, \"%s\\t%s\\n\", db.Path, db.Replica)\n\t}\n\n\treturn nil\n}\n\ntype DatabaseInfo struct {\n\tPath string `json:\"path\"`\n\tReplica string `json:\"replica\"`\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *DatabasesCommand) Usage() {\n\tfmt.Printf(`\nThe databases command lists all databases in the configuration file.\n\nUsage:\n\n\tlitestream databases [arguments]\n\nArguments:\n\n\t-config PATH\n\t Specifies the configuration file.\n\t Defaults to %s\n\n\t-json\n\t Output raw JSON instead of human-readable text.\n\n\t-no-expand-env\n\t Disables environment variable expansion in configuration file.\n\nExamples:\n\n\t$ litestream databases\n\t$ litestream databases -config /path/to/litestream.yml\n\t$ litestream databases -no-expand-env -config /path/to/litestream.yml\n\n`[1:],\n\t\tDefaultConfigPath(),\n\t)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[34,50]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[51,58]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[59,65]}},{"name":"os","path":"os","span":{"start":[8,1],"end":[8,6],"bytes":[66,71]}},{"name":"tabwriter","path":"text/tabwriter","span":{"start":[9,1],"end":[9,18],"bytes":[72,89]}}],"types":{"DatabaseInfo":{"id":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabaseInfo","kind":"struct","span":{"start":[69,1],"end":[72,2],"bytes":[1497,1588]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabaseInfo/Path","kind":"field","type":"string","span":{"start":[70,1],"end":[70,30],"bytes":[1524,1553]}},"Replica":{"id":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabaseInfo/Replica","kind":"field","type":"string","span":{"start":[71,1],"end":[71,33],"bytes":[1554,1586]}}}},"DatabasesCommand":{"id":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand","kind":"struct","span":{"start":[13,1],"end":[13,31],"bytes":[157,187]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run":{"id":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","kind":"method","span":{"start":[16,1],"end":[67,2],"bytes":[218,1495]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","parameters":[{"name":"_","type":"context.Context","span":{"start":[16,1],"end":[16,79],"bytes":[218,296]}},{"name":"args","type":"[]string","span":{"start":[16,1],"end":[16,79],"bytes":[218,296]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"17:8":{"kind":"call","span":{"start":[17,8],"end":[17,69],"bytes":[304,365]},"callee":null},"18:29":{"kind":"call","span":{"start":[18,29],"end":[18,51],"bytes":[394,416]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag"},"19:16":{"kind":"call","span":{"start":[19,16],"end":[19,57],"bytes":[432,473]},"callee":null},"21:12":{"kind":"call","span":{"start":[21,12],"end":[21,26],"bytes":[505,519]},"callee":null},"23:12":{"kind":"call","span":{"start":[23,12],"end":[23,21],"bytes":[558,567]},"callee":null},"24:10":{"kind":"call","span":{"start":[24,10],"end":[24,42],"bytes":[584,616]},"callee":null},"29:17":{"kind":"call","span":{"start":[29,17],"end":[29,36],"bytes":[685,704]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"},"31:17":{"kind":"call","span":{"start":[31,17],"end":[31,59],"bytes":[724,766]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"36:15":{"kind":"call","span":{"start":[36,15],"end":[36,55],"bytes":[815,855]},"callee":null},"36:39":{"kind":"call","span":{"start":[36,39],"end":[36,54],"bytes":[839,854]},"callee":null},"38:14":{"kind":"call","span":{"start":[38,14],"end":[38,39],"bytes":[908,933]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"43:15":{"kind":"call","span":{"start":[43,15],"end":[46,5],"bytes":[985,1082]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig"},"44:13":{"kind":"call","span":{"start":[44,13],"end":[44,22],"bytes":[1029,1038]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"45:13":{"kind":"call","span":{"start":[45,13],"end":[45,37],"bytes":[1052,1076]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type"},"50:18":{"kind":"call","span":{"start":[50,18],"end":[50,57],"bytes":[1122,1161]},"callee":null},"52:11":{"kind":"call","span":{"start":[52,11],"end":[52,59],"bytes":[1190,1238]},"callee":null},"54:15":{"kind":"call","span":{"start":[54,15],"end":[54,29],"bytes":[1257,1271]},"callee":null},"54:3":{"kind":"call","span":{"start":[54,3],"end":[54,30],"bytes":[1245,1272]},"callee":null},"58:7":{"kind":"call","span":{"start":[58,7],"end":[58,54],"bytes":[1296,1343]},"callee":null},"59:8":{"kind":"call","span":{"start":[59,8],"end":[59,17],"bytes":[1351,1360]},"callee":null},"61:2":{"kind":"call","span":{"start":[61,2],"end":[61,34],"bytes":[1363,1395]},"callee":null},"63:3":{"kind":"call","span":{"start":[63,3],"end":[63,50],"bytes":[1430,1477]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Usage":{"id":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Usage","kind":"method","span":{"start":[75,1],"end":[104,2],"bytes":[1633,2226]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"102:3":{"kind":"call","span":{"start":[102,3],"end":[102,22],"bytes":[2201,2220]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"},"76:2":{"kind":"call","span":{"start":[76,2],"end":[103,3],"bytes":[1671,2224]},"callee":null}}}}}},"functions":{},"content_hash":"d9ceaff1b578d1b0bbebb2951a94207cbb6581ef0c59e2fcb7bd1ec27426851b"},"cmd/litestream/directory_watcher.go":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go","kind":"module","span":{"start":[1,1],"end":[439,1],"bytes":[0,9884]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\nconst debounceInterval = 250 * time.Millisecond\n\n// DirectoryMonitor watches a directory tree for SQLite databases and dynamically\n// manages database instances within the store as files are created or removed.\ntype DirectoryMonitor struct {\n\tstore *litestream.Store\n\tconfig *DBConfig\n\tdirPath string\n\tpattern string\n\trecursive bool\n\n\twatcher *fsnotify.Watcher\n\tctx context.Context\n\tcancel context.CancelFunc\n\n\tlogger *slog.Logger\n\n\tmu sync.Mutex\n\tdbs map[string]*litestream.DB\n\twatchedDirs map[string]struct{}\n\n\t// Only accessed from the run() goroutine, so no mutex is needed.\n\tpendingEvents map[string]fsnotify.Op\n\tdebounceActive bool\n\n\twg sync.WaitGroup\n}\n\n// NewDirectoryMonitor returns a new monitor for directory-based replication.\nfunc NewDirectoryMonitor(ctx context.Context, store *litestream.Store, dbc *DBConfig, existing []*litestream.DB) (*DirectoryMonitor, error) {\n\tif dbc == nil {\n\t\treturn nil, errors.New(\"database config required\")\n\t}\n\tif store == nil {\n\t\treturn nil, errors.New(\"store required\")\n\t}\n\n\tdirPath, err := expand(dbc.Dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif _, err := os.Stat(dirPath); err != nil {\n\t\treturn nil, err\n\t}\n\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmonitorCtx, cancel := context.WithCancel(ctx)\n\tdm := \u0026DirectoryMonitor{\n\t\tstore: store,\n\t\tconfig: dbc,\n\t\tdirPath: dirPath,\n\t\tpattern: dbc.Pattern,\n\t\trecursive: dbc.Recursive,\n\t\twatcher: watcher,\n\t\tctx: monitorCtx,\n\t\tcancel: cancel,\n\t\tlogger: slog.With(\"dir\", dirPath),\n\t\tdbs: make(map[string]*litestream.DB),\n\t\twatchedDirs: make(map[string]struct{}),\n\t\tpendingEvents: make(map[string]fsnotify.Op),\n\t}\n\n\tfor _, db := range existing {\n\t\tdm.dbs[db.Path()] = db\n\t}\n\n\tif err := dm.addInitialWatches(); err != nil {\n\t\twatcher.Close()\n\t\tcancel()\n\t\treturn nil, err\n\t}\n\n\tdm.scanDirectory(dm.dirPath)\n\n\tdm.wg.Add(1)\n\tgo dm.run()\n\n\treturn dm, nil\n}\n\n// Close stops the directory monitor and releases resources.\nfunc (dm *DirectoryMonitor) Close() {\n\tdm.cancel()\n\t_ = dm.watcher.Close()\n\tdm.wg.Wait()\n}\n\nfunc (dm *DirectoryMonitor) run() {\n\tdefer dm.wg.Done()\n\n\t// Debounce timer lives in this goroutine so shutdown via dm.wg.Wait() is clean.\n\tdebounceTimer := time.NewTimer(debounceInterval)\n\tdebounceTimer.Stop()\n\tdefer debounceTimer.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase \u003c-dm.ctx.Done():\n\t\t\treturn\n\t\tcase event, ok := \u003c-dm.watcher.Events:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif dm.handleEvent(event) \u0026\u0026 !dm.debounceActive {\n\t\t\t\tdebounceTimer.Reset(debounceInterval)\n\t\t\t\tdm.debounceActive = true\n\t\t\t}\n\t\tcase \u003c-debounceTimer.C:\n\t\t\tdm.flushPendingEvents()\n\t\tcase err, ok := \u003c-dm.watcher.Errors:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdm.logger.Error(\"directory watcher error\", \"error\", err)\n\t\t}\n\t}\n}\n\nfunc (dm *DirectoryMonitor) addInitialWatches() error {\n\tif dm.recursive {\n\t\treturn filepath.WalkDir(dm.dirPath, func(path string, d os.DirEntry, err error) error {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif !d.IsDir() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn dm.addDirectoryWatch(path)\n\t\t})\n\t}\n\n\treturn dm.addDirectoryWatch(dm.dirPath)\n}\n\nfunc (dm *DirectoryMonitor) addDirectoryWatch(path string) error {\n\tabspath := filepath.Clean(path)\n\n\tdm.mu.Lock()\n\tdefer dm.mu.Unlock()\n\n\tif _, ok := dm.watchedDirs[abspath]; ok {\n\t\treturn nil\n\t}\n\tdm.watchedDirs[abspath] = struct{}{}\n\n\tif err := dm.watcher.Add(abspath); err != nil {\n\t\tdelete(dm.watchedDirs, abspath)\n\t\treturn err\n\t}\n\n\tdm.logger.Debug(\"watching directory\", \"path\", abspath)\n\treturn nil\n}\n\nfunc (dm *DirectoryMonitor) removeDirectoryWatch(path string) {\n\tabspath := filepath.Clean(path)\n\n\tdm.mu.Lock()\n\tdefer dm.mu.Unlock()\n\n\tif _, ok := dm.watchedDirs[abspath]; !ok {\n\t\treturn\n\t}\n\tdelete(dm.watchedDirs, abspath)\n\n\tif err := dm.watcher.Remove(abspath); err != nil {\n\t\tdm.logger.Debug(\"remove directory watch\", \"path\", abspath, \"error\", err)\n\t}\n}\n\n// handleEvent processes a single fsnotify event. Returns true if pending events\n// were queued and the debounce timer should be reset.\nfunc (dm *DirectoryMonitor) handleEvent(event fsnotify.Event) bool {\n\tpath := filepath.Clean(event.Name)\n\tif path == \"\" {\n\t\treturn false\n\t}\n\n\tif dm.shouldSkipPath(path) {\n\t\treturn false\n\t}\n\n\tinfo, statErr := os.Stat(path)\n\tisDir := statErr == nil \u0026\u0026 info.IsDir()\n\n\t// Early pattern check for create/write only. Rename must pass through\n\t// to removal handling below, since the old path may be a tracked DB.\n\tif !isDir \u0026\u0026 event.Op\u0026(fsnotify.Create|fsnotify.Write) != 0 {\n\t\tif !dm.matchesPattern(path) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tdm.mu.Lock()\n\t_, wasWatchedDir := dm.watchedDirs[path]\n\tdm.mu.Unlock()\n\n\tif isDir \u0026\u0026 event.Op\u0026(fsnotify.Create|fsnotify.Rename) != 0 {\n\t\tif dm.recursive {\n\t\t\tif err := dm.addDirectoryWatch(path); err != nil {\n\t\t\t\tdm.logger.Error(\"add directory watch\", \"path\", path, \"error\", err)\n\t\t\t}\n\t\t\tdm.scanDirectory(path)\n\t\t}\n\t}\n\n\tif (isDir || wasWatchedDir) \u0026\u0026 event.Op\u0026(fsnotify.Remove|fsnotify.Rename) != 0 {\n\t\tdm.removeDirectoryWatch(path)\n\t\tdm.removeDatabasesUnder(path)\n\t\treturn false\n\t}\n\n\tif isDir {\n\t\treturn false\n\t}\n\n\tif statErr != nil \u0026\u0026 !os.IsNotExist(statErr) {\n\t\tdm.logger.Debug(\"stat event path\", \"path\", path, \"error\", statErr)\n\t\treturn false\n\t}\n\n\tif event.Op\u0026(fsnotify.Remove|fsnotify.Rename) != 0 {\n\t\tdm.removeDatabase(path)\n\t\treturn false\n\t}\n\n\tif event.Op\u0026(fsnotify.Create|fsnotify.Write|fsnotify.Rename) != 0 {\n\t\tdm.pendingEvents[path] |= event.Op\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// flushPendingEvents processes all queued potential database events.\nfunc (dm *DirectoryMonitor) flushPendingEvents() {\n\tfor path := range dm.pendingEvents {\n\t\tselect {\n\t\tcase \u003c-dm.ctx.Done():\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\t\tdm.handlePotentialDatabase(path)\n\t}\n\tdm.pendingEvents = make(map[string]fsnotify.Op)\n\tdm.debounceActive = false\n}\n\nfunc (dm *DirectoryMonitor) handlePotentialDatabase(path string) {\n\tif !dm.matchesPattern(path) {\n\t\treturn\n\t}\n\n\tdm.mu.Lock()\n\tif _, exists := dm.dbs[path]; exists {\n\t\tdm.mu.Unlock()\n\t\treturn\n\t}\n\tdm.dbs[path] = nil\n\tdm.mu.Unlock()\n\n\tvar db *litestream.DB\n\tsuccess := false\n\tdefer func() {\n\t\tif !success {\n\t\t\tif db != nil {\n\t\t\t\t_ = dm.store.UnregisterDB(dm.ctx, db.Path())\n\t\t\t}\n\t\t\tdm.mu.Lock()\n\t\t\tdelete(dm.dbs, path)\n\t\t\tdm.mu.Unlock()\n\t\t}\n\t}()\n\n\tif !IsSQLiteDatabase(path) {\n\t\treturn\n\t}\n\n\tvar err error\n\tdb, err = newDBFromDirectoryEntry(dm.config, dm.dirPath, path)\n\tif err != nil {\n\t\tdm.logger.Error(\"configure database\", \"path\", path, \"error\", err)\n\t\treturn\n\t}\n\n\tif err := dm.store.RegisterDB(db); err != nil {\n\t\tdm.logger.Error(\"register database with store\", \"path\", path, \"error\", err)\n\t\treturn\n\t}\n\n\tdm.mu.Lock()\n\tdm.dbs[path] = db\n\tdm.mu.Unlock()\n\n\tsuccess = true\n\tdm.logger.Info(\"added database to replication\", \"path\", path)\n}\n\nfunc (dm *DirectoryMonitor) removeDatabase(path string) {\n\tdm.mu.Lock()\n\tdb := dm.dbs[path]\n\tdm.mu.Unlock()\n\n\tif db == nil {\n\t\treturn\n\t}\n\n\tif err := dm.store.UnregisterDB(dm.ctx, db.Path()); err != nil {\n\t\tdm.logger.Error(\"unregister database from store\", \"path\", path, \"error\", err)\n\t\treturn\n\t}\n\n\tdm.mu.Lock()\n\tdelete(dm.dbs, path)\n\tdm.mu.Unlock()\n\n\tdm.logger.Info(\"removed database from replication\", \"path\", path)\n}\n\nfunc (dm *DirectoryMonitor) removeDatabasesUnder(dir string) {\n\tprefix := dir + string(os.PathSeparator)\n\n\tdm.mu.Lock()\n\tvar toClose []*litestream.DB\n\tvar toClosePaths []string\n\tfor path, db := range dm.dbs {\n\t\tif path == dir || strings.HasPrefix(path, prefix) {\n\t\t\ttoClose = append(toClose, db)\n\t\t\ttoClosePaths = append(toClosePaths, path)\n\t\t}\n\t}\n\tdm.mu.Unlock()\n\n\tfor i, db := range toClose {\n\t\tif db == nil {\n\t\t\tdm.mu.Lock()\n\t\t\tdelete(dm.dbs, toClosePaths[i])\n\t\t\tdm.mu.Unlock()\n\t\t\tcontinue\n\t\t}\n\t\tif err := dm.store.UnregisterDB(dm.ctx, db.Path()); err != nil {\n\t\t\tdm.logger.Error(\"unregister database from store\", \"path\", db.Path(), \"error\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tdm.mu.Lock()\n\t\tdelete(dm.dbs, toClosePaths[i])\n\t\tdm.mu.Unlock()\n\t}\n}\n\nfunc (dm *DirectoryMonitor) matchesPattern(path string) bool {\n\tmatched, err := filepath.Match(dm.pattern, filepath.Base(path))\n\tif err != nil {\n\t\tdm.logger.Error(\"pattern match failed\", \"pattern\", dm.pattern, \"path\", path, \"error\", err)\n\t\treturn false\n\t}\n\treturn matched\n}\n\n// shouldSkipPath returns true for SQLite auxiliary files (WAL, SHM, journal)\n// that generate many events but are never databases themselves.\nfunc (dm *DirectoryMonitor) shouldSkipPath(path string) bool {\n\tbase := filepath.Base(path)\n\treturn strings.HasSuffix(base, \"-wal\") ||\n\t\tstrings.HasSuffix(base, \"-shm\") ||\n\t\tstrings.HasSuffix(base, \"-journal\")\n}\n\n// scanDirectory discovers pre-existing databases and is also called when new\n// directories appear to close the race window between watch registration and file creation.\nfunc (dm *DirectoryMonitor) scanDirectory(dir string) {\n\tif !dm.recursive {\n\t\tentries, err := os.ReadDir(dir)\n\t\tif err != nil {\n\t\t\tif !os.IsNotExist(err) {\n\t\t\t\tdm.logger.Debug(\"read directory\", \"path\", dir, \"error\", err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tfor _, entry := range entries {\n\t\t\tif entry.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpath := filepath.Join(dir, entry.Name())\n\t\t\tdm.handlePotentialDatabase(path)\n\t\t}\n\t\treturn\n\t}\n\n\terr := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdm.logger.Debug(\"scan directory entry\", \"path\", path, \"error\", err)\n\t\t\treturn nil\n\t\t}\n\n\t\tif d.IsDir() {\n\t\t\tif path != dir {\n\t\t\t\tif err := dm.addDirectoryWatch(path); err != nil {\n\t\t\t\t\tdm.logger.Error(\"add directory watch\", \"path\", path, \"error\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tdm.handlePotentialDatabase(path)\n\t\treturn nil\n\t})\n\tif err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\tdm.logger.Debug(\"scan directory\", \"path\", dir, \"error\", err)\n\t}\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"errors","path":"errors","span":{"start":[5,1],"end":[5,10],"bytes":[34,43]}},{"name":"slog","path":"log/slog","span":{"start":[6,1],"end":[6,12],"bytes":[44,55]}},{"name":"os","path":"os","span":{"start":[7,1],"end":[7,6],"bytes":[56,61]}},{"name":"filepath","path":"path/filepath","span":{"start":[8,1],"end":[8,17],"bytes":[62,78]}},{"name":"strings","path":"strings","span":{"start":[9,1],"end":[9,11],"bytes":[79,89]}},{"name":"sync","path":"sync","span":{"start":[10,1],"end":[10,8],"bytes":[90,97]}},{"name":"time","path":"time","span":{"start":[11,1],"end":[11,8],"bytes":[98,105]}},{"name":"fsnotify","path":"github.com/fsnotify/fsnotify","span":{"start":[13,1],"end":[13,32],"bytes":[107,138]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[15,1],"end":[15,37],"bytes":[140,176]}}],"types":{"DirectoryMonitor":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor","kind":"struct","span":{"start":[22,1],"end":[44,2],"bytes":[391,873]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close","kind":"method","span":{"start":[104,1],"end":[108,2],"bytes":[2225,2315]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close","parameters":[],"metrics":{"cyclomatic":1},"body":{"105:2":{"kind":"call","span":{"start":[105,2],"end":[105,13],"bytes":[2264,2275]},"callee":null},"106:6":{"kind":"call","span":{"start":[106,6],"end":[106,24],"bytes":[2281,2299]},"callee":null},"107:2":{"kind":"call","span":{"start":[107,2],"end":[107,14],"bytes":[2301,2313]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch","kind":"method","span":{"start":[157,1],"end":[175,2],"bytes":[3329,3734]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch","parameters":[{"name":"path","type":"string","span":{"start":[157,1],"end":[157,67],"bytes":[3329,3395]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"158:13":{"kind":"call","span":{"start":[158,13],"end":[158,33],"bytes":[3408,3428]},"callee":null},"160:2":{"kind":"call","span":{"start":[160,2],"end":[160,14],"bytes":[3431,3443]},"callee":null},"161:8":{"kind":"call","span":{"start":[161,8],"end":[161,22],"bytes":[3451,3465]},"callee":null},"168:12":{"kind":"call","span":{"start":[168,12],"end":[168,35],"bytes":[3576,3599]},"callee":null},"169:3":{"kind":"call","span":{"start":[169,3],"end":[169,34],"bytes":[3616,3647]},"callee":null},"173:2":{"kind":"call","span":{"start":[173,2],"end":[173,56],"bytes":[3666,3720]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches","kind":"method","span":{"start":[141,1],"end":[155,2],"bytes":[2996,3327]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"143:10":{"kind":"call","span":{"start":[143,10],"end":[151,5],"bytes":[3080,3280]},"callee":null},"154:9":{"kind":"call","span":{"start":[154,9],"end":[154,41],"bytes":[3293,3325]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch"}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches.closure_1","kind":"function","span":{"start":[143,1],"end":[151,5],"bytes":[3071,3280]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches.closure_1","parameters":[{"name":"path","type":"string","span":{"start":[143,1],"end":[143,90],"bytes":[3071,3160]}},{"name":"d","type":"os.DirEntry","span":{"start":[143,1],"end":[143,90],"bytes":[3071,3160]}},{"name":"err","type":"error","span":{"start":[143,1],"end":[143,90],"bytes":[3071,3160]}}],"return_type":"error","error_channel":["error"],"body":{"147:8":{"kind":"call","span":{"start":[147,8],"end":[147,17],"bytes":[3207,3216]},"callee":null},"150:11":{"kind":"call","span":{"start":[150,11],"end":[150,37],"bytes":[3249,3275]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.flushPendingEvents":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.flushPendingEvents","kind":"method","span":{"start":[258,1],"end":[269,2],"bytes":[5718,5982]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.flushPendingEvents","parameters":[],"metrics":{"cyclomatic":5},"body":{"261:10":{"kind":"call","span":{"start":[261,10],"end":[261,23],"bytes":[5827,5840]},"callee":null},"265:3":{"kind":"call","span":{"start":[265,3],"end":[265,35],"bytes":[5869,5901]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase"},"267:21":{"kind":"call","span":{"start":[267,21],"end":[267,49],"bytes":[5925,5953]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","kind":"method","span":{"start":[195,1],"end":[255,2],"bytes":[4230,5646]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","parameters":[{"name":"event","type":"github.com/fsnotify/fsnotify.Event","span":{"start":[195,1],"end":[195,69],"bytes":[4230,4298]}}],"return_type":"bool","metrics":{"cyclomatic":13},"body":{"196:10":{"kind":"call","span":{"start":[196,10],"end":[196,36],"bytes":[4308,4334]},"callee":null},"201:5":{"kind":"call","span":{"start":[201,5],"end":[201,28],"bytes":[4375,4398]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.shouldSkipPath"},"205:19":{"kind":"call","span":{"start":[205,19],"end":[205,32],"bytes":[4438,4451]},"callee":null},"206:29":{"kind":"call","span":{"start":[206,29],"end":[206,41],"bytes":[4480,4492]},"callee":null},"211:7":{"kind":"call","span":{"start":[211,7],"end":[211,30],"bytes":[4706,4729]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern"},"216:2":{"kind":"call","span":{"start":[216,2],"end":[216,14],"bytes":[4757,4769]},"callee":null},"218:2":{"kind":"call","span":{"start":[218,2],"end":[218,16],"bytes":[4813,4827]},"callee":null},"222:14":{"kind":"call","span":{"start":[222,14],"end":[222,40],"bytes":[4925,4951]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch"},"223:5":{"kind":"call","span":{"start":[223,5],"end":[223,71],"bytes":[4970,5036]},"callee":null},"225:4":{"kind":"call","span":{"start":[225,4],"end":[225,26],"bytes":[5045,5067]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory"},"230:3":{"kind":"call","span":{"start":[230,3],"end":[230,32],"bytes":[5160,5189]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDirectoryWatch"},"231:3":{"kind":"call","span":{"start":[231,3],"end":[231,32],"bytes":[5192,5221]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder"},"239:24":{"kind":"call","span":{"start":[239,24],"end":[239,46],"bytes":[5295,5317]},"callee":null},"240:3":{"kind":"call","span":{"start":[240,3],"end":[240,69],"bytes":[5322,5388]},"callee":null},"245:3":{"kind":"call","span":{"start":[245,3],"end":[245,26],"bytes":[5464,5487]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase"}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","kind":"method","span":{"start":[271,1],"end":[319,2],"bytes":[5984,6918]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","parameters":[{"name":"path","type":"string","span":{"start":[271,1],"end":[271,67],"bytes":[5984,6050]}}],"metrics":{"cyclomatic":8},"body":{"272:6":{"kind":"call","span":{"start":[272,6],"end":[272,29],"bytes":[6056,6079]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern"},"276:2":{"kind":"call","span":{"start":[276,2],"end":[276,14],"bytes":[6096,6108]},"callee":null},"278:3":{"kind":"call","span":{"start":[278,3],"end":[278,17],"bytes":[6151,6165]},"callee":null},"282:2":{"kind":"call","span":{"start":[282,2],"end":[282,16],"bytes":[6199,6213]},"callee":null},"286:8":{"kind":"call","span":{"start":[286,8],"end":[295,5],"bytes":[6263,6426]},"callee":null},"297:6":{"kind":"call","span":{"start":[297,6],"end":[297,28],"bytes":[6433,6455]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.IsSQLiteDatabase"},"302:12":{"kind":"call","span":{"start":[302,12],"end":[302,64],"bytes":[6497,6549]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry"},"304:3":{"kind":"call","span":{"start":[304,3],"end":[304,68],"bytes":[6569,6634]},"callee":null},"308:12":{"kind":"call","span":{"start":[308,12],"end":[308,35],"bytes":[6659,6682]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB"},"309:3":{"kind":"call","span":{"start":[309,3],"end":[309,78],"bytes":[6699,6774]},"callee":null},"313:2":{"kind":"call","span":{"start":[313,2],"end":[313,14],"bytes":[6789,6801]},"callee":null},"315:2":{"kind":"call","span":{"start":[315,2],"end":[315,16],"bytes":[6822,6836]},"callee":null},"318:2":{"kind":"call","span":{"start":[318,2],"end":[318,63],"bytes":[6855,6916]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase.closure_1","kind":"function","span":{"start":[286,1],"end":[295,5],"bytes":[6256,6426]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase.closure_1","parameters":[],"body":{"289:39":{"kind":"call","span":{"start":[289,39],"end":[289,48],"bytes":[6344,6353]},"callee":null},"289:9":{"kind":"call","span":{"start":[289,9],"end":[289,49],"bytes":[6314,6354]},"callee":null},"291:4":{"kind":"call","span":{"start":[291,4],"end":[291,16],"bytes":[6363,6375]},"callee":null},"292:4":{"kind":"call","span":{"start":[292,4],"end":[292,24],"bytes":[6379,6399]},"callee":null},"293:4":{"kind":"call","span":{"start":[293,4],"end":[293,18],"bytes":[6403,6417]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern","kind":"method","span":{"start":[374,1],"end":[381,2],"bytes":[8079,8352]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern","parameters":[{"name":"path","type":"string","span":{"start":[374,1],"end":[374,63],"bytes":[8079,8141]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"375:18":{"kind":"call","span":{"start":[375,18],"end":[375,65],"bytes":[8159,8206]},"callee":null},"375:45":{"kind":"call","span":{"start":[375,45],"end":[375,64],"bytes":[8186,8205]},"callee":null},"377:3":{"kind":"call","span":{"start":[377,3],"end":[377,93],"bytes":[8226,8316]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase","kind":"method","span":{"start":[321,1],"end":[340,2],"bytes":[6920,7338]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase","parameters":[{"name":"path","type":"string","span":{"start":[321,1],"end":[321,58],"bytes":[6920,6977]}}],"metrics":{"cyclomatic":3},"body":{"322:2":{"kind":"call","span":{"start":[322,2],"end":[322,14],"bytes":[6979,6991]},"callee":null},"324:2":{"kind":"call","span":{"start":[324,2],"end":[324,16],"bytes":[7013,7027]},"callee":null},"330:12":{"kind":"call","span":{"start":[330,12],"end":[330,52],"bytes":[7069,7109]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB"},"330:42":{"kind":"call","span":{"start":[330,42],"end":[330,51],"bytes":[7099,7108]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"331:3":{"kind":"call","span":{"start":[331,3],"end":[331,80],"bytes":[7126,7203]},"callee":null},"335:2":{"kind":"call","span":{"start":[335,2],"end":[335,14],"bytes":[7218,7230]},"callee":null},"336:2":{"kind":"call","span":{"start":[336,2],"end":[336,22],"bytes":[7232,7252]},"callee":null},"337:2":{"kind":"call","span":{"start":[337,2],"end":[337,16],"bytes":[7254,7268]},"callee":null},"339:2":{"kind":"call","span":{"start":[339,2],"end":[339,67],"bytes":[7271,7336]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder","kind":"method","span":{"start":[342,1],"end":[372,2],"bytes":[7340,8077]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder","parameters":[{"name":"dir","type":"string","span":{"start":[342,1],"end":[342,63],"bytes":[7340,7402]}}],"metrics":{"cyclomatic":6},"body":{"343:18":{"kind":"call","span":{"start":[343,18],"end":[343,42],"bytes":[7420,7444]},"callee":null},"345:2":{"kind":"call","span":{"start":[345,2],"end":[345,14],"bytes":[7447,7459]},"callee":null},"349:21":{"kind":"call","span":{"start":[349,21],"end":[349,52],"bytes":[7569,7600]},"callee":null},"350:14":{"kind":"call","span":{"start":[350,14],"end":[350,33],"bytes":[7616,7635]},"callee":null},"351:19":{"kind":"call","span":{"start":[351,19],"end":[351,45],"bytes":[7654,7680]},"callee":null},"354:2":{"kind":"call","span":{"start":[354,2],"end":[354,16],"bytes":[7689,7703]},"callee":null},"358:4":{"kind":"call","span":{"start":[358,4],"end":[358,16],"bytes":[7755,7767]},"callee":null},"359:4":{"kind":"call","span":{"start":[359,4],"end":[359,35],"bytes":[7771,7802]},"callee":null},"360:4":{"kind":"call","span":{"start":[360,4],"end":[360,18],"bytes":[7806,7820]},"callee":null},"363:13":{"kind":"call","span":{"start":[363,13],"end":[363,53],"bytes":[7849,7889]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB"},"363:43":{"kind":"call","span":{"start":[363,43],"end":[363,52],"bytes":[7879,7888]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"364:4":{"kind":"call","span":{"start":[364,4],"end":[364,86],"bytes":[7907,7989]},"callee":null},"364:62":{"kind":"call","span":{"start":[364,62],"end":[364,71],"bytes":[7965,7974]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"368:3":{"kind":"call","span":{"start":[368,3],"end":[368,15],"bytes":[8009,8021]},"callee":null},"369:3":{"kind":"call","span":{"start":[369,3],"end":[369,34],"bytes":[8024,8055]},"callee":null},"370:3":{"kind":"call","span":{"start":[370,3],"end":[370,17],"bytes":[8058,8072]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDirectoryWatch":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDirectoryWatch","kind":"method","span":{"start":[177,1],"end":[191,2],"bytes":[3736,4092]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDirectoryWatch","parameters":[{"name":"path","type":"string","span":{"start":[177,1],"end":[177,64],"bytes":[3736,3799]}}],"metrics":{"cyclomatic":3},"body":{"178:13":{"kind":"call","span":{"start":[178,13],"end":[178,33],"bytes":[3812,3832]},"callee":null},"180:2":{"kind":"call","span":{"start":[180,2],"end":[180,14],"bytes":[3835,3847]},"callee":null},"181:8":{"kind":"call","span":{"start":[181,8],"end":[181,22],"bytes":[3855,3869]},"callee":null},"186:2":{"kind":"call","span":{"start":[186,2],"end":[186,33],"bytes":[3928,3959]},"callee":null},"188:12":{"kind":"call","span":{"start":[188,12],"end":[188,38],"bytes":[3972,3998]},"callee":null},"189:3":{"kind":"call","span":{"start":[189,3],"end":[189,75],"bytes":[4015,4087]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run","kind":"method","span":{"start":[110,1],"end":[139,2],"bytes":[2317,2994]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run","parameters":[],"metrics":{"cyclomatic":10},"body":{"111:8":{"kind":"call","span":{"start":[111,8],"end":[111,20],"bytes":[2360,2372]},"callee":null},"114:19":{"kind":"call","span":{"start":[114,19],"end":[114,50],"bytes":[2474,2505]},"callee":null},"115:2":{"kind":"call","span":{"start":[115,2],"end":[115,22],"bytes":[2507,2527]},"callee":null},"116:8":{"kind":"call","span":{"start":[116,8],"end":[116,28],"bytes":[2535,2555]},"callee":null},"120:10":{"kind":"call","span":{"start":[120,10],"end":[120,23],"bytes":[2584,2597]},"callee":null},"126:7":{"kind":"call","span":{"start":[126,7],"end":[126,28],"bytes":[2684,2705]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent"},"127:5":{"kind":"call","span":{"start":[127,5],"end":[127,42],"bytes":[2734,2771]},"callee":null},"131:4":{"kind":"call","span":{"start":[131,4],"end":[131,27],"bytes":[2835,2858]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.flushPendingEvents"},"136:4":{"kind":"call","span":{"start":[136,4],"end":[136,60],"bytes":[2929,2985]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory","kind":"method","span":{"start":[394,1],"end":[438,2],"bytes":[8881,9883]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory","parameters":[{"name":"dir","type":"string","span":{"start":[394,1],"end":[394,56],"bytes":[8881,8936]}}],"metrics":{"cyclomatic":12},"body":{"396:19":{"kind":"call","span":{"start":[396,19],"end":[396,34],"bytes":[8975,8990]},"callee":null},"398:8":{"kind":"call","span":{"start":[398,8],"end":[398,26],"bytes":[9016,9034]},"callee":null},"399:5":{"kind":"call","span":{"start":[399,5],"end":[399,65],"bytes":[9041,9101]},"callee":null},"405:7":{"kind":"call","span":{"start":[405,7],"end":[405,20],"bytes":[9162,9175]},"callee":null},"408:12":{"kind":"call","span":{"start":[408,12],"end":[408,44],"bytes":[9207,9239]},"callee":null},"408:31":{"kind":"call","span":{"start":[408,31],"end":[408,43],"bytes":[9226,9238]},"callee":null},"409:4":{"kind":"call","span":{"start":[409,4],"end":[409,36],"bytes":[9243,9275]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase"},"414:9":{"kind":"call","span":{"start":[414,9],"end":[434,4],"bytes":[9301,9775]},"callee":null},"435:20":{"kind":"call","span":{"start":[435,20],"end":[435,38],"bytes":[9795,9813]},"callee":null},"436:3":{"kind":"call","span":{"start":[436,3],"end":[436,63],"bytes":[9818,9878]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory.closure_1","kind":"function","span":{"start":[414,1],"end":[434,4],"bytes":[9293,9775]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory.closure_1","parameters":[{"name":"path","type":"string","span":{"start":[414,1],"end":[414,82],"bytes":[9293,9374]}},{"name":"d","type":"os.DirEntry","span":{"start":[414,1],"end":[414,82],"bytes":[9293,9374]}},{"name":"err","type":"error","span":{"start":[414,1],"end":[414,82],"bytes":[9293,9374]}}],"return_type":"error","error_channel":["error"],"body":{"416:7":{"kind":"call","span":{"start":[416,7],"end":[416,25],"bytes":[9399,9417]},"callee":null},"419:4":{"kind":"call","span":{"start":[419,4],"end":[419,71],"bytes":[9443,9510]},"callee":null},"423:6":{"kind":"call","span":{"start":[423,6],"end":[423,15],"bytes":[9535,9544]},"callee":null},"425:15":{"kind":"call","span":{"start":[425,15],"end":[425,41],"bytes":[9581,9607]},"callee":null},"426:6":{"kind":"call","span":{"start":[426,6],"end":[426,72],"bytes":[9627,9693]},"callee":null},"432:3":{"kind":"call","span":{"start":[432,3],"end":[432,35],"bytes":[9726,9758]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.shouldSkipPath":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.shouldSkipPath","kind":"method","span":{"start":[385,1],"end":[390,2],"bytes":[8497,8708]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.shouldSkipPath","parameters":[{"name":"path","type":"string","span":{"start":[385,1],"end":[385,63],"bytes":[8497,8559]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"386:10":{"kind":"call","span":{"start":[386,10],"end":[386,29],"bytes":[8569,8588]},"callee":null},"387:9":{"kind":"call","span":{"start":[387,9],"end":[387,40],"bytes":[8597,8628]},"callee":null},"388:3":{"kind":"call","span":{"start":[388,3],"end":[388,34],"bytes":[8634,8665]},"callee":null},"389:3":{"kind":"call","span":{"start":[389,3],"end":[389,38],"bytes":[8671,8706]},"callee":null}}}},"fields":{"cancel":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/cancel","kind":"field","type":"context.CancelFunc","span":{"start":[31,1],"end":[31,28],"bytes":[577,604]}},"config":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/config","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[24,1],"end":[24,21],"bytes":[451,471]}},"ctx":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/ctx","kind":"field","type":"context.Context","span":{"start":[30,1],"end":[30,25],"bytes":[552,576]}},"dbs":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/dbs","kind":"field","type":"map[string]*github.com/benbjohnson/litestream.DB","span":{"start":[36,1],"end":[36,39],"bytes":[652,690]}},"debounceActive":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/debounceActive","kind":"field","type":"bool","span":{"start":[41,1],"end":[41,21],"bytes":[831,851]}},"dirPath":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/dirPath","kind":"field","type":"string","span":{"start":[25,1],"end":[25,18],"bytes":[472,489]}},"logger":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[33,1],"end":[33,21],"bytes":[606,626]}},"mu":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/mu","kind":"field","type":"sync.Mutex","span":{"start":[35,1],"end":[35,24],"bytes":[628,651]}},"pattern":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/pattern","kind":"field","type":"string","span":{"start":[26,1],"end":[26,18],"bytes":[490,507]}},"pendingEvents":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/pendingEvents","kind":"field","type":"map[string]github.com/fsnotify/fsnotify.Op","span":{"start":[40,1],"end":[40,39],"bytes":[792,830]}},"recursive":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/recursive","kind":"field","type":"bool","span":{"start":[27,1],"end":[27,16],"bytes":[508,523]}},"store":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/store","kind":"field","type":"*github.com/benbjohnson/litestream.Store","span":{"start":[23,1],"end":[23,29],"bytes":[422,450]}},"watchedDirs":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/watchedDirs","kind":"field","type":"map[string]struct{}","span":{"start":[37,1],"end":[37,33],"bytes":[691,723]}},"watcher":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/watcher","kind":"field","type":"*github.com/fsnotify/fsnotify.Watcher","span":{"start":[29,1],"end":[29,27],"bytes":[525,551]}},"wg":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/wg","kind":"field","type":"sync.WaitGroup","span":{"start":[43,1],"end":[43,19],"bytes":[853,871]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor":{"id":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","kind":"function","span":{"start":[47,1],"end":[101,2],"bytes":[953,2162]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[47,1],"end":[47,142],"bytes":[953,1094]}},{"name":"store","type":"*github.com/benbjohnson/litestream.Store","span":{"start":[47,1],"end":[47,142],"bytes":[953,1094]}},{"name":"dbc","type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[47,1],"end":[47,142],"bytes":[953,1094]}},{"name":"existing","type":"[]*github.com/benbjohnson/litestream.DB","span":{"start":[47,1],"end":[47,142],"bytes":[953,1094]}}],"return_type":"(*github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"49:15":{"kind":"call","span":{"start":[49,15],"end":[49,53],"bytes":[1126,1164]},"callee":null},"52:15":{"kind":"call","span":{"start":[52,15],"end":[52,43],"bytes":[1201,1229]},"callee":null},"55:18":{"kind":"call","span":{"start":[55,18],"end":[55,33],"bytes":[1251,1266]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"60:15":{"kind":"call","span":{"start":[60,15],"end":[60,31],"bytes":[1320,1336]},"callee":null},"64:18":{"kind":"call","span":{"start":[64,18],"end":[64,39],"bytes":[1390,1411]},"callee":null},"69:24":{"kind":"call","span":{"start":[69,24],"end":[69,47],"bytes":[1474,1497]},"callee":null},"79:18":{"kind":"call","span":{"start":[79,18],"end":[79,43],"bytes":[1755,1780]},"callee":null},"80:18":{"kind":"call","span":{"start":[80,18],"end":[80,49],"bytes":[1799,1830]},"callee":null},"81:18":{"kind":"call","span":{"start":[81,18],"end":[81,43],"bytes":[1849,1874]},"callee":null},"82:18":{"kind":"call","span":{"start":[82,18],"end":[82,46],"bytes":[1893,1921]},"callee":null},"86:10":{"kind":"call","span":{"start":[86,10],"end":[86,19],"bytes":[1967,1976]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"89:12":{"kind":"call","span":{"start":[89,12],"end":[89,34],"bytes":[1998,2020]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches"},"90:3":{"kind":"call","span":{"start":[90,3],"end":[90,18],"bytes":[2037,2052]},"callee":null},"91:3":{"kind":"call","span":{"start":[91,3],"end":[91,11],"bytes":[2055,2063]},"callee":null},"95:2":{"kind":"call","span":{"start":[95,2],"end":[95,30],"bytes":[2087,2115]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory"},"97:2":{"kind":"call","span":{"start":[97,2],"end":[97,14],"bytes":[2118,2130]},"callee":null},"98:5":{"kind":"call","span":{"start":[98,5],"end":[98,13],"bytes":[2135,2143]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run","is_goroutine":true}}}},"content_hash":"4ce58f90eab9eeb776b3513c62283d921b77d2c3b830358b012af2d7e7ac2f36"},"cmd/litestream/info.go":{"id":"can://go/litestream/cmd/litestream/info.go","kind":"module","span":{"start":[1,1],"end":[113,1],"bytes":[0,2888]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// InfoCommand represents the command to show daemon information.\ntype InfoCommand struct{}\n\n// Run executes the info command.\nfunc (c *InfoCommand) Run(_ context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-info\", flag.ContinueOnError)\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\ttimeout := fs.Int(\"timeout\", 10, \"timeout in seconds\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() \u003e 0 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tif *timeout \u003c= 0 {\n\t\treturn fmt.Errorf(\"timeout must be greater than 0\")\n\t}\n\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\tresp, err := client.Get(\"http://localhost/info\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"info failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"info failed: %s\", string(body))\n\t}\n\n\tvar result litestream.InfoResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(result, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t} else {\n\t\tuptime := time.Duration(result.UptimeSeconds) * time.Second\n\t\tfmt.Printf(\"Litestream %s\\n\", result.Version)\n\t\tfmt.Printf(\" PID: %d\\n\", result.PID)\n\t\tfmt.Printf(\" Uptime: %s\\n\", uptime)\n\t\tfmt.Printf(\" Started at: %s\\n\", result.StartedAt.Format(time.RFC3339))\n\t\tfmt.Printf(\" Databases: %d\\n\", result.DatabaseCount)\n\t}\n\n\treturn nil\n}\n\n// Usage prints the help text for the info command.\nfunc (c *InfoCommand) Usage() {\n\tfmt.Println(`\nusage: litestream info [OPTIONS]\n\nShow daemon information from a running Litestream instance.\n\nOptions:\n -json\n Output raw JSON instead of human-readable text.\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -timeout SECONDS\n Maximum time to wait in seconds (default: 10).\n\nExamples:\n $ litestream info\n $ litestream info -json\n $ litestream info -socket /tmp/litestream.sock\n`[1:])\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[34,50]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[51,58]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[59,65]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[66,71]}},{"name":"net","path":"net","span":{"start":[9,1],"end":[9,7],"bytes":[72,78]}},{"name":"http","path":"net/http","span":{"start":[10,1],"end":[10,12],"bytes":[79,90]}},{"name":"time","path":"time","span":{"start":[11,1],"end":[11,8],"bytes":[91,98]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[13,1],"end":[13,37],"bytes":[100,136]}}],"types":{"InfoCommand":{"id":"can://go/litestream/cmd/litestream/info.go/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand","kind":"struct","span":{"start":[17,1],"end":[17,26],"bytes":[206,231]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run":{"id":"can://go/litestream/cmd/litestream/info.go/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run","kind":"method","span":{"start":[20,1],"end":[88,2],"bytes":[267,2351]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run","parameters":[{"name":"_","type":"context.Context","span":{"start":[20,1],"end":[20,68],"bytes":[267,334]}},{"name":"args","type":"[]string","span":{"start":[20,1],"end":[20,68],"bytes":[267,334]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"21:8":{"kind":"call","span":{"start":[21,8],"end":[21,64],"bytes":[342,398]},"callee":null},"22:16":{"kind":"call","span":{"start":[22,16],"end":[22,86],"bytes":[414,484]},"callee":null},"23:13":{"kind":"call","span":{"start":[23,13],"end":[23,56],"bytes":[497,540]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,57],"bytes":[556,597]},"callee":null},"26:12":{"kind":"call","span":{"start":[26,12],"end":[26,26],"bytes":[629,643]},"callee":null},"30:5":{"kind":"call","span":{"start":[30,5],"end":[30,14],"bytes":[679,688]},"callee":null},"31:10":{"kind":"call","span":{"start":[31,10],"end":[31,42],"bytes":[704,736]},"callee":null},"35:10":{"kind":"call","span":{"start":[35,10],"end":[35,54],"bytes":[770,814]},"callee":null},"38:19":{"kind":"call","span":{"start":[38,19],"end":[38,42],"bytes":[837,860]},"callee":null},"48:15":{"kind":"call","span":{"start":[48,15],"end":[48,50],"bytes":[1121,1156]},"callee":null},"50:10":{"kind":"call","span":{"start":[50,10],"end":[50,68],"bytes":[1183,1241]},"callee":null},"52:8":{"kind":"call","span":{"start":[52,8],"end":[52,25],"bytes":[1252,1269]},"callee":null},"54:15":{"kind":"call","span":{"start":[54,15],"end":[54,36],"bytes":[1285,1306]},"callee":null},"56:10":{"kind":"call","span":{"start":[56,10],"end":[56,56],"bytes":[1333,1379]},"callee":null},"61:13":{"kind":"call","span":{"start":[61,13],"end":[61,43],"bytes":[1474,1504]},"callee":null},"62:11":{"kind":"call","span":{"start":[62,11],"end":[62,55],"bytes":[1552,1596]},"callee":null},"64:10":{"kind":"call","span":{"start":[64,10],"end":[64,53],"bytes":[1610,1653]},"callee":null},"64:40":{"kind":"call","span":{"start":[64,40],"end":[64,52],"bytes":[1640,1652]},"callee":null},"68:12":{"kind":"call","span":{"start":[68,12],"end":[68,41],"bytes":[1705,1734]},"callee":null},"69:10":{"kind":"call","span":{"start":[69,10],"end":[69,57],"bytes":[1758,1805]},"callee":null},"73:18":{"kind":"call","span":{"start":[73,18],"end":[73,54],"bytes":[1845,1881]},"callee":null},"75:11":{"kind":"call","span":{"start":[75,11],"end":[75,59],"bytes":[1910,1958]},"callee":null},"77:15":{"kind":"call","span":{"start":[77,15],"end":[77,29],"bytes":[1977,1991]},"callee":null},"77:3":{"kind":"call","span":{"start":[77,3],"end":[77,30],"bytes":[1965,1992]},"callee":null},"79:13":{"kind":"call","span":{"start":[79,13],"end":[79,48],"bytes":[2015,2050]},"callee":null},"80:3":{"kind":"call","span":{"start":[80,3],"end":[80,48],"bytes":[2067,2112]},"callee":null},"81:3":{"kind":"call","span":{"start":[81,3],"end":[81,47],"bytes":[2115,2159]},"callee":null},"82:3":{"kind":"call","span":{"start":[82,3],"end":[82,43],"bytes":[2162,2202]},"callee":null},"83:3":{"kind":"call","span":{"start":[83,3],"end":[83,74],"bytes":[2205,2276]},"callee":null},"83:36":{"kind":"call","span":{"start":[83,36],"end":[83,73],"bytes":[2238,2275]},"callee":null},"84:3":{"kind":"call","span":{"start":[84,3],"end":[84,57],"bytes":[2279,2333]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/info.go/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run.closure_1","kind":"function","span":{"start":[42,1],"end":[44,6],"bytes":[956,1097]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[42,1],"end":[42,73],"bytes":[956,1028]}},{"name":"_","type":"string","span":{"start":[42,1],"end":[42,73],"bytes":[956,1028]}},{"name":"_","type":"string","span":{"start":[42,1],"end":[42,73],"bytes":[956,1028]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"43:12":{"kind":"call","span":{"start":[43,12],"end":[43,63],"bytes":[1040,1091]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Usage":{"id":"can://go/litestream/cmd/litestream/info.go/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Usage","kind":"method","span":{"start":[91,1],"end":[112,2],"bytes":[2405,2887]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"92:2":{"kind":"call","span":{"start":[92,2],"end":[111,7],"bytes":[2438,2885]},"callee":null}}}}}},"functions":{},"content_hash":"c67f1ed730a0f301fb7642ebc95fca5357a437b59373b2e2eaf4b0acd322a2d7"},"cmd/litestream/list.go":{"id":"can://go/litestream/cmd/litestream/list.go","kind":"module","span":{"start":[1,1],"end":[118,1],"bytes":[0,2872]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// ListCommand represents the command to list all managed databases.\ntype ListCommand struct{}\n\n// Run executes the list command.\nfunc (c *ListCommand) Run(_ context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-list\", flag.ContinueOnError)\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\ttimeout := fs.Int(\"timeout\", 10, \"timeout in seconds\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() \u003e 0 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tif *timeout \u003c= 0 {\n\t\treturn fmt.Errorf(\"timeout must be greater than 0\")\n\t}\n\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\tresp, err := client.Get(\"http://localhost/list\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"list failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"list failed: %s\", string(body))\n\t}\n\n\tvar result litestream.ListResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(result, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t} else {\n\t\tif len(result.Databases) == 0 {\n\t\t\tfmt.Println(\"No databases configured\")\n\t\t} else {\n\t\t\tfor _, db := range result.Databases {\n\t\t\t\tsyncInfo := \"never\"\n\t\t\t\tif db.LastSyncAt != nil {\n\t\t\t\t\tsyncInfo = db.LastSyncAt.Format(time.RFC3339)\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"%s [%s] (last sync: %s)\\n\", db.Path, db.Status, syncInfo)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Usage prints the help text for the list command.\nfunc (c *ListCommand) Usage() {\n\tfmt.Println(`\nusage: litestream list [OPTIONS]\n\nList all managed databases from a running daemon.\n\nOptions:\n -json\n Output raw JSON instead of human-readable text.\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -timeout SECONDS\n Maximum time to wait in seconds (default: 10).\n\nExamples:\n $ litestream list\n $ litestream list -json\n $ litestream list -socket /tmp/litestream.sock\n`[1:])\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[34,50]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[51,58]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[59,65]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[66,71]}},{"name":"net","path":"net","span":{"start":[9,1],"end":[9,7],"bytes":[72,78]}},{"name":"http","path":"net/http","span":{"start":[10,1],"end":[10,12],"bytes":[79,90]}},{"name":"time","path":"time","span":{"start":[11,1],"end":[11,8],"bytes":[91,98]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[13,1],"end":[13,37],"bytes":[100,136]}}],"types":{"ListCommand":{"id":"can://go/litestream/cmd/litestream/list.go/github.com/benbjohnson/litestream/cmd/litestream.ListCommand","kind":"struct","span":{"start":[17,1],"end":[17,26],"bytes":[209,234]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run":{"id":"can://go/litestream/cmd/litestream/list.go/github.com/benbjohnson/litestream/cmd/litestream.ListCommand/github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run","kind":"method","span":{"start":[20,1],"end":[93,2],"bytes":[270,2345]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run","parameters":[{"name":"_","type":"context.Context","span":{"start":[20,1],"end":[20,68],"bytes":[270,337]}},{"name":"args","type":"[]string","span":{"start":[20,1],"end":[20,68],"bytes":[270,337]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":14},"body":{"21:8":{"kind":"call","span":{"start":[21,8],"end":[21,64],"bytes":[345,401]},"callee":null},"22:16":{"kind":"call","span":{"start":[22,16],"end":[22,86],"bytes":[417,487]},"callee":null},"23:13":{"kind":"call","span":{"start":[23,13],"end":[23,56],"bytes":[500,543]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,57],"bytes":[559,600]},"callee":null},"26:12":{"kind":"call","span":{"start":[26,12],"end":[26,26],"bytes":[632,646]},"callee":null},"30:5":{"kind":"call","span":{"start":[30,5],"end":[30,14],"bytes":[682,691]},"callee":null},"31:10":{"kind":"call","span":{"start":[31,10],"end":[31,42],"bytes":[707,739]},"callee":null},"35:10":{"kind":"call","span":{"start":[35,10],"end":[35,54],"bytes":[773,817]},"callee":null},"38:19":{"kind":"call","span":{"start":[38,19],"end":[38,42],"bytes":[840,863]},"callee":null},"48:15":{"kind":"call","span":{"start":[48,15],"end":[48,50],"bytes":[1124,1159]},"callee":null},"50:10":{"kind":"call","span":{"start":[50,10],"end":[50,68],"bytes":[1186,1244]},"callee":null},"52:8":{"kind":"call","span":{"start":[52,8],"end":[52,25],"bytes":[1255,1272]},"callee":null},"54:15":{"kind":"call","span":{"start":[54,15],"end":[54,36],"bytes":[1288,1309]},"callee":null},"56:10":{"kind":"call","span":{"start":[56,10],"end":[56,56],"bytes":[1336,1382]},"callee":null},"61:13":{"kind":"call","span":{"start":[61,13],"end":[61,43],"bytes":[1477,1507]},"callee":null},"62:11":{"kind":"call","span":{"start":[62,11],"end":[62,55],"bytes":[1555,1599]},"callee":null},"64:10":{"kind":"call","span":{"start":[64,10],"end":[64,53],"bytes":[1613,1656]},"callee":null},"64:40":{"kind":"call","span":{"start":[64,40],"end":[64,52],"bytes":[1643,1655]},"callee":null},"68:12":{"kind":"call","span":{"start":[68,12],"end":[68,41],"bytes":[1708,1737]},"callee":null},"69:10":{"kind":"call","span":{"start":[69,10],"end":[69,57],"bytes":[1761,1808]},"callee":null},"73:18":{"kind":"call","span":{"start":[73,18],"end":[73,54],"bytes":[1848,1884]},"callee":null},"75:11":{"kind":"call","span":{"start":[75,11],"end":[75,59],"bytes":[1913,1961]},"callee":null},"77:15":{"kind":"call","span":{"start":[77,15],"end":[77,29],"bytes":[1980,1994]},"callee":null},"77:3":{"kind":"call","span":{"start":[77,3],"end":[77,30],"bytes":[1968,1995]},"callee":null},"79:6":{"kind":"call","span":{"start":[79,6],"end":[79,27],"bytes":[2011,2032]},"callee":null},"80:4":{"kind":"call","span":{"start":[80,4],"end":[80,42],"bytes":[2043,2081]},"callee":null},"85:17":{"kind":"call","span":{"start":[85,17],"end":[85,51],"bytes":[2204,2238]},"callee":null},"87:5":{"kind":"call","span":{"start":[87,5],"end":[87,74],"bytes":[2249,2318]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/list.go/github.com/benbjohnson/litestream/cmd/litestream.ListCommand/github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run.closure_1","kind":"function","span":{"start":[42,1],"end":[44,6],"bytes":[959,1100]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[42,1],"end":[42,73],"bytes":[959,1031]}},{"name":"_","type":"string","span":{"start":[42,1],"end":[42,73],"bytes":[959,1031]}},{"name":"_","type":"string","span":{"start":[42,1],"end":[42,73],"bytes":[959,1031]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"43:12":{"kind":"call","span":{"start":[43,12],"end":[43,63],"bytes":[1043,1094]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Usage":{"id":"can://go/litestream/cmd/litestream/list.go/github.com/benbjohnson/litestream/cmd/litestream.ListCommand/github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Usage","kind":"method","span":{"start":[96,1],"end":[117,2],"bytes":[2399,2871]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"97:2":{"kind":"call","span":{"start":[97,2],"end":[116,7],"bytes":[2432,2869]},"callee":null}}}}}},"functions":{},"content_hash":"dc9c59c70f84ba2a92cb656c5b6d52a9d74272c89b53fea8d58a1087b60d66ee"},"cmd/litestream/ltx.go":{"id":"can://go/litestream/cmd/litestream/ltx.go","kind":"module","span":{"start":[1,1],"end":[186,1],"bytes":[0,4202]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"text/tabwriter\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\n// LTXCommand represents a command to list LTX files for a database.\ntype LTXCommand struct{}\n\n// Run executes the command.\nfunc (c *LTXCommand) Run(ctx context.Context, args []string) (err error) {\n\tfs := flag.NewFlagSet(\"litestream-ltx\", flag.ContinueOnError)\n\tconfigPath, noExpandEnv := registerConfigFlag(fs)\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tvar level levelVar\n\tfs.Var(\u0026level, \"level\", \"compaction level (0-9 or \\\"all\\\")\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t} else if fs.NArg() == 0 || fs.Arg(0) == \"\" {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path or replica URL required\",\n\t\t\thint: \"litestream ltx /path/to/db\",\n\t\t}\n\t} else if fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tvar r *litestream.Replica\n\tif litestream.IsURL(fs.Arg(0)) {\n\t\tif *configPath != \"\" {\n\t\t\treturn fmt.Errorf(\"cannot specify a replica URL and the -config flag\")\n\t\t}\n\t\tif r, err = NewReplicaFromConfig(\u0026ReplicaConfig{URL: fs.Arg(0)}, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinternal.InitLog(os.Stdout, \"INFO\", \"text\", false)\n\t} else {\n\t\tif *configPath == \"\" {\n\t\t\t*configPath = DefaultConfigPath()\n\t\t}\n\n\t\t// Load configuration.\n\t\tconfig, err := ReadConfigFile(*configPath, !*noExpandEnv)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Lookup database from configuration file by path.\n\t\tpath, err := expand(fs.Arg(0))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdbc := config.DBConfig(path)\n\t\tif dbc == nil {\n\t\t\treturn fmt.Errorf(\"database not found in config: %s\", path)\n\t\t}\n\n\t\tdb, err := NewDBFromConfig(dbc)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if db.Replica == nil {\n\t\t\treturn fmt.Errorf(\"database has no replica\")\n\t\t}\n\t\tr = db.Replica\n\t}\n\n\t// Determine which levels to iterate.\n\tvar levels []int\n\tif int(level) == levelAll {\n\t\tfor lvl := 0; lvl \u003c= litestream.SnapshotLevel; lvl++ {\n\t\t\tlevels = append(levels, lvl)\n\t\t}\n\t} else {\n\t\tlevels = []int{int(level)}\n\t}\n\n\tfiles := make([]LTXFileInfo, 0)\n\tfor _, lvl := range levels {\n\t\titr, err := r.Client.LTXFiles(ctx, lvl, 0, false)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor itr.Next() {\n\t\t\tinfo := itr.Item()\n\t\t\tfiles = append(files, LTXFileInfo{\n\t\t\t\tLevel: lvl,\n\t\t\t\tMinTXID: info.MinTXID.String(),\n\t\t\t\tMaxTXID: info.MaxTXID.String(),\n\t\t\t\tSize: info.Size,\n\t\t\t\tTimestamp: info.CreatedAt.Format(time.RFC3339),\n\t\t\t})\n\t\t}\n\t\tif err := itr.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(files, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)\n\tdefer w.Flush()\n\n\tfmt.Fprintln(w, \"level\\tmin_txid\\tmax_txid\\tsize\\tcreated\")\n\tfor _, file := range files {\n\t\tfmt.Fprintf(w, \"%d\\t%s\\t%s\\t%d\\t%s\\n\",\n\t\t\tfile.Level,\n\t\t\tfile.MinTXID,\n\t\t\tfile.MaxTXID,\n\t\t\tfile.Size,\n\t\t\tfile.Timestamp,\n\t\t)\n\t}\n\treturn nil\n}\n\ntype LTXFileInfo struct {\n\tLevel int `json:\"level\"`\n\tMinTXID string `json:\"min_txid\"`\n\tMaxTXID string `json:\"max_txid\"`\n\tSize int64 `json:\"size\"`\n\tTimestamp string `json:\"timestamp\"`\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *LTXCommand) Usage() {\n\tfmt.Printf(`\nThe ltx command lists all LTX files available for a database.\n\nUsage:\n\n\tlitestream ltx [arguments] DB_PATH\n\n\tlitestream ltx [arguments] REPLICA_URL\n\nArguments:\n\n\t-config PATH\n\t Specifies the configuration file.\n\t Defaults to %s\n\n\t-no-expand-env\n\t Disables environment variable expansion in configuration file.\n\n\t-level LEVEL\n\t Compaction level to list (0-9 or \"all\").\n\t Defaults to 0.\n\n\t-json\n\t Output raw JSON instead of human-readable text.\n\nExamples:\n\n\t# List all LTX files for a database.\n\t$ litestream ltx /path/to/db\n\n\t# List all LTX files for replica URL.\n\t$ litestream ltx s3://mybkt/db\n\n\t# List LTX files at snapshot level (level 9).\n\t$ litestream ltx -level 9 /path/to/db\n\n\t# List LTX files across all compaction levels.\n\t$ litestream ltx -level all /path/to/db\n\n`[1:],\n\t\tDefaultConfigPath(),\n\t)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[34,50]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[51,58]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[59,65]}},{"name":"os","path":"os","span":{"start":[8,1],"end":[8,6],"bytes":[66,71]}},{"name":"tabwriter","path":"text/tabwriter","span":{"start":[9,1],"end":[9,18],"bytes":[72,89]}},{"name":"time","path":"time","span":{"start":[10,1],"end":[10,8],"bytes":[90,97]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[12,1],"end":[12,37],"bytes":[99,135]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[13,1],"end":[13,46],"bytes":[136,181]}}],"types":{"LTXCommand":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand","kind":"struct","span":{"start":[17,1],"end":[17,25],"bytes":[254,278]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","kind":"method","span":{"start":[20,1],"end":[131,2],"bytes":[309,3083]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[20,1],"end":[20,75],"bytes":[309,383]}},{"name":"args","type":"[]string","span":{"start":[20,1],"end":[20,75],"bytes":[309,383]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":22},"body":{"100:16":{"kind":"call","span":{"start":[100,16],"end":[100,51],"bytes":[2471,2506]},"callee":null},"103:13":{"kind":"call","span":{"start":[103,13],"end":[103,24],"bytes":[2530,2541]},"callee":null},"109:18":{"kind":"call","span":{"start":[109,18],"end":[109,53],"bytes":[2613,2648]},"callee":null},"111:11":{"kind":"call","span":{"start":[111,11],"end":[111,59],"bytes":[2677,2725]},"callee":null},"113:15":{"kind":"call","span":{"start":[113,15],"end":[113,29],"bytes":[2744,2758]},"callee":null},"113:3":{"kind":"call","span":{"start":[113,3],"end":[113,30],"bytes":[2732,2759]},"callee":null},"117:7":{"kind":"call","span":{"start":[117,7],"end":[117,54],"bytes":[2783,2830]},"callee":null},"118:8":{"kind":"call","span":{"start":[118,8],"end":[118,17],"bytes":[2838,2847]},"callee":null},"120:2":{"kind":"call","span":{"start":[120,2],"end":[120,61],"bytes":[2850,2909]},"callee":null},"122:3":{"kind":"call","span":{"start":[122,3],"end":[128,4],"bytes":[2942,3066]},"callee":null},"21:8":{"kind":"call","span":{"start":[21,8],"end":[21,63],"bytes":[391,446]},"callee":null},"22:29":{"kind":"call","span":{"start":[22,29],"end":[22,51],"bytes":[475,497]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag"},"23:16":{"kind":"call","span":{"start":[23,16],"end":[23,57],"bytes":[513,554]},"callee":null},"25:2":{"kind":"call","span":{"start":[25,2],"end":[25,62],"bytes":[576,636]},"callee":null},"27:12":{"kind":"call","span":{"start":[27,12],"end":[27,26],"bytes":[668,682]},"callee":null},"29:12":{"kind":"call","span":{"start":[29,12],"end":[29,21],"bytes":[721,730]},"callee":null},"29:30":{"kind":"call","span":{"start":[29,30],"end":[29,39],"bytes":[739,748]},"callee":null},"34:12":{"kind":"call","span":{"start":[34,12],"end":[34,21],"bytes":[889,898]},"callee":null},"35:10":{"kind":"call","span":{"start":[35,10],"end":[35,42],"bytes":[914,946]},"callee":null},"39:22":{"kind":"call","span":{"start":[39,22],"end":[39,31],"bytes":[999,1008]},"callee":null},"39:5":{"kind":"call","span":{"start":[39,5],"end":[39,32],"bytes":[982,1009]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL"},"41:11":{"kind":"call","span":{"start":[41,11],"end":[41,74],"bytes":[1047,1110]},"callee":null},"43:15":{"kind":"call","span":{"start":[43,15],"end":[43,72],"bytes":[1129,1186]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig"},"43:56":{"kind":"call","span":{"start":[43,56],"end":[43,65],"bytes":[1170,1179]},"callee":null},"46:3":{"kind":"call","span":{"start":[46,3],"end":[46,53],"bytes":[1221,1271]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"},"49:18":{"kind":"call","span":{"start":[49,18],"end":[49,37],"bytes":[1324,1343]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"},"53:18":{"kind":"call","span":{"start":[53,18],"end":[53,60],"bytes":[1391,1433]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"59:16":{"kind":"call","span":{"start":[59,16],"end":[59,33],"bytes":[1540,1557]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"59:23":{"kind":"call","span":{"start":[59,23],"end":[59,32],"bytes":[1547,1556]},"callee":null},"63:10":{"kind":"call","span":{"start":[63,10],"end":[63,31],"bytes":[1603,1624]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig"},"65:11":{"kind":"call","span":{"start":[65,11],"end":[65,63],"bytes":[1653,1705]},"callee":null},"68:14":{"kind":"call","span":{"start":[68,14],"end":[68,34],"bytes":[1724,1744]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"72:11":{"kind":"call","span":{"start":[72,11],"end":[72,48],"bytes":[1819,1856]},"callee":null},"79:5":{"kind":"call","span":{"start":[79,5],"end":[79,15],"bytes":[1943,1953]},"callee":null},"81:13":{"kind":"call","span":{"start":[81,13],"end":[81,32],"bytes":[2037,2056]},"callee":null},"84:18":{"kind":"call","span":{"start":[84,18],"end":[84,28],"bytes":[2088,2098]},"callee":null},"87:11":{"kind":"call","span":{"start":[87,11],"end":[87,33],"bytes":[2114,2136]},"callee":null},"89:15":{"kind":"call","span":{"start":[89,15],"end":[89,52],"bytes":[2181,2218]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"93:7":{"kind":"call","span":{"start":[93,7],"end":[93,17],"bytes":[2261,2271]},"callee":null},"94:12":{"kind":"call","span":{"start":[94,12],"end":[94,22],"bytes":[2285,2295]},"callee":null},"95:12":{"kind":"call","span":{"start":[95,12],"end":[101,6],"bytes":[2307,2513]},"callee":null},"97:16":{"kind":"call","span":{"start":[97,16],"end":[97,37],"bytes":[2369,2390]},"callee":null},"98:16":{"kind":"call","span":{"start":[98,16],"end":[98,37],"bytes":[2407,2428]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Usage":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Usage","kind":"method","span":{"start":[142,1],"end":[185,2],"bytes":[3331,4201]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"143:2":{"kind":"call","span":{"start":[143,2],"end":[184,3],"bytes":[3363,4199]},"callee":null},"183:3":{"kind":"call","span":{"start":[183,3],"end":[183,22],"bytes":[4176,4195]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"}}}}},"LTXFileInfo":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXFileInfo","kind":"struct","span":{"start":[133,1],"end":[139,2],"bytes":[3085,3286]},"callables":{},"fields":{"Level":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXFileInfo/Level","kind":"field","type":"int","span":{"start":[134,1],"end":[134,33],"bytes":[3111,3143]}},"MaxTXID":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXFileInfo/MaxTXID","kind":"field","type":"string","span":{"start":[136,1],"end":[136,36],"bytes":[3180,3215]}},"MinTXID":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXFileInfo/MinTXID","kind":"field","type":"string","span":{"start":[135,1],"end":[135,36],"bytes":[3144,3179]}},"Size":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXFileInfo/Size","kind":"field","type":"int64","span":{"start":[137,1],"end":[137,32],"bytes":[3216,3247]}},"Timestamp":{"id":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXFileInfo/Timestamp","kind":"field","type":"string","span":{"start":[138,1],"end":[138,37],"bytes":[3248,3284]}}}}},"functions":{},"content_hash":"c867d7c1e1585c6d60a877522f63f540250d718c555f7798562841631be556b5"},"cmd/litestream/main.go":{"id":"can://go/litestream/cmd/litestream/main.go","kind":"module","span":{"start":[1,1],"end":[2209,1],"bytes":[0,65049]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"math\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime/debug\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/dustin/go-humanize\"\n\t\"github.com/superfly/ltx\"\n\t_ \"golang.org/x/crypto/x509roots/fallback\"\n\t\"gopkg.in/yaml.v2\"\n\t_ \"modernc.org/sqlite\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/abs\"\n\t\"github.com/benbjohnson/litestream/file\"\n\t\"github.com/benbjohnson/litestream/gs\"\n\t\"github.com/benbjohnson/litestream/internal\"\n\t\"github.com/benbjohnson/litestream/nats\"\n\t\"github.com/benbjohnson/litestream/oss\"\n\t\"github.com/benbjohnson/litestream/s3\"\n\t\"github.com/benbjohnson/litestream/sftp\"\n\t\"github.com/benbjohnson/litestream/webdav\"\n)\n\n// Version is set via -ldflags \"-X main.Version=...\" on release and Makefile\n// builds; otherwise it is resolved from embedded VCS build info at startup.\nvar Version = defaultVersion\n\nfunc init() {\n\tbi, _ := debug.ReadBuildInfo()\n\tVersion = resolveVersion(Version, bi)\n}\n\n// errStop is a terminal error for indicating program should quit.\nvar errStop = errors.New(\"stop\")\n\n// Sentinel errors for configuration validation\nvar (\n\tErrInvalidSnapshotInterval = errors.New(\"snapshot interval must be greater than 0\")\n\tErrInvalidSnapshotRetention = errors.New(\"snapshot retention must be greater than 0\")\n\tErrInvalidCompactionInterval = errors.New(\"compaction interval must be greater than 0\")\n\tErrInvalidSyncInterval = errors.New(\"sync interval must be greater than 0\")\n\tErrInvalidL0Retention = errors.New(\"l0 retention must not be negative\")\n\tErrInvalidL0RetentionCheckInterval = errors.New(\"l0 retention check interval must be greater than 0\")\n\tErrInvalidShutdownSyncTimeout = errors.New(\"shutdown-sync-timeout must be \u003e= 0\")\n\tErrInvalidShutdownSyncInterval = errors.New(\"shutdown sync interval must be greater than 0\")\n\tErrInvalidHeartbeatURL = errors.New(\"heartbeat URL must be a valid HTTP or HTTPS URL\")\n\tErrInvalidHeartbeatInterval = errors.New(\"heartbeat interval must be at least 1 minute\")\n\tErrConfigFileNotFound = errors.New(\"config file not found\")\n)\n\n// ConfigValidationError wraps a validation error with additional context\ntype ConfigValidationError struct {\n\tErr error\n\tField string\n\tValue interface{}\n}\n\nfunc (e *ConfigValidationError) Error() string {\n\tif e.Value != nil {\n\t\treturn fmt.Sprintf(\"%s: %v (got %v)\", e.Field, e.Err, e.Value)\n\t}\n\treturn fmt.Sprintf(\"%s: %v\", e.Field, e.Err)\n}\n\nfunc (e *ConfigValidationError) Unwrap() error {\n\treturn e.Err\n}\n\nfunc main() {\n\tinternal.InitLog(os.Stdout, \"INFO\", \"text\", false)\n\n\tm := NewMain()\n\tif err := m.Run(context.Background(), os.Args[1:]); errors.Is(err, errStop) {\n\t\tos.Exit(1)\n\t} else if errors.Is(err, flag.ErrHelp) {\n\t\tfor _, arg := range os.Args[1:] {\n\t\t\tif arg == \"-h\" || arg == \"-help\" || arg == \"--help\" {\n\t\t\t\tos.Exit(0)\n\t\t\t}\n\t\t}\n\t\tos.Exit(1)\n\t} else if err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"Error: %s\\n\", err)\n\t\tvar usageErr *usageError\n\t\tif errors.As(err, \u0026usageErr) \u0026\u0026 usageErr.hint != \"\" {\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"Try: %s\\n\", usageErr.hint)\n\t\t}\n\t\tos.Exit(1)\n\t}\n}\n\ntype usageError struct {\n\tmessage string\n\thint string\n}\n\nfunc (e *usageError) Error() string {\n\treturn e.message\n}\n\n// Main represents the main program execution.\ntype Main struct{}\n\n// NewMain returns a new instance of Main.\nfunc NewMain() *Main {\n\treturn \u0026Main{}\n}\n\n// Run executes the program.\nfunc (m *Main) Run(ctx context.Context, args []string) (err error) {\n\t// Execute replication command if running as a Windows service.\n\tif isService, err := isWindowsService(); err != nil {\n\t\treturn err\n\t} else if isService {\n\t\treturn runWindowsService(ctx)\n\t}\n\n\t// Copy \"LITESTEAM\" environment credentials.\n\tapplyLitestreamEnv()\n\n\t// Extract command name.\n\tvar cmd string\n\tif len(args) \u003e 0 {\n\t\tcmd, args = args[0], args[1:]\n\t}\n\n\tswitch cmd {\n\tcase \"databases\":\n\t\treturn (\u0026DatabasesCommand{}).Run(ctx, args)\n\tcase \"replicate\":\n\t\tc := NewReplicateCommand()\n\t\tif err := c.ParseFlags(ctx, args); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Setup signal handler.\n\t\tsignalCh := signalChan()\n\n\t\t// Create done channel for interrupt during shutdown. It will be\n\t\t// closed when a second signal arrives, allowing each database's\n\t\t// retry loop to observe the interrupt.\n\t\tdone := make(chan struct{})\n\t\tc.SetDone(done)\n\n\t\tif err := c.Run(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Wait for signal to stop program.\n\t\tselect {\n\t\tcase err = \u003c-c.execCh:\n\t\t\tif c.cmd != nil {\n\t\t\t\tslog.Info(\"subprocess exited, litestream shutting down\")\n\t\t\t} else {\n\t\t\t\tslog.Info(\"replication complete, litestream shutting down\")\n\t\t\t}\n\t\tcase sig := \u003c-signalCh:\n\t\t\tslog.Info(\"signal received, litestream shutting down\", \"signal\", sig)\n\n\t\t\tif c.cmd != nil {\n\t\t\t\tslog.Info(\"sending signal to exec process\")\n\t\t\t\tif err := c.cmd.Process.Signal(sig); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"cannot signal exec process: %w\", err)\n\t\t\t\t}\n\n\t\t\t\tslog.Info(\"waiting for exec process to close\")\n\t\t\t\tif err := \u003c-c.execCh; err != nil \u0026\u0026 !strings.HasPrefix(err.Error(), \"signal:\") {\n\t\t\t\t\treturn fmt.Errorf(\"cannot wait for exec process: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Listen for a second signal to close done and interrupt retry loops.\n\t\t\tgo func() {\n\t\t\t\t\u003c-signalCh\n\t\t\t\tclose(done)\n\t\t\t}()\n\t\t}\n\n\t\t// Gracefully close.\n\t\tif e := c.Close(ctx); e != nil \u0026\u0026 err == nil {\n\t\t\terr = e\n\t\t}\n\t\tslog.Info(\"litestream shut down\")\n\t\treturn err\n\n\tcase \"start\":\n\t\treturn (\u0026StartCommand{}).Run(ctx, args)\n\tcase \"stop\":\n\t\treturn (\u0026StopCommand{}).Run(ctx, args)\n\tcase \"register\":\n\t\treturn (\u0026RegisterCommand{}).Run(ctx, args)\n\tcase \"unregister\":\n\t\treturn (\u0026UnregisterCommand{}).Run(ctx, args)\n\tcase \"reset\":\n\t\treturn (\u0026ResetCommand{}).Run(ctx, args)\n\tcase \"restore\":\n\t\treturn (\u0026RestoreCommand{}).Run(ctx, args)\n\tcase \"status\":\n\t\treturn (\u0026StatusCommand{}).Run(ctx, args)\n\tcase \"sync\":\n\t\treturn (\u0026SyncCommand{}).Run(ctx, args)\n\tcase \"list\":\n\t\treturn (\u0026ListCommand{}).Run(ctx, args)\n\tcase \"info\":\n\t\treturn (\u0026InfoCommand{}).Run(ctx, args)\n\tcase \"version\":\n\t\treturn (\u0026VersionCommand{}).Run(ctx, args)\n\tcase \"ltx\":\n\t\treturn (\u0026LTXCommand{}).Run(ctx, args)\n\tcase \"wal\":\n\t\t// Deprecated: Keep for backward compatibility\n\t\tfmt.Fprintln(os.Stderr, \"Warning: 'wal' command is deprecated, please use 'ltx' instead\")\n\t\treturn (\u0026LTXCommand{}).Run(ctx, args)\n\tdefault:\n\t\tif cmd == \"help\" || cmd == \"-h\" || cmd == \"-help\" || cmd == \"--help\" {\n\t\t\tm.Usage()\n\t\t\treturn nil\n\t\t} else if cmd == \"\" || strings.HasPrefix(cmd, \"-\") {\n\t\t\tm.Usage()\n\t\t\treturn flag.ErrHelp\n\t\t}\n\t\treturn fmt.Errorf(\"litestream %s: unknown command\", cmd)\n\t}\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (m *Main) Usage() {\n\tfmt.Println(`\nlitestream is a tool for replicating SQLite databases.\n\nUsage:\n\n\tlitestream \u003ccommand\u003e [arguments]\n\nThe commands are:\n\n\tdatabases list databases specified in config file\n\tinfo show daemon information\n\tlist list all managed databases\n\tltx list available LTX files for a database\n\tregister register a database for replication\n\treplicate runs a server to replicate databases\n\treset reset local state for a database\n\trestore recovers database backup from a replica\n\tstart start replication for a database\n\tstatus display replication status for databases\n\tstop stop replication for a database\n\tsync force an immediate sync for a database\n\tunregister unregister a database from replication\n\tversion prints the binary version\n`[1:])\n}\n\n// Config represents a configuration file for the litestream daemon.\ntype Config struct {\n\t// Global replica settings that serve as defaults for all replicas\n\tReplicaSettings `yaml:\",inline\"`\n\n\t// Bind address for serving metrics.\n\tAddr string `yaml:\"addr\"`\n\n\t// Socket configuration for control commands.\n\tSocket litestream.SocketConfig `yaml:\"socket\"`\n\n\t// List of stages in a multi-level compaction.\n\t// Only includes L1 through the last non-snapshot level.\n\tLevels []*CompactionLevelConfig `yaml:\"levels\"`\n\n\t// Snapshot configuration\n\tSnapshot SnapshotConfig `yaml:\"snapshot\"`\n\n\t// Validation configuration\n\tValidation ValidationConfig `yaml:\"validation\"`\n\n\t// L0 retention settings\n\tL0Retention *time.Duration `yaml:\"l0-retention\"`\n\tL0RetentionCheckInterval *time.Duration `yaml:\"l0-retention-check-interval\"`\n\n\t// Verify TXID consistency at destination level after each compaction.\n\t// When enabled, logs warnings if gaps or overlaps are detected.\n\tVerifyCompaction bool `yaml:\"verify-compaction\"`\n\n\t// Retention configuration\n\tRetention RetentionConfig `yaml:\"retention\"`\n\n\t// Heartbeat settings (global defaults)\n\tHeartbeatURL string `yaml:\"heartbeat-url\"`\n\tHeartbeatInterval *time.Duration `yaml:\"heartbeat-interval\"`\n\n\t// List of databases to manage.\n\tDBs []*DBConfig `yaml:\"dbs\"`\n\n\t// Subcommand to execute during replication.\n\t// Litestream will shutdown when subcommand exits.\n\tExec string `yaml:\"exec\"`\n\n\t// Logging\n\tLogging internal.LoggingConfig `yaml:\"logging\"`\n\n\t// MCP server options\n\tMCPAddr string `yaml:\"mcp-addr\"`\n\n\t// Shutdown sync retry settings\n\tShutdownSyncTimeout *time.Duration `yaml:\"shutdown-sync-timeout\"`\n\tShutdownSyncInterval *time.Duration `yaml:\"shutdown-sync-interval\"`\n\n\t// Path to the config file\n\t// This is only used internally to pass the config path to the MCP tool\n\tConfigPath string `yaml:\"-\"`\n}\n\n// SnapshotConfig configures snapshots.\ntype SnapshotConfig struct {\n\tInterval *time.Duration `yaml:\"interval\"`\n\tRetention *time.Duration `yaml:\"retention\"`\n}\n\n// RetentionConfig configures retention enforcement behavior.\ntype RetentionConfig struct {\n\tEnabled *bool `yaml:\"enabled\"`\n}\n\n// ValidationConfig configures periodic validation checks.\ntype ValidationConfig struct {\n\tInterval *time.Duration `yaml:\"interval\"`\n}\n\n// propagateGlobalSettings copies global replica settings to individual replica configs.\nfunc (c *Config) propagateGlobalSettings() {\n\tfor _, dbc := range c.DBs {\n\t\t// Handle both old-style 'replicas' and new-style 'replica'\n\t\tif dbc.Replica != nil {\n\t\t\tdbc.Replica.SetDefaults(\u0026c.ReplicaSettings)\n\t\t}\n\t\tfor _, rc := range dbc.Replicas {\n\t\t\trc.SetDefaults(\u0026c.ReplicaSettings)\n\t\t}\n\t}\n}\n\nfunc (c *Config) applyDBSnapshotConfig(globalIntervalSet, globalRetentionSet bool) error {\n\tvar interval *time.Duration\n\tvar intervalDB string\n\tvar retention *time.Duration\n\tvar retentionDB string\n\n\tfor _, db := range c.DBs {\n\t\tdbID := db.Path\n\t\tif dbID == \"\" {\n\t\t\tdbID = db.Dir\n\t\t}\n\t\tif db.Snapshot.Interval != nil {\n\t\t\tif interval != nil \u0026\u0026 *interval != *db.Snapshot.Interval {\n\t\t\t\treturn fmt.Errorf(\"conflicting database snapshot intervals: %s has %v, %s has %v\", intervalDB, *interval, dbID, *db.Snapshot.Interval)\n\t\t\t}\n\t\t\tinterval = db.Snapshot.Interval\n\t\t\tintervalDB = dbID\n\t\t}\n\t\tif db.Snapshot.Retention != nil {\n\t\t\tif retention != nil \u0026\u0026 *retention != *db.Snapshot.Retention {\n\t\t\t\treturn fmt.Errorf(\"conflicting database snapshot retentions: %s has %v, %s has %v\", retentionDB, *retention, dbID, *db.Snapshot.Retention)\n\t\t\t}\n\t\t\tretention = db.Snapshot.Retention\n\t\t\tretentionDB = dbID\n\t\t}\n\t}\n\n\tif !globalIntervalSet \u0026\u0026 interval != nil {\n\t\tc.Snapshot.Interval = interval\n\t}\n\tif !globalRetentionSet \u0026\u0026 retention != nil {\n\t\tc.Snapshot.Retention = retention\n\t}\n\treturn nil\n}\n\n// DefaultConfig returns a new instance of Config with defaults set.\nfunc DefaultConfig() Config {\n\tdefaultSnapshotInterval := 24 * time.Hour\n\tdefaultSnapshotRetention := 24 * time.Hour\n\tdefaultL0Retention := litestream.DefaultL0Retention\n\tdefaultL0RetentionCheckInterval := litestream.DefaultL0RetentionCheckInterval\n\tdefaultShutdownSyncTimeout := litestream.DefaultShutdownSyncTimeout\n\tdefaultShutdownSyncInterval := litestream.DefaultShutdownSyncInterval\n\treturn Config{\n\t\tLevels: []*CompactionLevelConfig{\n\t\t\t{Interval: litestream.DefaultCompactionLevels[1].Interval},\n\t\t\t{Interval: litestream.DefaultCompactionLevels[2].Interval},\n\t\t\t{Interval: litestream.DefaultCompactionLevels[3].Interval},\n\t\t},\n\t\tSnapshot: SnapshotConfig{\n\t\t\tInterval: \u0026defaultSnapshotInterval,\n\t\t\tRetention: \u0026defaultSnapshotRetention,\n\t\t},\n\t\tSocket: litestream.DefaultSocketConfig(),\n\t\tL0Retention: \u0026defaultL0Retention,\n\t\tL0RetentionCheckInterval: \u0026defaultL0RetentionCheckInterval,\n\t\tShutdownSyncTimeout: \u0026defaultShutdownSyncTimeout,\n\t\tShutdownSyncInterval: \u0026defaultShutdownSyncInterval,\n\t}\n}\n\n// Validate returns an error if config contains invalid settings.\nfunc (c *Config) Validate() error {\n\t// Validate snapshot intervals\n\tif c.Snapshot.Interval != nil \u0026\u0026 *c.Snapshot.Interval \u003c= 0 {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidSnapshotInterval,\n\t\t\tField: \"snapshot.interval\",\n\t\t\tValue: *c.Snapshot.Interval,\n\t\t}\n\t}\n\tif c.Snapshot.Retention != nil \u0026\u0026 *c.Snapshot.Retention \u003c= 0 {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidSnapshotRetention,\n\t\t\tField: \"snapshot.retention\",\n\t\t\tValue: *c.Snapshot.Retention,\n\t\t}\n\t}\n\tif c.L0Retention != nil \u0026\u0026 *c.L0Retention \u003c 0 {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidL0Retention,\n\t\t\tField: \"l0-retention\",\n\t\t\tValue: *c.L0Retention,\n\t\t}\n\t}\n\tif c.L0RetentionCheckInterval != nil \u0026\u0026 *c.L0RetentionCheckInterval \u003c= 0 {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidL0RetentionCheckInterval,\n\t\t\tField: \"l0-retention-check-interval\",\n\t\t\tValue: *c.L0RetentionCheckInterval,\n\t\t}\n\t}\n\tif c.ShutdownSyncTimeout != nil \u0026\u0026 *c.ShutdownSyncTimeout \u003c 0 {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidShutdownSyncTimeout,\n\t\t\tField: \"shutdown-sync-timeout\",\n\t\t\tValue: *c.ShutdownSyncTimeout,\n\t\t}\n\t}\n\tif c.ShutdownSyncInterval != nil \u0026\u0026 *c.ShutdownSyncInterval \u003c= 0 {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidShutdownSyncInterval,\n\t\t\tField: \"shutdown-sync-interval\",\n\t\t\tValue: *c.ShutdownSyncInterval,\n\t\t}\n\t}\n\n\t// Validate global heartbeat settings\n\tif c.HeartbeatURL != \"\" \u0026\u0026 !isValidHeartbeatURL(c.HeartbeatURL) {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidHeartbeatURL,\n\t\t\tField: \"heartbeat-url\",\n\t\t\tValue: c.HeartbeatURL,\n\t\t}\n\t}\n\tif c.HeartbeatInterval != nil \u0026\u0026 *c.HeartbeatInterval \u003c litestream.MinHeartbeatInterval {\n\t\treturn \u0026ConfigValidationError{\n\t\t\tErr: ErrInvalidHeartbeatInterval,\n\t\t\tField: \"heartbeat-interval\",\n\t\t\tValue: *c.HeartbeatInterval,\n\t\t}\n\t}\n\n\t// Validate compaction level intervals\n\tfor i, level := range c.Levels {\n\t\tif level.Interval \u003c= 0 {\n\t\t\treturn \u0026ConfigValidationError{\n\t\t\t\tErr: ErrInvalidCompactionInterval,\n\t\t\t\tField: fmt.Sprintf(\"levels[%d].interval\", i),\n\t\t\t\tValue: level.Interval,\n\t\t\t}\n\t\t}\n\t}\n\n\t// Validate database configs\n\tseenPaths := make(map[string]int) // cleaned path -\u003e 1-based config index\n\tfor idx, db := range c.DBs {\n\t\t// Validate that either path or dir is specified, but not both\n\t\tif db.Path != \"\" \u0026\u0026 db.Dir != \"\" {\n\t\t\treturn fmt.Errorf(\"database config #%d: cannot specify both 'path' and 'dir'\", idx+1)\n\t\t}\n\t\tif db.Path == \"\" \u0026\u0026 db.Dir == \"\" {\n\t\t\treturn fmt.Errorf(\"database config #%d: must specify either 'path' or 'dir'\", idx+1)\n\t\t}\n\n\t\t// Reject the same database path listed twice. The metadata directory is\n\t\t// derived from the path, so two entries would share it and race on the\n\t\t// same LTX temp files.\n\t\tif db.Path != \"\" {\n\t\t\tkey := filepath.Clean(db.Path)\n\t\t\tif first, ok := seenPaths[key]; ok {\n\t\t\t\treturn fmt.Errorf(\"database config #%d: duplicate path %q (already used by database config #%d); each database can be listed only once\", idx+1, db.Path, first)\n\t\t\t}\n\t\t\tseenPaths[key] = idx + 1\n\t\t}\n\n\t\t// When using dir, pattern must be specified\n\t\tif db.Dir != \"\" \u0026\u0026 db.Pattern == \"\" {\n\t\t\treturn fmt.Errorf(\"database config #%d: 'pattern' is required when using 'dir'\", idx+1)\n\t\t}\n\t\tif db.Watch \u0026\u0026 db.Dir == \"\" {\n\t\t\treturn fmt.Errorf(\"database config #%d: 'watch' can only be enabled with a directory\", idx+1)\n\t\t}\n\t\tif db.MetaDir != nil \u0026\u0026 db.Dir == \"\" {\n\t\t\treturn fmt.Errorf(\"database config #%d: 'meta-dir' can only be used with a directory\", idx+1)\n\t\t}\n\t\tif db.MetaPath != nil \u0026\u0026 db.MetaDir != nil {\n\t\t\treturn fmt.Errorf(\"database config #%d: cannot specify both 'meta-path' and 'meta-dir'\", idx+1)\n\t\t}\n\n\t\t// Use path or dir for identifying the config in error messages\n\t\tdbIdentifier := db.Path\n\t\tif dbIdentifier == \"\" {\n\t\t\tdbIdentifier = db.Dir\n\t\t}\n\n\t\tif db.Snapshot.Interval != nil \u0026\u0026 *db.Snapshot.Interval \u003c= 0 {\n\t\t\treturn \u0026ConfigValidationError{\n\t\t\t\tErr: ErrInvalidSnapshotInterval,\n\t\t\t\tField: fmt.Sprintf(\"dbs[%s].snapshot.interval\", dbIdentifier),\n\t\t\t\tValue: *db.Snapshot.Interval,\n\t\t\t}\n\t\t}\n\t\tif db.Snapshot.Retention != nil \u0026\u0026 *db.Snapshot.Retention \u003c= 0 {\n\t\t\treturn \u0026ConfigValidationError{\n\t\t\t\tErr: ErrInvalidSnapshotRetention,\n\t\t\t\tField: fmt.Sprintf(\"dbs[%s].snapshot.retention\", dbIdentifier),\n\t\t\t\tValue: *db.Snapshot.Retention,\n\t\t\t}\n\t\t}\n\n\t\t// Validate sync intervals for replicas\n\t\tif db.Replica != nil \u0026\u0026 db.Replica.SyncInterval != nil \u0026\u0026 *db.Replica.SyncInterval \u003c= 0 {\n\t\t\treturn \u0026ConfigValidationError{\n\t\t\t\tErr: ErrInvalidSyncInterval,\n\t\t\t\tField: fmt.Sprintf(\"dbs[%s].replica.sync-interval\", dbIdentifier),\n\t\t\t\tValue: *db.Replica.SyncInterval,\n\t\t\t}\n\t\t}\n\t\tfor i, replica := range db.Replicas {\n\t\t\tif replica.SyncInterval != nil \u0026\u0026 *replica.SyncInterval \u003c= 0 {\n\t\t\t\treturn \u0026ConfigValidationError{\n\t\t\t\t\tErr: ErrInvalidSyncInterval,\n\t\t\t\t\tField: fmt.Sprintf(\"dbs[%s].replicas[%d].sync-interval\", dbIdentifier, i),\n\t\t\t\t\tValue: *replica.SyncInterval,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// CompactionLevels returns a full list of compaction levels include L0.\nfunc (c *Config) CompactionLevels() litestream.CompactionLevels {\n\tlevels := litestream.CompactionLevels{\n\t\t{Level: 0},\n\t}\n\n\tfor i, lvl := range c.Levels {\n\t\tlevels = append(levels, \u0026litestream.CompactionLevel{\n\t\t\tLevel: i + 1,\n\t\t\tInterval: lvl.Interval,\n\t\t})\n\t}\n\n\treturn levels\n}\n\n// DBConfig returns database configuration by path.\nfunc (c *Config) DBConfig(configPath string) *DBConfig {\n\tfor _, dbConfig := range c.DBs {\n\t\tif dbConfig.Path == configPath {\n\t\t\treturn dbConfig\n\t\t}\n\t}\n\treturn nil\n}\n\n// OpenConfigFile opens a configuration file and returns a reader.\n// Expands the filename path if needed.\nfunc OpenConfigFile(filename string) (io.ReadCloser, error) {\n\t// Expand filename, if necessary.\n\tfilename, err := expand(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Open configuration file.\n\tf, err := os.Open(filename)\n\tif os.IsNotExist(err) {\n\t\treturn nil, fmt.Errorf(\"%w: %s\", ErrConfigFileNotFound, filename)\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn f, nil\n}\n\n// ReadConfigFile unmarshals config from filename. Expands path if needed.\n// If expandEnv is true then environment variables are expanded in the config.\nfunc ReadConfigFile(filename string, expandEnv bool) (Config, error) {\n\tf, err := OpenConfigFile(filename)\n\tif err != nil {\n\t\treturn DefaultConfig(), err\n\t}\n\tdefer f.Close()\n\n\treturn ParseConfig(f, expandEnv)\n}\n\n// ParseConfig unmarshals config from a reader.\n// If expandEnv is true then environment variables are expanded in the config.\nfunc ParseConfig(r io.Reader, expandEnv bool) (_ Config, err error) {\n\tconfig := DefaultConfig()\n\n\t// Read configuration.\n\tbuf, err := io.ReadAll(r)\n\tif err != nil {\n\t\treturn config, err\n\t}\n\n\t// Expand environment variables, if enabled.\n\tif expandEnv {\n\t\tbuf = []byte(os.Expand(string(buf), func(key string) string {\n\t\t\tif key == \"PID\" {\n\t\t\t\treturn strconv.Itoa(os.Getpid())\n\t\t\t}\n\t\t\treturn os.Getenv(key)\n\t\t}))\n\t}\n\n\tvar raw struct {\n\t\tSnapshot *SnapshotConfig `yaml:\"snapshot\"`\n\t}\n\tif err := yaml.Unmarshal(buf, \u0026raw); err != nil {\n\t\treturn config, err\n\t}\n\tglobalSnapshotIntervalSet := raw.Snapshot != nil \u0026\u0026 raw.Snapshot.Interval != nil\n\tglobalSnapshotRetentionSet := raw.Snapshot != nil \u0026\u0026 raw.Snapshot.Retention != nil\n\n\t// Save defaults before unmarshaling\n\tdefaultSnapshotInterval := config.Snapshot.Interval\n\tdefaultSnapshotRetention := config.Snapshot.Retention\n\tdefaultL0Retention := config.L0Retention\n\tdefaultL0RetentionCheckInterval := config.L0RetentionCheckInterval\n\n\tif err := yaml.Unmarshal(buf, \u0026config); err != nil {\n\t\treturn config, err\n\t}\n\n\t// Restore defaults if they were overwritten with nil by empty YAML sections\n\tif config.Snapshot.Interval == nil {\n\t\tconfig.Snapshot.Interval = defaultSnapshotInterval\n\t}\n\tif config.Snapshot.Retention == nil {\n\t\tconfig.Snapshot.Retention = defaultSnapshotRetention\n\t}\n\tif config.L0Retention == nil {\n\t\tconfig.L0Retention = defaultL0Retention\n\t}\n\tif config.L0RetentionCheckInterval == nil {\n\t\tconfig.L0RetentionCheckInterval = defaultL0RetentionCheckInterval\n\t}\n\tif err := config.applyDBSnapshotConfig(globalSnapshotIntervalSet, globalSnapshotRetentionSet); err != nil {\n\t\treturn config, err\n\t}\n\n\t// Normalize paths.\n\tfor _, dbConfig := range config.DBs {\n\t\tif dbConfig.Path == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif dbConfig.Path, err = expand(dbConfig.Path); err != nil {\n\t\t\treturn config, err\n\t\t}\n\t}\n\n\t// Propagate settings from global config to individual configs.\n\tconfig.propagateGlobalSettings()\n\n\t// Validate configuration\n\tif err := config.Validate(); err != nil {\n\t\treturn config, err\n\t}\n\n\t// Configure logging.\n\tlogOutput := os.Stdout\n\tif config.Logging.Stderr {\n\t\tlogOutput = os.Stderr\n\t}\n\tif v := os.Getenv(\"LOG_LEVEL\"); v != \"\" {\n\t\tconfig.Logging.Level = v\n\t}\n\tinternal.InitLog(logOutput, config.Logging.Level, config.Logging.Type, config.Logging.Source)\n\n\treturn config, nil\n}\n\n// CompactionLevelConfig the configuration for a single level of compaction.\ntype CompactionLevelConfig struct {\n\tInterval time.Duration `yaml:\"interval\"`\n}\n\n// DBConfig represents the configuration for a single database or directory of databases.\ntype DBConfig struct {\n\tPath string `yaml:\"path\"`\n\tDir string `yaml:\"dir\"` // Directory to scan for databases\n\tPattern string `yaml:\"pattern\"` // File pattern to match (e.g., \"*.db\", \"*.sqlite\")\n\tRecursive bool `yaml:\"recursive\"` // Scan subdirectories recursively\n\tWatch bool `yaml:\"watch\"` // Enable directory monitoring for changes\n\tSnapshot SnapshotConfig `yaml:\"snapshot\"`\n\tMetaPath *string `yaml:\"meta-path\"`\n\tMetaDir *string `yaml:\"meta-dir\"`\n\tMonitorInterval *time.Duration `yaml:\"monitor-interval\"`\n\tCheckpointInterval *time.Duration `yaml:\"checkpoint-interval\"`\n\tBusyTimeout *time.Duration `yaml:\"busy-timeout\"`\n\tMinCheckpointPageN *int `yaml:\"min-checkpoint-page-count\"`\n\tTruncatePageN *int `yaml:\"truncate-page-n\"`\n\tMaxSyncWALBytes *int64 `yaml:\"max-sync-wal-bytes\"`\n\n\tRestoreIfDBNotExists bool `yaml:\"restore-if-db-not-exists\"`\n\n\tReplica *ReplicaConfig `yaml:\"replica\"`\n\tReplicas []*ReplicaConfig `yaml:\"replicas\"` // Deprecated\n}\n\n// NewDBFromConfig instantiates a DB based on a configuration.\nfunc NewDBFromConfig(dbc *DBConfig) (*litestream.DB, error) {\n\tif dbc.MetaDir != nil {\n\t\treturn nil, fmt.Errorf(\"'meta-dir' can only be used with a directory\")\n\t}\n\n\tconfigPath, err := expand(dbc.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Initialize database with given path.\n\tdb := litestream.NewDB(configPath)\n\n\t// Override default database settings if specified in configuration.\n\tif dbc.MetaPath != nil {\n\t\texpandedMetaPath, err := expand(*dbc.MetaPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to expand meta path: %w\", err)\n\t\t}\n\t\tdbc.MetaPath = \u0026expandedMetaPath\n\t\tdb.SetMetaPath(expandedMetaPath)\n\t}\n\tif dbc.MonitorInterval != nil {\n\t\tdb.MonitorInterval = *dbc.MonitorInterval\n\t}\n\tif dbc.CheckpointInterval != nil {\n\t\tdb.CheckpointInterval = *dbc.CheckpointInterval\n\t}\n\tif dbc.BusyTimeout != nil {\n\t\tdb.BusyTimeout = *dbc.BusyTimeout\n\t}\n\tif dbc.MinCheckpointPageN != nil {\n\t\tdb.MinCheckpointPageN = *dbc.MinCheckpointPageN\n\t}\n\tif dbc.TruncatePageN != nil {\n\t\tdb.TruncatePageN = *dbc.TruncatePageN\n\t}\n\tif dbc.MaxSyncWALBytes != nil {\n\t\tdb.MaxSyncWALBytes = *dbc.MaxSyncWALBytes\n\t}\n\n\t// Instantiate and attach replica.\n\t// v0.3.x and before supported multiple replicas but that was dropped to\n\t// ensure there's a single remote data authority.\n\tswitch {\n\tcase dbc.Replica == nil \u0026\u0026 len(dbc.Replicas) == 0:\n\t\treturn nil, fmt.Errorf(\"must specify replica for database\")\n\tcase dbc.Replica != nil \u0026\u0026 len(dbc.Replicas) \u003e 0:\n\t\treturn nil, fmt.Errorf(\"cannot specify 'replica' and 'replicas' on a database\")\n\tcase len(dbc.Replicas) \u003e 1:\n\t\treturn nil, fmt.Errorf(\"multiple replicas on a single database are no longer supported\")\n\t}\n\n\tvar rc *ReplicaConfig\n\tif dbc.Replica != nil {\n\t\trc = dbc.Replica\n\t} else {\n\t\trc = dbc.Replicas[0]\n\t}\n\n\tr, err := NewReplicaFromConfig(rc, db)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdb.Replica = r\n\n\treturn db, nil\n}\n\n// NewDBsFromDirectoryConfig scans a directory and creates DB instances for all SQLite databases found.\nfunc NewDBsFromDirectoryConfig(dbc *DBConfig) ([]*litestream.DB, error) {\n\tif dbc.Dir == \"\" {\n\t\treturn nil, fmt.Errorf(\"directory path is required for directory replication\")\n\t}\n\n\tif dbc.Pattern == \"\" {\n\t\treturn nil, fmt.Errorf(\"pattern is required for directory replication\")\n\t}\n\tif dbc.MetaPath != nil \u0026\u0026 dbc.MetaDir != nil {\n\t\treturn nil, fmt.Errorf(\"cannot specify both 'meta-path' and 'meta-dir'\")\n\t}\n\n\tdirPath, err := expand(dbc.Dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Find all SQLite databases in the directory\n\tdbPaths, err := FindSQLiteDatabases(dirPath, dbc.Pattern, dbc.Recursive)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to scan directory %s: %w\", dirPath, err)\n\t}\n\n\tif len(dbPaths) == 0 \u0026\u0026 !dbc.Watch {\n\t\treturn nil, fmt.Errorf(\"no SQLite databases found in directory %s with pattern %s\", dirPath, dbc.Pattern)\n\t}\n\n\t// Create DB instances for each found database\n\tvar dbs []*litestream.DB\n\tmetaPaths := make(map[string]string)\n\n\tfor _, dbPath := range dbPaths {\n\t\tdb, err := newDBFromDirectoryEntry(dbc, dirPath, dbPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create DB for %s: %w\", dbPath, err)\n\t\t}\n\n\t\t// Validate unique meta-path to prevent replication state corruption\n\t\tif mp := db.MetaPath(); mp != \"\" {\n\t\t\tif existingDB, exists := metaPaths[mp]; exists {\n\t\t\t\treturn nil, fmt.Errorf(\"meta-path collision: databases %s and %s would share meta-path %s, causing replication state corruption\", existingDB, dbPath, mp)\n\t\t\t}\n\t\t\tmetaPaths[mp] = dbPath\n\t\t}\n\n\t\tdbs = append(dbs, db)\n\t}\n\n\treturn dbs, nil\n}\n\n// newDBFromDirectoryEntry creates a DB instance for a database discovered via directory replication.\nfunc newDBFromDirectoryEntry(dbc *DBConfig, dirPath, dbPath string) (*litestream.DB, error) {\n\t// Calculate relative path from directory root\n\trelPath, err := filepath.Rel(dirPath, dbPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to calculate relative path for %s: %w\", dbPath, err)\n\t}\n\n\t// Create a copy of the config for the discovered database\n\tdbConfigCopy := *dbc\n\tdbConfigCopy.Path = dbPath\n\tdbConfigCopy.Dir = \"\" // Clear dir field for individual DB\n\tdbConfigCopy.Pattern = \"\" // Clear pattern field\n\tdbConfigCopy.Recursive = false // Clear recursive flag\n\tdbConfigCopy.Watch = false // Individual DBs do not watch directories\n\n\t// Ensure every database discovered beneath a directory receives a unique\n\t// metadata path. Without this, all databases share the same meta-path and\n\t// clobber each other's replication state.\n\tswitch {\n\tcase dbc.MetaDir != nil:\n\t\tbaseMetaDir, err := expand(*dbc.MetaDir)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to expand meta dir for %s: %w\", dbPath, err)\n\t\t}\n\t\tmetaPathCopy := filepath.Join(baseMetaDir, relPath+litestream.MetaDirSuffix)\n\t\tdbConfigCopy.MetaPath = \u0026metaPathCopy\n\t\tdbConfigCopy.MetaDir = nil\n\tcase dbc.MetaPath != nil:\n\t\tbaseMetaPath, err := expand(*dbc.MetaPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to expand meta path for %s: %w\", dbPath, err)\n\t\t}\n\t\tmetaPathCopy := deriveMetaPathForDirectoryEntry(baseMetaPath, relPath)\n\t\tdbConfigCopy.MetaPath = \u0026metaPathCopy\n\t\tdbConfigCopy.MetaDir = nil\n\t}\n\n\t// Deep copy replica config and make path unique per database.\n\t// This prevents all databases from writing to the same replica path.\n\tif dbc.Replica != nil {\n\t\treplicaCopy, err := cloneReplicaConfigWithRelativePath(dbc.Replica, relPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to configure replica for %s: %w\", dbPath, err)\n\t\t}\n\t\tdbConfigCopy.Replica = replicaCopy\n\t}\n\n\t// Also handle deprecated 'replicas' array field.\n\tif len(dbc.Replicas) \u003e 0 {\n\t\tdbConfigCopy.Replicas = make([]*ReplicaConfig, len(dbc.Replicas))\n\t\tfor i, replica := range dbc.Replicas {\n\t\t\treplicaCopy, err := cloneReplicaConfigWithRelativePath(replica, relPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to configure replica %d for %s: %w\", i, dbPath, err)\n\t\t\t}\n\t\t\tdbConfigCopy.Replicas[i] = replicaCopy\n\t\t}\n\t}\n\n\treturn NewDBFromConfig(\u0026dbConfigCopy)\n}\n\n// cloneReplicaConfigWithRelativePath returns a copy of the replica configuration with the\n// database-relative path appended to either the replica path or URL, depending on how the\n// replica was configured.\nfunc cloneReplicaConfigWithRelativePath(base *ReplicaConfig, relPath string) (*ReplicaConfig, error) {\n\tif base == nil {\n\t\treturn nil, nil\n\t}\n\n\treplicaCopy := *base\n\trelPath = filepath.ToSlash(relPath)\n\tif relPath == \"\" || relPath == \".\" {\n\t\treturn \u0026replicaCopy, nil\n\t}\n\n\tif replicaCopy.URL != \"\" {\n\t\tu, err := url.Parse(replicaCopy.URL)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse replica url: %w\", err)\n\t\t}\n\t\tappendRelativePathToURL(u, relPath)\n\t\treplicaCopy.URL = u.String()\n\t\treturn \u0026replicaCopy, nil\n\t}\n\n\tswitch base.ReplicaType() {\n\tcase \"file\":\n\t\trelOSPath := filepath.FromSlash(relPath)\n\t\tif replicaCopy.Path != \"\" {\n\t\t\treplicaCopy.Path = filepath.Join(replicaCopy.Path, relOSPath)\n\t\t} else {\n\t\t\treplicaCopy.Path = relOSPath\n\t\t}\n\tdefault:\n\t\t// Normalize to forward slashes for cloud/object storage backends.\n\t\tbasePath := filepath.ToSlash(replicaCopy.Path)\n\t\tif basePath != \"\" {\n\t\t\treplicaCopy.Path = path.Join(basePath, relPath)\n\t\t} else {\n\t\t\treplicaCopy.Path = relPath\n\t\t}\n\t}\n\n\treturn \u0026replicaCopy, nil\n}\n\n// deriveMetaPathForDirectoryEntry returns a unique metadata directory for a\n// database discovered through directory replication by appending the database's\n// relative path and the standard Litestream suffix to the configured base path.\nfunc deriveMetaPathForDirectoryEntry(basePath, relPath string) string {\n\trelPath = filepath.Clean(relPath)\n\tif relPath == \".\" || relPath == \"\" {\n\t\treturn basePath\n\t}\n\n\trelDir, relFile := filepath.Split(relPath)\n\tif relFile == \"\" || relFile == \".\" {\n\t\treturn filepath.Join(basePath, relPath)\n\t}\n\n\tmetaDirName := \".\" + relFile + litestream.MetaDirSuffix\n\treturn filepath.Join(basePath, relDir, metaDirName)\n}\n\n// appendRelativePathToURL appends relPath to the URL's path component, ensuring\n// the result remains rooted and uses forward slashes.\nfunc appendRelativePathToURL(u *url.URL, relPath string) {\n\tcleanRel := strings.TrimPrefix(relPath, \"/\")\n\tif cleanRel == \"\" || cleanRel == \".\" {\n\t\treturn\n\t}\n\n\tbasePath := u.Path\n\tvar joined string\n\tif basePath == \"\" {\n\t\tjoined = cleanRel\n\t} else {\n\t\tjoined = path.Join(basePath, cleanRel)\n\t}\n\n\tjoined = \"/\" + strings.TrimPrefix(joined, \"/\")\n\tu.Path = joined\n}\n\n// FindSQLiteDatabases recursively finds all SQLite database files in a directory.\n// Exported for testing.\nfunc FindSQLiteDatabases(dir string, pattern string, recursive bool) ([]string, error) {\n\tvar dbPaths []string\n\n\terr := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Skip directories unless recursive\n\t\tif info.IsDir() {\n\t\t\tif !recursive \u0026\u0026 path != dir {\n\t\t\t\treturn filepath.SkipDir\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\t// Check if file matches pattern\n\t\tmatched, err := filepath.Match(pattern, filepath.Base(path))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !matched {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Check if it's a SQLite database\n\t\tif IsSQLiteDatabase(path) {\n\t\t\tdbPaths = append(dbPaths, path)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn dbPaths, err\n}\n\n// IsSQLiteDatabase checks if a file is a SQLite database by reading its header.\n// Exported for testing.\nfunc IsSQLiteDatabase(path string) bool {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer file.Close()\n\n\t// SQLite files start with \"SQLite format 3\\x00\"\n\theader := make([]byte, 16)\n\tif _, err := file.Read(header); err != nil {\n\t\treturn false\n\t}\n\n\treturn string(header) == \"SQLite format 3\\x00\"\n}\n\n// ByteSize is a custom type for parsing byte sizes from YAML.\n// It supports both SI units (KB, MB, GB using base 1000) and IEC units\n// (KiB, MiB, GiB using base 1024) as well as short forms (K, M, G).\ntype ByteSize int64\n\n// UnmarshalYAML implements yaml.Unmarshaler for ByteSize.\nfunc (b *ByteSize) UnmarshalYAML(unmarshal func(interface{}) error) error {\n\tvar s string\n\tif err := unmarshal(\u0026s); err != nil {\n\t\treturn err\n\t}\n\n\tsize, err := ParseByteSize(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*b = ByteSize(size)\n\treturn nil\n}\n\n// ParseByteSize parses a byte size string using github.com/dustin/go-humanize.\n// Supports both SI units (KB=1000, MB=1000², etc.) and IEC units (KiB=1024, MiB=1024², etc.).\n// Examples: \"1MB\", \"5MiB\", \"1.5GB\", \"100B\", \"1024KB\"\nfunc ParseByteSize(s string) (int64, error) {\n\ts = strings.TrimSpace(s)\n\tif s == \"\" {\n\t\treturn 0, fmt.Errorf(\"empty size string\")\n\t}\n\n\t// Use go-humanize to parse the byte size string\n\tbytes, err := humanize.ParseBytes(s)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid size format: %w\", err)\n\t}\n\n\t// Check that the value fits in int64\n\tif bytes \u003e math.MaxInt64 {\n\t\treturn 0, fmt.Errorf(\"size %d exceeds maximum allowed value (%d)\", bytes, int64(math.MaxInt64))\n\t}\n\n\treturn int64(bytes), nil\n}\n\n// ReplicaSettings contains settings shared across replica configurations.\n// These can be set globally in Config or per-replica in ReplicaConfig.\ntype ReplicaSettings struct {\n\tSyncInterval *time.Duration `yaml:\"sync-interval\"`\n\tValidationInterval *time.Duration `yaml:\"validation-interval\"`\n\n\t// Maximum L0 files to upload in a single monitor sync run.\n\t// Set to zero to process all pending L0 files in one run.\n\tMaxSyncLTXFiles *int `yaml:\"max-sync-ltx-files\"`\n\n\t// If true, automatically reset local state when LTX errors are detected.\n\t// This allows recovery from corrupted/missing LTX files by forcing a fresh sync.\n\t// Disabled by default to prevent silent data loss scenarios.\n\tAutoRecover *bool `yaml:\"auto-recover\"`\n\n\t// S3 settings\n\tAccessKeyID string `yaml:\"access-key-id\"`\n\tSecretAccessKey string `yaml:\"secret-access-key\"`\n\tRegion string `yaml:\"region\"`\n\tBucket string `yaml:\"bucket\"`\n\tEndpoint string `yaml:\"endpoint\"`\n\tForcePathStyle *bool `yaml:\"force-path-style\"`\n\tSignPayload *bool `yaml:\"sign-payload\"`\n\tSignAcceptEncoding *bool `yaml:\"sign-accept-encoding\"`\n\tRequireContentMD5 *bool `yaml:\"require-content-md5\"`\n\tSkipVerify bool `yaml:\"skip-verify\"`\n\tStorageClass string `yaml:\"storage-class\"`\n\tPartSize *ByteSize `yaml:\"part-size\"`\n\tConcurrency *int `yaml:\"concurrency\"`\n\n\t// S3 Server-Side Encryption (SSE-C: Customer-provided keys)\n\tSSECustomerAlgorithm string `yaml:\"sse-customer-algorithm\"`\n\tSSECustomerKey string `yaml:\"sse-customer-key\"`\n\tSSECustomerKeyPath string `yaml:\"sse-customer-key-path\"`\n\n\t// S3 Server-Side Encryption (SSE-KMS: AWS Key Management Service)\n\tSSEKMSKeyID string `yaml:\"sse-kms-key-id\"`\n\n\t// ABS settings\n\tAccountName string `yaml:\"account-name\"`\n\tAccountKey string `yaml:\"account-key\"`\n\tSASToken string `yaml:\"sas-token\"`\n\n\t// SFTP settings\n\tHost string `yaml:\"host\"`\n\tUser string `yaml:\"user\"`\n\tPassword string `yaml:\"password\"`\n\tKeyPath string `yaml:\"key-path\"`\n\tConcurrentWrites *bool `yaml:\"concurrent-writes\"`\n\tHostKey string `yaml:\"host-key\"`\n\n\t// WebDAV settings\n\tWebDAVURL string `yaml:\"webdav-url\"`\n\tWebDAVUsername string `yaml:\"webdav-username\"`\n\tWebDAVPassword string `yaml:\"webdav-password\"`\n\n\t// NATS settings\n\tJWT string `yaml:\"jwt\"`\n\tSeed string `yaml:\"seed\"`\n\tCreds string `yaml:\"creds\"`\n\tNKey string `yaml:\"nkey\"`\n\tUsername string `yaml:\"username\"`\n\tToken string `yaml:\"token\"`\n\tTLS *bool `yaml:\"tls\"`\n\tRootCAs []string `yaml:\"root-cas\"`\n\tClientCert string `yaml:\"client-cert\"`\n\tClientKey string `yaml:\"client-key\"`\n\tMaxReconnects *int `yaml:\"max-reconnects\"`\n\tReconnectWait *time.Duration `yaml:\"reconnect-wait\"`\n\tTimeout *time.Duration `yaml:\"timeout\"`\n\n\t// Encryption identities and recipients\n\tAge struct {\n\t\tIdentities []string `yaml:\"identities\"`\n\t\tRecipients []string `yaml:\"recipients\"`\n\t} `yaml:\"age\"`\n}\n\n// SetDefaults merges default settings from src into the current ReplicaSettings.\n// Individual settings override defaults when already set.\nfunc (rs *ReplicaSettings) SetDefaults(src *ReplicaSettings) {\n\tif src == nil {\n\t\treturn\n\t}\n\n\t// Timing settings\n\tif rs.SyncInterval == nil \u0026\u0026 src.SyncInterval != nil {\n\t\trs.SyncInterval = src.SyncInterval\n\t}\n\tif rs.ValidationInterval == nil \u0026\u0026 src.ValidationInterval != nil {\n\t\trs.ValidationInterval = src.ValidationInterval\n\t}\n\tif rs.MaxSyncLTXFiles == nil \u0026\u0026 src.MaxSyncLTXFiles != nil {\n\t\trs.MaxSyncLTXFiles = src.MaxSyncLTXFiles\n\t}\n\n\t// Recovery settings\n\tif rs.AutoRecover == nil \u0026\u0026 src.AutoRecover != nil {\n\t\trs.AutoRecover = src.AutoRecover\n\t}\n\n\t// S3 settings\n\tif rs.AccessKeyID == \"\" {\n\t\trs.AccessKeyID = src.AccessKeyID\n\t}\n\tif rs.SecretAccessKey == \"\" {\n\t\trs.SecretAccessKey = src.SecretAccessKey\n\t}\n\tif rs.Region == \"\" {\n\t\trs.Region = src.Region\n\t}\n\tif rs.Bucket == \"\" {\n\t\trs.Bucket = src.Bucket\n\t}\n\tif rs.Endpoint == \"\" {\n\t\trs.Endpoint = src.Endpoint\n\t}\n\tif rs.ForcePathStyle == nil {\n\t\trs.ForcePathStyle = src.ForcePathStyle\n\t}\n\tif rs.SignPayload == nil {\n\t\trs.SignPayload = src.SignPayload\n\t}\n\tif rs.SignAcceptEncoding == nil {\n\t\trs.SignAcceptEncoding = src.SignAcceptEncoding\n\t}\n\tif rs.RequireContentMD5 == nil {\n\t\trs.RequireContentMD5 = src.RequireContentMD5\n\t}\n\tif src.SkipVerify {\n\t\trs.SkipVerify = true\n\t}\n\tif rs.StorageClass == \"\" {\n\t\trs.StorageClass = src.StorageClass\n\t}\n\n\t// S3 SSE settings\n\tif rs.SSECustomerAlgorithm == \"\" {\n\t\trs.SSECustomerAlgorithm = src.SSECustomerAlgorithm\n\t}\n\tif rs.SSECustomerKey == \"\" {\n\t\trs.SSECustomerKey = src.SSECustomerKey\n\t}\n\tif rs.SSECustomerKeyPath == \"\" {\n\t\trs.SSECustomerKeyPath = src.SSECustomerKeyPath\n\t}\n\tif rs.SSEKMSKeyID == \"\" {\n\t\trs.SSEKMSKeyID = src.SSEKMSKeyID\n\t}\n\n\t// ABS settings\n\tif rs.AccountName == \"\" {\n\t\trs.AccountName = src.AccountName\n\t}\n\tif rs.AccountKey == \"\" {\n\t\trs.AccountKey = src.AccountKey\n\t}\n\tif rs.SASToken == \"\" {\n\t\trs.SASToken = src.SASToken\n\t}\n\n\t// SFTP settings\n\tif rs.Host == \"\" {\n\t\trs.Host = src.Host\n\t}\n\tif rs.User == \"\" {\n\t\trs.User = src.User\n\t}\n\tif rs.Password == \"\" {\n\t\trs.Password = src.Password\n\t}\n\tif rs.KeyPath == \"\" {\n\t\trs.KeyPath = src.KeyPath\n\t}\n\tif rs.ConcurrentWrites == nil {\n\t\trs.ConcurrentWrites = src.ConcurrentWrites\n\t}\n\n\t// NATS settings\n\tif rs.JWT == \"\" {\n\t\trs.JWT = src.JWT\n\t}\n\tif rs.Seed == \"\" {\n\t\trs.Seed = src.Seed\n\t}\n\tif rs.Creds == \"\" {\n\t\trs.Creds = src.Creds\n\t}\n\tif rs.NKey == \"\" {\n\t\trs.NKey = src.NKey\n\t}\n\tif rs.Username == \"\" {\n\t\trs.Username = src.Username\n\t}\n\tif rs.Token == \"\" {\n\t\trs.Token = src.Token\n\t}\n\tif rs.TLS == nil {\n\t\trs.TLS = src.TLS\n\t}\n\tif len(rs.RootCAs) == 0 {\n\t\trs.RootCAs = src.RootCAs\n\t}\n\tif rs.ClientCert == \"\" {\n\t\trs.ClientCert = src.ClientCert\n\t}\n\tif rs.ClientKey == \"\" {\n\t\trs.ClientKey = src.ClientKey\n\t}\n\tif rs.MaxReconnects == nil {\n\t\trs.MaxReconnects = src.MaxReconnects\n\t}\n\tif rs.ReconnectWait == nil {\n\t\trs.ReconnectWait = src.ReconnectWait\n\t}\n\tif rs.Timeout == nil {\n\t\trs.Timeout = src.Timeout\n\t}\n\n\t// Age encryption settings\n\tif len(rs.Age.Identities) == 0 {\n\t\trs.Age.Identities = src.Age.Identities\n\t}\n\tif len(rs.Age.Recipients) == 0 {\n\t\trs.Age.Recipients = src.Age.Recipients\n\t}\n}\n\n// ReplicaConfig represents the configuration for a single replica in a database.\ntype ReplicaConfig struct {\n\tReplicaSettings `yaml:\",inline\"`\n\n\tType string `yaml:\"type\"` // \"file\", \"s3\"\n\tName string `yaml:\"name\"` // Deprecated\n\tPath string `yaml:\"path\"`\n\tURL string `yaml:\"url\"`\n}\n\n// NewReplicaFromConfig instantiates a replica for a DB based on a config.\nfunc NewReplicaFromConfig(c *ReplicaConfig, db *litestream.DB) (_ *litestream.Replica, err error) {\n\t// Ensure user did not specify URL in path.\n\tif litestream.IsURL(c.Path) {\n\t\treturn nil, fmt.Errorf(\"replica path cannot be a url, please use the 'url' field instead: %s\", c.Path)\n\t}\n\n\t// Reject age encryption configuration as it's currently non-functional.\n\t// Age encryption support was removed during the LTX storage layer refactor\n\t// and has not been reimplemented. Accepting this config would silently\n\t// write plaintext data to remote storage instead of encrypted data.\n\t// See: https://github.com/benbjohnson/litestream/issues/790\n\tif len(c.Age.Identities) \u003e 0 || len(c.Age.Recipients) \u003e 0 {\n\t\treturn nil, fmt.Errorf(\"age encryption is not currently supported, if you need encryption please revert back to Litestream v0.3.x\")\n\t}\n\n\t// Build replica.\n\tr := litestream.NewReplica(db)\n\tif v := c.SyncInterval; v != nil {\n\t\tr.SyncInterval = *v\n\t}\n\tif v := c.MaxSyncLTXFiles; v != nil {\n\t\tr.MaxSyncLTXFiles = *v\n\t}\n\tif v := c.AutoRecover; v != nil {\n\t\tr.AutoRecoverEnabled = *v\n\t}\n\n\t// Build and set client on replica.\n\tswitch c.ReplicaType() {\n\tcase \"file\":\n\t\tif r.Client, err = newFileReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"s3\":\n\t\tif r.Client, err = NewS3ReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"gs\":\n\t\tif r.Client, err = newGSReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"abs\":\n\t\tif r.Client, err = newABSReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"sftp\":\n\t\tif r.Client, err = newSFTPReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"webdav\":\n\t\tif r.Client, err = newWebDAVReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"nats\":\n\t\tif r.Client, err = newNATSReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"oss\":\n\t\tif r.Client, err = newOSSReplicaClientFromConfig(c, r); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown replica type in config: %q\", c.ReplicaType())\n\t}\n\n\tr.Client.SetLogger(r.Logger())\n\n\treturn r, nil\n}\n\n// newFileReplicaClientFromConfig returns a new instance of file.ReplicaClient built from config.\nfunc newFileReplicaClientFromConfig(c *ReplicaConfig, r *litestream.Replica) (_ *file.ReplicaClient, err error) {\n\t// Ensure URL \u0026 path are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for file replica\")\n\t}\n\n\t// Parse configPath from URL, if specified.\n\tconfigPath := c.Path\n\tif c.URL != \"\" {\n\t\tif _, _, configPath, err = litestream.ParseReplicaURL(c.URL); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Ensure path is set explicitly or derived from URL field.\n\tif configPath == \"\" {\n\t\treturn nil, fmt.Errorf(\"file replica path required\")\n\t}\n\n\t// Expand home prefix and return absolute path.\n\tif configPath, err = expand(configPath); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Instantiate replica and apply time fields, if set.\n\tclient := file.NewReplicaClient(configPath)\n\tclient.Replica = r\n\treturn client, nil\n}\n\n// NewS3ReplicaClientFromConfig returns a new instance of s3.ReplicaClient built from config.\n// Exported for testing.\nfunc NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *s3.ReplicaClient, err error) {\n\t// Ensure URL \u0026 constituent parts are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for s3 replica\")\n\t} else if c.URL != \"\" \u0026\u0026 c.Bucket != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 bucket for s3 replica\")\n\t}\n\n\tbucket, configPath := c.Bucket, c.Path\n\tregion, endpoint, skipVerify := c.Region, c.Endpoint, c.SkipVerify\n\tstorageClass := c.StorageClass\n\tsignSetting := newBoolSetting(true)\n\tif v := c.SignPayload; v != nil {\n\t\tsignSetting.Set(*v)\n\t}\n\tsignAcceptEncodingSetting := newBoolSetting(true)\n\tif v := c.SignAcceptEncoding; v != nil {\n\t\tsignAcceptEncodingSetting.Set(*v)\n\t}\n\trequireSetting := newBoolSetting(true)\n\tif v := c.RequireContentMD5; v != nil {\n\t\trequireSetting.Set(*v)\n\t}\n\n\t// Use path style if an endpoint is explicitly set. This works because the\n\t// only service to not use path style is AWS which does not use an endpoint.\n\tforcePathStyle := (endpoint != \"\")\n\tif v := c.ForcePathStyle; v != nil {\n\t\tforcePathStyle = *v\n\t}\n\n\t// Apply settings from URL, if specified.\n\tvar (\n\t\tendpointWasSet bool\n\t\tusignPayload bool\n\t\tusignPayloadSet bool\n\t\tusignAcceptEncoding bool\n\t\tusignAcceptEncodingSet bool\n\t\turequireContentMD5 bool\n\t\turequireContentMD5Set bool\n\t\tustorageClass string\n\t\tupartSize int64\n\t\tupartSizeSet bool\n\t\tuconcurrency int64\n\t\tuconcurrencySet bool\n\t)\n\tif endpoint != \"\" {\n\t\tendpointWasSet = true\n\t}\n\n\tif c.URL != \"\" {\n\t\t_, host, upath, query, _, err := litestream.ParseReplicaURLWithQuery(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar (\n\t\t\tubucket string\n\t\t\turegion string\n\t\t\tuendpoint string\n\t\t\tuforcePathStyle bool\n\t\t)\n\n\t\tif strings.HasPrefix(host, \"arn:\") {\n\t\t\tubucket = host\n\t\t\turegion = litestream.RegionFromS3ARN(host)\n\t\t} else {\n\t\t\tubucket, uregion, uendpoint, uforcePathStyle = s3.ParseHost(host)\n\t\t}\n\n\t\t// Override with query parameters if provided\n\t\tif qEndpoint := query.Get(\"endpoint\"); qEndpoint != \"\" {\n\t\t\t// Ensure endpoint has a scheme (defaults to https:// for cloud, http:// for local)\n\t\t\tqEndpoint, _ = litestream.EnsureEndpointScheme(qEndpoint)\n\t\t\tuendpoint = qEndpoint\n\t\t\t// Default to path style for custom endpoints unless explicitly set to false\n\t\t\tif query.Get(\"forcePathStyle\") != \"false\" {\n\t\t\t\tuforcePathStyle = true\n\t\t\t}\n\t\t\tendpointWasSet = true\n\t\t}\n\t\tif qRegion := query.Get(\"region\"); qRegion != \"\" {\n\t\t\turegion = qRegion\n\t\t}\n\t\tif qForcePathStyle := query.Get(\"forcePathStyle\"); qForcePathStyle != \"\" {\n\t\t\tuforcePathStyle = qForcePathStyle == \"true\"\n\t\t}\n\t\tif qSkipVerify := query.Get(\"skipVerify\"); qSkipVerify != \"\" {\n\t\t\tskipVerify = qSkipVerify == \"true\"\n\t\t}\n\t\tif v, ok := litestream.BoolQueryValue(query, \"signPayload\", \"sign-payload\"); ok {\n\t\t\tusignPayload = v\n\t\t\tusignPayloadSet = true\n\t\t}\n\t\tif v, ok := litestream.BoolQueryValue(query, \"signAcceptEncoding\", \"sign-accept-encoding\"); ok {\n\t\t\tusignAcceptEncoding = v\n\t\t\tusignAcceptEncodingSet = true\n\t\t}\n\t\tif v, ok := litestream.BoolQueryValue(query, \"requireContentMD5\", \"require-content-md5\"); ok {\n\t\t\turequireContentMD5 = v\n\t\t\turequireContentMD5Set = true\n\t\t}\n\t\tif v := query.Get(\"storageClass\"); v != \"\" {\n\t\t\tustorageClass = v\n\t\t} else if v := query.Get(\"storage-class\"); v != \"\" {\n\t\t\tustorageClass = v\n\t\t}\n\t\tif v, ok, err := litestream.IntQueryValue(query, \"partSize\", \"part-size\"); err != nil {\n\t\t\treturn nil, err\n\t\t} else if ok {\n\t\t\tupartSize = v\n\t\t\tupartSizeSet = true\n\t\t}\n\t\tif v, ok, err := litestream.IntQueryValue(query, \"concurrency\"); err != nil {\n\t\t\treturn nil, err\n\t\t} else if ok {\n\t\t\tuconcurrency = v\n\t\t\tuconcurrencySet = true\n\t\t}\n\n\t\t// Only apply URL parts to field that have not been overridden.\n\t\tif configPath == \"\" {\n\t\t\tconfigPath = upath\n\t\t}\n\t\tif bucket == \"\" {\n\t\t\tbucket = ubucket\n\t\t}\n\t\tif region == \"\" {\n\t\t\tregion = uregion\n\t\t}\n\t\tif endpoint == \"\" {\n\t\t\tendpoint = uendpoint\n\t\t}\n\t\tif storageClass == \"\" {\n\t\t\tstorageClass = ustorageClass\n\t\t}\n\t\tif !forcePathStyle {\n\t\t\tforcePathStyle = uforcePathStyle\n\t\t}\n\t\tif !signSetting.set \u0026\u0026 usignPayloadSet {\n\t\t\tsignSetting.Set(usignPayload)\n\t\t}\n\t\tif !signAcceptEncodingSetting.set \u0026\u0026 usignAcceptEncodingSet {\n\t\t\tsignAcceptEncodingSetting.Set(usignAcceptEncoding)\n\t\t}\n\t\tif !requireSetting.set \u0026\u0026 urequireContentMD5Set {\n\t\t\trequireSetting.Set(urequireContentMD5)\n\t\t}\n\t}\n\n\t// Ensure required settings are set.\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for s3 replica\")\n\t}\n\n\t// Detect S3-compatible provider endpoints for applying appropriate defaults.\n\t// These providers require specific settings to work correctly with AWS SDK v2.\n\tisHetzner := litestream.IsHetznerEndpoint(endpoint)\n\tisTigris := litestream.IsTigrisEndpoint(endpoint)\n\tif !isTigris \u0026\u0026 !endpointWasSet \u0026\u0026 litestream.IsTigrisEndpoint(c.Endpoint) {\n\t\tisTigris = true\n\t}\n\tisDigitalOcean := litestream.IsDigitalOceanEndpoint(endpoint)\n\tisBackblaze := litestream.IsBackblazeEndpoint(endpoint)\n\tisFilebase := litestream.IsFilebaseEndpoint(endpoint)\n\tisScaleway := litestream.IsScalewayEndpoint(endpoint)\n\tisMinIO := litestream.IsMinIOEndpoint(endpoint)\n\tisCloudflareR2 := litestream.IsCloudflareR2Endpoint(endpoint)\n\tisSupabase := litestream.IsSupabaseEndpoint(endpoint)\n\n\t// Track if forcePathStyle was explicitly set by user (config or URL query param).\n\tforcePathStyleSet := c.ForcePathStyle != nil\n\n\t// Apply provider-specific defaults for S3-compatible providers.\n\t// These settings ensure compatibility with each provider's S3 implementation.\n\tif isTigris {\n\t\t// Tigris: requires signed payloads, no MD5\n\t\tsignSetting.ApplyDefault(true)\n\t\trequireSetting.ApplyDefault(false)\n\t}\n\tif isHetzner || isDigitalOcean || isBackblaze || isFilebase || isScaleway || isCloudflareR2 || isMinIO || isSupabase {\n\t\t// All these providers require signed payloads (don't support UNSIGNED-PAYLOAD)\n\t\tsignSetting.ApplyDefault(true)\n\t}\n\tif !forcePathStyleSet {\n\t\t// Filebase, Backblaze B2, MinIO, and Supabase require path-style URLs\n\t\tif isFilebase || isBackblaze || isMinIO || isSupabase {\n\t\t\tforcePathStyle = true\n\t\t}\n\t}\n\n\t// Build replica.\n\tclient := s3.NewReplicaClient()\n\tclient.AccessKeyID = c.AccessKeyID\n\tclient.SecretAccessKey = c.SecretAccessKey\n\tclient.Bucket = bucket\n\tclient.Path = configPath\n\tclient.Region = region\n\tclient.Endpoint = endpoint\n\tclient.ForcePathStyle = forcePathStyle\n\tclient.SkipVerify = skipVerify\n\tclient.StorageClass = storageClass\n\n\tclient.SignPayload = signSetting.value\n\tclient.SignAcceptEncoding = signAcceptEncodingSetting.value\n\tclient.RequireContentMD5 = requireSetting.value\n\n\tif isCloudflareR2 {\n\t\tclient.Concurrency = s3.DefaultR2Concurrency\n\t}\n\tif isTigris {\n\t\tclient.Concurrency = s3.DefaultTigrisConcurrency\n\t\tclient.PartSize = s3.DefaultTigrisPartSize\n\t}\n\n\t// Apply upload configuration from URL query, then config overrides.\n\tif upartSizeSet {\n\t\tclient.PartSize = upartSize\n\t}\n\tif uconcurrencySet {\n\t\tclient.Concurrency = int(uconcurrency)\n\t}\n\tif c.PartSize != nil {\n\t\tclient.PartSize = int64(*c.PartSize)\n\t}\n\tif c.Concurrency != nil {\n\t\tclient.Concurrency = *c.Concurrency\n\t}\n\n\t// Apply SSE-C configuration if specified.\n\tif c.SSECustomerKey != \"\" || c.SSECustomerKeyPath != \"\" {\n\t\tclient.SSECustomerAlgorithm = c.SSECustomerAlgorithm\n\t\tif client.SSECustomerAlgorithm == \"\" {\n\t\t\tclient.SSECustomerAlgorithm = \"AES256\"\n\t\t}\n\n\t\t// Read key from file if path is specified, otherwise use direct value.\n\t\tif c.SSECustomerKeyPath != \"\" {\n\t\t\tkeyPath := c.SSECustomerKeyPath\n\t\t\t// Expand ~ to home directory\n\t\t\tif strings.HasPrefix(keyPath, \"~\") {\n\t\t\t\thome, err := os.UserHomeDir()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"cannot expand home directory for sse-customer-key-path: %w\", err)\n\t\t\t\t}\n\t\t\t\tkeyPath = home + keyPath[1:]\n\t\t\t}\n\t\t\tkeyData, err := os.ReadFile(keyPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot read sse-customer-key-path %q: %w\", c.SSECustomerKeyPath, err)\n\t\t\t}\n\t\t\tclient.SSECustomerKey = strings.TrimSpace(string(keyData))\n\t\t} else {\n\t\t\tclient.SSECustomerKey = c.SSECustomerKey\n\t\t}\n\t}\n\n\t// Apply SSE-KMS configuration if specified.\n\tif c.SSEKMSKeyID != \"\" {\n\t\tclient.SSEKMSKeyID = c.SSEKMSKeyID\n\t}\n\n\treturn client, nil\n}\n\n// newGSReplicaClientFromConfig returns a new instance of gs.ReplicaClient built from config.\nfunc newGSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *gs.ReplicaClient, err error) {\n\t// Ensure URL \u0026 constituent parts are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for gs replica\")\n\t} else if c.URL != \"\" \u0026\u0026 c.Bucket != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 bucket for gs replica\")\n\t}\n\n\tbucket, configPath := c.Bucket, c.Path\n\n\t// Apply settings from URL, if specified.\n\tif c.URL != \"\" {\n\t\t_, uhost, upath, err := litestream.ParseReplicaURL(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Only apply URL parts to field that have not been overridden.\n\t\tif configPath == \"\" {\n\t\t\tconfigPath = upath\n\t\t}\n\t\tif bucket == \"\" {\n\t\t\tbucket = uhost\n\t\t}\n\t}\n\n\t// Ensure required settings are set.\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for gs replica\")\n\t}\n\n\t// Build replica.\n\tclient := gs.NewReplicaClient()\n\tclient.Bucket = bucket\n\tclient.Path = configPath\n\treturn client, nil\n}\n\n// newABSReplicaClientFromConfig returns a new instance of abs.ReplicaClient built from config.\nfunc newABSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *abs.ReplicaClient, err error) {\n\t// Ensure URL \u0026 constituent parts are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for abs replica\")\n\t} else if c.URL != \"\" \u0026\u0026 c.Bucket != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 bucket for abs replica\")\n\t}\n\n\t// Build replica.\n\tclient := abs.NewReplicaClient()\n\tclient.AccountName = c.AccountName\n\tclient.AccountKey = c.AccountKey\n\tclient.SASToken = c.SASToken\n\tclient.Bucket = c.Bucket\n\tclient.Path = c.Path\n\tclient.Endpoint = c.Endpoint\n\n\t// Apply settings from URL, if specified.\n\tif c.URL != \"\" {\n\t\tu, err := url.Parse(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif client.AccountName == \"\" \u0026\u0026 u.User != nil {\n\t\t\tclient.AccountName = u.User.Username()\n\t\t}\n\t\tif client.Bucket == \"\" {\n\t\t\tclient.Bucket = u.Host\n\t\t}\n\t\tif client.Path == \"\" {\n\t\t\tclient.Path = strings.TrimPrefix(path.Clean(u.Path), \"/\")\n\t\t}\n\t}\n\n\t// Ensure required settings are set.\n\tif client.Bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for abs replica\")\n\t}\n\n\treturn client, nil\n}\n\n// newSFTPReplicaClientFromConfig returns a new instance of sftp.ReplicaClient built from config.\nfunc newSFTPReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *sftp.ReplicaClient, err error) {\n\t// Ensure URL \u0026 constituent parts are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for sftp replica\")\n\t} else if c.URL != \"\" \u0026\u0026 c.Host != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 host for sftp replica\")\n\t}\n\n\thost, user, password, path := c.Host, c.User, c.Password, c.Path\n\n\t// Apply settings from URL, if specified.\n\tif c.URL != \"\" {\n\t\tu, err := url.Parse(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Only apply URL parts to field that have not been overridden.\n\t\tif host == \"\" {\n\t\t\thost = u.Host\n\t\t}\n\t\tif user == \"\" \u0026\u0026 u.User != nil {\n\t\t\tuser = u.User.Username()\n\t\t}\n\t\tif password == \"\" \u0026\u0026 u.User != nil {\n\t\t\tpassword, _ = u.User.Password()\n\t\t}\n\t\tif path == \"\" {\n\t\t\tpath = u.Path\n\t\t}\n\t}\n\n\t// Ensure required settings are set.\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"host required for sftp replica\")\n\t} else if user == \"\" {\n\t\treturn nil, fmt.Errorf(\"user required for sftp replica\")\n\t}\n\n\t// Build replica.\n\tclient := sftp.NewReplicaClient()\n\tclient.Host = host\n\tclient.User = user\n\tclient.Password = password\n\tclient.Path = path\n\tclient.KeyPath = c.KeyPath\n\tclient.HostKey = c.HostKey\n\n\t// Set concurrent writes if specified, otherwise use default (true)\n\tif c.ConcurrentWrites != nil {\n\t\tclient.ConcurrentWrites = *c.ConcurrentWrites\n\t}\n\n\treturn client, nil\n}\n\n// newWebDAVReplicaClientFromConfig returns a new instance of webdav.ReplicaClient built from config.\nfunc newWebDAVReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *webdav.ReplicaClient, err error) {\n\t// Ensure URL \u0026 constituent parts are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for webdav replica\")\n\t} else if c.URL != \"\" \u0026\u0026 c.WebDAVURL != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 webdav-url for webdav replica\")\n\t}\n\n\twebdavURL, username, password, path := c.WebDAVURL, c.WebDAVUsername, c.WebDAVPassword, c.Path\n\n\t// Apply settings from URL, if specified.\n\tif c.URL != \"\" {\n\t\tu, err := url.Parse(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Build WebDAV URL from scheme and host\n\t\tscheme := \"http\"\n\t\tif u.Scheme == \"webdavs\" {\n\t\t\tscheme = \"https\"\n\t\t}\n\t\tif webdavURL == \"\" \u0026\u0026 u.Host != \"\" {\n\t\t\twebdavURL = fmt.Sprintf(\"%s://%s\", scheme, u.Host)\n\t\t}\n\n\t\t// Extract credentials from URL\n\t\tif username == \"\" \u0026\u0026 u.User != nil {\n\t\t\tusername = u.User.Username()\n\t\t}\n\t\tif password == \"\" \u0026\u0026 u.User != nil {\n\t\t\tpassword, _ = u.User.Password()\n\t\t}\n\t\tif path == \"\" {\n\t\t\tpath = u.Path\n\t\t}\n\t}\n\n\t// Ensure required settings are set.\n\tif webdavURL == \"\" {\n\t\treturn nil, fmt.Errorf(\"webdav-url required for webdav replica\")\n\t}\n\n\t// Build replica.\n\tclient := webdav.NewReplicaClient()\n\tclient.URL = webdavURL\n\tclient.Username = username\n\tclient.Password = password\n\tclient.Path = path\n\n\treturn client, nil\n}\n\n// newNATSReplicaClientFromConfig returns a new instance of nats.ReplicaClient built from config.\nfunc newNATSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *nats.ReplicaClient, err error) {\n\t// Parse URL if provided to extract bucket name and server URL\n\tvar url, bucket string\n\tif c.URL != \"\" {\n\t\tscheme, host, bucketPath, err := litestream.ParseReplicaURL(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid NATS URL: %w\", err)\n\t\t}\n\t\tif scheme != \"nats\" {\n\t\t\treturn nil, fmt.Errorf(\"invalid scheme for NATS replica: %s\", scheme)\n\t\t}\n\n\t\t// Reconstruct URL without bucket path\n\t\tif host != \"\" {\n\t\t\turl = fmt.Sprintf(\"nats://%s\", host)\n\t\t}\n\n\t\t// Extract bucket name from path\n\t\tif bucketPath != \"\" {\n\t\t\tbucket = strings.Trim(bucketPath, \"/\")\n\t\t}\n\t}\n\n\t// Use bucket from config if not extracted from URL\n\tif bucket == \"\" {\n\t\tbucket = c.Bucket\n\t}\n\n\t// Ensure required settings are set\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for NATS replica\")\n\t}\n\n\t// Validate TLS configuration\n\t// Both client cert and key must be specified together\n\tif (c.ClientCert != \"\") != (c.ClientKey != \"\") {\n\t\treturn nil, fmt.Errorf(\"client-cert and client-key must both be specified for mutual TLS authentication\")\n\t}\n\n\t// Build replica client\n\tclient := nats.NewReplicaClient()\n\tclient.URL = url\n\tclient.BucketName = bucket\n\n\t// Set authentication options\n\tclient.JWT = c.JWT\n\tclient.Seed = c.Seed\n\tclient.Creds = c.Creds\n\tclient.NKey = c.NKey\n\tclient.Username = c.Username\n\tclient.Password = c.Password\n\tclient.Token = c.Token\n\n\t// Set TLS options\n\tif c.TLS != nil {\n\t\tclient.TLS = *c.TLS\n\t}\n\tclient.RootCAs = c.RootCAs\n\tclient.ClientCert = c.ClientCert\n\tclient.ClientKey = c.ClientKey\n\n\t// Set connection options with defaults\n\tif c.MaxReconnects != nil {\n\t\tclient.MaxReconnects = *c.MaxReconnects\n\t}\n\tif c.ReconnectWait != nil {\n\t\tclient.ReconnectWait = *c.ReconnectWait\n\t}\n\tif c.Timeout != nil {\n\t\tclient.Timeout = *c.Timeout\n\t}\n\n\treturn client, nil\n}\n\n// newOSSReplicaClientFromConfig returns a new instance of oss.ReplicaClient built from config.\nfunc newOSSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replica) (_ *oss.ReplicaClient, err error) {\n\t// Ensure URL \u0026 constituent parts are not both specified.\n\tif c.URL != \"\" \u0026\u0026 c.Path != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 path for oss replica\")\n\t} else if c.URL != \"\" \u0026\u0026 c.Bucket != \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot specify url \u0026 bucket for oss replica\")\n\t}\n\n\tbucket, configPath := c.Bucket, c.Path\n\tregion, endpoint := c.Region, c.Endpoint\n\n\t// Apply settings from URL, if specified.\n\tif c.URL != \"\" {\n\t\t_, host, upath, err := litestream.ParseReplicaURL(c.URL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar (\n\t\t\tubucket string\n\t\t\turegion string\n\t\t)\n\n\t\tubucket, uregion, _ = oss.ParseHost(host)\n\n\t\t// Only apply URL parts to fields that have not been overridden.\n\t\tif configPath == \"\" {\n\t\t\tconfigPath = upath\n\t\t}\n\t\tif bucket == \"\" {\n\t\t\tbucket = ubucket\n\t\t}\n\t\tif region == \"\" {\n\t\t\tregion = uregion\n\t\t}\n\t}\n\n\t// Ensure required settings are set.\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for oss replica\")\n\t}\n\n\t// Build replica client.\n\tclient := oss.NewReplicaClient()\n\tclient.AccessKeyID = c.AccessKeyID\n\tclient.AccessKeySecret = c.SecretAccessKey\n\tclient.Bucket = bucket\n\tclient.Path = configPath\n\tclient.Region = region\n\tclient.Endpoint = endpoint\n\n\t// Apply upload configuration if specified.\n\tif c.PartSize != nil {\n\t\tclient.PartSize = int64(*c.PartSize)\n\t}\n\tif c.Concurrency != nil {\n\t\tclient.Concurrency = *c.Concurrency\n\t}\n\n\treturn client, nil\n}\n\n// applyLitestreamEnv copies \"LITESTREAM\" prefixed environment variables to\n// their AWS counterparts as the \"AWS\" prefix can be confusing when using a\n// non-AWS S3-compatible service.\nfunc applyLitestreamEnv() {\n\tif v, ok := os.LookupEnv(\"LITESTREAM_ACCESS_KEY_ID\"); ok {\n\t\tif _, ok := os.LookupEnv(\"AWS_ACCESS_KEY_ID\"); !ok {\n\t\t\tos.Setenv(\"AWS_ACCESS_KEY_ID\", v)\n\t\t}\n\t}\n\tif v, ok := os.LookupEnv(\"LITESTREAM_SECRET_ACCESS_KEY\"); ok {\n\t\tif _, ok := os.LookupEnv(\"AWS_SECRET_ACCESS_KEY\"); !ok {\n\t\t\tos.Setenv(\"AWS_SECRET_ACCESS_KEY\", v)\n\t\t}\n\t}\n}\n\ntype boolSetting struct {\n\tvalue bool\n\tset bool\n}\n\nfunc newBoolSetting(defaultValue bool) boolSetting {\n\treturn boolSetting{value: defaultValue}\n}\n\nfunc (s *boolSetting) Set(value bool) {\n\ts.value = value\n\ts.set = true\n}\n\nfunc (s *boolSetting) ApplyDefault(value bool) {\n\tif !s.set {\n\t\ts.value = value\n\t}\n}\n\n// ReplicaType returns the type based on the type field or extracted from the URL.\nfunc (c *ReplicaConfig) ReplicaType() string {\n\tif replicaType := litestream.ReplicaTypeFromURL(c.URL); replicaType != \"\" {\n\t\treturn replicaType\n\t} else if c.Type != \"\" {\n\t\treturn c.Type\n\t}\n\treturn \"file\"\n}\n\n// DefaultConfigPath returns the default config path.\nfunc DefaultConfigPath() string {\n\tif v := os.Getenv(\"LITESTREAM_CONFIG\"); v != \"\" {\n\t\treturn v\n\t}\n\treturn defaultConfigPath\n}\n\nfunc registerConfigFlag(fs *flag.FlagSet) (configPath *string, noExpandEnv *bool) {\n\treturn fs.String(\"config\", \"\", \"config path\"),\n\t\tfs.Bool(\"no-expand-env\", false, \"do not expand env vars in config\")\n}\n\n// isValidHeartbeatURL checks if the URL is a valid HTTP or HTTPS URL.\nfunc isValidHeartbeatURL(u string) bool {\n\treturn strings.HasPrefix(u, \"http://\") || strings.HasPrefix(u, \"https://\")\n}\n\n// expand returns an absolute path for s.\n// It also strips SQLite connection string prefixes (sqlite://, sqlite3://).\nfunc expand(s string) (string, error) {\n\t// Strip SQLite connection string prefixes if present.\n\ts = StripSQLitePrefix(s)\n\n\t// Just expand to absolute path if there is no home directory prefix.\n\tprefix := \"~\" + string(os.PathSeparator)\n\tif s != \"~\" \u0026\u0026 !strings.HasPrefix(s, prefix) {\n\t\treturn filepath.Abs(s)\n\t}\n\n\t// Look up home directory.\n\tu, err := user.Current()\n\tif err != nil {\n\t\treturn \"\", err\n\t} else if u.HomeDir == \"\" {\n\t\treturn \"\", fmt.Errorf(\"cannot expand path %s, no home directory available\", s)\n\t}\n\n\t// Return path with tilde replaced by the home directory.\n\tif s == \"~\" {\n\t\treturn u.HomeDir, nil\n\t}\n\treturn filepath.Join(u.HomeDir, strings.TrimPrefix(s, prefix)), nil\n}\n\n// StripSQLitePrefix removes SQLite connection string prefixes (sqlite://, sqlite3://)\n// from the given path. This allows users to use standard connection string formats\n// across their tooling while Litestream extracts just the file path.\nfunc StripSQLitePrefix(s string) string {\n\tif len(s) \u003c 9 || s[0] != 's' {\n\t\treturn s\n\t}\n\tfor _, prefix := range []string{\"sqlite3://\", \"sqlite://\"} {\n\t\tif strings.HasPrefix(s, prefix) {\n\t\t\treturn strings.TrimPrefix(s, prefix)\n\t\t}\n\t}\n\treturn s\n}\n\n// txidVar allows the flag package to parse index flags as hex-formatted TXIDs\ntype txidVar ltx.TXID\n\n// Ensure type implements interface.\nvar _ flag.Value = (*txidVar)(nil)\n\n// String returns an 8-character hexadecimal value.\nfunc (v *txidVar) String() string {\n\treturn ltx.TXID(*v).String()\n}\n\n// Set parses s into an integer from a hexadecimal value.\nfunc (v *txidVar) Set(s string) error {\n\ttxID, err := ltx.ParseTXID(s)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid txid format\")\n\t}\n\t*v = txidVar(txID)\n\treturn nil\n}\n\n// levelAll is a sentinel value indicating all compaction levels should be shown.\nconst levelAll = -1\n\n// levelVar allows the flag package to parse compaction level flags.\n// Accepts integers 0-9 or \"all\" for all levels.\ntype levelVar int\n\nvar _ flag.Value = (*levelVar)(nil)\n\nfunc (v *levelVar) String() string {\n\tif *v == levelAll {\n\t\treturn \"all\"\n\t}\n\treturn strconv.Itoa(int(*v))\n}\n\nfunc (v *levelVar) Set(s string) error {\n\tif s == \"all\" {\n\t\t*v = levelAll\n\t\treturn nil\n\t}\n\tn, err := strconv.Atoi(s)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid level: must be 0-%d or \\\"all\\\"\", litestream.SnapshotLevel)\n\t}\n\tif n \u003c 0 || n \u003e litestream.SnapshotLevel {\n\t\treturn fmt.Errorf(\"level must be between 0 and %d\", litestream.SnapshotLevel)\n\t}\n\t*v = levelVar(n)\n\treturn nil\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"errors","path":"errors","span":{"start":[5,1],"end":[5,10],"bytes":[34,43]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[44,51]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[52,58]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[59,64]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[65,76]}},{"name":"math","path":"math","span":{"start":[10,1],"end":[10,8],"bytes":[77,84]}},{"name":"url","path":"net/url","span":{"start":[11,1],"end":[11,11],"bytes":[85,95]}},{"name":"os","path":"os","span":{"start":[12,1],"end":[12,6],"bytes":[96,101]}},{"name":"user","path":"os/user","span":{"start":[13,1],"end":[13,11],"bytes":[102,112]}},{"name":"path","path":"path","span":{"start":[14,1],"end":[14,8],"bytes":[113,120]}},{"name":"filepath","path":"path/filepath","span":{"start":[15,1],"end":[15,17],"bytes":[121,137]}},{"name":"debug","path":"runtime/debug","span":{"start":[16,1],"end":[16,17],"bytes":[138,154]}},{"name":"strconv","path":"strconv","span":{"start":[17,1],"end":[17,11],"bytes":[155,165]}},{"name":"strings","path":"strings","span":{"start":[18,1],"end":[18,11],"bytes":[166,176]}},{"name":"time","path":"time","span":{"start":[19,1],"end":[19,8],"bytes":[177,184]}},{"name":"go-humanize","path":"github.com/dustin/go-humanize","span":{"start":[21,1],"end":[21,33],"bytes":[186,218]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[22,1],"end":[22,27],"bytes":[219,245]}},{"name":"fallback","path":"golang.org/x/crypto/x509roots/fallback","alias":"_","span":{"start":[23,1],"end":[23,44],"bytes":[246,289]}},{"name":"yaml.v2","path":"gopkg.in/yaml.v2","span":{"start":[24,1],"end":[24,20],"bytes":[290,309]}},{"name":"sqlite","path":"modernc.org/sqlite","alias":"_","span":{"start":[25,1],"end":[25,24],"bytes":[310,333]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[27,1],"end":[27,37],"bytes":[335,371]}},{"name":"abs","path":"github.com/benbjohnson/litestream/abs","span":{"start":[28,1],"end":[28,41],"bytes":[372,412]}},{"name":"file","path":"github.com/benbjohnson/litestream/file","span":{"start":[29,1],"end":[29,42],"bytes":[413,454]}},{"name":"gs","path":"github.com/benbjohnson/litestream/gs","span":{"start":[30,1],"end":[30,40],"bytes":[455,494]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[31,1],"end":[31,46],"bytes":[495,540]}},{"name":"nats","path":"github.com/benbjohnson/litestream/nats","span":{"start":[32,1],"end":[32,42],"bytes":[541,582]}},{"name":"oss","path":"github.com/benbjohnson/litestream/oss","span":{"start":[33,1],"end":[33,41],"bytes":[583,623]}},{"name":"s3","path":"github.com/benbjohnson/litestream/s3","span":{"start":[34,1],"end":[34,40],"bytes":[624,663]}},{"name":"sftp","path":"github.com/benbjohnson/litestream/sftp","span":{"start":[35,1],"end":[35,42],"bytes":[664,705]}},{"name":"webdav","path":"github.com/benbjohnson/litestream/webdav","span":{"start":[36,1],"end":[36,44],"bytes":[706,749]}}],"types":{"ByteSize":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ByteSize","kind":"struct","span":{"start":[1071,1],"end":[1071,20],"bytes":[32518,32537]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ByteSize.UnmarshalYAML":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ByteSize/github.com/benbjohnson/litestream/cmd/litestream.ByteSize.UnmarshalYAML","kind":"method","span":{"start":[1074,1],"end":[1086,2],"bytes":[32598,32842]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ByteSize.UnmarshalYAML","parameters":[{"name":"unmarshal","type":"func(interface{}) error","span":{"start":[1074,1],"end":[1074,76],"bytes":[32598,32673]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"1076:12":{"kind":"call","span":{"start":[1076,12],"end":[1076,25],"bytes":[32699,32712]},"callee":null},"1080:15":{"kind":"call","span":{"start":[1080,15],"end":[1080,31],"bytes":[32758,32774]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseByteSize"},"1084:7":{"kind":"call","span":{"start":[1084,7],"end":[1084,21],"bytes":[32814,32828]},"callee":null}}}}},"CompactionLevelConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.CompactionLevelConfig","kind":"struct","span":{"start":[717,1],"end":[719,2],"bytes":[21344,21423]},"callables":{},"fields":{"Interval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.CompactionLevelConfig/Interval","kind":"field","type":"time.Duration","span":{"start":[718,1],"end":[718,42],"bytes":[21380,21421]}}}},"Config":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config","kind":"struct","span":{"start":[270,1],"end":[325,2],"bytes":[7596,9394]},"base_types":["github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings"],"callables":{"github.com/benbjohnson/litestream/cmd/litestream.Config.CompactionLevels":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.CompactionLevels","kind":"method","span":{"start":[571,1],"end":[584,2],"bytes":[17438,17721]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Config.CompactionLevels","parameters":[],"return_type":"github.com/benbjohnson/litestream.CompactionLevels","metrics":{"cyclomatic":2},"body":{"577:12":{"kind":"call","span":{"start":[577,12],"end":[580,5],"bytes":[17605,17700]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig","kind":"method","span":{"start":[587,1],"end":[594,2],"bytes":[17775,17940]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig","parameters":[{"name":"configPath","type":"string","span":{"start":[587,1],"end":[587,57],"bytes":[17775,17831]}}],"return_type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","metrics":{"cyclomatic":3},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream.Config.Validate":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.Validate","kind":"method","span":{"start":[419,1],"end":[568,2],"bytes":[12463,17363]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Config.Validate","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":26},"body":{"465:30":{"kind":"call","span":{"start":[465,30],"end":[465,65],"bytes":[13863,13898]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.isValidHeartbeatURL"},"485:12":{"kind":"call","span":{"start":[485,12],"end":[485,49],"bytes":[14450,14487]},"callee":null},"492:15":{"kind":"call","span":{"start":[492,15],"end":[492,35],"bytes":[14573,14593]},"callee":null},"496:11":{"kind":"call","span":{"start":[496,11],"end":[496,89],"bytes":[14776,14854]},"callee":null},"499:11":{"kind":"call","span":{"start":[499,11],"end":[499,88],"bytes":[14906,14983]},"callee":null},"506:11":{"kind":"call","span":{"start":[506,11],"end":[506,34],"bytes":[15195,15218]},"callee":null},"508:12":{"kind":"call","span":{"start":[508,12],"end":[508,164],"bytes":[15270,15422]},"callee":null},"515:11":{"kind":"call","span":{"start":[515,11],"end":[515,91],"bytes":[15558,15638]},"callee":null},"518:11":{"kind":"call","span":{"start":[518,11],"end":[518,97],"bytes":[15685,15771]},"callee":null},"521:11":{"kind":"call","span":{"start":[521,11],"end":[521,97],"bytes":[15827,15913]},"callee":null},"524:11":{"kind":"call","span":{"start":[524,11],"end":[524,99],"bytes":[15975,16063]},"callee":null},"536:12":{"kind":"call","span":{"start":[536,12],"end":[536,66],"bytes":[16366,16420]},"callee":null},"543:12":{"kind":"call","span":{"start":[543,12],"end":[543,67],"bytes":[16617,16672]},"callee":null},"552:12":{"kind":"call","span":{"start":[552,12],"end":[552,70],"bytes":[16933,16991]},"callee":null},"560:13":{"kind":"call","span":{"start":[560,13],"end":[560,79],"bytes":[17228,17294]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.Config.applyDBSnapshotConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.applyDBSnapshotConfig","kind":"method","span":{"start":[356,1],"end":[390,2],"bytes":[10206,11283]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Config.applyDBSnapshotConfig","parameters":[{"name":"globalIntervalSet","type":"bool","span":{"start":[356,1],"end":[356,91],"bytes":[10206,10296]}},{"name":"globalRetentionSet","type":"bool","span":{"start":[356,1],"end":[356,91],"bytes":[10206,10296]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"369:12":{"kind":"call","span":{"start":[369,12],"end":[369,139],"bytes":[10597,10724]},"callee":null},"376:12":{"kind":"call","span":{"start":[376,12],"end":[376,143],"bytes":[10902,11033]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.Config.propagateGlobalSettings":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.propagateGlobalSettings","kind":"method","span":{"start":[344,1],"end":[354,2],"bytes":[9909,10204]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Config.propagateGlobalSettings","parameters":[],"metrics":{"cyclomatic":4},"body":{"348:4":{"kind":"call","span":{"start":[348,4],"end":[348,47],"bytes":[10074,10117]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings.SetDefaults"},"351:4":{"kind":"call","span":{"start":[351,4],"end":[351,38],"bytes":[10161,10195]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings.SetDefaults"}}}},"fields":{"Addr":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Addr","kind":"field","type":"string","span":{"start":[275,1],"end":[275,27],"bytes":[7758,7784]}},"ConfigPath":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/ConfigPath","kind":"field","type":"string","span":{"start":[324,1],"end":[324,30],"bytes":[9363,9392]}},"DBs":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/DBs","kind":"field","type":"[]*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[306,1],"end":[306,30],"bytes":[8814,8843]}},"Exec":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Exec","kind":"field","type":"string","span":{"start":[310,1],"end":[310,27],"bytes":[8943,8969]}},"HeartbeatInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/HeartbeatInterval","kind":"field","type":"*time.Duration","span":{"start":[303,1],"end":[303,62],"bytes":[8718,8779]}},"HeartbeatURL":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/HeartbeatURL","kind":"field","type":"string","span":{"start":[302,1],"end":[302,57],"bytes":[8661,8717]}},"L0Retention":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/L0Retention","kind":"field","type":"*time.Duration","span":{"start":[291,1],"end":[291,63],"bytes":[8214,8276]}},"L0RetentionCheckInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/L0RetentionCheckInterval","kind":"field","type":"*time.Duration","span":{"start":[292,1],"end":[292,78],"bytes":[8277,8354]}},"Levels":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Levels","kind":"field","type":"[]*github.com/benbjohnson/litestream/cmd/litestream.CompactionLevelConfig","span":{"start":[282,1],"end":[282,49],"bytes":[7988,8036]}},"Logging":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Logging","kind":"field","type":"github.com/benbjohnson/litestream/internal.LoggingConfig","span":{"start":[313,1],"end":[313,49],"bytes":[8983,9031]}},"MCPAddr":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/MCPAddr","kind":"field","type":"string","span":{"start":[316,1],"end":[316,34],"bytes":[9056,9089]}},"Retention":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Retention","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.RetentionConfig","span":{"start":[299,1],"end":[299,46],"bytes":[8573,8618]}},"ShutdownSyncInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/ShutdownSyncInterval","kind":"field","type":"*time.Duration","span":{"start":[320,1],"end":[320,69],"bytes":[9192,9260]}},"ShutdownSyncTimeout":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/ShutdownSyncTimeout","kind":"field","type":"*time.Duration","span":{"start":[319,1],"end":[319,68],"bytes":[9124,9191]}},"Snapshot":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Snapshot","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.SnapshotConfig","span":{"start":[285,1],"end":[285,43],"bytes":[8065,8107]}},"Socket":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Socket","kind":"field","type":"github.com/benbjohnson/litestream.SocketConfig","span":{"start":[278,1],"end":[278,48],"bytes":[7833,7880]}},"Validation":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/Validation","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.ValidationConfig","span":{"start":[288,1],"end":[288,49],"bytes":[8138,8186]}},"VerifyCompaction":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/VerifyCompaction","kind":"field","type":"bool","span":{"start":[296,1],"end":[296,50],"bytes":[8494,8543]}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings","span":{"start":[272,1],"end":[272,34],"bytes":[7685,7718]}}}},"ConfigValidationError":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError","kind":"struct","span":{"start":[67,1],"end":[71,2],"bytes":[2273,2356]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError.Error":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError.Error","kind":"method","span":{"start":[73,1],"end":[78,2],"bytes":[2358,2543]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError.Error","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{"75:10":{"kind":"call","span":{"start":[75,10],"end":[75,65],"bytes":[2437,2492]},"callee":null},"77:9":{"kind":"call","span":{"start":[77,9],"end":[77,46],"bytes":[2504,2541]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError.Unwrap":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError.Unwrap","kind":"method","span":{"start":[80,1],"end":[82,2],"bytes":[2545,2609]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError.Unwrap","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}}},"fields":{"Err":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError/Err","kind":"field","type":"error","span":{"start":[68,1],"end":[68,13],"bytes":[2309,2321]}},"Field":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError/Field","kind":"field","type":"string","span":{"start":[69,1],"end":[69,14],"bytes":[2322,2335]}},"Value":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ConfigValidationError/Value","kind":"field","type":"interface{}","span":{"start":[70,1],"end":[70,19],"bytes":[2336,2354]}}}},"DBConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig","kind":"struct","span":{"start":[722,1],"end":[742,2],"bytes":[21515,22670]},"callables":{},"fields":{"BusyTimeout":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/BusyTimeout","kind":"field","type":"*time.Duration","span":{"start":[733,1],"end":[733,57],"bytes":[22253,22309]}},"CheckpointInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/CheckpointInterval","kind":"field","type":"*time.Duration","span":{"start":[732,1],"end":[732,64],"bytes":[22189,22252]}},"Dir":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Dir","kind":"field","type":"string","span":{"start":[724,1],"end":[724,89],"bytes":[21587,21675]}},"MaxSyncWALBytes":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/MaxSyncWALBytes","kind":"field","type":"*int64","span":{"start":[736,1],"end":[736,63],"bytes":[22440,22502]}},"MetaDir":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/MetaDir","kind":"field","type":"*string","span":{"start":[730,1],"end":[730,53],"bytes":[22075,22127]}},"MetaPath":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/MetaPath","kind":"field","type":"*string","span":{"start":[729,1],"end":[729,54],"bytes":[22021,22074]}},"MinCheckpointPageN":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/MinCheckpointPageN","kind":"field","type":"*int","span":{"start":[734,1],"end":[734,70],"bytes":[22310,22379]}},"MonitorInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/MonitorInterval","kind":"field","type":"*time.Duration","span":{"start":[731,1],"end":[731,61],"bytes":[22128,22188]}},"Path":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Path","kind":"field","type":"string","span":{"start":[723,1],"end":[723,49],"bytes":[21538,21586]}},"Pattern":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Pattern","kind":"field","type":"string","span":{"start":[725,1],"end":[725,106],"bytes":[21676,21781]}},"Recursive":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Recursive","kind":"field","type":"bool","span":{"start":[726,1],"end":[726,89],"bytes":[21782,21870]}},"Replica":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Replica","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[740,1],"end":[740,44],"bytes":[22566,22609]}},"Replicas":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Replicas","kind":"field","type":"[]*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[741,1],"end":[741,59],"bytes":[22610,22668]}},"RestoreIfDBNotExists":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/RestoreIfDBNotExists","kind":"field","type":"bool","span":{"start":[738,1],"end":[738,61],"bytes":[22504,22564]}},"Snapshot":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Snapshot","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.SnapshotConfig","span":{"start":[728,1],"end":[728,53],"bytes":[21968,22020]}},"TruncatePageN":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/TruncatePageN","kind":"field","type":"*int","span":{"start":[735,1],"end":[735,60],"bytes":[22380,22439]}},"Watch":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DBConfig/Watch","kind":"field","type":"bool","span":{"start":[727,1],"end":[727,97],"bytes":[21871,21967]}}}},"Main":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main","kind":"struct","span":{"start":[117,1],"end":[117,19],"bytes":[3367,3385]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.Main.Run":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","kind":"method","span":{"start":[125,1],"end":[239,2],"bytes":[3501,6631]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Main.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[125,1],"end":[125,69],"bytes":[3501,3569]}},{"name":"args","type":"[]string","span":{"start":[125,1],"end":[125,69],"bytes":[3501,3569]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":33},"body":{"127:23":{"kind":"call","span":{"start":[127,23],"end":[127,41],"bytes":[3657,3675]},"callee":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.isWindowsService"},"130:10":{"kind":"call","span":{"start":[130,10],"end":[130,32],"bytes":[3735,3757]},"callee":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.runWindowsService"},"134:2":{"kind":"call","span":{"start":[134,2],"end":[134,22],"bytes":[3809,3829]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv"},"138:5":{"kind":"call","span":{"start":[138,5],"end":[138,14],"bytes":[3877,3886]},"callee":null},"144:10":{"kind":"call","span":{"start":[144,10],"end":[144,46],"bytes":[3971,4007]},"callee":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run"},"146:8":{"kind":"call","span":{"start":[146,8],"end":[146,29],"bytes":[4034,4055]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicateCommand"},"147:13":{"kind":"call","span":{"start":[147,13],"end":[147,36],"bytes":[4068,4091]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags"},"152:15":{"kind":"call","span":{"start":[152,15],"end":[152,27],"bytes":[4166,4178]},"callee":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.signalChan"},"157:11":{"kind":"call","span":{"start":[157,11],"end":[157,30],"bytes":[4366,4385]},"callee":null},"158:3":{"kind":"call","span":{"start":[158,3],"end":[158,18],"bytes":[4388,4403]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.SetDone"},"160:13":{"kind":"call","span":{"start":[160,13],"end":[160,23],"bytes":[4417,4427]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run"},"168:5":{"kind":"call","span":{"start":[168,5],"end":[168,61],"bytes":[4560,4616]},"callee":null},"170:5":{"kind":"call","span":{"start":[170,5],"end":[170,64],"bytes":[4633,4692]},"callee":null},"173:4":{"kind":"call","span":{"start":[173,4],"end":[173,73],"bytes":[4727,4796]},"callee":null},"176:5":{"kind":"call","span":{"start":[176,5],"end":[176,48],"bytes":[4823,4866]},"callee":null},"177:15":{"kind":"call","span":{"start":[177,15],"end":[177,40],"bytes":[4881,4906]},"callee":null},"178:13":{"kind":"call","span":{"start":[178,13],"end":[178,62],"bytes":[4933,4982]},"callee":null},"181:5":{"kind":"call","span":{"start":[181,5],"end":[181,51],"bytes":[4994,5040]},"callee":null},"182:42":{"kind":"call","span":{"start":[182,42],"end":[182,83],"bytes":[5082,5123]},"callee":null},"182:60":{"kind":"call","span":{"start":[182,60],"end":[182,71],"bytes":[5100,5111]},"callee":null},"183:13":{"kind":"call","span":{"start":[183,13],"end":[183,64],"bytes":[5138,5189]},"callee":null},"188:7":{"kind":"call","span":{"start":[188,7],"end":[191,7],"bytes":[5282,5328]},"callee":null,"is_goroutine":true},"195:11":{"kind":"call","span":{"start":[195,11],"end":[195,23],"bytes":[5367,5379]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close"},"198:3":{"kind":"call","span":{"start":[198,3],"end":[198,36],"bytes":[5423,5456]},"callee":null},"202:10":{"kind":"call","span":{"start":[202,10],"end":[202,42],"bytes":[5495,5527]},"callee":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartCommand/github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run"},"204:10":{"kind":"call","span":{"start":[204,10],"end":[204,41],"bytes":[5551,5582]},"callee":"can://go/litestream/cmd/litestream/stop.go/github.com/benbjohnson/litestream/cmd/litestream.StopCommand/github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run"},"206:10":{"kind":"call","span":{"start":[206,10],"end":[206,45],"bytes":[5610,5645]},"callee":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run"},"208:10":{"kind":"call","span":{"start":[208,10],"end":[208,47],"bytes":[5675,5712]},"callee":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run"},"210:10":{"kind":"call","span":{"start":[210,10],"end":[210,42],"bytes":[5737,5769]},"callee":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run"},"212:10":{"kind":"call","span":{"start":[212,10],"end":[212,44],"bytes":[5796,5830]},"callee":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run"},"214:10":{"kind":"call","span":{"start":[214,10],"end":[214,43],"bytes":[5856,5889]},"callee":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run"},"216:10":{"kind":"call","span":{"start":[216,10],"end":[216,41],"bytes":[5913,5944]},"callee":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run"},"218:10":{"kind":"call","span":{"start":[218,10],"end":[218,41],"bytes":[5968,5999]},"callee":"can://go/litestream/cmd/litestream/list.go/github.com/benbjohnson/litestream/cmd/litestream.ListCommand/github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run"},"220:10":{"kind":"call","span":{"start":[220,10],"end":[220,41],"bytes":[6023,6054]},"callee":"can://go/litestream/cmd/litestream/info.go/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run"},"222:10":{"kind":"call","span":{"start":[222,10],"end":[222,44],"bytes":[6081,6115]},"callee":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Run"},"224:10":{"kind":"call","span":{"start":[224,10],"end":[224,40],"bytes":[6138,6168]},"callee":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run"},"227:3":{"kind":"call","span":{"start":[227,3],"end":[227,92],"bytes":[6233,6322]},"callee":null},"228:10":{"kind":"call","span":{"start":[228,10],"end":[228,40],"bytes":[6332,6362]},"callee":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run"},"231:4":{"kind":"call","span":{"start":[231,4],"end":[231,13],"bytes":[6449,6458]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Usage"},"233:26":{"kind":"call","span":{"start":[233,26],"end":[233,53],"bytes":[6498,6525]},"callee":null},"234:4":{"kind":"call","span":{"start":[234,4],"end":[234,13],"bytes":[6531,6540]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Usage"},"237:10":{"kind":"call","span":{"start":[237,10],"end":[237,59],"bytes":[6577,6626]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run/github.com/benbjohnson/litestream/cmd/litestream.Main.Run.closure_1","kind":"function","span":{"start":[188,1],"end":[191,7],"bytes":[5276,5328]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Main.Run.closure_1","parameters":[],"body":{"190:5":{"kind":"call","span":{"start":[190,5],"end":[190,16],"bytes":[5310,5321]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.Main.Usage":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Usage","kind":"method","span":{"start":[242,1],"end":[267,2],"bytes":[6676,7525]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.Main.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"243:2":{"kind":"call","span":{"start":[243,2],"end":[266,7],"bytes":[6702,7523]},"callee":null}}}}},"ReplicaConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","kind":"struct","span":{"start":[1340,1],"end":[1347,2],"bytes":[39984,40185]},"base_types":["github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings"],"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType","kind":"method","span":{"start":[2088,1],"end":[2095,2],"bytes":[61675,61881]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType","parameters":[],"return_type":"string","metrics":{"cyclomatic":3},"body":{"2089:20":{"kind":"call","span":{"start":[2089,20],"end":[2089,56],"bytes":[61741,61777]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ReplicaTypeFromURL"}}}},"fields":{"Name":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/Name","kind":"field","type":"string","span":{"start":[1344,1],"end":[1344,41],"bytes":[40090,40130]}},"Path":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/Path","kind":"field","type":"string","span":{"start":[1345,1],"end":[1345,27],"bytes":[40131,40157]}},"Type":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/Type","kind":"field","type":"string","span":{"start":[1343,1],"end":[1343,43],"bytes":[40047,40089]}},"URL":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/URL","kind":"field","type":"string","span":{"start":[1346,1],"end":[1346,26],"bytes":[40158,40183]}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings","span":{"start":[1341,1],"end":[1341,34],"bytes":[40012,40045]}}}},"ReplicaSettings":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings","kind":"struct","span":{"start":[1113,1],"end":[1187,2],"bytes":[33718,36725]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings.SetDefaults":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings.SetDefaults","kind":"method","span":{"start":[1191,1],"end":[1337,2],"bytes":[36868,39900]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings.SetDefaults","parameters":[{"name":"src","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings","span":{"start":[1191,1],"end":[1191,63],"bytes":[36868,36930]}}],"metrics":{"cyclomatic":44},"body":{"1311:5":{"kind":"call","span":{"start":[1311,5],"end":[1311,20],"bytes":[39342,39357]},"callee":null},"1331:5":{"kind":"call","span":{"start":[1331,5],"end":[1331,27],"bytes":[39747,39769]},"callee":null},"1334:5":{"kind":"call","span":{"start":[1334,5],"end":[1334,27],"bytes":[39825,39847]},"callee":null}}}},"fields":{"AccessKeyID":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/AccessKeyID","kind":"field","type":"string","span":{"start":[1127,1],"end":[1127,53],"bytes":[34322,34374]}},"AccountKey":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/AccountKey","kind":"field","type":"string","span":{"start":[1151,1],"end":[1151,41],"bytes":[35415,35455]}},"AccountName":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/AccountName","kind":"field","type":"string","span":{"start":[1150,1],"end":[1150,42],"bytes":[35373,35414]}},"Age":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Age","kind":"field","type":"struct{Identities []string \"yaml:\\\"identities\\\"\"; Recipients []string \"yaml:\\\"recipients\\\"\"}","span":{"start":[1183,1],"end":[1186,16],"bytes":[36610,36723]}},"AutoRecover":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/AutoRecover","kind":"field","type":"*bool","span":{"start":[1124,1],"end":[1124,41],"bytes":[34264,34304]}},"Bucket":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Bucket","kind":"field","type":"string","span":{"start":[1130,1],"end":[1130,46],"bytes":[34478,34523]}},"ClientCert":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/ClientCert","kind":"field","type":"string","span":{"start":[1176,1],"end":[1176,51],"bytes":[36312,36362]}},"ClientKey":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/ClientKey","kind":"field","type":"string","span":{"start":[1177,1],"end":[1177,50],"bytes":[36363,36412]}},"Concurrency":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Concurrency","kind":"field","type":"*int","span":{"start":[1139,1],"end":[1139,51],"bytes":[34952,35002]}},"ConcurrentWrites":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/ConcurrentWrites","kind":"field","type":"*bool","span":{"start":[1159,1],"end":[1159,52],"bytes":[35678,35729]}},"Creds":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Creds","kind":"field","type":"string","span":{"start":[1170,1],"end":[1170,45],"bytes":[36039,36083]}},"Endpoint":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Endpoint","kind":"field","type":"string","span":{"start":[1131,1],"end":[1131,48],"bytes":[34524,34571]}},"ForcePathStyle":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/ForcePathStyle","kind":"field","type":"*bool","span":{"start":[1132,1],"end":[1132,56],"bytes":[34572,34627]}},"Host":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Host","kind":"field","type":"string","span":{"start":[1155,1],"end":[1155,39],"bytes":[35514,35552]}},"HostKey":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/HostKey","kind":"field","type":"string","span":{"start":[1160,1],"end":[1160,43],"bytes":[35730,35772]}},"JWT":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/JWT","kind":"field","type":"string","span":{"start":[1168,1],"end":[1168,43],"bytes":[35952,35994]}},"KeyPath":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/KeyPath","kind":"field","type":"string","span":{"start":[1158,1],"end":[1158,43],"bytes":[35635,35677]}},"MaxReconnects":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/MaxReconnects","kind":"field","type":"*int","span":{"start":[1178,1],"end":[1178,54],"bytes":[36413,36466]}},"MaxSyncLTXFiles":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/MaxSyncLTXFiles","kind":"field","type":"*int","span":{"start":[1119,1],"end":[1119,50],"bytes":[33992,34041]}},"NKey":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/NKey","kind":"field","type":"string","span":{"start":[1171,1],"end":[1171,44],"bytes":[36084,36127]}},"PartSize":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/PartSize","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream.ByteSize","span":{"start":[1138,1],"end":[1138,49],"bytes":[34903,34951]}},"Password":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Password","kind":"field","type":"string","span":{"start":[1157,1],"end":[1157,43],"bytes":[35592,35634]}},"ReconnectWait":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/ReconnectWait","kind":"field","type":"*time.Duration","span":{"start":[1179,1],"end":[1179,54],"bytes":[36467,36520]}},"Region":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Region","kind":"field","type":"string","span":{"start":[1129,1],"end":[1129,46],"bytes":[34432,34477]}},"RequireContentMD5":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/RequireContentMD5","kind":"field","type":"*bool","span":{"start":[1135,1],"end":[1135,59],"bytes":[34740,34798]}},"RootCAs":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/RootCAs","kind":"field","type":"[]string","span":{"start":[1175,1],"end":[1175,48],"bytes":[36264,36311]}},"SASToken":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SASToken","kind":"field","type":"string","span":{"start":[1152,1],"end":[1152,39],"bytes":[35456,35494]}},"SSECustomerAlgorithm":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SSECustomerAlgorithm","kind":"field","type":"string","span":{"start":[1142,1],"end":[1142,61],"bytes":[35066,35126]}},"SSECustomerKey":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SSECustomerKey","kind":"field","type":"string","span":{"start":[1143,1],"end":[1143,55],"bytes":[35127,35181]}},"SSECustomerKeyPath":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SSECustomerKeyPath","kind":"field","type":"string","span":{"start":[1144,1],"end":[1144,60],"bytes":[35182,35241]}},"SSEKMSKeyID":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SSEKMSKeyID","kind":"field","type":"string","span":{"start":[1147,1],"end":[1147,44],"bytes":[35311,35354]}},"SecretAccessKey":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SecretAccessKey","kind":"field","type":"string","span":{"start":[1128,1],"end":[1128,57],"bytes":[34375,34431]}},"Seed":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Seed","kind":"field","type":"string","span":{"start":[1169,1],"end":[1169,44],"bytes":[35995,36038]}},"SignAcceptEncoding":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SignAcceptEncoding","kind":"field","type":"*bool","span":{"start":[1134,1],"end":[1134,60],"bytes":[34680,34739]}},"SignPayload":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SignPayload","kind":"field","type":"*bool","span":{"start":[1133,1],"end":[1133,52],"bytes":[34628,34679]}},"SkipVerify":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SkipVerify","kind":"field","type":"bool","span":{"start":[1136,1],"end":[1136,51],"bytes":[34799,34849]}},"StorageClass":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/StorageClass","kind":"field","type":"string","span":{"start":[1137,1],"end":[1137,53],"bytes":[34850,34902]}},"SyncInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/SyncInterval","kind":"field","type":"*time.Duration","span":{"start":[1114,1],"end":[1114,58],"bytes":[33748,33805]}},"TLS":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/TLS","kind":"field","type":"*bool","span":{"start":[1174,1],"end":[1174,43],"bytes":[36221,36263]}},"Timeout":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Timeout","kind":"field","type":"*time.Duration","span":{"start":[1180,1],"end":[1180,47],"bytes":[36521,36567]}},"Token":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Token","kind":"field","type":"string","span":{"start":[1173,1],"end":[1173,45],"bytes":[36176,36220]}},"User":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/User","kind":"field","type":"string","span":{"start":[1156,1],"end":[1156,39],"bytes":[35553,35591]}},"Username":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/Username","kind":"field","type":"string","span":{"start":[1172,1],"end":[1172,48],"bytes":[36128,36175]}},"ValidationInterval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/ValidationInterval","kind":"field","type":"*time.Duration","span":{"start":[1115,1],"end":[1115,64],"bytes":[33806,33869]}},"WebDAVPassword":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/WebDAVPassword","kind":"field","type":"string","span":{"start":[1165,1],"end":[1165,48],"bytes":[35885,35932]}},"WebDAVURL":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/WebDAVURL","kind":"field","type":"string","span":{"start":[1163,1],"end":[1163,43],"bytes":[35794,35836]}},"WebDAVUsername":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/WebDAVUsername","kind":"field","type":"string","span":{"start":[1164,1],"end":[1164,48],"bytes":[35837,35884]}}}},"RetentionConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.RetentionConfig","kind":"struct","span":{"start":[334,1],"end":[336,2],"bytes":[9619,9682]},"callables":{},"fields":{"Enabled":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.RetentionConfig/Enabled","kind":"field","type":"*bool","span":{"start":[335,1],"end":[335,32],"bytes":[9649,9680]}}}},"SnapshotConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.SnapshotConfig","kind":"struct","span":{"start":[328,1],"end":[331,2],"bytes":[9436,9555]},"callables":{},"fields":{"Interval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.SnapshotConfig/Interval","kind":"field","type":"*time.Duration","span":{"start":[329,1],"end":[329,44],"bytes":[9465,9508]}},"Retention":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.SnapshotConfig/Retention","kind":"field","type":"*time.Duration","span":{"start":[330,1],"end":[330,45],"bytes":[9509,9553]}}}},"ValidationConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ValidationConfig","kind":"struct","span":{"start":[339,1],"end":[341,2],"bytes":[9743,9818]},"callables":{},"fields":{"Interval":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ValidationConfig/Interval","kind":"field","type":"*time.Duration","span":{"start":[340,1],"end":[340,43],"bytes":[9774,9816]}}}},"boolSetting":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting","kind":"struct","span":{"start":[2067,1],"end":[2070,2],"bytes":[61282,61333]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault","kind":"method","span":{"start":[2081,1],"end":[2085,2],"bytes":[61506,61590]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault","parameters":[{"name":"value","type":"bool","span":{"start":[2081,1],"end":[2081,49],"bytes":[61506,61554]}}],"metrics":{"cyclomatic":2},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set","kind":"method","span":{"start":[2076,1],"end":[2079,2],"bytes":[61432,61504]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set","parameters":[{"name":"value","type":"bool","span":{"start":[2076,1],"end":[2076,40],"bytes":[61432,61471]}}],"metrics":{"cyclomatic":1},"body":{}}},"fields":{"set":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/set","kind":"field","type":"bool","span":{"start":[2069,1],"end":[2069,12],"bytes":[61320,61331]}},"value":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/value","kind":"field","type":"bool","span":{"start":[2068,1],"end":[2068,12],"bytes":[61308,61319]}}}},"levelVar":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.levelVar","kind":"struct","span":{"start":[2183,1],"end":[2183,18],"bytes":[64500,64517]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.levelVar.Set":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.levelVar/github.com/benbjohnson/litestream/cmd/litestream.levelVar.Set","kind":"method","span":{"start":[2194,1],"end":[2208,2],"bytes":[64665,65048]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.levelVar.Set","parameters":[{"name":"s","type":"string","span":{"start":[2194,1],"end":[2194,41],"bytes":[64665,64705]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"2199:12":{"kind":"call","span":{"start":[2199,12],"end":[2199,27],"bytes":[64766,64781]},"callee":null},"2201:10":{"kind":"call","span":{"start":[2201,10],"end":[2201,88],"bytes":[64808,64886]},"callee":null},"2204:10":{"kind":"call","span":{"start":[2204,10],"end":[2204,80],"bytes":[64943,65013]},"callee":null},"2206:7":{"kind":"call","span":{"start":[2206,7],"end":[2206,18],"bytes":[65023,65034]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.levelVar.String":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.levelVar/github.com/benbjohnson/litestream/cmd/litestream.levelVar.String","kind":"method","span":{"start":[2187,1],"end":[2192,2],"bytes":[64556,64663]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.levelVar.String","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{"2191:22":{"kind":"call","span":{"start":[2191,22],"end":[2191,29],"bytes":[64653,64660]},"callee":null},"2191:9":{"kind":"call","span":{"start":[2191,9],"end":[2191,30],"bytes":[64640,64661]},"callee":null}}}}},"txidVar":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.txidVar","kind":"struct","span":{"start":[2158,1],"end":[2158,22],"bytes":[63835,63856]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.txidVar.Set":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.txidVar/github.com/benbjohnson/litestream/cmd/litestream.txidVar.Set","kind":"method","span":{"start":[2169,1],"end":[2176,2],"bytes":[64110,64277]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.txidVar.Set","parameters":[{"name":"s","type":"string","span":{"start":[2169,1],"end":[2169,40],"bytes":[64110,64149]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"2170:15":{"kind":"call","span":{"start":[2170,15],"end":[2170,31],"bytes":[64164,64180]},"callee":null},"2172:10":{"kind":"call","span":{"start":[2172,10],"end":[2172,43],"bytes":[64207,64240]},"callee":null},"2174:7":{"kind":"call","span":{"start":[2174,7],"end":[2174,20],"bytes":[64250,64263]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.txidVar.String":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.txidVar/github.com/benbjohnson/litestream/cmd/litestream.txidVar.String","kind":"method","span":{"start":[2164,1],"end":[2166,2],"bytes":[63983,64050]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.txidVar.String","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{"2165:9":{"kind":"call","span":{"start":[2165,9],"end":[2165,21],"bytes":[64027,64039]},"callee":null}}}}},"usageError":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.usageError","kind":"struct","span":{"start":[107,1],"end":[110,2],"bytes":[3201,3259]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.usageError.Error":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.usageError/github.com/benbjohnson/litestream/cmd/litestream.usageError.Error","kind":"method","span":{"start":[112,1],"end":[114,2],"bytes":[3261,3318]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.usageError.Error","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}}},"fields":{"hint":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.usageError/hint","kind":"field","type":"string","span":{"start":[109,1],"end":[109,16],"bytes":[3242,3257]}},"message":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.usageError/message","kind":"field","type":"string","span":{"start":[108,1],"end":[108,16],"bytes":[3226,3241]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig","kind":"function","span":{"start":[393,1],"end":[416,2],"bytes":[11354,12395]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig","parameters":[],"return_type":"github.com/benbjohnson/litestream/cmd/litestream.Config","metrics":{"cyclomatic":1},"body":{"410:29":{"kind":"call","span":{"start":[410,29],"end":[410,61],"bytes":[12131,12163]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DefaultSocketConfig"}}},"github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","kind":"function","span":{"start":[2098,1],"end":[2103,2],"bytes":[61937,62063]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{"2099:10":{"kind":"call","span":{"start":[2099,10],"end":[2099,40],"bytes":[61980,62010]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases","kind":"function","span":{"start":[1014,1],"end":[1048,2],"bytes":[31194,31886]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases","parameters":[{"name":"dir","type":"string","span":{"start":[1014,1],"end":[1014,89],"bytes":[31194,31282]}},{"name":"pattern","type":"string","span":{"start":[1014,1],"end":[1014,89],"bytes":[31194,31282]}},{"name":"recursive","type":"bool","span":{"start":[1014,1],"end":[1014,89],"bytes":[31194,31282]}}],"return_type":"([]string, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"1017:9":{"kind":"call","span":{"start":[1017,9],"end":[1045,4],"bytes":[31314,31862]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases/github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases.closure_1","kind":"function","span":{"start":[1017,1],"end":[1045,4],"bytes":[31306,31862]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases.closure_1","parameters":[{"name":"path","type":"string","span":{"start":[1017,1],"end":[1017,82],"bytes":[31306,31387]}},{"name":"info","type":"os.FileInfo","span":{"start":[1017,1],"end":[1017,82],"bytes":[31306,31387]}},{"name":"err","type":"error","span":{"start":[1017,1],"end":[1017,82],"bytes":[31306,31387]}}],"return_type":"error","error_channel":["error"],"body":{"1023:6":{"kind":"call","span":{"start":[1023,6],"end":[1023,18],"bytes":[31469,31481]},"callee":null},"1031:19":{"kind":"call","span":{"start":[1031,19],"end":[1031,63],"bytes":[31623,31667]},"callee":null},"1031:43":{"kind":"call","span":{"start":[1031,43],"end":[1031,62],"bytes":[31647,31666]},"callee":null},"1040:6":{"kind":"call","span":{"start":[1040,6],"end":[1040,28],"bytes":[31781,31803]},"callee":null},"1041:14":{"kind":"call","span":{"start":[1041,14],"end":[1041,35],"bytes":[31819,31840]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.IsSQLiteDatabase":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.IsSQLiteDatabase","kind":"function","span":{"start":[1052,1],"end":[1066,2],"bytes":[31994,32312]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.IsSQLiteDatabase","parameters":[{"name":"path","type":"string","span":{"start":[1052,1],"end":[1052,42],"bytes":[31994,32035]}}],"return_type":"bool","metrics":{"cyclomatic":3},"body":{"1053:15":{"kind":"call","span":{"start":[1053,15],"end":[1053,28],"bytes":[32050,32063]},"callee":null},"1057:8":{"kind":"call","span":{"start":[1057,8],"end":[1057,20],"bytes":[32106,32118]},"callee":null},"1060:12":{"kind":"call","span":{"start":[1060,12],"end":[1060,28],"bytes":[32181,32197]},"callee":null},"1061:15":{"kind":"call","span":{"start":[1061,15],"end":[1061,32],"bytes":[32212,32229]},"callee":null},"1065:9":{"kind":"call","span":{"start":[1065,9],"end":[1065,23],"bytes":[32271,32285]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","kind":"function","span":{"start":[745,1],"end":[812,2],"bytes":[22735,24597]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","parameters":[{"name":"dbc","type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[745,1],"end":[745,62],"bytes":[22735,22796]}}],"return_type":"(*github.com/benbjohnson/litestream.DB, error)","error_channel":["error"],"metrics":{"cyclomatic":17},"body":{"747:15":{"kind":"call","span":{"start":[747,15],"end":[747,73],"bytes":[22836,22894]},"callee":null},"750:21":{"kind":"call","span":{"start":[750,21],"end":[750,37],"bytes":[22919,22935]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"756:8":{"kind":"call","span":{"start":[756,8],"end":[756,36],"bytes":[23023,23051]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB"},"760:28":{"kind":"call","span":{"start":[760,28],"end":[760,49],"bytes":[23176,23197]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"762:16":{"kind":"call","span":{"start":[762,16],"end":[762,65],"bytes":[23231,23280]},"callee":null},"765:3":{"kind":"call","span":{"start":[765,3],"end":[765,35],"bytes":[23322,23354]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetMetaPath"},"790:29":{"kind":"call","span":{"start":[790,29],"end":[790,46],"bytes":[24038,24055]},"callee":null},"791:15":{"kind":"call","span":{"start":[791,15],"end":[791,62],"bytes":[24076,24123]},"callee":null},"792:29":{"kind":"call","span":{"start":[792,29],"end":[792,46],"bytes":[24152,24169]},"callee":null},"793:15":{"kind":"call","span":{"start":[793,15],"end":[793,82],"bytes":[24189,24256]},"callee":null},"794:7":{"kind":"call","span":{"start":[794,7],"end":[794,24],"bytes":[24263,24280]},"callee":null},"795:15":{"kind":"call","span":{"start":[795,15],"end":[795,91],"bytes":[24300,24376]},"callee":null},"805:12":{"kind":"call","span":{"start":[805,12],"end":[805,40],"bytes":[24496,24524]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig"}}},"github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","kind":"function","span":{"start":[815,1],"end":[864,2],"bytes":[24703,26250]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","parameters":[{"name":"dbc","type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[815,1],"end":[815,74],"bytes":[24703,24776]}}],"return_type":"([]*github.com/benbjohnson/litestream.DB, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"817:15":{"kind":"call","span":{"start":[817,15],"end":[817,81],"bytes":[24811,24877]},"callee":null},"821:15":{"kind":"call","span":{"start":[821,15],"end":[821,74],"bytes":[24920,24979]},"callee":null},"824:15":{"kind":"call","span":{"start":[824,15],"end":[824,75],"bytes":[25045,25105]},"callee":null},"827:18":{"kind":"call","span":{"start":[827,18],"end":[827,33],"bytes":[25127,25142]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"833:18":{"kind":"call","span":{"start":[833,18],"end":[833,74],"bytes":[25246,25302]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases"},"835:15":{"kind":"call","span":{"start":[835,15],"end":[835,74],"bytes":[25334,25393]},"callee":null},"838:5":{"kind":"call","span":{"start":[838,5],"end":[838,17],"bytes":[25402,25414]},"callee":null},"839:15":{"kind":"call","span":{"start":[839,15],"end":[839,108],"bytes":[25450,25543]},"callee":null},"844:15":{"kind":"call","span":{"start":[844,15],"end":[844,38],"bytes":[25636,25659]},"callee":null},"847:14":{"kind":"call","span":{"start":[847,14],"end":[847,59],"bytes":[25708,25753]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry"},"849:16":{"kind":"call","span":{"start":[849,16],"end":[849,73],"bytes":[25787,25844]},"callee":null},"853:12":{"kind":"call","span":{"start":[853,12],"end":[853,25],"bytes":[25932,25945]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MetaPath"},"855:17":{"kind":"call","span":{"start":[855,17],"end":[855,158],"bytes":[26026,26167]},"callee":null},"860:9":{"kind":"call","span":{"start":[860,9],"end":[860,24],"bytes":[26212,26227]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.NewMain":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewMain","kind":"function","span":{"start":[120,1],"end":[122,2],"bytes":[3430,3470]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewMain","parameters":[],"return_type":"*github.com/benbjohnson/litestream/cmd/litestream.Main","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","kind":"function","span":{"start":[1350,1],"end":[1418,2],"bytes":[40262,42418]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1350,1],"end":[1350,100],"bytes":[40262,40361]}},{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[1350,1],"end":[1350,100],"bytes":[40262,40361]}}],"return_type":"(*github.com/benbjohnson/litestream.Replica, error)","error_channel":["error"],"metrics":{"cyclomatic":24},"body":{"1352:5":{"kind":"call","span":{"start":[1352,5],"end":[1352,29],"bytes":[40411,40435]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL"},"1353:15":{"kind":"call","span":{"start":[1353,15],"end":[1353,105],"bytes":[40452,40542]},"callee":null},"1361:34":{"kind":"call","span":{"start":[1361,34],"end":[1361,55],"bytes":[40936,40957]},"callee":null},"1361:5":{"kind":"call","span":{"start":[1361,5],"end":[1361,26],"bytes":[40907,40928]},"callee":null},"1362:15":{"kind":"call","span":{"start":[1362,15],"end":[1362,134],"bytes":[40978,41097]},"callee":null},"1366:7":{"kind":"call","span":{"start":[1366,7],"end":[1366,32],"bytes":[41127,41152]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica"},"1378:9":{"kind":"call","span":{"start":[1378,9],"end":[1378,24],"bytes":[41393,41408]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType"},"1380:22":{"kind":"call","span":{"start":[1380,22],"end":[1380,58],"bytes":[41446,41482]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig"},"1384:22":{"kind":"call","span":{"start":[1384,22],"end":[1384,56],"bytes":[41553,41587]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig"},"1388:22":{"kind":"call","span":{"start":[1388,22],"end":[1388,56],"bytes":[41658,41692]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig"},"1392:22":{"kind":"call","span":{"start":[1392,22],"end":[1392,57],"bytes":[41764,41799]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newABSReplicaClientFromConfig"},"1396:22":{"kind":"call","span":{"start":[1396,22],"end":[1396,58],"bytes":[41872,41908]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newSFTPReplicaClientFromConfig"},"1400:22":{"kind":"call","span":{"start":[1400,22],"end":[1400,60],"bytes":[41983,42021]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newWebDAVReplicaClientFromConfig"},"1404:22":{"kind":"call","span":{"start":[1404,22],"end":[1404,58],"bytes":[42094,42130]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig"},"1408:22":{"kind":"call","span":{"start":[1408,22],"end":[1408,57],"bytes":[42202,42237]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig"},"1412:15":{"kind":"call","span":{"start":[1412,15],"end":[1412,80],"bytes":[42299,42364]},"callee":null},"1412:64":{"kind":"call","span":{"start":[1412,64],"end":[1412,79],"bytes":[42348,42363]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType"},"1415:2":{"kind":"call","span":{"start":[1415,2],"end":[1415,32],"bytes":[42370,42400]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.SetLogger"},"1415:21":{"kind":"call","span":{"start":[1415,21],"end":[1415,31],"bytes":[42389,42399]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger"}}},"github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","kind":"function","span":{"start":[1453,1],"end":[1716,2],"bytes":[43507,51740]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1453,1],"end":[1453,110],"bytes":[43507,43616]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1453,1],"end":[1453,110],"bytes":[43507,43616]}}],"return_type":"(*github.com/benbjohnson/litestream/s3.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":53},"body":{"1456:15":{"kind":"call","span":{"start":[1456,15],"end":[1456,69],"bytes":[43724,43778]},"callee":null},"1458:15":{"kind":"call","span":{"start":[1458,15],"end":[1458,71],"bytes":[43836,43892]},"callee":null},"1464:17":{"kind":"call","span":{"start":[1464,17],"end":[1464,37],"bytes":[44053,44073]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting"},"1466:3":{"kind":"call","span":{"start":[1466,3],"end":[1466,22],"bytes":[44111,44130]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set"},"1468:31":{"kind":"call","span":{"start":[1468,31],"end":[1468,51],"bytes":[44164,44184]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting"},"1470:3":{"kind":"call","span":{"start":[1470,3],"end":[1470,36],"bytes":[44229,44262]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set"},"1472:20":{"kind":"call","span":{"start":[1472,20],"end":[1472,40],"bytes":[44285,44305]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting"},"1474:3":{"kind":"call","span":{"start":[1474,3],"end":[1474,25],"bytes":[44349,44371]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set"},"1504:36":{"kind":"call","span":{"start":[1504,36],"end":[1504,78],"bytes":[45149,45191]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery"},"1516:6":{"kind":"call","span":{"start":[1516,6],"end":[1516,37],"bytes":[45354,45385]},"callee":null},"1518:14":{"kind":"call","span":{"start":[1518,14],"end":[1518,46],"bytes":[45419,45451]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegionFromS3ARN"},"1520:51":{"kind":"call","span":{"start":[1520,51],"end":[1520,69],"bytes":[45513,45531]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost"},"1524:19":{"kind":"call","span":{"start":[1524,19],"end":[1524,40],"bytes":[45603,45624]},"callee":null},"1526:19":{"kind":"call","span":{"start":[1526,19],"end":[1526,61],"bytes":[45749,45791]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme"},"1529:7":{"kind":"call","span":{"start":[1529,7],"end":[1529,34],"bytes":[45903,45930]},"callee":null},"1534:17":{"kind":"call","span":{"start":[1534,17],"end":[1534,36],"bytes":[46021,46040]},"callee":null},"1537:25":{"kind":"call","span":{"start":[1537,25],"end":[1537,52],"bytes":[46107,46134]},"callee":null},"1540:21":{"kind":"call","span":{"start":[1540,21],"end":[1540,44],"bytes":[46231,46254]},"callee":null},"1543:15":{"kind":"call","span":{"start":[1543,15],"end":[1543,78],"bytes":[46332,46395]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"1547:15":{"kind":"call","span":{"start":[1547,15],"end":[1547,93],"bytes":[46466,46544]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"1551:15":{"kind":"call","span":{"start":[1551,15],"end":[1551,91],"bytes":[46629,46705]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"1555:11":{"kind":"call","span":{"start":[1555,11],"end":[1555,36],"bytes":[46784,46809]},"callee":null},"1557:18":{"kind":"call","span":{"start":[1557,18],"end":[1557,44],"bytes":[46859,46885]},"callee":null},"1560:20":{"kind":"call","span":{"start":[1560,20],"end":[1560,76],"bytes":[46941,46997]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue"},"1566:20":{"kind":"call","span":{"start":[1566,20],"end":[1566,66],"bytes":[47111,47157]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue"},"1593:4":{"kind":"call","span":{"start":[1593,4],"end":[1593,33],"bytes":[47684,47713]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set"},"1596:4":{"kind":"call","span":{"start":[1596,4],"end":[1596,54],"bytes":[47785,47835]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set"},"1599:4":{"kind":"call","span":{"start":[1599,4],"end":[1599,42],"bytes":[47895,47933]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set"},"1605:15":{"kind":"call","span":{"start":[1605,15],"end":[1605,59],"bytes":[48013,48057]},"callee":null},"1610:15":{"kind":"call","span":{"start":[1610,15],"end":[1610,53],"bytes":[48236,48274]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsHetznerEndpoint"},"1611:14":{"kind":"call","span":{"start":[1611,14],"end":[1611,51],"bytes":[48288,48325]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsTigrisEndpoint"},"1612:37":{"kind":"call","span":{"start":[1612,37],"end":[1612,76],"bytes":[48362,48401]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsTigrisEndpoint"},"1615:20":{"kind":"call","span":{"start":[1615,20],"end":[1615,63],"bytes":[48444,48487]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsDigitalOceanEndpoint"},"1616:17":{"kind":"call","span":{"start":[1616,17],"end":[1616,57],"bytes":[48504,48544]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsBackblazeEndpoint"},"1617:16":{"kind":"call","span":{"start":[1617,16],"end":[1617,55],"bytes":[48560,48599]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsFilebaseEndpoint"},"1618:16":{"kind":"call","span":{"start":[1618,16],"end":[1618,55],"bytes":[48615,48654]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsScalewayEndpoint"},"1619:13":{"kind":"call","span":{"start":[1619,13],"end":[1619,49],"bytes":[48667,48703]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsMinIOEndpoint"},"1620:20":{"kind":"call","span":{"start":[1620,20],"end":[1620,63],"bytes":[48723,48766]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsCloudflareR2Endpoint"},"1621:16":{"kind":"call","span":{"start":[1621,16],"end":[1621,55],"bytes":[48782,48821]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsSupabaseEndpoint"},"1630:3":{"kind":"call","span":{"start":[1630,3],"end":[1630,33],"bytes":[49163,49193]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault"},"1631:3":{"kind":"call","span":{"start":[1631,3],"end":[1631,37],"bytes":[49196,49230]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault"},"1635:3":{"kind":"call","span":{"start":[1635,3],"end":[1635,33],"bytes":[49438,49468]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault"},"1645:12":{"kind":"call","span":{"start":[1645,12],"end":[1645,33],"bytes":[49691,49712]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient"},"1673:24":{"kind":"call","span":{"start":[1673,24],"end":[1673,41],"bytes":[50508,50525]},"callee":null},"1676:21":{"kind":"call","span":{"start":[1676,21],"end":[1676,39],"bytes":[50573,50591]},"callee":null},"1693:7":{"kind":"call","span":{"start":[1693,7],"end":[1693,38],"bytes":[51092,51123]},"callee":null},"1694:18":{"kind":"call","span":{"start":[1694,18],"end":[1694,34],"bytes":[51143,51159]},"callee":null},"1696:18":{"kind":"call","span":{"start":[1696,18],"end":[1696,95],"bytes":[51197,51274]},"callee":null},"1700:20":{"kind":"call","span":{"start":[1700,20],"end":[1700,40],"bytes":[51338,51358]},"callee":null},"1702:17":{"kind":"call","span":{"start":[1702,17],"end":[1702,98],"bytes":[51394,51475]},"callee":null},"1704:28":{"kind":"call","span":{"start":[1704,28],"end":[1704,62],"bytes":[51508,51542]},"callee":null},"1704:46":{"kind":"call","span":{"start":[1704,46],"end":[1704,61],"bytes":[51526,51541]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.OpenConfigFile":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.OpenConfigFile","kind":"function","span":{"start":[598,1],"end":[614,2],"bytes":[18049,18433]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.OpenConfigFile","parameters":[{"name":"filename","type":"string","span":{"start":[598,1],"end":[598,62],"bytes":[18049,18110]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"600:19":{"kind":"call","span":{"start":[600,19],"end":[600,35],"bytes":[18164,18180]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"606:12":{"kind":"call","span":{"start":[606,12],"end":[606,29],"bytes":[18260,18277]},"callee":null},"607:5":{"kind":"call","span":{"start":[607,5],"end":[607,23],"bytes":[18282,18300]},"callee":null},"608:15":{"kind":"call","span":{"start":[608,15],"end":[608,68],"bytes":[18317,18370]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ParseByteSize":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseByteSize","kind":"function","span":{"start":[1091,1],"end":[1109,2],"bytes":[33075,33569]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ParseByteSize","parameters":[{"name":"s","type":"string","span":{"start":[1091,1],"end":[1091,46],"bytes":[33075,33120]}}],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1092:6":{"kind":"call","span":{"start":[1092,6],"end":[1092,26],"bytes":[33126,33146]},"callee":null},"1094:13":{"kind":"call","span":{"start":[1094,13],"end":[1094,44],"bytes":[33173,33204]},"callee":null},"1098:16":{"kind":"call","span":{"start":[1098,16],"end":[1098,38],"bytes":[33274,33296]},"callee":null},"1100:13":{"kind":"call","span":{"start":[1100,13],"end":[1100,55],"bytes":[33326,33368]},"callee":null},"1105:13":{"kind":"call","span":{"start":[1105,13],"end":[1105,98],"bytes":[33452,33537]},"callee":null},"1105:77":{"kind":"call","span":{"start":[1105,77],"end":[1105,97],"bytes":[33516,33536]},"callee":null},"1108:9":{"kind":"call","span":{"start":[1108,9],"end":[1108,21],"bytes":[33550,33562]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ParseConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","kind":"function","span":{"start":[630,1],"end":[714,2],"bytes":[18928,21265]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","parameters":[{"name":"r","type":"io.Reader","span":{"start":[630,1],"end":[630,70],"bytes":[18928,18997]}},{"name":"expandEnv","type":"bool","span":{"start":[630,1],"end":[630,70],"bytes":[18928,18997]}}],"return_type":"(github.com/benbjohnson/litestream/cmd/litestream.Config, error)","error_channel":["error"],"metrics":{"cyclomatic":17},"body":{"631:12":{"kind":"call","span":{"start":[631,12],"end":[631,27],"bytes":[19009,19024]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig"},"634:14":{"kind":"call","span":{"start":[634,14],"end":[634,27],"bytes":[19063,19076]},"callee":null},"641:16":{"kind":"call","span":{"start":[641,16],"end":[646,5],"bytes":[19196,19337]},"callee":null},"641:26":{"kind":"call","span":{"start":[641,26],"end":[641,37],"bytes":[19206,19217]},"callee":null},"641:9":{"kind":"call","span":{"start":[641,9],"end":[646,6],"bytes":[19189,19338]},"callee":null},"652:12":{"kind":"call","span":{"start":[652,12],"end":[652,37],"bytes":[19420,19445]},"callee":null},"664:12":{"kind":"call","span":{"start":[664,12],"end":[664,40],"bytes":[19919,19947]},"callee":null},"681:12":{"kind":"call","span":{"start":[681,12],"end":[681,95],"bytes":[20460,20543]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.applyDBSnapshotConfig"},"690:27":{"kind":"call","span":{"start":[690,27],"end":[690,48],"bytes":[20712,20733]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"696:2":{"kind":"call","span":{"start":[696,2],"end":[696,34],"bytes":[20844,20876]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.propagateGlobalSettings"},"699:12":{"kind":"call","span":{"start":[699,12],"end":[699,29],"bytes":[20916,20933]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.Validate"},"708:10":{"kind":"call","span":{"start":[708,10],"end":[708,32],"bytes":[21084,21106]},"callee":null},"711:2":{"kind":"call","span":{"start":[711,2],"end":[711,95],"bytes":[21149,21242]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig.closure_1","kind":"function","span":{"start":[641,1],"end":[646,6],"bytes":[19181,19338]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ParseConfig.closure_1","parameters":[{"name":"key","type":"string","span":{"start":[641,1],"end":[641,64],"bytes":[19181,19244]}}],"return_type":"string","body":{"643:12":{"kind":"call","span":{"start":[643,12],"end":[643,37],"bytes":[19277,19302]},"callee":null},"643:25":{"kind":"call","span":{"start":[643,25],"end":[643,36],"bytes":[19290,19301]},"callee":null},"645:11":{"kind":"call","span":{"start":[645,11],"end":[645,25],"bytes":[19318,19332]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","kind":"function","span":{"start":[618,1],"end":[626,2],"bytes":[18589,18799]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","parameters":[{"name":"filename","type":"string","span":{"start":[618,1],"end":[618,71],"bytes":[18589,18659]}},{"name":"expandEnv","type":"bool","span":{"start":[618,1],"end":[618,71],"bytes":[18589,18659]}}],"return_type":"(github.com/benbjohnson/litestream/cmd/litestream.Config, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"619:12":{"kind":"call","span":{"start":[619,12],"end":[619,36],"bytes":[18671,18695]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.OpenConfigFile"},"621:10":{"kind":"call","span":{"start":[621,10],"end":[621,25],"bytes":[18722,18737]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig"},"623:8":{"kind":"call","span":{"start":[623,8],"end":[623,17],"bytes":[18753,18762]},"callee":null},"625:9":{"kind":"call","span":{"start":[625,9],"end":[625,34],"bytes":[18772,18797]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig"}}},"github.com/benbjohnson/litestream/cmd/litestream.StripSQLitePrefix":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.StripSQLitePrefix","kind":"function","span":{"start":[2145,1],"end":[2155,2],"bytes":[63510,63754]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StripSQLitePrefix","parameters":[{"name":"s","type":"string","span":{"start":[2145,1],"end":[2145,42],"bytes":[63510,63551]}}],"return_type":"string","metrics":{"cyclomatic":4},"body":{"2146:5":{"kind":"call","span":{"start":[2146,5],"end":[2146,11],"bytes":[63556,63562]},"callee":null},"2150:6":{"kind":"call","span":{"start":[2150,6],"end":[2150,34],"bytes":[63665,63693]},"callee":null},"2151:11":{"kind":"call","span":{"start":[2151,11],"end":[2151,40],"bytes":[63706,63735]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.appendRelativePathToURL":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.appendRelativePathToURL","kind":"function","span":{"start":[994,1],"end":[1010,2],"bytes":[30725,31084]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.appendRelativePathToURL","parameters":[{"name":"u","type":"*net/url.URL","span":{"start":[994,1],"end":[994,59],"bytes":[30725,30783]}},{"name":"relPath","type":"string","span":{"start":[994,1],"end":[994,59],"bytes":[30725,30783]}}],"metrics":{"cyclomatic":3},"body":{"1005:12":{"kind":"call","span":{"start":[1005,12],"end":[1005,41],"bytes":[30984,31013]},"callee":null},"1008:17":{"kind":"call","span":{"start":[1008,17],"end":[1008,48],"bytes":[31034,31065]},"callee":null},"995:14":{"kind":"call","span":{"start":[995,14],"end":[995,46],"bytes":[30797,30829]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv","kind":"function","span":{"start":[2054,1],"end":[2065,2],"bytes":[60921,61280]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv","parameters":[],"metrics":{"cyclomatic":5},"body":{"2055:14":{"kind":"call","span":{"start":[2055,14],"end":[2055,54],"bytes":[60962,61002]},"callee":null},"2056:15":{"kind":"call","span":{"start":[2056,15],"end":[2056,48],"bytes":[61023,61056]},"callee":null},"2057:4":{"kind":"call","span":{"start":[2057,4],"end":[2057,37],"bytes":[61067,61100]},"callee":null},"2060:14":{"kind":"call","span":{"start":[2060,14],"end":[2060,58],"bytes":[61121,61165]},"callee":null},"2061:15":{"kind":"call","span":{"start":[2061,15],"end":[2061,52],"bytes":[61186,61223]},"callee":null},"2062:4":{"kind":"call","span":{"start":[2062,4],"end":[2062,41],"bytes":[61234,61271]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath","kind":"function","span":{"start":[932,1],"end":[972,2],"bytes":[28919,29940]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath","parameters":[{"name":"base","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[932,1],"end":[932,103],"bytes":[28919,29021]}},{"name":"relPath","type":"string","span":{"start":[932,1],"end":[932,103],"bytes":[28919,29021]}}],"return_type":"(*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig, error)","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"938:12":{"kind":"call","span":{"start":[938,12],"end":[938,37],"bytes":[29095,29120]},"callee":null},"944:13":{"kind":"call","span":{"start":[944,13],"end":[944,39],"bytes":[29230,29256]},"callee":null},"946:16":{"kind":"call","span":{"start":[946,16],"end":[946,56],"bytes":[29290,29330]},"callee":null},"948:3":{"kind":"call","span":{"start":[948,3],"end":[948,38],"bytes":[29337,29372]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.appendRelativePathToURL"},"949:21":{"kind":"call","span":{"start":[949,21],"end":[949,31],"bytes":[29393,29403]},"callee":null},"953:9":{"kind":"call","span":{"start":[953,9],"end":[953,27],"bytes":[29443,29461]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType"},"955:16":{"kind":"call","span":{"start":[955,16],"end":[955,43],"bytes":[29493,29520]},"callee":null},"957:23":{"kind":"call","span":{"start":[957,23],"end":[957,65],"bytes":[29573,29615]},"callee":null},"963:15":{"kind":"call","span":{"start":[963,15],"end":[963,49],"bytes":[29756,29790]},"callee":null},"965:23":{"kind":"call","span":{"start":[965,23],"end":[965,51],"bytes":[29835,29863]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.deriveMetaPathForDirectoryEntry":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.deriveMetaPathForDirectoryEntry","kind":"function","span":{"start":[977,1],"end":[990,2],"bytes":[30181,30587]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.deriveMetaPathForDirectoryEntry","parameters":[{"name":"basePath","type":"string","span":{"start":[977,1],"end":[977,72],"bytes":[30181,30252]}},{"name":"relPath","type":"string","span":{"start":[977,1],"end":[977,72],"bytes":[30181,30252]}}],"return_type":"string","metrics":{"cyclomatic":3},"body":{"978:12":{"kind":"call","span":{"start":[978,12],"end":[978,35],"bytes":[30264,30287]},"callee":null},"983:21":{"kind":"call","span":{"start":[983,21],"end":[983,44],"bytes":[30368,30391]},"callee":null},"985:10":{"kind":"call","span":{"start":[985,10],"end":[985,42],"bytes":[30439,30471]},"callee":null},"989:9":{"kind":"call","span":{"start":[989,9],"end":[989,53],"bytes":[30541,30585]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.expand":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","kind":"function","span":{"start":[2117,1],"end":[2140,2],"bytes":[62581,63267]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.expand","parameters":[{"name":"s","type":"string","span":{"start":[2117,1],"end":[2117,40],"bytes":[62581,62620]}}],"return_type":"(string, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"2119:6":{"kind":"call","span":{"start":[2119,6],"end":[2119,26],"bytes":[62682,62702]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.StripSQLitePrefix"},"2122:18":{"kind":"call","span":{"start":[2122,18],"end":[2122,42],"bytes":[62792,62816]},"callee":null},"2123:18":{"kind":"call","span":{"start":[2123,18],"end":[2123,46],"bytes":[62834,62862]},"callee":null},"2124:10":{"kind":"call","span":{"start":[2124,10],"end":[2124,25],"bytes":[62874,62889]},"callee":null},"2128:12":{"kind":"call","span":{"start":[2128,12],"end":[2128,26],"bytes":[62933,62947]},"callee":null},"2132:14":{"kind":"call","span":{"start":[2132,14],"end":[2132,81],"bytes":[63024,63091]},"callee":null},"2139:34":{"kind":"call","span":{"start":[2139,34],"end":[2139,63],"bytes":[63230,63259]},"callee":null},"2139:9":{"kind":"call","span":{"start":[2139,9],"end":[2139,64],"bytes":[63205,63260]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.init":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.init","kind":"function","span":{"start":[43,1],"end":[46,2],"bytes":[937,1023]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"44:11":{"kind":"call","span":{"start":[44,11],"end":[44,32],"bytes":[961,982]},"callee":null},"45:12":{"kind":"call","span":{"start":[45,12],"end":[45,39],"bytes":[994,1021]},"callee":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion"}}},"github.com/benbjohnson/litestream/cmd/litestream.isValidHeartbeatURL":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.isValidHeartbeatURL","kind":"function","span":{"start":[2111,1],"end":[2113,2],"bytes":[62341,62460]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.isValidHeartbeatURL","parameters":[{"name":"u","type":"string","span":{"start":[2111,1],"end":[2111,42],"bytes":[62341,62382]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"2112:44":{"kind":"call","span":{"start":[2112,44],"end":[2112,76],"bytes":[62426,62458]},"callee":null},"2112:9":{"kind":"call","span":{"start":[2112,9],"end":[2112,40],"bytes":[62391,62422]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.main":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.main","kind":"function","span":{"start":[84,1],"end":[105,2],"bytes":[2611,3199]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.main","parameters":[],"metrics":{"cyclomatic":7},"body":{"100:6":{"kind":"call","span":{"start":[100,6],"end":[100,31],"bytes":[3066,3091]},"callee":null},"101:11":{"kind":"call","span":{"start":[101,11],"end":[101,61],"bytes":[3127,3177]},"callee":null},"103:3":{"kind":"call","span":{"start":[103,3],"end":[103,13],"bytes":[3184,3194]},"callee":null},"85:2":{"kind":"call","span":{"start":[85,2],"end":[85,52],"bytes":[2626,2676]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"},"87:7":{"kind":"call","span":{"start":[87,7],"end":[87,16],"bytes":[2684,2693]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewMain"},"88:12":{"kind":"call","span":{"start":[88,12],"end":[88,52],"bytes":[2705,2745]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run"},"88:18":{"kind":"call","span":{"start":[88,18],"end":[88,38],"bytes":[2711,2731]},"callee":null},"88:54":{"kind":"call","span":{"start":[88,54],"end":[88,77],"bytes":[2747,2770]},"callee":null},"89:3":{"kind":"call","span":{"start":[89,3],"end":[89,13],"bytes":[2775,2785]},"callee":null},"90:12":{"kind":"call","span":{"start":[90,12],"end":[90,40],"bytes":[2797,2825]},"callee":null},"93:5":{"kind":"call","span":{"start":[93,5],"end":[93,15],"bytes":[2925,2935]},"callee":null},"96:3":{"kind":"call","span":{"start":[96,3],"end":[96,13],"bytes":[2947,2957]},"callee":null},"98:10":{"kind":"call","span":{"start":[98,10],"end":[98,52],"bytes":[2991,3033]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.newABSReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newABSReplicaClientFromConfig","kind":"function","span":{"start":[1758,1],"end":[1799,2],"bytes":[52933,54086]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newABSReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1758,1],"end":[1758,112],"bytes":[52933,53044]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1758,1],"end":[1758,112],"bytes":[52933,53044]}}],"return_type":"(*github.com/benbjohnson/litestream/abs.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"1761:15":{"kind":"call","span":{"start":[1761,15],"end":[1761,70],"bytes":[53152,53207]},"callee":null},"1763:15":{"kind":"call","span":{"start":[1763,15],"end":[1763,72],"bytes":[53265,53322]},"callee":null},"1767:12":{"kind":"call","span":{"start":[1767,12],"end":[1767,34],"bytes":[53357,53379]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient"},"1777:13":{"kind":"call","span":{"start":[1777,13],"end":[1777,29],"bytes":[53632,53648]},"callee":null},"1783:25":{"kind":"call","span":{"start":[1783,25],"end":[1783,42],"bytes":[53764,53781]},"callee":null},"1789:18":{"kind":"call","span":{"start":[1789,18],"end":[1789,61],"bytes":[53885,53928]},"callee":null},"1789:37":{"kind":"call","span":{"start":[1789,37],"end":[1789,55],"bytes":[53904,53922]},"callee":null},"1795:15":{"kind":"call","span":{"start":[1795,15],"end":[1795,60],"bytes":[54015,54060]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting","kind":"function","span":{"start":[2072,1],"end":[2074,2],"bytes":[61335,61430]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting","parameters":[{"name":"defaultValue","type":"bool","span":{"start":[2072,1],"end":[2072,53],"bytes":[61335,61387]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream.boolSetting","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","kind":"function","span":{"start":[867,1],"end":[927,2],"bytes":[26354,28708]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","parameters":[{"name":"dbc","type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[867,1],"end":[867,94],"bytes":[26354,26447]}},{"name":"dirPath","type":"string","span":{"start":[867,1],"end":[867,94],"bytes":[26354,26447]}},{"name":"dbPath","type":"string","span":{"start":[867,1],"end":[867,94],"bytes":[26354,26447]}}],"return_type":"(*github.com/benbjohnson/litestream.DB, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"869:18":{"kind":"call","span":{"start":[869,18],"end":[869,47],"bytes":[26513,26542]},"callee":null},"871:15":{"kind":"call","span":{"start":[871,15],"end":[871,86],"bytes":[26574,26645]},"callee":null},"887:23":{"kind":"call","span":{"start":[887,23],"end":[887,43],"bytes":[27269,27289]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"889:16":{"kind":"call","span":{"start":[889,16],"end":[889,79],"bytes":[27323,27386]},"callee":null},"891:19":{"kind":"call","span":{"start":[891,19],"end":[891,79],"bytes":[27409,27469]},"callee":null},"895:24":{"kind":"call","span":{"start":[895,24],"end":[895,45],"bytes":[27589,27610]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"897:16":{"kind":"call","span":{"start":[897,16],"end":[897,80],"bytes":[27644,27708]},"callee":null},"899:19":{"kind":"call","span":{"start":[899,19],"end":[899,73],"bytes":[27731,27785]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.deriveMetaPathForDirectoryEntry"},"907:23":{"kind":"call","span":{"start":[907,23],"end":[907,79],"bytes":[28041,28097]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath"},"909:16":{"kind":"call","span":{"start":[909,16],"end":[909,81],"bytes":[28131,28196]},"callee":null},"915:5":{"kind":"call","span":{"start":[915,5],"end":[915,22],"bytes":[28297,28314]},"callee":null},"916:27":{"kind":"call","span":{"start":[916,27],"end":[916,68],"bytes":[28347,28388]},"callee":null},"916:50":{"kind":"call","span":{"start":[916,50],"end":[916,67],"bytes":[28370,28387]},"callee":null},"918:24":{"kind":"call","span":{"start":[918,24],"end":[918,76],"bytes":[28453,28505]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath"},"920:17":{"kind":"call","span":{"start":[920,17],"end":[920,88],"bytes":[28541,28612]},"callee":null},"926:9":{"kind":"call","span":{"start":[926,9],"end":[926,39],"bytes":[28676,28706]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"}}},"github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig","kind":"function","span":{"start":[1421,1],"end":[1449,2],"bytes":[42518,43386]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1421,1],"end":[1421,114],"bytes":[42518,42631]}},{"name":"r","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1421,1],"end":[1421,114],"bytes":[42518,42631]}}],"return_type":"(*github.com/benbjohnson/litestream/file.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"1424:15":{"kind":"call","span":{"start":[1424,15],"end":[1424,71],"bytes":[42726,42782]},"callee":null},"1430:30":{"kind":"call","span":{"start":[1430,30],"end":[1430,63],"bytes":[42901,42934]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL"},"1437:15":{"kind":"call","span":{"start":[1437,15],"end":[1437,55],"bytes":[43074,43114]},"callee":null},"1441:23":{"kind":"call","span":{"start":[1441,23],"end":[1441,41],"bytes":[43190,43208]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"1446:12":{"kind":"call","span":{"start":[1446,12],"end":[1446,45],"bytes":[43311,43344]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient"}}},"github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig","kind":"function","span":{"start":[1719,1],"end":[1755,2],"bytes":[51836,52835]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1719,1],"end":[1719,110],"bytes":[51836,51945]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1719,1],"end":[1719,110],"bytes":[51836,51945]}}],"return_type":"(*github.com/benbjohnson/litestream/gs.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"1722:15":{"kind":"call","span":{"start":[1722,15],"end":[1722,69],"bytes":[52053,52107]},"callee":null},"1724:15":{"kind":"call","span":{"start":[1724,15],"end":[1724,71],"bytes":[52165,52221]},"callee":null},"1731:27":{"kind":"call","span":{"start":[1731,27],"end":[1731,60],"bytes":[52354,52387]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL"},"1747:15":{"kind":"call","span":{"start":[1747,15],"end":[1747,59],"bytes":[52663,52707]},"callee":null},"1751:12":{"kind":"call","span":{"start":[1751,12],"end":[1751,33],"bytes":[52742,52763]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient"}}},"github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig","kind":"function","span":{"start":[1913,1],"end":[1986,2],"bytes":[57244,59128]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1913,1],"end":[1913,114],"bytes":[57244,57357]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1913,1],"end":[1913,114],"bytes":[57244,57357]}}],"return_type":"(*github.com/benbjohnson/litestream/nats.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"1917:36":{"kind":"call","span":{"start":[1917,36],"end":[1917,69],"bytes":[57499,57532]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL"},"1919:16":{"kind":"call","span":{"start":[1919,16],"end":[1919,55],"bytes":[57566,57605]},"callee":null},"1922:16":{"kind":"call","span":{"start":[1922,16],"end":[1922,73],"bytes":[57649,57706]},"callee":null},"1927:10":{"kind":"call","span":{"start":[1927,10],"end":[1927,40],"bytes":[57780,57810]},"callee":null},"1932:13":{"kind":"call","span":{"start":[1932,13],"end":[1932,42],"bytes":[57887,57916]},"callee":null},"1943:15":{"kind":"call","span":{"start":[1943,15],"end":[1943,61],"bytes":[58091,58137]},"callee":null},"1949:15":{"kind":"call","span":{"start":[1949,15],"end":[1949,108],"bytes":[58293,58386]},"callee":null},"1953:12":{"kind":"call","span":{"start":[1953,12],"end":[1953,35],"bytes":[58427,58450]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient"}}},"github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig","kind":"function","span":{"start":[1989,1],"end":[2049,2],"bytes":[59226,60733]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1989,1],"end":[1989,112],"bytes":[59226,59337]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1989,1],"end":[1989,112],"bytes":[59226,59337]}}],"return_type":"(*github.com/benbjohnson/litestream/oss.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"1992:15":{"kind":"call","span":{"start":[1992,15],"end":[1992,70],"bytes":[59445,59500]},"callee":null},"1994:15":{"kind":"call","span":{"start":[1994,15],"end":[1994,72],"bytes":[59558,59615]},"callee":null},"2002:26":{"kind":"call","span":{"start":[2002,26],"end":[2002,59],"bytes":[59789,59822]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL"},"2012:25":{"kind":"call","span":{"start":[2012,25],"end":[2012,44],"bytes":[59938,59957]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost"},"2028:15":{"kind":"call","span":{"start":[2028,15],"end":[2028,60],"bytes":[60239,60284]},"callee":null},"2032:12":{"kind":"call","span":{"start":[2032,12],"end":[2032,34],"bytes":[60326,60348]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient"},"2042:21":{"kind":"call","span":{"start":[2042,21],"end":[2042,39],"bytes":[60621,60639]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.newSFTPReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newSFTPReplicaClientFromConfig","kind":"function","span":{"start":[1802,1],"end":[1856,2],"bytes":[54186,55646]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newSFTPReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1802,1],"end":[1802,114],"bytes":[54186,54299]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1802,1],"end":[1802,114],"bytes":[54186,54299]}}],"return_type":"(*github.com/benbjohnson/litestream/sftp.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"1805:15":{"kind":"call","span":{"start":[1805,15],"end":[1805,71],"bytes":[54407,54463]},"callee":null},"1807:15":{"kind":"call","span":{"start":[1807,15],"end":[1807,71],"bytes":[54519,54575]},"callee":null},"1814:13":{"kind":"call","span":{"start":[1814,13],"end":[1814,29],"bytes":[54720,54736]},"callee":null},"1824:11":{"kind":"call","span":{"start":[1824,11],"end":[1824,28],"bytes":[54929,54946]},"callee":null},"1827:18":{"kind":"call","span":{"start":[1827,18],"end":[1827,35],"bytes":[55007,55024]},"callee":null},"1836:15":{"kind":"call","span":{"start":[1836,15],"end":[1836,59],"bytes":[55141,55185]},"callee":null},"1838:15":{"kind":"call","span":{"start":[1838,15],"end":[1838,59],"bytes":[55224,55268]},"callee":null},"1842:12":{"kind":"call","span":{"start":[1842,12],"end":[1842,35],"bytes":[55303,55326]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient"}}},"github.com/benbjohnson/litestream/cmd/litestream.newWebDAVReplicaClientFromConfig":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newWebDAVReplicaClientFromConfig","kind":"function","span":{"start":[1859,1],"end":[1910,2],"bytes":[55750,57144]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.newWebDAVReplicaClientFromConfig","parameters":[{"name":"c","type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig","span":{"start":[1859,1],"end":[1859,118],"bytes":[55750,55867]}},{"name":"_","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[1859,1],"end":[1859,118],"bytes":[55750,55867]}}],"return_type":"(*github.com/benbjohnson/litestream/webdav.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"1862:15":{"kind":"call","span":{"start":[1862,15],"end":[1862,73],"bytes":[55975,56033]},"callee":null},"1864:15":{"kind":"call","span":{"start":[1864,15],"end":[1864,79],"bytes":[56094,56158]},"callee":null},"1871:13":{"kind":"call","span":{"start":[1871,13],"end":[1871,29],"bytes":[56333,56349]},"callee":null},"1882:16":{"kind":"call","span":{"start":[1882,16],"end":[1882,54],"bytes":[56561,56599]},"callee":null},"1887:15":{"kind":"call","span":{"start":[1887,15],"end":[1887,32],"bytes":[56692,56709]},"callee":null},"1890:18":{"kind":"call","span":{"start":[1890,18],"end":[1890,35],"bytes":[56770,56787]},"callee":null},"1899:15":{"kind":"call","span":{"start":[1899,15],"end":[1899,67],"bytes":[56909,56961]},"callee":null},"1903:12":{"kind":"call","span":{"start":[1903,12],"end":[1903,37],"bytes":[56996,57021]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient"}}},"github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag":{"id":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","kind":"function","span":{"start":[2105,1],"end":[2108,2],"bytes":[62065,62268]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","parameters":[{"name":"fs","type":"*flag.FlagSet","span":{"start":[2105,1],"end":[2105,84],"bytes":[62065,62148]}}],"return_type":"(*string, *bool)","metrics":{"cyclomatic":1},"body":{"2106:9":{"kind":"call","span":{"start":[2106,9],"end":[2106,47],"bytes":[62157,62195]},"callee":null},"2107:3":{"kind":"call","span":{"start":[2107,3],"end":[2107,70],"bytes":[62199,62266]},"callee":null}}}},"content_hash":"51dcf1279e9042b62f269d2b622f9f7fcf39650907d9e2c10f654c2b328a4ff6"},"cmd/litestream/main_notwindows.go":{"id":"can://go/litestream/cmd/litestream/main_notwindows.go","kind":"module","span":{"start":[1,1],"end":[27,1],"bytes":[0,440]},"package":"main","source":"//go:build !windows\n\npackage main\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nconst defaultConfigPath = \"/etc/litestream.yml\"\n\nfunc isWindowsService() (bool, error) {\n\treturn false, nil\n}\n\nfunc runWindowsService(ctx context.Context) error {\n\tpanic(\"cannot run windows service as unix process\")\n}\n\nfunc signalChan() \u003c-chan os.Signal {\n\tch := make(chan os.Signal, 2)\n\tsignal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)\n\treturn ch\n}\n","imports":[{"name":"context","path":"context","span":{"start":[6,1],"end":[6,11],"bytes":[44,54]}},{"name":"os","path":"os","span":{"start":[7,1],"end":[7,6],"bytes":[55,60]}},{"name":"signal","path":"os/signal","span":{"start":[8,1],"end":[8,13],"bytes":[61,73]}},{"name":"syscall","path":"syscall","span":{"start":[9,1],"end":[9,11],"bytes":[74,84]}}],"types":{},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.isWindowsService":{"id":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.isWindowsService","kind":"function","span":{"start":[14,1],"end":[16,2],"bytes":[137,197]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.isWindowsService","parameters":[],"return_type":"(bool, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream.runWindowsService":{"id":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.runWindowsService","kind":"function","span":{"start":[18,1],"end":[20,2],"bytes":[199,305]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.runWindowsService","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[18,1],"end":[18,52],"bytes":[199,250]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"19:2":{"kind":"call","span":{"start":[19,2],"end":[19,53],"bytes":[252,303]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.signalChan":{"id":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.signalChan","kind":"function","span":{"start":[22,1],"end":[26,2],"bytes":[307,439]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.signalChan","parameters":[],"return_type":"\u003c-chan os.Signal","metrics":{"cyclomatic":1},"body":{"23:8":{"kind":"call","span":{"start":[23,8],"end":[23,31],"bytes":[351,374]},"callee":null},"24:2":{"kind":"call","span":{"start":[24,2],"end":[24,52],"bytes":[376,426]},"callee":null}}}},"content_hash":"63ed2fa53604bc509016c51dbc1552a4e4618d1a981a15c1301942a52a501bf3"},"cmd/litestream/mcp.go":{"id":"can://go/litestream/cmd/litestream/mcp.go","kind":"module","span":{"start":[1,1],"end":[347,1],"bytes":[0,12226]},"package":"main","source":"package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/MadAppGang/httplog\"\n\t\"github.com/mark3labs/mcp-go/mcp\"\n\t\"github.com/mark3labs/mcp-go/server\"\n)\n\ntype MCPServer struct {\n\tctx context.Context\n\tmux *http.ServeMux\n\thttpServer *http.Server\n\tconfigPath string\n}\n\nfunc NewMCP(ctx context.Context, configPath string) (*MCPServer, error) {\n\ts := \u0026MCPServer{\n\t\tctx: ctx,\n\t\tconfigPath: configPath,\n\t}\n\n\tmcpServer := server.NewMCPServer(\n\t\t\"Litestream MCP Server\",\n\t\t\"1.0.0\",\n\t\tserver.WithToolCapabilities(false),\n\t\tserver.WithRecovery(),\n\t\tserver.WithLogging(),\n\t)\n\t// Add the tools to the server\n\tmcpServer.AddTool(InfoTool(configPath))\n\tmcpServer.AddTool(DatabasesTool(configPath))\n\tmcpServer.AddTool(RestoreTool(configPath))\n\tmcpServer.AddTool(LTXTool(configPath))\n\tmcpServer.AddTool(VersionTool())\n\tmcpServer.AddTool(StatusTool(configPath))\n\tmcpServer.AddTool(ResetTool(configPath))\n\n\ts.mux = http.NewServeMux()\n\ts.mux.Handle(\"/\", httplog.Logger(server.NewStreamableHTTPServer(mcpServer)))\n\treturn s, nil\n}\n\nfunc (s *MCPServer) Start(addr string) {\n\ts.httpServer = \u0026http.Server{\n\t\tAddr: addr,\n\t\tHandler: s.mux,\n\t\tReadHeaderTimeout: 30 * time.Second,\n\t}\n\tgo func() {\n\t\tslog.Info(\"Starting MCP Streamable HTTP server\", \"addr\", addr)\n\t\tif err := s.httpServer.ListenAndServe(); err != nil {\n\t\t\tslog.Error(\"MCP server error\", \"error\", err)\n\t\t}\n\t}()\n}\n\nfunc (s *MCPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\ts.mux.ServeHTTP(w, r)\n}\n\n// Close attempts to gracefully shutdown the server.\nfunc (s *MCPServer) Close() error {\n\tctx, cancel := context.WithTimeout(s.ctx, 10*time.Second)\n\tdefer cancel()\n\treturn s.httpServer.Shutdown(ctx)\n}\n\n// isReplicaURL returns true if the path looks like a replica URL (s3://, gs://, etc.)\n// rather than a local database path. The CLI rejects -config when using replica URLs.\nfunc isReplicaURL(path string) bool {\n\treturn strings.Contains(path, \"://\")\n}\n\nfunc DatabasesTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_databases\",\n\t\tmcp.WithDescription(\"List databases and their replicas as defined in the Litestream config file. The default path is /etc/litestream.yml but is not required.\"),\n\t\tmcp.WithString(\"config\", mcp.Description(\"Path to the Litestream config file. Optional.\")),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\targs := []string{\"databases\"}\n\t\tconfig := configPath\n\t\tif configVal, err := req.RequireString(\"config\"); err == nil {\n\t\t\tconfig = configVal\n\t\t}\n\t\targs = append(args, \"-config\", config)\n\t\tcmd := exec.CommandContext(ctx, \"litestream\", args...)\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(strings.TrimSpace(string(output)) + \": \" + err.Error()), nil\n\t\t}\n\t\treturn mcp.NewToolResultText(string(output)), nil\n\t}\n}\n\nfunc InfoTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_info\",\n\t\tmcp.WithDescription(\"Get a comprehensive summary of Litestream's current status including databases, LTX files, and version information.\"),\n\t\tmcp.WithString(\"config\", mcp.Description(\"Path to the Litestream config file. Optional.\")),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tvar summary strings.Builder\n\t\tsummary.WriteString(\"=== Litestream Status Report ===\\n\\n\")\n\n\t\t// Get version info\n\t\tversionCmd := exec.CommandContext(ctx, \"litestream\", \"version\")\n\t\tversionOutput, err := versionCmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\tslog.Error(\"Failed to get version info\", \"error\", err)\n\t\t\treturn mcp.NewToolResultError(\"Failed to get version info: \" + err.Error()), nil\n\t\t}\n\t\tsummary.WriteString(\"Version Information:\\n\")\n\t\tsummary.WriteString(string(versionOutput))\n\t\tsummary.WriteString(\"\\n\")\n\n\t\t// Get databases info\n\t\targs := []string{\"databases\"}\n\t\tconfig := configPath\n\t\tif configVal, err := req.RequireString(\"config\"); err == nil {\n\t\t\tconfig = configVal\n\t\t}\n\t\tsummary.WriteString(\"Current Config Path:\\n\")\n\t\tsummary.WriteString(config + \"\\n\\n\")\n\n\t\targs = append(args, \"-config\", config)\n\t\tdbCmd := exec.CommandContext(ctx, \"litestream\", args...)\n\t\tdbOutput, err := dbCmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\tslog.Error(\"Failed to get databases info\", \"error\", err)\n\t\t\treturn mcp.NewToolResultError(\"Failed to get databases info: \" + err.Error()), nil\n\t\t}\n\n\t\tsummary.WriteString(\"Databases:\\n\")\n\t\tsummary.WriteString(string(dbOutput))\n\t\tsummary.WriteString(\"\\n\")\n\n\t\t// Parse database paths from output\n\t\tscanner := bufio.NewScanner(strings.NewReader(string(dbOutput)))\n\t\t// Skip header line\n\t\tscanner.Scan()\n\t\tvar dbPaths []string\n\t\tfor scanner.Scan() {\n\t\t\tfields := strings.Fields(scanner.Text())\n\t\t\tif len(fields) \u003e 0 {\n\t\t\t\tdbPaths = append(dbPaths, fields[0])\n\t\t\t}\n\t\t}\n\n\t\t// Get LTX files info for each database\n\t\tsummary.WriteString(\"LTX Files:\\n\")\n\t\tfor _, dbPath := range dbPaths {\n\t\t\tltxArgs := []string{\"ltx\"}\n\t\t\tif config != \"\" {\n\t\t\t\tltxArgs = append(ltxArgs, \"-config\", config)\n\t\t\t}\n\t\t\tltxArgs = append(ltxArgs, dbPath)\n\t\t\tltxCmd := exec.CommandContext(ctx, \"litestream\", ltxArgs...)\n\t\t\tltxOutput, err := ltxCmd.CombinedOutput()\n\t\t\tif err != nil {\n\t\t\t\tsummary.WriteString(\"Failed to get LTX files for \" + dbPath + \": \" + err.Error() + \"\\n\")\n\t\t\t\tsummary.WriteString(string(ltxOutput))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsummary.WriteString(\"Database: \" + dbPath + \"\\n\")\n\t\t\tsummary.WriteString(string(ltxOutput))\n\t\t\tsummary.WriteString(\"\\n\")\n\t\t}\n\n\t\treturn mcp.NewToolResultText(summary.String()), nil\n\t}\n}\n\nfunc RestoreTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_restore\",\n\t\tmcp.WithDescription(\"Restore a database from a Litestream replica.\"),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Database path or replica URL.\")),\n\t\tmcp.WithString(\"o\", mcp.Description(\"Output path for the restored database. Optional.\")),\n\t\tmcp.WithString(\"config\", mcp.Description(\"Path to the Litestream config file. Optional.\")),\n\t\tmcp.WithString(\"txid\", mcp.Description(\"Restore up to a specific transaction ID. Optional.\")),\n\t\tmcp.WithString(\"timestamp\", mcp.Description(\"Restore to a specific point-in-time (RFC3339). Optional.\")),\n\t\tmcp.WithString(\"parallelism\", mcp.Description(\"Number of WAL files to download in parallel. Optional.\")),\n\t\tmcp.WithBoolean(\"if_db_not_exists\", mcp.Description(\"Return 0 if the database already exists. Optional.\")),\n\t\tmcp.WithBoolean(\"if_replica_exists\", mcp.Description(\"Return 0 if no backups found. Optional.\")),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\targs := []string{\"restore\"}\n\t\tif o, err := req.RequireString(\"o\"); err == nil {\n\t\t\targs = append(args, \"-o\", o)\n\t\t}\n\n\t\t// Get path first to determine if it's a replica URL\n\t\tpath, _ := req.RequireString(\"path\")\n\n\t\t// Only add -config for database paths, not replica URLs\n\t\t// The CLI rejects -config when restoring from a replica URL\n\t\tif !isReplicaURL(path) {\n\t\t\tconfig := configPath\n\t\t\tif configVal, err := req.RequireString(\"config\"); err == nil {\n\t\t\t\tconfig = configVal\n\t\t\t}\n\t\t\tif config != \"\" {\n\t\t\t\targs = append(args, \"-config\", config)\n\t\t\t}\n\t\t}\n\n\t\tif txid, err := req.RequireString(\"txid\"); err == nil {\n\t\t\targs = append(args, \"-txid\", txid)\n\t\t}\n\t\tif timestamp, err := req.RequireString(\"timestamp\"); err == nil {\n\t\t\targs = append(args, \"-timestamp\", timestamp)\n\t\t}\n\t\tif parallelism, err := req.RequireString(\"parallelism\"); err == nil {\n\t\t\targs = append(args, \"-parallelism\", parallelism)\n\t\t}\n\t\tif ifDBNotExists, err := req.RequireBool(\"if_db_not_exists\"); err == nil {\n\t\t\targs = append(args, \"-if-db-not-exists\", strconv.FormatBool(ifDBNotExists))\n\t\t}\n\t\tif ifReplicaExists, err := req.RequireBool(\"if_replica_exists\"); err == nil {\n\t\t\targs = append(args, \"-if-replica-exists\", strconv.FormatBool(ifReplicaExists))\n\t\t}\n\t\tif path != \"\" {\n\t\t\targs = append(args, path)\n\t\t}\n\t\tcmd := exec.CommandContext(ctx, \"litestream\", args...)\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(strings.TrimSpace(string(output)) + \": \" + err.Error()), nil\n\t\t}\n\t\treturn mcp.NewToolResultText(string(output)), nil\n\t}\n}\n\nfunc VersionTool() (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_version\",\n\t\tmcp.WithDescription(\"Print the Litestream binary version.\"),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tcmd := exec.CommandContext(ctx, \"litestream\", \"version\")\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(strings.TrimSpace(string(output)) + \": \" + err.Error()), nil\n\t\t}\n\t\treturn mcp.NewToolResultText(string(output)), nil\n\t}\n}\n\nfunc LTXTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_ltx\",\n\t\tmcp.WithDescription(\"List all LTX files for a database or replica URL.\"),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Database path or replica URL.\")),\n\t\tmcp.WithString(\"config\", mcp.Description(\"Path to the Litestream config file. Optional, ignored for replica URLs.\")),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\targs := []string{\"ltx\"}\n\n\t\t// Get path first to determine if it's a replica URL\n\t\tpath, _ := req.RequireString(\"path\")\n\n\t\t// Only add -config for database paths, not replica URLs\n\t\t// The CLI rejects -config when using a replica URL\n\t\tif !isReplicaURL(path) {\n\t\t\tconfig := configPath\n\t\t\tif configVal, err := req.RequireString(\"config\"); err == nil {\n\t\t\t\tconfig = configVal\n\t\t\t}\n\t\t\tif config != \"\" {\n\t\t\t\targs = append(args, \"-config\", config)\n\t\t\t}\n\t\t}\n\n\t\tif path != \"\" {\n\t\t\targs = append(args, path)\n\t\t}\n\t\tcmd := exec.CommandContext(ctx, \"litestream\", args...)\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(strings.TrimSpace(string(output)) + \": \" + err.Error()), nil\n\t\t}\n\t\treturn mcp.NewToolResultText(string(output)), nil\n\t}\n}\n\nfunc StatusTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_status\",\n\t\tmcp.WithDescription(\"Display replication status including database path, status, local transaction ID, and WAL size.\"),\n\t\tmcp.WithString(\"config\", mcp.Description(\"Path to the Litestream config file. Optional.\")),\n\t\tmcp.WithString(\"path\", mcp.Description(\"Filter to a specific database path. Optional.\")),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\targs := []string{\"status\"}\n\t\tconfig := configPath\n\t\tif configVal, err := req.RequireString(\"config\"); err == nil {\n\t\t\tconfig = configVal\n\t\t}\n\t\targs = append(args, \"-config\", config)\n\t\tif path, err := req.RequireString(\"path\"); err == nil {\n\t\t\targs = append(args, path)\n\t\t}\n\t\tcmd := exec.CommandContext(ctx, \"litestream\", args...)\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(strings.TrimSpace(string(output)) + \": \" + err.Error()), nil\n\t\t}\n\t\treturn mcp.NewToolResultText(string(output)), nil\n\t}\n}\n\nfunc ResetTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {\n\ttool := mcp.NewTool(\"litestream_reset\",\n\t\tmcp.WithDescription(\"Clear local Litestream state for a database. Removes local LTX files, forcing fresh snapshot on next sync. Database file is not modified.\"),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Database path to reset.\")),\n\t\tmcp.WithString(\"config\", mcp.Description(\"Path to the Litestream config file. Optional.\")),\n\t)\n\n\treturn tool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\targs := []string{\"reset\"}\n\t\tconfig := configPath\n\t\tif configVal, err := req.RequireString(\"config\"); err == nil {\n\t\t\tconfig = configVal\n\t\t}\n\t\targs = append(args, \"-config\", config)\n\t\tif path, err := req.RequireString(\"path\"); err == nil {\n\t\t\targs = append(args, path)\n\t\t}\n\t\tcmd := exec.CommandContext(ctx, \"litestream\", args...)\n\t\toutput, err := cmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(strings.TrimSpace(string(output)) + \": \" + err.Error()), nil\n\t\t}\n\t\treturn mcp.NewToolResultText(string(output)), nil\n\t}\n}\n","imports":[{"name":"bufio","path":"bufio","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"slog","path":"log/slog","span":{"start":[6,1],"end":[6,12],"bytes":[43,54]}},{"name":"http","path":"net/http","span":{"start":[7,1],"end":[7,12],"bytes":[55,66]}},{"name":"exec","path":"os/exec","span":{"start":[8,1],"end":[8,11],"bytes":[67,77]}},{"name":"strconv","path":"strconv","span":{"start":[9,1],"end":[9,11],"bytes":[78,88]}},{"name":"strings","path":"strings","span":{"start":[10,1],"end":[10,11],"bytes":[89,99]}},{"name":"time","path":"time","span":{"start":[11,1],"end":[11,8],"bytes":[100,107]}},{"name":"httplog","path":"github.com/MadAppGang/httplog","span":{"start":[13,1],"end":[13,33],"bytes":[109,141]}},{"name":"mcp","path":"github.com/mark3labs/mcp-go/mcp","span":{"start":[14,1],"end":[14,35],"bytes":[142,176]}},{"name":"server","path":"github.com/mark3labs/mcp-go/server","span":{"start":[15,1],"end":[15,38],"bytes":[177,214]}}],"types":{"MCPServer":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer","kind":"struct","span":{"start":[18,1],"end":[23,2],"bytes":[218,342]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Close":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Close","kind":"method","span":{"start":[71,1],"end":[75,2],"bytes":[1608,1755]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"72:17":{"kind":"call","span":{"start":[72,17],"end":[72,59],"bytes":[1660,1702]},"callee":null},"73:8":{"kind":"call","span":{"start":[73,8],"end":[73,16],"bytes":[1710,1718]},"callee":null},"74:9":{"kind":"call","span":{"start":[74,9],"end":[74,35],"bytes":[1727,1753]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.ServeHTTP":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.ServeHTTP","kind":"method","span":{"start":[66,1],"end":[68,2],"bytes":[1457,1553]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.ServeHTTP","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[66,1],"end":[66,72],"bytes":[1457,1528]}},{"name":"r","type":"*net/http.Request","span":{"start":[66,1],"end":[66,72],"bytes":[1457,1528]}}],"metrics":{"cyclomatic":1},"body":{"67:2":{"kind":"call","span":{"start":[67,2],"end":[67,23],"bytes":[1530,1551]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start","kind":"method","span":{"start":[52,1],"end":[64,2],"bytes":[1095,1455]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start","parameters":[{"name":"addr","type":"string","span":{"start":[52,1],"end":[52,41],"bytes":[1095,1135]}}],"metrics":{"cyclomatic":2},"body":{"58:5":{"kind":"call","span":{"start":[58,5],"end":[63,5],"bytes":[1267,1453]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start.closure_1","kind":"function","span":{"start":[58,1],"end":[63,5],"bytes":[1263,1453]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start.closure_1","parameters":[],"body":{"59:3":{"kind":"call","span":{"start":[59,3],"end":[59,65],"bytes":[1278,1340]},"callee":null},"60:13":{"kind":"call","span":{"start":[60,13],"end":[60,42],"bytes":[1353,1382]},"callee":null},"61:4":{"kind":"call","span":{"start":[61,4],"end":[61,48],"bytes":[1400,1444]},"callee":null}}}}}},"fields":{"configPath":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/configPath","kind":"field","type":"string","span":{"start":[22,1],"end":[22,19],"bytes":[322,340]}},"ctx":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/ctx","kind":"field","type":"context.Context","span":{"start":[19,1],"end":[19,28],"bytes":[242,269]}},"httpServer":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/httpServer","kind":"field","type":"*net/http.Server","span":{"start":[21,1],"end":[21,25],"bytes":[297,321]}},"mux":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/mux","kind":"field","type":"*net/http.ServeMux","span":{"start":[20,1],"end":[20,27],"bytes":[270,296]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool","kind":"function","span":{"start":[83,1],"end":[103,2],"bytes":[2010,2941]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool","parameters":[{"name":"configPath","type":"string","span":{"start":[83,1],"end":[83,75],"bytes":[2010,2084]}}],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":3},"body":{"84:10":{"kind":"call","span":{"start":[84,10],"end":[87,3],"bytes":[2094,2389]},"callee":null},"85:3":{"kind":"call","span":{"start":[85,3],"end":[85,162],"bytes":[2132,2291]},"callee":null},"86:28":{"kind":"call","span":{"start":[86,28],"end":[86,92],"bytes":[2320,2384]},"callee":null},"86:3":{"kind":"call","span":{"start":[86,3],"end":[86,93],"bytes":[2295,2385]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool/github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool.closure_1","kind":"function","span":{"start":[89,1],"end":[102,3],"bytes":[2391,2939]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[89,1],"end":[89,96],"bytes":[2391,2486]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[89,1],"end":[89,96],"bytes":[2391,2486]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"101:10":{"kind":"call","span":{"start":[101,10],"end":[101,47],"bytes":[2894,2931]},"callee":null},"101:32":{"kind":"call","span":{"start":[101,32],"end":[101,46],"bytes":[2916,2930]},"callee":null},"92:24":{"kind":"call","span":{"start":[92,24],"end":[92,51],"bytes":[2565,2592]},"callee":null},"95:10":{"kind":"call","span":{"start":[95,10],"end":[95,41],"bytes":[2642,2673]},"callee":null},"96:10":{"kind":"call","span":{"start":[96,10],"end":[96,57],"bytes":[2683,2730]},"callee":null},"97:18":{"kind":"call","span":{"start":[97,18],"end":[97,38],"bytes":[2748,2768]},"callee":null},"99:11":{"kind":"call","span":{"start":[99,11],"end":[99,89],"bytes":[2797,2875]},"callee":null},"99:34":{"kind":"call","span":{"start":[99,34],"end":[99,67],"bytes":[2820,2853]},"callee":null},"99:52":{"kind":"call","span":{"start":[99,52],"end":[99,66],"bytes":[2838,2852]},"callee":null},"99:77":{"kind":"call","span":{"start":[99,77],"end":[99,88],"bytes":[2863,2874]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.InfoTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.InfoTool","kind":"function","span":{"start":[105,1],"end":[181,2],"bytes":[2943,5619]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.InfoTool","parameters":[{"name":"configPath","type":"string","span":{"start":[105,1],"end":[105,70],"bytes":[2943,3012]}}],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":9},"body":{"106:10":{"kind":"call","span":{"start":[106,10],"end":[109,3],"bytes":[3022,3291]},"callee":null},"107:3":{"kind":"call","span":{"start":[107,3],"end":[107,141],"bytes":[3055,3193]},"callee":null},"108:28":{"kind":"call","span":{"start":[108,28],"end":[108,92],"bytes":[3222,3286]},"callee":null},"108:3":{"kind":"call","span":{"start":[108,3],"end":[108,93],"bytes":[3197,3287]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.InfoTool/github.com/benbjohnson/litestream/cmd/litestream.InfoTool.closure_1","kind":"function","span":{"start":[111,1],"end":[180,3],"bytes":[3293,5617]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.InfoTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[111,1],"end":[111,96],"bytes":[3293,3388]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[111,1],"end":[111,96],"bytes":[3293,3388]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"113:3":{"kind":"call","span":{"start":[113,3],"end":[113,62],"bytes":[3421,3480]},"callee":null},"116:17":{"kind":"call","span":{"start":[116,17],"end":[116,66],"bytes":[3520,3569]},"callee":null},"117:25":{"kind":"call","span":{"start":[117,25],"end":[117,52],"bytes":[3594,3621]},"callee":null},"119:4":{"kind":"call","span":{"start":[119,4],"end":[119,58],"bytes":[3643,3697]},"callee":null},"120:11":{"kind":"call","span":{"start":[120,11],"end":[120,79],"bytes":[3708,3776]},"callee":null},"120:67":{"kind":"call","span":{"start":[120,67],"end":[120,78],"bytes":[3764,3775]},"callee":null},"122:3":{"kind":"call","span":{"start":[122,3],"end":[122,48],"bytes":[3788,3833]},"callee":null},"123:23":{"kind":"call","span":{"start":[123,23],"end":[123,44],"bytes":[3856,3877]},"callee":null},"123:3":{"kind":"call","span":{"start":[123,3],"end":[123,45],"bytes":[3836,3878]},"callee":null},"124:3":{"kind":"call","span":{"start":[124,3],"end":[124,28],"bytes":[3881,3906]},"callee":null},"129:24":{"kind":"call","span":{"start":[129,24],"end":[129,51],"bytes":[4010,4037]},"callee":null},"132:3":{"kind":"call","span":{"start":[132,3],"end":[132,48],"bytes":[4080,4125]},"callee":null},"133:3":{"kind":"call","span":{"start":[133,3],"end":[133,39],"bytes":[4128,4164]},"callee":null},"135:10":{"kind":"call","span":{"start":[135,10],"end":[135,41],"bytes":[4175,4206]},"callee":null},"136:12":{"kind":"call","span":{"start":[136,12],"end":[136,59],"bytes":[4218,4265]},"callee":null},"137:20":{"kind":"call","span":{"start":[137,20],"end":[137,42],"bytes":[4285,4307]},"callee":null},"139:4":{"kind":"call","span":{"start":[139,4],"end":[139,60],"bytes":[4329,4385]},"callee":null},"140:11":{"kind":"call","span":{"start":[140,11],"end":[140,81],"bytes":[4396,4466]},"callee":null},"140:69":{"kind":"call","span":{"start":[140,69],"end":[140,80],"bytes":[4454,4465]},"callee":null},"143:3":{"kind":"call","span":{"start":[143,3],"end":[143,38],"bytes":[4479,4514]},"callee":null},"144:23":{"kind":"call","span":{"start":[144,23],"end":[144,39],"bytes":[4537,4553]},"callee":null},"144:3":{"kind":"call","span":{"start":[144,3],"end":[144,40],"bytes":[4517,4554]},"callee":null},"145:3":{"kind":"call","span":{"start":[145,3],"end":[145,28],"bytes":[4557,4582]},"callee":null},"148:14":{"kind":"call","span":{"start":[148,14],"end":[148,67],"bytes":[4635,4688]},"callee":null},"148:31":{"kind":"call","span":{"start":[148,31],"end":[148,66],"bytes":[4652,4687]},"callee":null},"148:49":{"kind":"call","span":{"start":[148,49],"end":[148,65],"bytes":[4670,4686]},"callee":null},"150:3":{"kind":"call","span":{"start":[150,3],"end":[150,17],"bytes":[4713,4727]},"callee":null},"152:7":{"kind":"call","span":{"start":[152,7],"end":[152,21],"bytes":[4757,4771]},"callee":null},"153:14":{"kind":"call","span":{"start":[153,14],"end":[153,44],"bytes":[4787,4817]},"callee":null},"153:29":{"kind":"call","span":{"start":[153,29],"end":[153,43],"bytes":[4802,4816]},"callee":null},"154:7":{"kind":"call","span":{"start":[154,7],"end":[154,18],"bytes":[4824,4835]},"callee":null},"155:15":{"kind":"call","span":{"start":[155,15],"end":[155,41],"bytes":[4856,4882]},"callee":null},"160:3":{"kind":"call","span":{"start":[160,3],"end":[160,38],"bytes":[4937,4972]},"callee":null},"164:15":{"kind":"call","span":{"start":[164,15],"end":[164,49],"bytes":[5073,5107]},"callee":null},"166:14":{"kind":"call","span":{"start":[166,14],"end":[166,37],"bytes":[5126,5149]},"callee":null},"167:14":{"kind":"call","span":{"start":[167,14],"end":[167,64],"bytes":[5163,5213]},"callee":null},"168:22":{"kind":"call","span":{"start":[168,22],"end":[168,45],"bytes":[5235,5258]},"callee":null},"170:5":{"kind":"call","span":{"start":[170,5],"end":[170,93],"bytes":[5282,5370]},"callee":null},"170:74":{"kind":"call","span":{"start":[170,74],"end":[170,85],"bytes":[5351,5362]},"callee":null},"171:25":{"kind":"call","span":{"start":[171,25],"end":[171,42],"bytes":[5395,5412]},"callee":null},"171:5":{"kind":"call","span":{"start":[171,5],"end":[171,43],"bytes":[5375,5413]},"callee":null},"174:4":{"kind":"call","span":{"start":[174,4],"end":[174,53],"bytes":[5435,5484]},"callee":null},"175:24":{"kind":"call","span":{"start":[175,24],"end":[175,41],"bytes":[5508,5525]},"callee":null},"175:4":{"kind":"call","span":{"start":[175,4],"end":[175,42],"bytes":[5488,5526]},"callee":null},"176:4":{"kind":"call","span":{"start":[176,4],"end":[176,29],"bytes":[5530,5555]},"callee":null},"179:10":{"kind":"call","span":{"start":[179,10],"end":[179,49],"bytes":[5570,5609]},"callee":null},"179:32":{"kind":"call","span":{"start":[179,32],"end":[179,48],"bytes":[5592,5608]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.LTXTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.LTXTool","kind":"function","span":{"start":[259,1],"end":[294,2],"bytes":[8792,10060]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.LTXTool","parameters":[{"name":"configPath","type":"string","span":{"start":[259,1],"end":[259,69],"bytes":[8792,8860]}}],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":6},"body":{"260:10":{"kind":"call","span":{"start":[260,10],"end":[264,3],"bytes":[8870,9190]},"callee":null},"261:3":{"kind":"call","span":{"start":[261,3],"end":[261,75],"bytes":[8902,8974]},"callee":null},"262:26":{"kind":"call","span":{"start":[262,26],"end":[262,40],"bytes":[9001,9015]},"callee":null},"262:3":{"kind":"call","span":{"start":[262,3],"end":[262,91],"bytes":[8978,9066]},"callee":null},"262:42":{"kind":"call","span":{"start":[262,42],"end":[262,90],"bytes":[9017,9065]},"callee":null},"263:28":{"kind":"call","span":{"start":[263,28],"end":[263,118],"bytes":[9095,9185]},"callee":null},"263:3":{"kind":"call","span":{"start":[263,3],"end":[263,119],"bytes":[9070,9186]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.LTXTool/github.com/benbjohnson/litestream/cmd/litestream.LTXTool.closure_1","kind":"function","span":{"start":[266,1],"end":[293,3],"bytes":[9192,10058]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.LTXTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[266,1],"end":[266,96],"bytes":[9192,9287]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[266,1],"end":[266,96],"bytes":[9192,9287]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"270:14":{"kind":"call","span":{"start":[270,14],"end":[270,39],"bytes":[9383,9408]},"callee":null},"274:7":{"kind":"call","span":{"start":[274,7],"end":[274,25],"bytes":[9529,9547]},"callee":null},"276:25":{"kind":"call","span":{"start":[276,25],"end":[276,52],"bytes":[9598,9625]},"callee":null},"280:12":{"kind":"call","span":{"start":[280,12],"end":[280,43],"bytes":[9700,9731]},"callee":null},"285:11":{"kind":"call","span":{"start":[285,11],"end":[285,29],"bytes":[9770,9788]},"callee":null},"287:10":{"kind":"call","span":{"start":[287,10],"end":[287,57],"bytes":[9802,9849]},"callee":null},"288:18":{"kind":"call","span":{"start":[288,18],"end":[288,38],"bytes":[9867,9887]},"callee":null},"290:11":{"kind":"call","span":{"start":[290,11],"end":[290,89],"bytes":[9916,9994]},"callee":null},"290:34":{"kind":"call","span":{"start":[290,34],"end":[290,67],"bytes":[9939,9972]},"callee":null},"290:52":{"kind":"call","span":{"start":[290,52],"end":[290,66],"bytes":[9957,9971]},"callee":null},"290:77":{"kind":"call","span":{"start":[290,77],"end":[290,88],"bytes":[9982,9993]},"callee":null},"292:10":{"kind":"call","span":{"start":[292,10],"end":[292,47],"bytes":[10013,10050]},"callee":null},"292:32":{"kind":"call","span":{"start":[292,32],"end":[292,46],"bytes":[10035,10049]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.NewMCP":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","kind":"function","span":{"start":[25,1],"end":[50,2],"bytes":[344,1093]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewMCP","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[25,1],"end":[25,74],"bytes":[344,417]}},{"name":"configPath","type":"string","span":{"start":[25,1],"end":[25,74],"bytes":[344,417]}}],"return_type":"(*github.com/benbjohnson/litestream/cmd/litestream.MCPServer, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"31:15":{"kind":"call","span":{"start":[31,15],"end":[37,3],"bytes":[499,647]},"callee":null},"34:3":{"kind":"call","span":{"start":[34,3],"end":[34,37],"bytes":[560,594]},"callee":null},"35:3":{"kind":"call","span":{"start":[35,3],"end":[35,24],"bytes":[598,619]},"callee":null},"36:3":{"kind":"call","span":{"start":[36,3],"end":[36,23],"bytes":[623,643]},"callee":null},"39:2":{"kind":"call","span":{"start":[39,2],"end":[39,41],"bytes":[681,720]},"callee":null},"39:20":{"kind":"call","span":{"start":[39,20],"end":[39,40],"bytes":[699,719]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.InfoTool"},"40:2":{"kind":"call","span":{"start":[40,2],"end":[40,46],"bytes":[722,766]},"callee":null},"40:20":{"kind":"call","span":{"start":[40,20],"end":[40,45],"bytes":[740,765]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool"},"41:2":{"kind":"call","span":{"start":[41,2],"end":[41,44],"bytes":[768,810]},"callee":null},"41:20":{"kind":"call","span":{"start":[41,20],"end":[41,43],"bytes":[786,809]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool"},"42:2":{"kind":"call","span":{"start":[42,2],"end":[42,40],"bytes":[812,850]},"callee":null},"42:20":{"kind":"call","span":{"start":[42,20],"end":[42,39],"bytes":[830,849]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.LTXTool"},"43:2":{"kind":"call","span":{"start":[43,2],"end":[43,34],"bytes":[852,884]},"callee":null},"43:20":{"kind":"call","span":{"start":[43,20],"end":[43,33],"bytes":[870,883]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.VersionTool"},"44:2":{"kind":"call","span":{"start":[44,2],"end":[44,43],"bytes":[886,927]},"callee":null},"44:20":{"kind":"call","span":{"start":[44,20],"end":[44,42],"bytes":[904,926]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.StatusTool"},"45:2":{"kind":"call","span":{"start":[45,2],"end":[45,42],"bytes":[929,969]},"callee":null},"45:20":{"kind":"call","span":{"start":[45,20],"end":[45,41],"bytes":[947,968]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.ResetTool"},"47:10":{"kind":"call","span":{"start":[47,10],"end":[47,28],"bytes":[980,998]},"callee":null},"48:2":{"kind":"call","span":{"start":[48,2],"end":[48,78],"bytes":[1000,1076]},"callee":null},"48:20":{"kind":"call","span":{"start":[48,20],"end":[48,77],"bytes":[1018,1075]},"callee":null},"48:35":{"kind":"call","span":{"start":[48,35],"end":[48,76],"bytes":[1033,1074]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ResetTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.ResetTool","kind":"function","span":{"start":[322,1],"end":[346,2],"bytes":[11128,12225]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ResetTool","parameters":[{"name":"configPath","type":"string","span":{"start":[322,1],"end":[322,71],"bytes":[11128,11198]}}],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":4},"body":{"323:10":{"kind":"call","span":{"start":[323,10],"end":[327,3],"bytes":[11208,11586]},"callee":null},"324:3":{"kind":"call","span":{"start":[324,3],"end":[324,163],"bytes":[11242,11402]},"callee":null},"325:26":{"kind":"call","span":{"start":[325,26],"end":[325,40],"bytes":[11429,11443]},"callee":null},"325:3":{"kind":"call","span":{"start":[325,3],"end":[325,85],"bytes":[11406,11488]},"callee":null},"325:42":{"kind":"call","span":{"start":[325,42],"end":[325,84],"bytes":[11445,11487]},"callee":null},"326:28":{"kind":"call","span":{"start":[326,28],"end":[326,92],"bytes":[11517,11581]},"callee":null},"326:3":{"kind":"call","span":{"start":[326,3],"end":[326,93],"bytes":[11492,11582]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.ResetTool/github.com/benbjohnson/litestream/cmd/litestream.ResetTool.closure_1","kind":"function","span":{"start":[329,1],"end":[345,3],"bytes":[11588,12223]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ResetTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[329,1],"end":[329,96],"bytes":[11588,11683]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[329,1],"end":[329,96],"bytes":[11588,11683]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"332:24":{"kind":"call","span":{"start":[332,24],"end":[332,51],"bytes":[11758,11785]},"callee":null},"335:10":{"kind":"call","span":{"start":[335,10],"end":[335,41],"bytes":[11835,11866]},"callee":null},"336:19":{"kind":"call","span":{"start":[336,19],"end":[336,44],"bytes":[11885,11910]},"callee":null},"337:11":{"kind":"call","span":{"start":[337,11],"end":[337,29],"bytes":[11935,11953]},"callee":null},"339:10":{"kind":"call","span":{"start":[339,10],"end":[339,57],"bytes":[11967,12014]},"callee":null},"340:18":{"kind":"call","span":{"start":[340,18],"end":[340,38],"bytes":[12032,12052]},"callee":null},"342:11":{"kind":"call","span":{"start":[342,11],"end":[342,89],"bytes":[12081,12159]},"callee":null},"342:34":{"kind":"call","span":{"start":[342,34],"end":[342,67],"bytes":[12104,12137]},"callee":null},"342:52":{"kind":"call","span":{"start":[342,52],"end":[342,66],"bytes":[12122,12136]},"callee":null},"342:77":{"kind":"call","span":{"start":[342,77],"end":[342,88],"bytes":[12147,12158]},"callee":null},"344:10":{"kind":"call","span":{"start":[344,10],"end":[344,47],"bytes":[12178,12215]},"callee":null},"344:32":{"kind":"call","span":{"start":[344,32],"end":[344,46],"bytes":[12200,12214]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool","kind":"function","span":{"start":[183,1],"end":[242,2],"bytes":[5621,8257]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreTool","parameters":[{"name":"configPath","type":"string","span":{"start":[183,1],"end":[183,73],"bytes":[5621,5693]}}],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":12},"body":{"184:10":{"kind":"call","span":{"start":[184,10],"end":[194,3],"bytes":[5703,6612]},"callee":null},"185:3":{"kind":"call","span":{"start":[185,3],"end":[185,71],"bytes":[5739,5807]},"callee":null},"186:26":{"kind":"call","span":{"start":[186,26],"end":[186,40],"bytes":[5834,5848]},"callee":null},"186:3":{"kind":"call","span":{"start":[186,3],"end":[186,91],"bytes":[5811,5899]},"callee":null},"186:42":{"kind":"call","span":{"start":[186,42],"end":[186,90],"bytes":[5850,5898]},"callee":null},"187:23":{"kind":"call","span":{"start":[187,23],"end":[187,90],"bytes":[5923,5990]},"callee":null},"187:3":{"kind":"call","span":{"start":[187,3],"end":[187,91],"bytes":[5903,5991]},"callee":null},"188:28":{"kind":"call","span":{"start":[188,28],"end":[188,92],"bytes":[6020,6084]},"callee":null},"188:3":{"kind":"call","span":{"start":[188,3],"end":[188,93],"bytes":[5995,6085]},"callee":null},"189:26":{"kind":"call","span":{"start":[189,26],"end":[189,95],"bytes":[6112,6181]},"callee":null},"189:3":{"kind":"call","span":{"start":[189,3],"end":[189,96],"bytes":[6089,6182]},"callee":null},"190:3":{"kind":"call","span":{"start":[190,3],"end":[190,107],"bytes":[6186,6290]},"callee":null},"190:31":{"kind":"call","span":{"start":[190,31],"end":[190,106],"bytes":[6214,6289]},"callee":null},"191:3":{"kind":"call","span":{"start":[191,3],"end":[191,107],"bytes":[6294,6398]},"callee":null},"191:33":{"kind":"call","span":{"start":[191,33],"end":[191,106],"bytes":[6324,6397]},"callee":null},"192:3":{"kind":"call","span":{"start":[192,3],"end":[192,109],"bytes":[6402,6508]},"callee":null},"192:39":{"kind":"call","span":{"start":[192,39],"end":[192,108],"bytes":[6438,6507]},"callee":null},"193:3":{"kind":"call","span":{"start":[193,3],"end":[193,99],"bytes":[6512,6608]},"callee":null},"193:40":{"kind":"call","span":{"start":[193,40],"end":[193,98],"bytes":[6549,6607]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool.closure_1","kind":"function","span":{"start":[196,1],"end":[241,3],"bytes":[6614,8255]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[196,1],"end":[196,96],"bytes":[6614,6709]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[196,1],"end":[196,96],"bytes":[6614,6709]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"198:16":{"kind":"call","span":{"start":[198,16],"end":[198,38],"bytes":[6755,6777]},"callee":null},"199:11":{"kind":"call","span":{"start":[199,11],"end":[199,32],"bytes":[6802,6823]},"callee":null},"203:14":{"kind":"call","span":{"start":[203,14],"end":[203,39],"bytes":[6897,6922]},"callee":null},"207:7":{"kind":"call","span":{"start":[207,7],"end":[207,25],"bytes":[7052,7070]},"callee":null},"209:25":{"kind":"call","span":{"start":[209,25],"end":[209,52],"bytes":[7121,7148]},"callee":null},"213:12":{"kind":"call","span":{"start":[213,12],"end":[213,43],"bytes":[7223,7254]},"callee":null},"217:19":{"kind":"call","span":{"start":[217,19],"end":[217,44],"bytes":[7283,7308]},"callee":null},"218:11":{"kind":"call","span":{"start":[218,11],"end":[218,38],"bytes":[7333,7360]},"callee":null},"220:24":{"kind":"call","span":{"start":[220,24],"end":[220,54],"bytes":[7388,7418]},"callee":null},"221:11":{"kind":"call","span":{"start":[221,11],"end":[221,48],"bytes":[7443,7480]},"callee":null},"223:26":{"kind":"call","span":{"start":[223,26],"end":[223,58],"bytes":[7510,7542]},"callee":null},"224:11":{"kind":"call","span":{"start":[224,11],"end":[224,52],"bytes":[7567,7608]},"callee":null},"226:28":{"kind":"call","span":{"start":[226,28],"end":[226,63],"bytes":[7640,7675]},"callee":null},"227:11":{"kind":"call","span":{"start":[227,11],"end":[227,79],"bytes":[7700,7768]},"callee":null},"227:45":{"kind":"call","span":{"start":[227,45],"end":[227,78],"bytes":[7734,7767]},"callee":null},"229:30":{"kind":"call","span":{"start":[229,30],"end":[229,66],"bytes":[7802,7838]},"callee":null},"230:11":{"kind":"call","span":{"start":[230,11],"end":[230,82],"bytes":[7863,7934]},"callee":null},"230:46":{"kind":"call","span":{"start":[230,46],"end":[230,81],"bytes":[7898,7933]},"callee":null},"233:11":{"kind":"call","span":{"start":[233,11],"end":[233,29],"bytes":[7967,7985]},"callee":null},"235:10":{"kind":"call","span":{"start":[235,10],"end":[235,57],"bytes":[7999,8046]},"callee":null},"236:18":{"kind":"call","span":{"start":[236,18],"end":[236,38],"bytes":[8064,8084]},"callee":null},"238:11":{"kind":"call","span":{"start":[238,11],"end":[238,89],"bytes":[8113,8191]},"callee":null},"238:34":{"kind":"call","span":{"start":[238,34],"end":[238,67],"bytes":[8136,8169]},"callee":null},"238:52":{"kind":"call","span":{"start":[238,52],"end":[238,66],"bytes":[8154,8168]},"callee":null},"238:77":{"kind":"call","span":{"start":[238,77],"end":[238,88],"bytes":[8179,8190]},"callee":null},"240:10":{"kind":"call","span":{"start":[240,10],"end":[240,47],"bytes":[8210,8247]},"callee":null},"240:32":{"kind":"call","span":{"start":[240,32],"end":[240,46],"bytes":[8232,8246]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.StatusTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.StatusTool","kind":"function","span":{"start":[296,1],"end":[320,2],"bytes":[10062,11126]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StatusTool","parameters":[{"name":"configPath","type":"string","span":{"start":[296,1],"end":[296,72],"bytes":[10062,10133]}}],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":4},"body":{"297:10":{"kind":"call","span":{"start":[297,10],"end":[301,3],"bytes":[10143,10486]},"callee":null},"298:3":{"kind":"call","span":{"start":[298,3],"end":[298,121],"bytes":[10178,10296]},"callee":null},"299:28":{"kind":"call","span":{"start":[299,28],"end":[299,92],"bytes":[10325,10389]},"callee":null},"299:3":{"kind":"call","span":{"start":[299,3],"end":[299,93],"bytes":[10300,10390]},"callee":null},"300:26":{"kind":"call","span":{"start":[300,26],"end":[300,90],"bytes":[10417,10481]},"callee":null},"300:3":{"kind":"call","span":{"start":[300,3],"end":[300,91],"bytes":[10394,10482]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.StatusTool/github.com/benbjohnson/litestream/cmd/litestream.StatusTool.closure_1","kind":"function","span":{"start":[303,1],"end":[319,3],"bytes":[10488,11124]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StatusTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[303,1],"end":[303,96],"bytes":[10488,10583]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[303,1],"end":[303,96],"bytes":[10488,10583]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"306:24":{"kind":"call","span":{"start":[306,24],"end":[306,51],"bytes":[10659,10686]},"callee":null},"309:10":{"kind":"call","span":{"start":[309,10],"end":[309,41],"bytes":[10736,10767]},"callee":null},"310:19":{"kind":"call","span":{"start":[310,19],"end":[310,44],"bytes":[10786,10811]},"callee":null},"311:11":{"kind":"call","span":{"start":[311,11],"end":[311,29],"bytes":[10836,10854]},"callee":null},"313:10":{"kind":"call","span":{"start":[313,10],"end":[313,57],"bytes":[10868,10915]},"callee":null},"314:18":{"kind":"call","span":{"start":[314,18],"end":[314,38],"bytes":[10933,10953]},"callee":null},"316:11":{"kind":"call","span":{"start":[316,11],"end":[316,89],"bytes":[10982,11060]},"callee":null},"316:34":{"kind":"call","span":{"start":[316,34],"end":[316,67],"bytes":[11005,11038]},"callee":null},"316:52":{"kind":"call","span":{"start":[316,52],"end":[316,66],"bytes":[11023,11037]},"callee":null},"316:77":{"kind":"call","span":{"start":[316,77],"end":[316,88],"bytes":[11048,11059]},"callee":null},"318:10":{"kind":"call","span":{"start":[318,10],"end":[318,47],"bytes":[11079,11116]},"callee":null},"318:32":{"kind":"call","span":{"start":[318,32],"end":[318,46],"bytes":[11101,11115]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.VersionTool":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.VersionTool","kind":"function","span":{"start":[244,1],"end":[257,2],"bytes":[8259,8790]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.VersionTool","parameters":[],"return_type":"(github.com/mark3labs/mcp-go/mcp.Tool, github.com/mark3labs/mcp-go/server.ToolHandlerFunc)","metrics":{"cyclomatic":2},"body":{"245:10":{"kind":"call","span":{"start":[245,10],"end":[247,3],"bytes":[8324,8423]},"callee":null},"246:3":{"kind":"call","span":{"start":[246,3],"end":[246,62],"bytes":[8360,8419]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.VersionTool/github.com/benbjohnson/litestream/cmd/litestream.VersionTool.closure_1","kind":"function","span":{"start":[249,1],"end":[256,3],"bytes":[8425,8788]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.VersionTool.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[249,1],"end":[249,96],"bytes":[8425,8520]}},{"name":"req","type":"github.com/mark3labs/mcp-go/mcp.CallToolRequest","span":{"start":[249,1],"end":[249,96],"bytes":[8425,8520]}}],"return_type":"(*github.com/mark3labs/mcp-go/mcp.CallToolResult, error)","error_channel":["error"],"body":{"250:10":{"kind":"call","span":{"start":[250,10],"end":[250,59],"bytes":[8530,8579]},"callee":null},"251:18":{"kind":"call","span":{"start":[251,18],"end":[251,38],"bytes":[8597,8617]},"callee":null},"253:11":{"kind":"call","span":{"start":[253,11],"end":[253,89],"bytes":[8646,8724]},"callee":null},"253:34":{"kind":"call","span":{"start":[253,34],"end":[253,67],"bytes":[8669,8702]},"callee":null},"253:52":{"kind":"call","span":{"start":[253,52],"end":[253,66],"bytes":[8687,8701]},"callee":null},"253:77":{"kind":"call","span":{"start":[253,77],"end":[253,88],"bytes":[8712,8723]},"callee":null},"255:10":{"kind":"call","span":{"start":[255,10],"end":[255,47],"bytes":[8743,8780]},"callee":null},"255:32":{"kind":"call","span":{"start":[255,32],"end":[255,46],"bytes":[8765,8779]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.isReplicaURL":{"id":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.isReplicaURL","kind":"function","span":{"start":[79,1],"end":[81,2],"bytes":[1931,2008]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.isReplicaURL","parameters":[{"name":"path","type":"string","span":{"start":[79,1],"end":[79,38],"bytes":[1931,1968]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"80:9":{"kind":"call","span":{"start":[80,9],"end":[80,38],"bytes":[1977,2006]},"callee":null}}}},"content_hash":"8cec3283fc0c78fde1799fdc8062ab78364af351093c2e6f302fa2bc67bb60a7"},"cmd/litestream/register.go":{"id":"can://go/litestream/cmd/litestream/register.go","kind":"module","span":{"start":[1,1],"end":[170,1],"bytes":[0,4462]},"package":"main","source":"package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\ntype RegisterCommand struct{}\n\nfunc (c *RegisterCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-register\", flag.ContinueOnError)\n\ttimeout := fs.Int(\"timeout\", 30, \"timeout in seconds\")\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\treplicaFlag := fs.String(\"replica\", \"\", \"replica URL (e.g., s3://bucket/prefix, file:///backup/path)\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() == 0 {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path required\",\n\t\t\thint: \"litestream register -replica s3://bucket/prefix /path/to/db\",\n\t\t}\n\t}\n\tif fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\tif *replicaFlag == \"\" {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"-replica is required\",\n\t\t\thint: \"litestream register -replica s3://bucket/prefix /path/to/db\",\n\t\t}\n\t}\n\tif *timeout \u003c= 0 {\n\t\treturn fmt.Errorf(\"timeout must be greater than 0\")\n\t}\n\n\tdbPath := fs.Arg(0)\n\treplicaURL := *replicaFlag\n\n\t// Create HTTP client that connects via Unix socket with timeout.\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\treq := litestream.RegisterDatabaseRequest{\n\t\tPath: dbPath,\n\t\tReplicaURL: replicaURL,\n\t}\n\treqBody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}\n\n\tresp, err := client.Post(\"http://localhost/register\", \"application/json\", bytes.NewReader(reqBody))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"register failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"register failed: %s\", string(body))\n\t}\n\n\tvar result litestream.RegisterDatabaseResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tconfirmation := RegisterResult{\n\t\tStatus: result.Status,\n\t\tDBPath: result.Path,\n\t\tReplica: replicaURL,\n\t\tSocket: *socketPath,\n\t}\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(confirmation, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tfmt.Printf(\"status: %s\\n\", registerDisplayStatus(confirmation.Status))\n\tfmt.Printf(\"db_path: %s\\n\", confirmation.DBPath)\n\tfmt.Printf(\"replica: %s\\n\", confirmation.Replica)\n\tfmt.Printf(\"socket: %s\\n\", confirmation.Socket)\n\n\treturn nil\n}\n\ntype RegisterResult struct {\n\tStatus string `json:\"status\"`\n\tDBPath string `json:\"db_path\"`\n\tReplica string `json:\"replica\"`\n\tSocket string `json:\"socket\"`\n}\n\nfunc registerDisplayStatus(status string) string {\n\tif status == \"already_registered\" {\n\t\treturn \"already registered\"\n\t}\n\treturn status\n}\n\nfunc (c *RegisterCommand) Usage() {\n\tfmt.Println(`\nusage: litestream register [OPTIONS] DB_PATH\n\nRegister a database for replication.\n\nArguments:\n DB_PATH Path to the SQLite database file.\n\nOptions:\n -replica URL\n Replica destination URL (e.g., s3://bucket/prefix, file:///backup/path).\n Required.\n\n -timeout SECONDS\n Maximum time to wait in seconds (default: 30).\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -json\n Output raw JSON instead of human-readable text.\n\nExamples:\n # Register a database with an S3 replica.\n $ litestream register -replica s3://mybucket/db /path/to/db\n\n # Register a database with a file replica.\n $ litestream register -replica file:///backup/path /path/to/db\n\n # Register using a non-default control socket.\n $ litestream register -socket /tmp/litestream.sock -replica s3://mybucket/db /path/to/db\n\n # Register and emit a JSON confirmation.\n $ litestream register -json -replica s3://mybucket/db /path/to/db\n`[1:])\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"json","path":"encoding/json","span":{"start":[6,1],"end":[6,17],"bytes":[43,59]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[60,67]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[68,74]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[75,80]}},{"name":"net","path":"net","span":{"start":[10,1],"end":[10,7],"bytes":[81,87]}},{"name":"http","path":"net/http","span":{"start":[11,1],"end":[11,12],"bytes":[88,99]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[100,107]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[14,1],"end":[14,37],"bytes":[109,145]}}],"types":{"RegisterCommand":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand","kind":"struct","span":{"start":[17,1],"end":[17,30],"bytes":[149,178]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run","kind":"method","span":{"start":[19,1],"end":[117,2],"bytes":[180,3136]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[19,1],"end":[19,74],"bytes":[180,253]}},{"name":"args","type":"[]string","span":{"start":[19,1],"end":[19,74],"bytes":[180,253]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":14},"body":{"103:18":{"kind":"call","span":{"start":[103,18],"end":[103,60],"bytes":[2729,2771]},"callee":null},"105:11":{"kind":"call","span":{"start":[105,11],"end":[105,59],"bytes":[2800,2848]},"callee":null},"107:15":{"kind":"call","span":{"start":[107,15],"end":[107,29],"bytes":[2867,2881]},"callee":null},"107:3":{"kind":"call","span":{"start":[107,3],"end":[107,30],"bytes":[2855,2882]},"callee":null},"111:2":{"kind":"call","span":{"start":[111,2],"end":[111,72],"bytes":[2901,2971]},"callee":null},"111:29":{"kind":"call","span":{"start":[111,29],"end":[111,71],"bytes":[2928,2970]},"callee":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.registerDisplayStatus"},"112:2":{"kind":"call","span":{"start":[112,2],"end":[112,50],"bytes":[2973,3021]},"callee":null},"113:2":{"kind":"call","span":{"start":[113,2],"end":[113,51],"bytes":[3023,3072]},"callee":null},"114:2":{"kind":"call","span":{"start":[114,2],"end":[114,49],"bytes":[3074,3121]},"callee":null},"20:8":{"kind":"call","span":{"start":[20,8],"end":[20,68],"bytes":[261,321]},"callee":null},"21:13":{"kind":"call","span":{"start":[21,13],"end":[21,56],"bytes":[334,377]},"callee":null},"22:16":{"kind":"call","span":{"start":[22,16],"end":[22,86],"bytes":[393,463]},"callee":null},"23:17":{"kind":"call","span":{"start":[23,17],"end":[23,104],"bytes":[480,567]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,57],"bytes":[583,624]},"callee":null},"26:12":{"kind":"call","span":{"start":[26,12],"end":[26,26],"bytes":[656,670]},"callee":null},"30:5":{"kind":"call","span":{"start":[30,5],"end":[30,14],"bytes":[706,715]},"callee":null},"36:5":{"kind":"call","span":{"start":[36,5],"end":[36,14],"bytes":[869,878]},"callee":null},"37:10":{"kind":"call","span":{"start":[37,10],"end":[37,42],"bytes":[894,926]},"callee":null},"46:10":{"kind":"call","span":{"start":[46,10],"end":[46,54],"bytes":[1124,1168]},"callee":null},"49:12":{"kind":"call","span":{"start":[49,12],"end":[49,21],"bytes":[1184,1193]},"callee":null},"53:19":{"kind":"call","span":{"start":[53,19],"end":[53,42],"bytes":[1308,1331]},"callee":null},"67:18":{"kind":"call","span":{"start":[67,18],"end":[67,35],"bytes":[1690,1707]},"callee":null},"69:10":{"kind":"call","span":{"start":[69,10],"end":[69,58],"bytes":[1734,1782]},"callee":null},"72:15":{"kind":"call","span":{"start":[72,15],"end":[72,101],"bytes":[1801,1887]},"callee":null},"72:76":{"kind":"call","span":{"start":[72,76],"end":[72,100],"bytes":[1862,1886]},"callee":null},"74:10":{"kind":"call","span":{"start":[74,10],"end":[74,68],"bytes":[1914,1972]},"callee":null},"76:8":{"kind":"call","span":{"start":[76,8],"end":[76,25],"bytes":[1983,2000]},"callee":null},"78:15":{"kind":"call","span":{"start":[78,15],"end":[78,36],"bytes":[2016,2037]},"callee":null},"80:10":{"kind":"call","span":{"start":[80,10],"end":[80,56],"bytes":[2064,2110]},"callee":null},"85:13":{"kind":"call","span":{"start":[85,13],"end":[85,43],"bytes":[2205,2235]},"callee":null},"86:11":{"kind":"call","span":{"start":[86,11],"end":[86,59],"bytes":[2283,2331]},"callee":null},"88:10":{"kind":"call","span":{"start":[88,10],"end":[88,57],"bytes":[2345,2392]},"callee":null},"88:44":{"kind":"call","span":{"start":[88,44],"end":[88,56],"bytes":[2379,2391]},"callee":null},"92:12":{"kind":"call","span":{"start":[92,12],"end":[92,41],"bytes":[2456,2485]},"callee":null},"93:10":{"kind":"call","span":{"start":[93,10],"end":[93,57],"bytes":[2509,2556]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run.closure_1","kind":"function","span":{"start":[57,1],"end":[59,6],"bytes":[1427,1568]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[57,1],"end":[57,73],"bytes":[1427,1499]}},{"name":"_","type":"string","span":{"start":[57,1],"end":[57,73],"bytes":[1427,1499]}},{"name":"_","type":"string","span":{"start":[57,1],"end":[57,73],"bytes":[1427,1499]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"58:12":{"kind":"call","span":{"start":[58,12],"end":[58,63],"bytes":[1511,1562]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Usage":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Usage","kind":"method","span":{"start":[133,1],"end":[169,2],"bytes":[3439,4461]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"134:2":{"kind":"call","span":{"start":[134,2],"end":[168,7],"bytes":[3476,4459]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv"}}}}},"RegisterResult":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterResult","kind":"struct","span":{"start":[119,1],"end":[124,2],"bytes":[3138,3298]},"callables":{},"fields":{"DBPath":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterResult/DBPath","kind":"field","type":"string","span":{"start":[121,1],"end":[121,33],"bytes":[3199,3231]}},"Replica":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterResult/Replica","kind":"field","type":"string","span":{"start":[122,1],"end":[122,33],"bytes":[3232,3264]}},"Socket":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterResult/Socket","kind":"field","type":"string","span":{"start":[123,1],"end":[123,32],"bytes":[3265,3296]}},"Status":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterResult/Status","kind":"field","type":"string","span":{"start":[120,1],"end":[120,32],"bytes":[3167,3198]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.registerDisplayStatus":{"id":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.registerDisplayStatus","kind":"function","span":{"start":[126,1],"end":[131,2],"bytes":[3300,3437]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.registerDisplayStatus","parameters":[{"name":"status","type":"string","span":{"start":[126,1],"end":[126,51],"bytes":[3300,3350]}}],"return_type":"string","metrics":{"cyclomatic":2},"body":{}}},"content_hash":"bf7bcf0236ce1cfa7d72cbccf088d6c7625953264d19f1ec781e8b562b17b80e"},"cmd/litestream/replicate.go":{"id":"can://go/litestream/cmd/litestream/replicate.go","kind":"module","span":{"start":[1,1],"end":[576,1],"bytes":[0,18077]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"net\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/mattn/go-shellwords\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/abs\"\n\t\"github.com/benbjohnson/litestream/file\"\n\t\"github.com/benbjohnson/litestream/gs\"\n\t\"github.com/benbjohnson/litestream/internal\"\n\t\"github.com/benbjohnson/litestream/nats\"\n\t\"github.com/benbjohnson/litestream/oss\"\n\t\"github.com/benbjohnson/litestream/s3\"\n\t\"github.com/benbjohnson/litestream/sftp\"\n)\n\n// ReplicateCommand represents a command that continuously replicates SQLite databases.\ntype ReplicateCommand struct {\n\tcmd *exec.Cmd // subcommand\n\texecCh chan error // subcommand error channel\n\n\t// One-shot replication flags\n\tonce bool // replicate once and exit\n\tforceSnapshot bool // force snapshot to all replicas\n\tenforceRetention bool // enforce retention of old snapshots\n\n\tConfig Config\n\n\t// MCP server\n\tMCP *MCPServer\n\n\t// Server for IPC control commands.\n\tServer *litestream.Server\n\n\t// Manages the set of databases \u0026 compaction levels.\n\tStore *litestream.Store\n\n\t// Directory monitors for dynamic database discovery.\n\tdirectoryMonitors []*DirectoryMonitor\n\n\t// Done channel for interrupt handling during shutdown. When closed,\n\t// the shutdown sync retry loop exits and any in-flight sync is cancelled.\n\tdone \u003c-chan struct{}\n}\n\nfunc NewReplicateCommand() *ReplicateCommand {\n\treturn \u0026ReplicateCommand{\n\t\texecCh: make(chan error),\n\t}\n}\n\n// ParseFlags parses the CLI flags and loads the configuration file.\nfunc (c *ReplicateCommand) ParseFlags(_ context.Context, args []string) (err error) {\n\tfs := flag.NewFlagSet(\"litestream-replicate\", flag.ContinueOnError)\n\texecFlag := fs.String(\"exec\", \"\", \"execute subcommand\")\n\tlogLevelFlag := fs.String(\"log-level\", \"\", \"log level (trace, debug, info, warn, error)\")\n\trestoreIfDBNotExists := fs.Bool(\"restore-if-db-not-exists\", false, \"restore from replica if database doesn't exist\")\n\tonceFlag := fs.Bool(\"once\", false, \"replicate once and exit\")\n\tforceSnapshotFlag := fs.Bool(\"force-snapshot\", false, \"force snapshot when replicating once\")\n\tenforceRetentionFlag := fs.Bool(\"enforce-retention\", false, \"enforce retention of old snapshots when replicating once\")\n\tconfigPath, noExpandEnv := registerConfigFlag(fs)\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\t// Load configuration or use CLI args to build db/replica.\n\tswitch fs.NArg() {\n\tcase 0:\n\t\t// No arguments provided, use config file\n\t\tif *configPath == \"\" {\n\t\t\t*configPath = DefaultConfigPath()\n\t\t}\n\t\tif c.Config, err = ReadConfigFile(*configPath, !*noExpandEnv); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Override log level if CLI flag provided (takes precedence over env var)\n\t\tif *logLevelFlag != \"\" {\n\t\t\tc.Config.Logging.Level = *logLevelFlag\n\t\t\t// Set env var so initLog sees CLI flag as highest priority\n\t\t\tos.Setenv(\"LOG_LEVEL\", *logLevelFlag)\n\t\t\tlogOutput := os.Stdout\n\t\t\tif c.Config.Logging.Stderr {\n\t\t\t\tlogOutput = os.Stderr\n\t\t\t}\n\t\t\tinternal.InitLog(logOutput, c.Config.Logging.Level, c.Config.Logging.Type, c.Config.Logging.Source)\n\t\t}\n\n\tcase 1:\n\t\t// Only database path provided, missing replica URL\n\t\treturn fmt.Errorf(\"must specify at least one replica URL for %s\", fs.Arg(0))\n\n\tdefault:\n\t\t// Database path and replica URLs provided via CLI\n\t\tif *configPath != \"\" {\n\t\t\treturn fmt.Errorf(\"cannot specify a replica URL and the -config flag\")\n\t\t}\n\n\t\t// Initialize config with defaults when using command-line arguments\n\t\tc.Config = DefaultConfig()\n\t\tlogLevel := \"INFO\"\n\t\tif *logLevelFlag != \"\" {\n\t\t\tlogLevel = *logLevelFlag\n\t\t\t// Set env var so initLog sees CLI flag as highest priority\n\t\t\tos.Setenv(\"LOG_LEVEL\", *logLevelFlag)\n\t\t}\n\t\tc.Config.Logging.Level = logLevel\n\t\tinternal.InitLog(os.Stdout, logLevel, \"text\", false)\n\n\t\tdbConfig := \u0026DBConfig{\n\t\t\tPath: fs.Arg(0),\n\t\t\tRestoreIfDBNotExists: *restoreIfDBNotExists,\n\t\t}\n\t\tfor _, u := range fs.Args()[1:] {\n\t\t\t// Check if this looks like a flag that was placed after positional arguments\n\t\t\tif strings.HasPrefix(u, \"-\") {\n\t\t\t\treturn fmt.Errorf(\"flag %q must be positioned before DB_PATH and REPLICA_URL arguments\", u)\n\t\t\t}\n\t\t\tsyncInterval := litestream.DefaultSyncInterval\n\t\t\tdbConfig.Replicas = append(dbConfig.Replicas, \u0026ReplicaConfig{\n\t\t\t\tURL: u,\n\t\t\t\tReplicaSettings: ReplicaSettings{\n\t\t\t\t\tSyncInterval: \u0026syncInterval,\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\tc.Config.DBs = []*DBConfig{dbConfig}\n\t}\n\n\tc.Config.ConfigPath = *configPath\n\n\t// Override config exec command, if specified.\n\tif *execFlag != \"\" {\n\t\tc.Config.Exec = *execFlag\n\t}\n\n\t// Apply restore-if-db-not-exists flag to all databases if specified.\n\t// This allows the CLI flag to work with config files.\n\tif *restoreIfDBNotExists {\n\t\tfor _, dbConfig := range c.Config.DBs {\n\t\t\tdbConfig.RestoreIfDBNotExists = true\n\t\t}\n\t}\n\n\t// Set one-shot replication flags and validate their usage.\n\tc.once = *onceFlag\n\tc.forceSnapshot = *forceSnapshotFlag\n\tc.enforceRetention = *enforceRetentionFlag\n\n\t// Validate flag combinations.\n\tif c.once \u0026\u0026 c.Config.Exec != \"\" {\n\t\treturn fmt.Errorf(\"cannot specify -once flag with -exec\")\n\t}\n\tif c.forceSnapshot \u0026\u0026 !c.once {\n\t\treturn fmt.Errorf(\"cannot specify -force-snapshot flag without -once\")\n\t}\n\tif c.enforceRetention \u0026\u0026 !c.once {\n\t\treturn fmt.Errorf(\"cannot specify -enforce-retention flag without -once\")\n\t}\n\n\treturn nil\n}\n\n// Run loads all databases specified in the configuration.\nfunc (c *ReplicateCommand) Run(ctx context.Context) (err error) {\n\t// Display version information.\n\tslog.Info(\"litestream\", \"version\", Version, \"level\", c.Config.Logging.Level)\n\n\t// Start MCP server if enabled\n\tif c.Config.MCPAddr != \"\" {\n\t\tc.MCP, err = NewMCP(ctx, c.Config.ConfigPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo c.MCP.Start(c.Config.MCPAddr)\n\t}\n\n\t// Setup databases.\n\tif len(c.Config.DBs) == 0 {\n\t\tslog.Error(\"no databases specified in configuration\")\n\t}\n\n\t// Attempt restore for databases that need it (before creating DB objects)\n\tfor _, dbConfig := range c.Config.DBs {\n\t\tif dbConfig.RestoreIfDBNotExists \u0026\u0026 dbConfig.Path != \"\" {\n\t\t\tif err := c.restoreIfNeeded(ctx, dbConfig); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tvar dbs []*litestream.DB\n\tvar watchables []struct {\n\t\tconfig *DBConfig\n\t\tdbs []*litestream.DB\n\t}\n\tfor _, dbConfig := range c.Config.DBs {\n\t\t// Handle directory configuration\n\t\tif dbConfig.Dir != \"\" {\n\t\t\tdirDbs, err := NewDBsFromDirectoryConfig(dbConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdbs = append(dbs, dirDbs...)\n\t\t\tslog.Info(\"found databases in directory\", \"dir\", dbConfig.Dir, \"count\", len(dirDbs), \"watch\", dbConfig.Watch)\n\t\t\tif dbConfig.Watch {\n\t\t\t\twatchables = append(watchables, struct {\n\t\t\t\t\tconfig *DBConfig\n\t\t\t\t\tdbs []*litestream.DB\n\t\t\t\t}{config: dbConfig, dbs: dirDbs})\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle single database configuration\n\t\t\tdb, err := NewDBFromConfig(dbConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdbs = append(dbs, db)\n\t\t}\n\t}\n\n\tlevels := c.Config.CompactionLevels()\n\tc.Store = litestream.NewStore(dbs, levels)\n\n\t// Only override default snapshot interval if explicitly set in config\n\tif c.Config.Snapshot.Interval != nil {\n\t\tc.Store.SnapshotInterval = *c.Config.Snapshot.Interval\n\t}\n\t// Only override default snapshot retention if explicitly set in config\n\tif c.Config.Snapshot.Retention != nil {\n\t\tc.Store.SnapshotRetention = *c.Config.Snapshot.Retention\n\t}\n\tif c.Config.L0Retention != nil {\n\t\tc.Store.SetL0Retention(*c.Config.L0Retention)\n\t}\n\tif c.Config.L0RetentionCheckInterval != nil {\n\t\tc.Store.L0RetentionCheckInterval = *c.Config.L0RetentionCheckInterval\n\t}\n\tif c.Config.ShutdownSyncTimeout != nil {\n\t\tc.Store.SetShutdownSyncTimeout(*c.Config.ShutdownSyncTimeout)\n\t}\n\tif c.Config.ShutdownSyncInterval != nil {\n\t\tc.Store.SetShutdownSyncInterval(*c.Config.ShutdownSyncInterval)\n\t}\n\tif c.Config.VerifyCompaction {\n\t\tc.Store.SetVerifyCompaction(true)\n\t}\n\tif c.Config.Retention.Enabled != nil \u0026\u0026 !*c.Config.Retention.Enabled {\n\t\tc.Store.SetRetentionEnabled(false)\n\t}\n\tif c.Config.Validation.Interval != nil {\n\t\tc.Store.ValidationInterval = *c.Config.Validation.Interval\n\t}\n\tif c.done != nil {\n\t\tc.Store.SetDone(c.done)\n\t}\n\tif c.Config.HeartbeatURL != \"\" {\n\t\tinterval := litestream.DefaultHeartbeatInterval\n\t\tif c.Config.HeartbeatInterval != nil {\n\t\t\tinterval = *c.Config.HeartbeatInterval\n\t\t}\n\t\tc.Store.Heartbeat = litestream.NewHeartbeatClient(c.Config.HeartbeatURL, interval)\n\t}\n\n\t// Disable all background monitors when running once.\n\t// This must be done after config settings are applied.\n\tif c.once {\n\t\tc.Store.CompactionMonitorEnabled = false\n\t\tc.Store.L0RetentionCheckInterval = 0\n\t\tfor _, db := range dbs {\n\t\t\tdb.MonitorInterval = 0\n\t\t\tif db.Replica != nil {\n\t\t\t\tdb.Replica.MonitorEnabled = false\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := c.Store.Open(ctx); err != nil {\n\t\treturn fmt.Errorf(\"cannot open store: %w\", err)\n\t}\n\n\tif !c.Store.RetentionEnabled {\n\t\tslog.Warn(\"retention disabled; cloud provider lifecycle policies must handle retention\",\n\t\t\t\"hint\", \"idle databases that stop receiving writes will not generate new snapshots and may lose backup coverage if cloud retention expires\")\n\t}\n\n\t// Start control server if socket is enabled\n\tif c.Config.Socket.Enabled {\n\t\tc.Server = litestream.NewServer(c.Store)\n\t\tc.Server.SocketPath = c.Config.Socket.Path\n\t\tc.Server.SocketPerms = c.Config.Socket.Permissions\n\t\tc.Server.PathExpander = expand\n\t\tc.Server.Version = Version\n\t\tif err := c.Server.Start(); err != nil {\n\t\t\tslog.Warn(\"failed to start control server\", \"error\", err)\n\t\t}\n\t}\n\n\tfor _, entry := range watchables {\n\t\tmonitor, err := NewDirectoryMonitor(ctx, c.Store, entry.config, entry.dbs)\n\t\tif err != nil {\n\t\t\tfor _, m := range c.directoryMonitors {\n\t\t\t\tm.Close()\n\t\t\t}\n\t\t\tif closeErr := c.Store.Close(ctx); closeErr != nil {\n\t\t\t\tslog.Error(\"failed to close store after monitor failure\", \"error\", closeErr)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"start directory monitor for %s: %w\", entry.config.Dir, err)\n\t\t}\n\t\tc.directoryMonitors = append(c.directoryMonitors, monitor)\n\t}\n\n\t// Notify user that initialization is done.\n\tfor _, db := range c.Store.DBs() {\n\t\tr := db.Replica\n\t\tslog.Info(\"initialized db\", \"path\", db.Path())\n\t\tslogWith := slog.With(\"type\", r.Client.Type(), \"sync-interval\", r.SyncInterval)\n\t\tswitch client := r.Client.(type) {\n\t\tcase *file.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"path\", client.Path())\n\t\tcase *s3.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"bucket\", client.Bucket, \"path\", client.Path, \"region\", client.Region, \"endpoint\", client.Endpoint)\n\t\tcase *gs.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"bucket\", client.Bucket, \"path\", client.Path)\n\t\tcase *abs.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"bucket\", client.Bucket, \"path\", client.Path, \"endpoint\", client.Endpoint)\n\t\tcase *sftp.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"host\", client.Host, \"user\", client.User, \"path\", client.Path)\n\t\tcase *nats.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"bucket\", client.BucketName, \"url\", client.URL)\n\t\tcase *oss.ReplicaClient:\n\t\t\tslogWith.Info(\"replicating to\", \"bucket\", client.Bucket, \"path\", client.Path, \"region\", client.Region)\n\t\tdefault:\n\t\t\tslogWith.Info(\"replicating to\")\n\t\t}\n\t}\n\n\t// Serve metrics over HTTP if enabled.\n\tif c.Config.Addr != \"\" {\n\t\thostport := c.Config.Addr\n\t\tif host, port, _ := net.SplitHostPort(c.Config.Addr); port == \"\" {\n\t\t\treturn fmt.Errorf(\"must specify port for bind address: %q\", c.Config.Addr)\n\t\t} else if host == \"\" {\n\t\t\thostport = net.JoinHostPort(\"localhost\", port)\n\t\t}\n\n\t\tslog.Info(\"serving metrics on\", \"url\", fmt.Sprintf(\"http://%s/metrics\", hostport))\n\t\tgo func() {\n\t\t\thttp.Handle(\"/metrics\", promhttp.Handler())\n\t\t\tif err := http.ListenAndServe(c.Config.Addr, nil); err != nil {\n\t\t\t\tslog.Error(\"cannot start metrics server\", \"error\", err)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Parse exec commands args \u0026 start subprocess.\n\tif c.Config.Exec != \"\" {\n\t\texecArgs, err := shellwords.Parse(c.Config.Exec)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot parse exec command: %w\", err)\n\t\t}\n\n\t\tc.cmd = exec.CommandContext(ctx, execArgs[0], execArgs[1:]...)\n\t\tc.cmd.Env = os.Environ()\n\t\tc.cmd.Stdout = os.Stdout\n\t\tc.cmd.Stderr = os.Stderr\n\t\tif err := c.cmd.Start(); err != nil {\n\t\t\treturn fmt.Errorf(\"cannot start exec command: %w\", err)\n\t\t}\n\t\tgo func() { c.execCh \u003c- c.cmd.Wait() }()\n\t} else if c.once {\n\t\t// Run one-shot replication in a goroutine so the caller can wait on execCh.\n\t\tgo c.runOnce(ctx)\n\t}\n\n\treturn nil\n}\n\n// runOnce performs one-shot replication for all databases.\n// It syncs all databases, optionally takes snapshots, and enforces retention.\nfunc (c *ReplicateCommand) runOnce(ctx context.Context) {\n\tvar err error\n\tdefer func() { c.execCh \u003c- err }()\n\n\tfor _, db := range c.Store.DBs() {\n\t\tslog.Info(\"syncing database\", \"path\", db.Path())\n\n\t\t// Sync the database to process any pending WAL changes.\n\t\tif err = db.Sync(ctx); err != nil {\n\t\t\terr = fmt.Errorf(\"sync database %s: %w\", db.Path(), err)\n\t\t\treturn\n\t\t}\n\n\t\t// Sync the replica to upload any pending LTX files.\n\t\tif err = db.Replica.Sync(ctx); err != nil {\n\t\t\terr = fmt.Errorf(\"sync replica for %s: %w\", db.Path(), err)\n\t\t\treturn\n\t\t}\n\n\t\t// Force a snapshot if requested.\n\t\tif c.forceSnapshot {\n\t\t\tslog.Info(\"taking snapshot\", \"path\", db.Path())\n\t\t\tif _, err = db.Snapshot(ctx); err != nil {\n\t\t\t\terr = fmt.Errorf(\"snapshot %s: %w\", db.Path(), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Enforce retention if requested.\n\t\tif c.enforceRetention {\n\t\t\tslog.Info(\"enforcing retention\", \"path\", db.Path())\n\t\t\tif err = c.Store.EnforceSnapshotRetention(ctx, db); err != nil {\n\t\t\t\terr = fmt.Errorf(\"enforce retention for %s: %w\", db.Path(), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tslog.Info(\"one-shot replication complete\")\n}\n\n// Close closes all open databases.\nfunc (c *ReplicateCommand) Close(ctx context.Context) error {\n\tfor _, monitor := range c.directoryMonitors {\n\t\tmonitor.Close()\n\t}\n\tc.directoryMonitors = nil\n\n\tif c.Server != nil {\n\t\tif err := c.Server.Close(); err != nil {\n\t\t\tslog.Error(\"error closing control server\", \"error\", err)\n\t\t}\n\t}\n\tif c.Store != nil {\n\t\tif err := c.Store.Close(ctx); err != nil {\n\t\t\tif errors.Is(err, litestream.ErrShutdownInterrupted) {\n\t\t\t\tslog.Warn(\"shutdown sync skipped by user interrupt\", \"error\", err)\n\t\t\t} else {\n\t\t\t\tslog.Error(\"failed to close database\", \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif c.Config.MCPAddr != \"\" \u0026\u0026 c.MCP != nil {\n\t\tif err := c.MCP.Close(); err != nil {\n\t\t\tslog.Error(\"error closing MCP server\", \"error\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// SetDone sets the done channel used for interrupt handling during shutdown.\n// When the channel is closed, the shutdown sync retry loop exits.\nfunc (c *ReplicateCommand) SetDone(done \u003c-chan struct{}) {\n\tc.done = done\n}\n\n// restoreIfNeeded restores a database from its replica if the database doesn't\n// exist and the RestoreIfDBNotExists option is enabled. If no backup exists\n// (first start scenario), it returns nil to allow fresh replication to begin.\nfunc (c *ReplicateCommand) restoreIfNeeded(ctx context.Context, dbConfig *DBConfig) error {\n\tdbPath, err := expand(dbConfig.Path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Skip if database already exists\n\tif _, err := os.Stat(dbPath); !os.IsNotExist(err) {\n\t\tslog.Info(\"database exists, skipping restore\", \"path\", dbPath)\n\t\treturn nil\n\t}\n\n\t// Get replica config (handles both Replica and Replicas fields)\n\tvar rc *ReplicaConfig\n\tif dbConfig.Replica != nil {\n\t\trc = dbConfig.Replica\n\t} else if len(dbConfig.Replicas) \u003e 0 {\n\t\trc = dbConfig.Replicas[0]\n\t} else {\n\t\treturn fmt.Errorf(\"no replica configured for database: %s\", dbPath)\n\t}\n\n\t// Create replica from config (nil db since we're just restoring)\n\tr, err := NewReplicaFromConfig(rc, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot create replica for restore: %w\", err)\n\t}\n\n\t// Attempt restore\n\topt := litestream.NewRestoreOptions()\n\topt.OutputPath = dbPath\n\n\tslog.Info(\"attempting restore before replication\", \"path\", dbPath)\n\tif err := r.Restore(ctx, opt); errors.Is(err, litestream.ErrTxNotAvailable) {\n\t\t// No backup exists yet (first start) - this is OK\n\t\tslog.Info(\"no backup found, starting fresh\", \"path\", dbPath)\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn fmt.Errorf(\"restore failed: %w\", err)\n\t}\n\n\tslog.Info(\"restore completed\", \"path\", dbPath)\n\treturn nil\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *ReplicateCommand) Usage() {\n\tfmt.Printf(`\nThe replicate command starts a server to monitor \u0026 replicate databases.\nYou can specify your database \u0026 replicas in a configuration file or you can\nreplicate a single database file by specifying its path and its replicas in the\ncommand line arguments.\n\nUsage:\n\n\tlitestream replicate [arguments]\n\n\tlitestream replicate [arguments] DB_PATH REPLICA_URL\n\nArguments:\n\n\t-config PATH\n\t Specifies the configuration file.\n\t Defaults to %s\n\n\t-exec CMD\n\t Executes a subcommand. Litestream will exit when the child\n\t process exits. Useful for simple process management.\n\n\t-once\n\t Replicate once and exit. This performs a single sync of all\n\t databases and their replicas, then exits. Cannot be used with -exec.\n\n\t-force-snapshot\n\t Force a snapshot to be taken for all databases. Requires -once.\n\t This is useful for creating a complete backup before maintenance\n\t or when migrating databases between hosts.\n\n\t-enforce-retention\n\t Enforce retention policies for old snapshots. Requires -once.\n\t This removes snapshots that are older than the configured\n\t snapshot retention period.\n\n\t-log-level LEVEL\n\t Sets the log level. Overrides the config file setting.\n\t Valid values: trace, debug, info, warn, error\n\n\t-no-expand-env\n\t Disables environment variable expansion in configuration file.\n\n\t-restore-if-db-not-exists\n\t Restores the database from the replica if it doesn't exist.\n\t On first start with no backup, proceeds normally.\n\nRuntime control commands require the daemon control socket. This includes\nstart, stop, sync, register, unregister, info, and list. Enable it in the config:\n\n\tsocket:\n\t enabled: true\n\t path: /tmp/litestream.sock\n\n`[1:], DefaultConfigPath())\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"errors","path":"errors","span":{"start":[5,1],"end":[5,10],"bytes":[34,43]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[44,51]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[52,58]}},{"name":"slog","path":"log/slog","span":{"start":[8,1],"end":[8,12],"bytes":[59,70]}},{"name":"net","path":"net","span":{"start":[9,1],"end":[9,7],"bytes":[71,77]}},{"name":"http","path":"net/http","span":{"start":[10,1],"end":[10,12],"bytes":[78,89]}},{"name":"pprof","path":"net/http/pprof","alias":"_","span":{"start":[11,1],"end":[11,20],"bytes":[90,109]}},{"name":"os","path":"os","span":{"start":[12,1],"end":[12,6],"bytes":[110,115]}},{"name":"exec","path":"os/exec","span":{"start":[13,1],"end":[13,11],"bytes":[116,126]}},{"name":"strings","path":"strings","span":{"start":[14,1],"end":[14,11],"bytes":[127,137]}},{"name":"go-shellwords","path":"github.com/mattn/go-shellwords","span":{"start":[16,1],"end":[16,34],"bytes":[139,172]}},{"name":"promhttp","path":"github.com/prometheus/client_golang/prometheus/promhttp","span":{"start":[17,1],"end":[17,59],"bytes":[173,231]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[19,1],"end":[19,37],"bytes":[233,269]}},{"name":"abs","path":"github.com/benbjohnson/litestream/abs","span":{"start":[20,1],"end":[20,41],"bytes":[270,310]}},{"name":"file","path":"github.com/benbjohnson/litestream/file","span":{"start":[21,1],"end":[21,42],"bytes":[311,352]}},{"name":"gs","path":"github.com/benbjohnson/litestream/gs","span":{"start":[22,1],"end":[22,40],"bytes":[353,392]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[23,1],"end":[23,46],"bytes":[393,438]}},{"name":"nats","path":"github.com/benbjohnson/litestream/nats","span":{"start":[24,1],"end":[24,42],"bytes":[439,480]}},{"name":"oss","path":"github.com/benbjohnson/litestream/oss","span":{"start":[25,1],"end":[25,41],"bytes":[481,521]}},{"name":"s3","path":"github.com/benbjohnson/litestream/s3","span":{"start":[26,1],"end":[26,40],"bytes":[522,561]}},{"name":"sftp","path":"github.com/benbjohnson/litestream/sftp","span":{"start":[27,1],"end":[27,42],"bytes":[562,603]}}],"types":{"ReplicateCommand":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand","kind":"struct","span":{"start":[31,1],"end":[57,2],"bytes":[695,1464]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","kind":"method","span":{"start":[436,1],"end":[462,2],"bytes":[13759,14486]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[436,1],"end":[436,62],"bytes":[13759,13820]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"438:3":{"kind":"call","span":{"start":[438,3],"end":[438,18],"bytes":[13870,13885]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close"},"443:13":{"kind":"call","span":{"start":[443,13],"end":[443,29],"bytes":[13951,13967]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Close"},"444:4":{"kind":"call","span":{"start":[444,4],"end":[444,60],"bytes":[13985,14041]},"callee":null},"448:13":{"kind":"call","span":{"start":[448,13],"end":[448,31],"bytes":[14082,14100]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Close"},"449:7":{"kind":"call","span":{"start":[449,7],"end":[449,56],"bytes":[14121,14170]},"callee":null},"450:5":{"kind":"call","span":{"start":[450,5],"end":[450,71],"bytes":[14177,14243]},"callee":null},"452:5":{"kind":"call","span":{"start":[452,5],"end":[452,57],"bytes":[14260,14312]},"callee":null},"457:13":{"kind":"call","span":{"start":[457,13],"end":[457,26],"bytes":[14382,14395]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Close"},"458:4":{"kind":"call","span":{"start":[458,4],"end":[458,56],"bytes":[14413,14465]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","kind":"method","span":{"start":[66,1],"end":[175,2],"bytes":[1643,5439]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","parameters":[{"name":"_","type":"context.Context","span":{"start":[66,1],"end":[66,86],"bytes":[1643,1728]}},{"name":"args","type":"[]string","span":{"start":[66,1],"end":[66,86],"bytes":[1643,1728]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":20},"body":{"104:10":{"kind":"call","span":{"start":[104,10],"end":[104,79],"bytes":[3285,3354]},"callee":null},"104:69":{"kind":"call","span":{"start":[104,69],"end":[104,78],"bytes":[3344,3353]},"callee":null},"109:11":{"kind":"call","span":{"start":[109,11],"end":[109,74],"bytes":[3454,3517]},"callee":null},"113:14":{"kind":"call","span":{"start":[113,14],"end":[113,29],"bytes":[3607,3622]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig"},"118:4":{"kind":"call","span":{"start":[118,4],"end":[118,41],"bytes":[3765,3802]},"callee":null},"121:3":{"kind":"call","span":{"start":[121,3],"end":[121,55],"bytes":[3845,3897]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"},"124:26":{"kind":"call","span":{"start":[124,26],"end":[124,35],"bytes":[3949,3958]},"callee":null},"127:21":{"kind":"call","span":{"start":[127,21],"end":[127,30],"bytes":[4032,4041]},"callee":null},"129:7":{"kind":"call","span":{"start":[129,7],"end":[129,32],"bytes":[4135,4160]},"callee":null},"130:12":{"kind":"call","span":{"start":[130,12],"end":[130,96],"bytes":[4174,4258]},"callee":null},"133:24":{"kind":"call","span":{"start":[133,24],"end":[138,6],"bytes":[4337,4475]},"callee":null},"165:10":{"kind":"call","span":{"start":[165,10],"end":[165,60],"bytes":[5147,5197]},"callee":null},"168:10":{"kind":"call","span":{"start":[168,10],"end":[168,73],"bytes":[5243,5306]},"callee":null},"171:10":{"kind":"call","span":{"start":[171,10],"end":[171,76],"bytes":[5355,5421]},"callee":null},"67:8":{"kind":"call","span":{"start":[67,8],"end":[67,69],"bytes":[1736,1797]},"callee":null},"68:14":{"kind":"call","span":{"start":[68,14],"end":[68,57],"bytes":[1811,1854]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"69:18":{"kind":"call","span":{"start":[69,18],"end":[69,91],"bytes":[1872,1945]},"callee":null},"70:26":{"kind":"call","span":{"start":[70,26],"end":[70,118],"bytes":[1971,2063]},"callee":null},"71:14":{"kind":"call","span":{"start":[71,14],"end":[71,63],"bytes":[2077,2126]},"callee":null},"72:23":{"kind":"call","span":{"start":[72,23],"end":[72,95],"bytes":[2149,2221]},"callee":null},"73:26":{"kind":"call","span":{"start":[73,26],"end":[73,121],"bytes":[2247,2342]},"callee":null},"74:29":{"kind":"call","span":{"start":[74,29],"end":[74,51],"bytes":[2371,2393]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag"},"76:12":{"kind":"call","span":{"start":[76,12],"end":[76,26],"bytes":[2425,2439]},"callee":null},"81:9":{"kind":"call","span":{"start":[81,9],"end":[81,18],"bytes":[2539,2548]},"callee":null},"85:18":{"kind":"call","span":{"start":[85,18],"end":[85,37],"bytes":[2646,2665]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"},"87:22":{"kind":"call","span":{"start":[87,22],"end":[87,64],"bytes":[2691,2733]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"94:4":{"kind":"call","span":{"start":[94,4],"end":[94,41],"bytes":[2978,3015]},"callee":null},"99:4":{"kind":"call","span":{"start":[99,4],"end":[99,103],"bytes":[3108,3207]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"}}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","kind":"method","span":{"start":[178,1],"end":[390,2],"bytes":[5500,12474]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[178,1],"end":[178,66],"bytes":[5500,5565]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":53},"body":{"180:2":{"kind":"call","span":{"start":[180,2],"end":[180,78],"bytes":[5600,5676]},"callee":null},"184:16":{"kind":"call","span":{"start":[184,16],"end":[184,48],"bytes":[5754,5786]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP"},"188:6":{"kind":"call","span":{"start":[188,6],"end":[188,35],"bytes":[5828,5857]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start","is_goroutine":true},"192:5":{"kind":"call","span":{"start":[192,5],"end":[192,22],"bytes":[5887,5904]},"callee":null},"193:3":{"kind":"call","span":{"start":[193,3],"end":[193,56],"bytes":[5914,5967]},"callee":null},"199:14":{"kind":"call","span":{"start":[199,14],"end":[199,46],"bytes":[6162,6194]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded"},"213:19":{"kind":"call","span":{"start":[213,19],"end":[213,54],"bytes":[6459,6494]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig"},"217:10":{"kind":"call","span":{"start":[217,10],"end":[217,32],"bytes":[6543,6565]},"callee":null},"218:4":{"kind":"call","span":{"start":[218,4],"end":[218,113],"bytes":[6569,6678]},"callee":null},"218:76":{"kind":"call","span":{"start":[218,76],"end":[218,87],"bytes":[6641,6652]},"callee":null},"220:18":{"kind":"call","span":{"start":[220,18],"end":[223,38],"bytes":[6719,6835]},"callee":null},"227:15":{"kind":"call","span":{"start":[227,15],"end":[227,40],"bytes":[6909,6934]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"231:10":{"kind":"call","span":{"start":[231,10],"end":[231,25],"bytes":[6983,6998]},"callee":null},"235:12":{"kind":"call","span":{"start":[235,12],"end":[235,39],"bytes":[7018,7045]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.CompactionLevels"},"236:12":{"kind":"call","span":{"start":[236,12],"end":[236,44],"bytes":[7057,7089]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.NewStore"},"247:3":{"kind":"call","span":{"start":[247,3],"end":[247,48],"bytes":[7475,7520]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetL0Retention"},"253:3":{"kind":"call","span":{"start":[253,3],"end":[253,64],"bytes":[7690,7751]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetShutdownSyncTimeout"},"256:3":{"kind":"call","span":{"start":[256,3],"end":[256,66],"bytes":[7800,7863]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetShutdownSyncInterval"},"259:3":{"kind":"call","span":{"start":[259,3],"end":[259,36],"bytes":[7901,7934]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetVerifyCompaction"},"262:3":{"kind":"call","span":{"start":[262,3],"end":[262,37],"bytes":[8012,8046]},"callee":null},"268:3":{"kind":"call","span":{"start":[268,3],"end":[268,26],"bytes":[8178,8201]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetDone"},"275:23":{"kind":"call","span":{"start":[275,23],"end":[275,85],"bytes":[8398,8460]},"callee":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.NewHeartbeatClient"},"291:12":{"kind":"call","span":{"start":[291,12],"end":[291,29],"bytes":[8813,8830]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open"},"292:10":{"kind":"call","span":{"start":[292,10],"end":[292,50],"bytes":[8854,8894]},"callee":null},"296:3":{"kind":"call","span":{"start":[296,3],"end":[297,144],"bytes":[8933,9165]},"callee":null},"302:14":{"kind":"call","span":{"start":[302,14],"end":[302,43],"bytes":[9259,9288]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.NewServer"},"307:13":{"kind":"call","span":{"start":[307,13],"end":[307,29],"bytes":[9461,9477]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Start"},"308:4":{"kind":"call","span":{"start":[308,4],"end":[308,61],"bytes":[9495,9552]},"callee":null},"313:19":{"kind":"call","span":{"start":[313,19],"end":[313,77],"bytes":[9615,9673]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor"},"316:5":{"kind":"call","span":{"start":[316,5],"end":[316,14],"bytes":[9739,9748]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close"},"318:19":{"kind":"call","span":{"start":[318,19],"end":[318,37],"bytes":[9772,9790]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Close"},"319:5":{"kind":"call","span":{"start":[319,5],"end":[319,81],"bytes":[9814,9890]},"callee":null},"321:11":{"kind":"call","span":{"start":[321,11],"end":[321,82],"bytes":[9906,9977]},"callee":null},"323:25":{"kind":"call","span":{"start":[323,25],"end":[323,61],"bytes":[10006,10042]},"callee":null},"327:21":{"kind":"call","span":{"start":[327,21],"end":[327,34],"bytes":[10112,10125]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"329:3":{"kind":"call","span":{"start":[329,3],"end":[329,49],"bytes":[10148,10194]},"callee":null},"329:39":{"kind":"call","span":{"start":[329,39],"end":[329,48],"bytes":[10184,10193]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"330:15":{"kind":"call","span":{"start":[330,15],"end":[330,82],"bytes":[10209,10276]},"callee":null},"330:33":{"kind":"call","span":{"start":[330,33],"end":[330,48],"bytes":[10227,10242]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type"},"333:4":{"kind":"call","span":{"start":[333,4],"end":[333,58],"bytes":[10345,10399]},"callee":null},"333:44":{"kind":"call","span":{"start":[333,44],"end":[333,57],"bytes":[10385,10398]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.Path"},"335:4":{"kind":"call","span":{"start":[335,4],"end":[335,135],"bytes":[10429,10560]},"callee":null},"337:4":{"kind":"call","span":{"start":[337,4],"end":[337,81],"bytes":[10590,10667]},"callee":null},"339:4":{"kind":"call","span":{"start":[339,4],"end":[339,110],"bytes":[10698,10804]},"callee":null},"341:4":{"kind":"call","span":{"start":[341,4],"end":[341,98],"bytes":[10836,10930]},"callee":null},"343:4":{"kind":"call","span":{"start":[343,4],"end":[343,83],"bytes":[10962,11041]},"callee":null},"345:4":{"kind":"call","span":{"start":[345,4],"end":[345,106],"bytes":[11072,11174]},"callee":null},"347:4":{"kind":"call","span":{"start":[347,4],"end":[347,35],"bytes":[11189,11220]},"callee":null},"354:23":{"kind":"call","span":{"start":[354,23],"end":[354,55],"bytes":[11345,11377]},"callee":null},"355:11":{"kind":"call","span":{"start":[355,11],"end":[355,78],"bytes":[11402,11469]},"callee":null},"357:15":{"kind":"call","span":{"start":[357,15],"end":[357,50],"bytes":[11509,11544]},"callee":null},"360:3":{"kind":"call","span":{"start":[360,3],"end":[360,85],"bytes":[11552,11634]},"callee":null},"360:42":{"kind":"call","span":{"start":[360,42],"end":[360,84],"bytes":[11591,11633]},"callee":null},"361:6":{"kind":"call","span":{"start":[361,6],"end":[366,6],"bytes":[11640,11833]},"callee":null,"is_goroutine":true},"371:20":{"kind":"call","span":{"start":[371,20],"end":[371,51],"bytes":[11932,11963]},"callee":null},"373:11":{"kind":"call","span":{"start":[373,11],"end":[373,59],"bytes":[11992,12040]},"callee":null},"376:11":{"kind":"call","span":{"start":[376,11],"end":[376,65],"bytes":[12056,12110]},"callee":null},"377:15":{"kind":"call","span":{"start":[377,15],"end":[377,27],"bytes":[12125,12137]},"callee":null},"380:13":{"kind":"call","span":{"start":[380,13],"end":[380,26],"bytes":[12204,12217]},"callee":null},"381:11":{"kind":"call","span":{"start":[381,11],"end":[381,59],"bytes":[12242,12290]},"callee":null},"383:6":{"kind":"call","span":{"start":[383,6],"end":[383,43],"bytes":[12300,12337]},"callee":null,"is_goroutine":true},"386:6":{"kind":"call","span":{"start":[386,6],"end":[386,20],"bytes":[12442,12456]},"callee":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run.closure_1","kind":"function","span":{"start":[361,1],"end":[366,6],"bytes":[11635,11833]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run.closure_1","parameters":[],"body":{"362:28":{"kind":"call","span":{"start":[362,28],"end":[362,46],"bytes":[11676,11694]},"callee":null},"362:4":{"kind":"call","span":{"start":[362,4],"end":[362,47],"bytes":[11652,11695]},"callee":null},"363:14":{"kind":"call","span":{"start":[363,14],"end":[363,53],"bytes":[11709,11748]},"callee":null},"364:5":{"kind":"call","span":{"start":[364,5],"end":[364,60],"bytes":[11767,11822]},"callee":null}}},"closure_2":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run.closure_2","kind":"function","span":{"start":[383,1],"end":[383,43],"bytes":[12295,12337]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run.closure_2","parameters":[],"body":{"383:27":{"kind":"call","span":{"start":[383,27],"end":[383,39],"bytes":[12321,12333]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.SetDone":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.SetDone","kind":"method","span":{"start":[466,1],"end":[468,2],"bytes":[14633,14708]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.SetDone","parameters":[{"name":"done","type":"\u003c-chan struct{}","span":{"start":[466,1],"end":[466,59],"bytes":[14633,14691]}}],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Usage":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Usage","kind":"method","span":{"start":[519,1],"end":[575,2],"bytes":[16313,18076]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"520:2":{"kind":"call","span":{"start":[520,2],"end":[574,28],"bytes":[16351,18074]},"callee":null},"574:8":{"kind":"call","span":{"start":[574,8],"end":[574,27],"bytes":[18054,18073]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"}}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","kind":"method","span":{"start":[473,1],"end":[516,2],"bytes":[14946,16268]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[473,1],"end":[473,92],"bytes":[14946,15037]}},{"name":"dbConfig","type":"*github.com/benbjohnson/litestream/cmd/litestream.DBConfig","span":{"start":[473,1],"end":[473,92],"bytes":[14946,15037]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"474:17":{"kind":"call","span":{"start":[474,17],"end":[474,38],"bytes":[15054,15075]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand"},"480:15":{"kind":"call","span":{"start":[480,15],"end":[480,30],"bytes":[15160,15175]},"callee":null},"480:33":{"kind":"call","span":{"start":[480,33],"end":[480,51],"bytes":[15178,15196]},"callee":null},"481:3":{"kind":"call","span":{"start":[481,3],"end":[481,65],"bytes":[15201,15263]},"callee":null},"489:12":{"kind":"call","span":{"start":[489,12],"end":[489,34],"bytes":[15435,15457]},"callee":null},"492:10":{"kind":"call","span":{"start":[492,10],"end":[492,70],"bytes":[15511,15571]},"callee":null},"496:12":{"kind":"call","span":{"start":[496,12],"end":[496,41],"bytes":[15654,15683]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig"},"498:10":{"kind":"call","span":{"start":[498,10],"end":[498,66],"bytes":[15710,15766]},"callee":null},"502:9":{"kind":"call","span":{"start":[502,9],"end":[502,39],"bytes":[15799,15829]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions"},"505:2":{"kind":"call","span":{"start":[505,2],"end":[505,68],"bytes":[15857,15923]},"callee":null},"506:12":{"kind":"call","span":{"start":[506,12],"end":[506,31],"bytes":[15935,15954]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore"},"506:33":{"kind":"call","span":{"start":[506,33],"end":[506,77],"bytes":[15956,16000]},"callee":null},"508:3":{"kind":"call","span":{"start":[508,3],"end":[508,63],"bytes":[16058,16118]},"callee":null},"511:10":{"kind":"call","span":{"start":[511,10],"end":[511,47],"bytes":[16165,16202]},"callee":null},"514:2":{"kind":"call","span":{"start":[514,2],"end":[514,48],"bytes":[16208,16254]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","kind":"method","span":{"start":[394,1],"end":[433,2],"bytes":[12615,13721]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[394,1],"end":[394,58],"bytes":[12615,12672]}}],"metrics":{"cyclomatic":8},"body":{"396:8":{"kind":"call","span":{"start":[396,8],"end":[396,36],"bytes":[12695,12723]},"callee":null},"398:21":{"kind":"call","span":{"start":[398,21],"end":[398,34],"bytes":[12745,12758]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"399:3":{"kind":"call","span":{"start":[399,3],"end":[399,51],"bytes":[12763,12811]},"callee":null},"399:41":{"kind":"call","span":{"start":[399,41],"end":[399,50],"bytes":[12801,12810]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"402:12":{"kind":"call","span":{"start":[402,12],"end":[402,24],"bytes":[12883,12895]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync"},"403:10":{"kind":"call","span":{"start":[403,10],"end":[403,60],"bytes":[12919,12969]},"callee":null},"403:45":{"kind":"call","span":{"start":[403,45],"end":[403,54],"bytes":[12954,12963]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"408:12":{"kind":"call","span":{"start":[408,12],"end":[408,32],"bytes":[13051,13071]},"callee":null},"409:10":{"kind":"call","span":{"start":[409,10],"end":[409,63],"bytes":[13095,13148]},"callee":null},"409:48":{"kind":"call","span":{"start":[409,48],"end":[409,57],"bytes":[13133,13142]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"415:4":{"kind":"call","span":{"start":[415,4],"end":[415,51],"bytes":[13226,13273]},"callee":null},"415:41":{"kind":"call","span":{"start":[415,41],"end":[415,50],"bytes":[13263,13272]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"416:16":{"kind":"call","span":{"start":[416,16],"end":[416,32],"bytes":[13289,13305]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot"},"417:11":{"kind":"call","span":{"start":[417,11],"end":[417,56],"bytes":[13330,13375]},"callee":null},"417:41":{"kind":"call","span":{"start":[417,41],"end":[417,50],"bytes":[13360,13369]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"424:4":{"kind":"call","span":{"start":[424,4],"end":[424,55],"bytes":[13463,13514]},"callee":null},"424:45":{"kind":"call","span":{"start":[424,45],"end":[424,54],"bytes":[13504,13513]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"425:13":{"kind":"call","span":{"start":[425,13],"end":[425,54],"bytes":[13527,13568]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention"},"426:11":{"kind":"call","span":{"start":[426,11],"end":[426,69],"bytes":[13593,13651]},"callee":null},"426:54":{"kind":"call","span":{"start":[426,54],"end":[426,63],"bytes":[13636,13645]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"432:2":{"kind":"call","span":{"start":[432,2],"end":[432,44],"bytes":[13677,13719]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce.closure_1","kind":"function","span":{"start":[396,1],"end":[396,36],"bytes":[12688,12723]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce.closure_1","parameters":[],"body":{}}}}},"fields":{"Config":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/Config","kind":"field","type":"github.com/benbjohnson/litestream/cmd/litestream.Config","span":{"start":[40,1],"end":[40,15],"bytes":[1007,1021]}},"MCP":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/MCP","kind":"field","type":"*github.com/benbjohnson/litestream/cmd/litestream.MCPServer","span":{"start":[43,1],"end":[43,16],"bytes":[1038,1053]}},"Server":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/Server","kind":"field","type":"*github.com/benbjohnson/litestream.Server","span":{"start":[46,1],"end":[46,27],"bytes":[1092,1118]}},"Store":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/Store","kind":"field","type":"*github.com/benbjohnson/litestream.Store","span":{"start":[49,1],"end":[49,25],"bytes":[1174,1198]}},"cmd":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/cmd","kind":"field","type":"*os/exec.Cmd","span":{"start":[32,1],"end":[32,33],"bytes":[726,758]}},"directoryMonitors":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/directoryMonitors","kind":"field","type":"[]*github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor","span":{"start":[52,1],"end":[52,39],"bytes":[1255,1293]}},"done":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/done","kind":"field","type":"\u003c-chan struct{}","span":{"start":[56,1],"end":[56,22],"bytes":[1441,1462]}},"enforceRetention":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/enforceRetention","kind":"field","type":"bool","span":{"start":[38,1],"end":[38,61],"bytes":[945,1005]}},"execCh":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/execCh","kind":"field","type":"chan error","span":{"start":[33,1],"end":[33,47],"bytes":[759,805]}},"forceSnapshot":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/forceSnapshot","kind":"field","type":"bool","span":{"start":[37,1],"end":[37,57],"bytes":[888,944]}},"once":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/once","kind":"field","type":"bool","span":{"start":[36,1],"end":[36,50],"bytes":[838,887]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.NewReplicateCommand":{"id":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicateCommand","kind":"function","span":{"start":[59,1],"end":[63,2],"bytes":[1466,1572]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.NewReplicateCommand","parameters":[],"return_type":"*github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand","metrics":{"cyclomatic":1},"body":{"61:11":{"kind":"call","span":{"start":[61,11],"end":[61,27],"bytes":[1550,1566]},"callee":null}}}},"content_hash":"aaa1236d303151b5f8390be24542cffb535b6a3842d7b2142b724ffe9b9f4991"},"cmd/litestream/reset.go":{"id":"can://go/litestream/cmd/litestream/reset.go","kind":"module","span":{"start":[1,1],"end":[190,1],"bytes":[0,4629]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// ResetCommand is a command for resetting local Litestream state for a database.\ntype ResetCommand struct{}\n\n// Run executes the command.\nfunc (c *ResetCommand) Run(ctx context.Context, args []string) (err error) {\n\tfs := flag.NewFlagSet(\"litestream-reset\", flag.ContinueOnError)\n\tconfigPath, noExpandEnv := registerConfigFlag(fs)\n\tdryRun := fs.Bool(\"dry-run\", false, \"print local state that would be removed without deleting\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\t// Validate arguments - need exactly one database path\n\tif fs.NArg() == 0 {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path required\",\n\t\t\thint: \"litestream reset /path/to/db\",\n\t\t}\n\t} else if fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tdbPath := fs.Arg(0)\n\n\t// Make absolute if needed\n\tif !filepath.IsAbs(dbPath) {\n\t\tif dbPath, err = filepath.Abs(dbPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Load configuration to find the database (if config exists)\n\tvar dbConfig *DBConfig\n\tif *configPath != \"\" {\n\t\tconfig, configErr := ReadConfigFile(*configPath, !*noExpandEnv)\n\t\tif configErr != nil {\n\t\t\treturn fmt.Errorf(\"cannot read config: %w\", configErr)\n\t\t}\n\n\t\t// Find database config\n\t\tfor _, dbc := range config.DBs {\n\t\t\texpandedPath := dbc.Path\n\t\t\tif !filepath.IsAbs(expandedPath) {\n\t\t\t\texpandedPath, _ = filepath.Abs(expandedPath)\n\t\t\t}\n\t\t\tif expandedPath == dbPath {\n\t\t\t\tdbConfig = dbc\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// If no config found, check if database file exists\n\tif _, err := os.Stat(dbPath); os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"database does not exist: %s\", dbPath)\n\t} else if err != nil {\n\t\treturn fmt.Errorf(\"cannot access database: %w\", err)\n\t}\n\n\t// Create DB instance\n\tvar db *litestream.DB\n\tif dbConfig != nil {\n\t\tdb, err = NewDBFromConfig(dbConfig)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot create database from config: %w\", err)\n\t\t}\n\t} else {\n\t\tdb = litestream.NewDB(dbPath)\n\t}\n\n\t// Check if meta path exists\n\tmetaPath := db.MetaPath()\n\tif _, err := os.Stat(metaPath); os.IsNotExist(err) {\n\t\tfmt.Printf(\"No local state to reset for %s\\n\", dbPath)\n\t\tfmt.Printf(\"Meta directory does not exist: %s\\n\", metaPath)\n\t\treturn nil\n\t}\n\n\tif *dryRun {\n\t\tfiles, err := localLTXFiles(db.LTXDir())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"dry run failed: %w\", err)\n\t\t}\n\n\t\tfmt.Printf(\"Dry run: local Litestream state would be reset for: %s\\n\", dbPath)\n\t\tfmt.Printf(\"Would remove: %s\\n\", db.LTXDir())\n\t\tif len(files) == 0 {\n\t\t\tfmt.Println(\"No local LTX files would be removed.\")\n\t\t\treturn nil\n\t\t}\n\n\t\tfmt.Println(\"Files that would be removed:\")\n\t\tfor _, file := range files {\n\t\t\tfmt.Printf(\" %s\\n\", file)\n\t\t}\n\t\tfmt.Println(\"No files were removed.\")\n\t\treturn nil\n\t}\n\n\t// Perform the reset\n\tfmt.Printf(\"Resetting local Litestream state for: %s\\n\", dbPath)\n\tfmt.Printf(\"Removing: %s\\n\", db.LTXDir())\n\n\tif err := db.ResetLocalState(ctx); err != nil {\n\t\treturn fmt.Errorf(\"reset failed: %w\", err)\n\t}\n\n\tfmt.Println(\"Reset complete. Next replication sync will create a fresh snapshot.\")\n\treturn nil\n}\n\nfunc localLTXFiles(root string) ([]string, error) {\n\tif _, err := os.Stat(root); os.IsNotExist(err) {\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar files []string\n\tif err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d.IsDir() {\n\t\t\treturn nil\n\t\t}\n\t\tfiles = append(files, path)\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\treturn files, nil\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *ResetCommand) Usage() {\n\tfmt.Printf(`\nThe reset command clears local Litestream state for a database.\n\nThis is useful for recovering from corrupted or missing LTX files. The reset\nremoves local LTX files from the metadata directory, forcing Litestream to\ncreate a fresh snapshot on the next sync. The database file itself is not\nmodified.\n\nUsage:\n\n\tlitestream reset [arguments] \u003cpath\u003e\n\nArguments:\n\n\t-config PATH\n\t Specifies the configuration file.\n\t Defaults to %s\n\n\t-no-expand-env\n\t Disables environment variable expansion in configuration file.\n\n\t-dry-run\n\t Print the local LTX files that would be removed without deleting them.\n\nExamples:\n\n\t# Reset local state for a specific database\n\tlitestream reset /path/to/database.db\n\n\t# Preview local files that would be removed\n\tlitestream reset -dry-run /path/to/database.db\n\n\t# Reset using a specific configuration file\n\tlitestream reset -config /etc/litestream.yml /path/to/database.db\n\n`[1:],\n\t\tDefaultConfigPath(),\n\t)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"flag","path":"flag","span":{"start":[5,1],"end":[5,8],"bytes":[34,41]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[42,48]}},{"name":"fs","path":"io/fs","span":{"start":[7,1],"end":[7,9],"bytes":[49,57]}},{"name":"os","path":"os","span":{"start":[8,1],"end":[8,6],"bytes":[58,63]}},{"name":"filepath","path":"path/filepath","span":{"start":[9,1],"end":[9,17],"bytes":[64,80]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[11,1],"end":[11,37],"bytes":[82,118]}}],"types":{"ResetCommand":{"id":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand","kind":"struct","span":{"start":[15,1],"end":[15,27],"bytes":[204,230]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run":{"id":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","kind":"method","span":{"start":[18,1],"end":[124,2],"bytes":[261,3146]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[18,1],"end":[18,77],"bytes":[261,337]}},{"name":"args","type":"[]string","span":{"start":[18,1],"end":[18,77],"bytes":[261,337]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":21},"body":{"100:3":{"kind":"call","span":{"start":[100,3],"end":[100,48],"bytes":[2508,2553]},"callee":null},"100:36":{"kind":"call","span":{"start":[100,36],"end":[100,47],"bytes":[2541,2552]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir"},"101:6":{"kind":"call","span":{"start":[101,6],"end":[101,16],"bytes":[2559,2569]},"callee":null},"102:4":{"kind":"call","span":{"start":[102,4],"end":[102,55],"bytes":[2580,2631]},"callee":null},"106:3":{"kind":"call","span":{"start":[106,3],"end":[106,46],"bytes":[2653,2696]},"callee":null},"108:4":{"kind":"call","span":{"start":[108,4],"end":[108,30],"bytes":[2731,2757]},"callee":null},"110:3":{"kind":"call","span":{"start":[110,3],"end":[110,40],"bytes":[2764,2801]},"callee":null},"115:2":{"kind":"call","span":{"start":[115,2],"end":[115,66],"bytes":[2842,2906]},"callee":null},"116:2":{"kind":"call","span":{"start":[116,2],"end":[116,43],"bytes":[2908,2949]},"callee":null},"116:31":{"kind":"call","span":{"start":[116,31],"end":[116,42],"bytes":[2937,2948]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir"},"118:12":{"kind":"call","span":{"start":[118,12],"end":[118,35],"bytes":[2962,2985]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState"},"119:10":{"kind":"call","span":{"start":[119,10],"end":[119,45],"bytes":[3009,3044]},"callee":null},"122:2":{"kind":"call","span":{"start":[122,2],"end":[122,84],"bytes":[3050,3132]},"callee":null},"19:8":{"kind":"call","span":{"start":[19,8],"end":[19,65],"bytes":[345,402]},"callee":null},"20:29":{"kind":"call","span":{"start":[20,29],"end":[20,51],"bytes":[431,453]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag"},"21:12":{"kind":"call","span":{"start":[21,12],"end":[21,97],"bytes":[465,550]},"callee":null},"23:12":{"kind":"call","span":{"start":[23,12],"end":[23,26],"bytes":[582,596]},"callee":null},"28:5":{"kind":"call","span":{"start":[28,5],"end":[28,14],"bytes":[688,697]},"callee":null},"33:12":{"kind":"call","span":{"start":[33,12],"end":[33,21],"bytes":[824,833]},"callee":null},"34:10":{"kind":"call","span":{"start":[34,10],"end":[34,42],"bytes":[849,881]},"callee":null},"37:12":{"kind":"call","span":{"start":[37,12],"end":[37,21],"bytes":[897,906]},"callee":null},"40:6":{"kind":"call","span":{"start":[40,6],"end":[40,28],"bytes":[941,963]},"callee":null},"41:20":{"kind":"call","span":{"start":[41,20],"end":[41,40],"bytes":[985,1005]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool"},"49:24":{"kind":"call","span":{"start":[49,24],"end":[49,66],"bytes":[1176,1218]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"51:11":{"kind":"call","span":{"start":[51,11],"end":[51,58],"bytes":[1253,1300]},"callee":null},"57:8":{"kind":"call","span":{"start":[57,8],"end":[57,36],"bytes":[1402,1430]},"callee":null},"58:23":{"kind":"call","span":{"start":[58,23],"end":[58,49],"bytes":[1455,1481]},"callee":null},"68:15":{"kind":"call","span":{"start":[68,15],"end":[68,30],"bytes":[1628,1643]},"callee":null},"68:32":{"kind":"call","span":{"start":[68,32],"end":[68,50],"bytes":[1645,1663]},"callee":null},"69:10":{"kind":"call","span":{"start":[69,10],"end":[69,59],"bytes":[1675,1724]},"callee":null},"71:10":{"kind":"call","span":{"start":[71,10],"end":[71,55],"bytes":[1758,1803]},"callee":null},"77:13":{"kind":"call","span":{"start":[77,13],"end":[77,38],"bytes":[1888,1913]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"79:11":{"kind":"call","span":{"start":[79,11],"end":[79,68],"bytes":[1942,1999]},"callee":null},"82:8":{"kind":"call","span":{"start":[82,8],"end":[82,32],"bytes":[2021,2045]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB"},"86:14":{"kind":"call","span":{"start":[86,14],"end":[86,27],"bytes":[2093,2106]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MetaPath"},"87:15":{"kind":"call","span":{"start":[87,15],"end":[87,32],"bytes":[2121,2138]},"callee":null},"87:34":{"kind":"call","span":{"start":[87,34],"end":[87,52],"bytes":[2140,2158]},"callee":null},"88:3":{"kind":"call","span":{"start":[88,3],"end":[88,57],"bytes":[2163,2217]},"callee":null},"89:3":{"kind":"call","span":{"start":[89,3],"end":[89,62],"bytes":[2220,2279]},"callee":null},"94:17":{"kind":"call","span":{"start":[94,17],"end":[94,43],"bytes":[2327,2353]},"callee":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles"},"94:31":{"kind":"call","span":{"start":[94,31],"end":[94,42],"bytes":[2341,2352]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir"},"96:11":{"kind":"call","span":{"start":[96,11],"end":[96,48],"bytes":[2382,2419]},"callee":null},"99:3":{"kind":"call","span":{"start":[99,3],"end":[99,81],"bytes":[2427,2505]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Usage":{"id":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Usage","kind":"method","span":{"start":[150,1],"end":[189,2],"bytes":[3638,4628]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"151:2":{"kind":"call","span":{"start":[151,2],"end":[188,3],"bytes":[3672,4626]},"callee":null},"187:3":{"kind":"call","span":{"start":[187,3],"end":[187,22],"bytes":[4603,4622]},"callee":null}}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles":{"id":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles","kind":"function","span":{"start":[126,1],"end":[147,2],"bytes":[3148,3593]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles","parameters":[{"name":"root","type":"string","span":{"start":[126,1],"end":[126,52],"bytes":[3148,3199]}}],"return_type":"([]string, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"127:15":{"kind":"call","span":{"start":[127,15],"end":[127,28],"bytes":[3214,3227]},"callee":null},"127:30":{"kind":"call","span":{"start":[127,30],"end":[127,48],"bytes":[3229,3247]},"callee":null},"134:12":{"kind":"call","span":{"start":[134,12],"end":[143,4],"bytes":[3345,3537]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles/github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles.closure_1","kind":"function","span":{"start":[134,1],"end":[143,18],"bytes":[3334,3551]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles.closure_1","parameters":[{"name":"path","type":"string","span":{"start":[134,1],"end":[134,86],"bytes":[3334,3419]}},{"name":"d","type":"io/fs.DirEntry","span":{"start":[134,1],"end":[134,86],"bytes":[3334,3419]}},{"name":"err","type":"error","span":{"start":[134,1],"end":[134,86],"bytes":[3334,3419]}}],"return_type":"error","error_channel":["error"],"body":{"138:6":{"kind":"call","span":{"start":[138,6],"end":[138,15],"bytes":[3461,3470]},"callee":null},"141:11":{"kind":"call","span":{"start":[141,11],"end":[141,30],"bytes":[3501,3520]},"callee":null}}}}}},"content_hash":"a57b2135a633a6531841eb509f9588a256f6a738aaaaf94ed221c80e70b17ec4"},"cmd/litestream/restore.go":{"id":"can://go/litestream/cmd/litestream/restore.go","kind":"module","span":{"start":[1,1],"end":[454,1],"bytes":[0,13339]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"strconv\"\n\t\"text/tabwriter\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\n// RestoreCommand represents a command to restore a database from a backup.\ntype RestoreCommand struct{}\n\n// Run executes the command.\nfunc (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) {\n\topt := litestream.NewRestoreOptions()\n\n\tfs := flag.NewFlagSet(\"litestream-restore\", flag.ContinueOnError)\n\tconfigPath, noExpandEnv := registerConfigFlag(fs)\n\tfs.StringVar(\u0026opt.OutputPath, \"o\", \"\", \"output path\")\n\tfs.Var((*txidVar)(\u0026opt.TXID), \"txid\", \"transaction ID\")\n\tfs.IntVar(\u0026opt.Parallelism, \"parallelism\", opt.Parallelism, \"parallelism\")\n\tifDBNotExists := fs.Bool(\"if-db-not-exists\", false, \"\")\n\tifReplicaExists := fs.Bool(\"if-replica-exists\", false, \"\")\n\ttimestampStr := fs.String(\"timestamp\", \"\", \"timestamp\")\n\tdryRun := fs.Bool(\"dry-run\", false, \"print restore plan without writing\")\n\tforce := fs.Bool(\"force\", false, \"overwrite existing output database\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.BoolVar(\u0026opt.Follow, \"f\", false, \"follow mode\")\n\tfs.DurationVar(\u0026opt.FollowInterval, \"follow-interval\", opt.FollowInterval, \"polling interval for follow mode\")\n\tintegrityCheck := fs.String(\"integrity-check\", \"none\", \"post-restore integrity check: none, quick, or full\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t} else if fs.NArg() == 0 || fs.Arg(0) == \"\" {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path or replica URL required\",\n\t\t\thint: \"litestream restore -o /path/to/db s3://bucket/prefix\",\n\t\t}\n\t} else if fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tlogOutput := os.Stdout\n\tif *jsonOutput {\n\t\tlogOutput = os.Stderr\n\t}\n\tinternal.InitLog(logOutput, \"INFO\", \"text\", false)\n\n\t// When follow mode is enabled, set up signal handling so Ctrl+C stops\n\t// the follow loop cleanly.\n\tif opt.Follow {\n\t\tch := signalChan()\n\t\tcancelCtx, cancel := context.WithCancel(ctx)\n\t\tgo func() {\n\t\t\tselect {\n\t\t\tcase \u003c-ch:\n\t\t\t\tcancel()\n\t\t\tcase \u003c-cancelCtx.Done():\n\t\t\t}\n\t\t}()\n\t\tctx = cancelCtx\n\t\tdefer cancel()\n\t}\n\n\tswitch *integrityCheck {\n\tcase \"none\":\n\t\topt.IntegrityCheck = litestream.IntegrityCheckNone\n\tcase \"quick\":\n\t\topt.IntegrityCheck = litestream.IntegrityCheckQuick\n\tcase \"full\":\n\t\topt.IntegrityCheck = litestream.IntegrityCheckFull\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid -integrity-check value: %s\", *integrityCheck)\n\t}\n\n\t// Parse timestamp, if specified.\n\tif *timestampStr != \"\" {\n\t\tif opt.Timestamp, err = time.Parse(time.RFC3339, *timestampStr); err != nil {\n\t\t\treturn errors.New(\"invalid -timestamp, must specify in ISO 8601 format (e.g. 2000-01-01T00:00:00Z)\")\n\t\t}\n\t}\n\n\t// Determine replica to restore from.\n\tvar r *litestream.Replica\n\tif litestream.IsURL(fs.Arg(0)) {\n\t\tif *configPath != \"\" {\n\t\t\treturn fmt.Errorf(\"cannot specify a replica URL and the -config flag\")\n\t\t}\n\t\tif r, err = c.loadFromURL(ctx, fs.Arg(0), *ifDBNotExists, \u0026opt); errors.Is(err, errSkipDBExists) {\n\t\t\tslog.Info(\"database already exists, skipping\")\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif *configPath == \"\" {\n\t\t\t*configPath = DefaultConfigPath()\n\t\t}\n\t\tif r, err = c.loadFromConfig(ctx, fs.Arg(0), *configPath, !*noExpandEnv, *ifDBNotExists, \u0026opt); errors.Is(err, errSkipDBExists) {\n\t\t\tslog.Info(\"database already exists, skipping\")\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif *dryRun {\n\t\tplan, err := c.dryRunPlan(ctx, fs.Arg(0), r, opt)\n\t\tif errors.Is(err, litestream.ErrTxNotAvailable) {\n\t\t\treturn fmt.Errorf(\"no matching backup files available\")\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif *jsonOutput {\n\t\t\toutput, err := json.MarshalIndent(plan, \"\", \" \")\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t\t}\n\t\t\tfmt.Println(string(output))\n\t\t\treturn nil\n\t\t}\n\t\tc.printDryRunPlan(plan)\n\t\treturn nil\n\t}\n\n\tif !opt.Follow {\n\t\tif err := c.prepareOutputPath(opt.OutputPath, *force); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\ttxid := c.restoreTXID(ctx, r, opt)\n\tstart := time.Now()\n\tif err := r.Restore(ctx, opt); errors.Is(err, litestream.ErrTxNotAvailable) {\n\t\tif *ifReplicaExists {\n\t\t\tslog.Info(\"no matching backups found\")\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"no matching backup files available\")\n\t} else if err != nil {\n\t\treturn err\n\t}\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(RestoreResult{\n\t\t\tDBPath: opt.OutputPath,\n\t\t\tReplica: r.Client.Type(),\n\t\t\tTXID: txid,\n\t\t\tDurationMS: time.Since(start).Milliseconds(),\n\t\t\tIntegrityCheck: *integrityCheck,\n\t\t}, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t}\n\treturn nil\n}\n\ntype RestorePlan struct {\n\tSource string `json:\"source\"`\n\tTargetPath string `json:\"target_path\"`\n\tReplica string `json:\"replica\"`\n\tMinTXID string `json:\"min_txid\"`\n\tMaxTXID string `json:\"max_txid\"`\n\tFiles []RestorePlanFile `json:\"files\"`\n}\n\ntype RestorePlanFile struct {\n\tLevel int `json:\"level\"`\n\tName string `json:\"name\"`\n\tMinTXID string `json:\"min_txid\"`\n\tMaxTXID string `json:\"max_txid\"`\n\tSize int64 `json:\"size\"`\n\tTimestamp string `json:\"timestamp\"`\n}\n\ntype RestoreResult struct {\n\tDBPath string `json:\"db_path\"`\n\tReplica string `json:\"replica\"`\n\tTXID string `json:\"txid\"`\n\tDurationMS int64 `json:\"duration_ms\"`\n\tIntegrityCheck string `json:\"integrity_check\"`\n}\n\nfunc (c *RestoreCommand) dryRunPlan(ctx context.Context, source string, r *litestream.Replica, opt litestream.RestoreOptions) (RestorePlan, error) {\n\tif opt.Follow {\n\t\treturn RestorePlan{}, fmt.Errorf(\"cannot use -dry-run with -f\")\n\t}\n\n\tinfos, err := litestream.CalcRestorePlan(ctx, r.Client, opt.TXID, opt.Timestamp, r.Logger())\n\tif err != nil {\n\t\treturn RestorePlan{}, err\n\t}\n\tif len(infos) == 0 {\n\t\treturn RestorePlan{}, litestream.ErrTxNotAvailable\n\t}\n\n\tplan := RestorePlan{\n\t\tSource: source,\n\t\tTargetPath: opt.OutputPath,\n\t\tReplica: r.Client.Type(),\n\t\tMinTXID: infos[0].MinTXID.String(),\n\t\tMaxTXID: infos[len(infos)-1].MaxTXID.String(),\n\t\tFiles: make([]RestorePlanFile, 0, len(infos)),\n\t}\n\tfor _, info := range infos {\n\t\tplan.Files = append(plan.Files, RestorePlanFile{\n\t\t\tLevel: info.Level,\n\t\t\tName: ltx.FormatFilename(info.MinTXID, info.MaxTXID),\n\t\t\tMinTXID: info.MinTXID.String(),\n\t\t\tMaxTXID: info.MaxTXID.String(),\n\t\t\tSize: info.Size,\n\t\t\tTimestamp: info.CreatedAt.Format(time.RFC3339),\n\t\t})\n\t}\n\treturn plan, nil\n}\n\nfunc (c *RestoreCommand) printDryRunPlan(plan RestorePlan) {\n\tfmt.Println(\"Restore plan:\")\n\tfmt.Printf(\" source: %s\\n\", plan.Source)\n\tfmt.Printf(\" target: %s\\n\", plan.TargetPath)\n\tfmt.Printf(\" replica: %s\\n\", plan.Replica)\n\tfmt.Printf(\" txid range: %s - %s\\n\", plan.MinTXID, plan.MaxTXID)\n\tfmt.Println()\n\tfmt.Println(\"Files to fetch:\")\n\n\tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)\n\tdefer w.Flush()\n\tfmt.Fprintln(w, \"level\\tfile\\tmin_txid\\tmax_txid\\tsize\\ttimestamp\")\n\tfor _, file := range plan.Files {\n\t\tfmt.Fprintf(w, \"%d\\t%s\\t%s\\t%s\\t%d\\t%s\\n\",\n\t\t\tfile.Level,\n\t\t\tfile.Name,\n\t\t\tfile.MinTXID,\n\t\t\tfile.MaxTXID,\n\t\t\tfile.Size,\n\t\t\tfile.Timestamp,\n\t\t)\n\t}\n}\n\nfunc (c *RestoreCommand) restoreTXID(ctx context.Context, r *litestream.Replica, opt litestream.RestoreOptions) string {\n\tif opt.TXID != 0 {\n\t\treturn opt.TXID.String()\n\t}\n\tif opt.Follow {\n\t\treturn \"\"\n\t}\n\tinfos, err := litestream.CalcRestorePlan(ctx, r.Client, opt.TXID, opt.Timestamp, r.Logger())\n\tif err != nil || len(infos) == 0 {\n\t\treturn \"\"\n\t}\n\treturn infos[len(infos)-1].MaxTXID.String()\n}\n\nfunc (c *RestoreCommand) prepareOutputPath(path string, force bool) error {\n\tinfo, err := os.Stat(path)\n\tif os.IsNotExist(err) {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn fmt.Errorf(\"cannot access output path: %w\", err)\n\t}\n\tif info.IsDir() {\n\t\treturn fmt.Errorf(\"cannot restore, output path is a directory: %s\", path)\n\t}\n\n\tif info.Size() \u003e 0 \u0026\u0026 !force {\n\t\treturn fmt.Errorf(\"cannot restore, output path already exists and is not empty: %s. Use -force to overwrite\", path)\n\t}\n\n\tfor _, sidecarPath := range []string{path + \"-wal\", path + \"-shm\", path + \"-journal\"} {\n\t\tif _, err := os.Stat(sidecarPath); err == nil \u0026\u0026 !force {\n\t\t\treturn fmt.Errorf(\"cannot restore, SQLite sidecar path already exists: %s. Use -force to overwrite\", sidecarPath)\n\t\t} else if err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"cannot access SQLite sidecar path: %w\", err)\n\t\t}\n\t}\n\n\tfor _, removePath := range []string{path, path + \"-wal\", path + \"-shm\", path + \"-journal\"} {\n\t\tif err := os.Remove(removePath); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"remove existing output path: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// loadFromURL creates a replica \u0026 updates the restore options from a replica URL.\nfunc (c *RestoreCommand) loadFromURL(ctx context.Context, replicaURL string, ifDBNotExists bool, opt *litestream.RestoreOptions) (*litestream.Replica, error) {\n\tif opt.OutputPath == \"\" {\n\t\treturn nil, \u0026usageError{\n\t\t\tmessage: \"-o is required when restoring from a replica URL\",\n\t\t\thint: fmt.Sprintf(\"litestream restore -o /path/to/db %s\", replicaURL),\n\t\t}\n\t}\n\n\t// Exit successfully if the output file already exists.\n\tif _, err := os.Stat(opt.OutputPath); !os.IsNotExist(err) \u0026\u0026 ifDBNotExists {\n\t\treturn nil, errSkipDBExists\n\t}\n\n\tsyncInterval := litestream.DefaultSyncInterval\n\tr, err := NewReplicaFromConfig(\u0026ReplicaConfig{\n\t\tURL: replicaURL,\n\t\tReplicaSettings: ReplicaSettings{\n\t\t\tSyncInterval: \u0026syncInterval,\n\t\t},\n\t}, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, err = r.CalcRestoreTarget(ctx, *opt)\n\treturn r, err\n}\n\n// loadFromConfig returns a replica \u0026 updates the restore options from a DB reference.\nfunc (c *RestoreCommand) loadFromConfig(_ context.Context, dbPath, configPath string, expandEnv, ifDBNotExists bool, opt *litestream.RestoreOptions) (*litestream.Replica, error) {\n\t// Load configuration.\n\tconfig, err := ReadConfigFile(configPath, expandEnv)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Lookup database from configuration file by path.\n\tif dbPath, err = expand(dbPath); err != nil {\n\t\treturn nil, err\n\t}\n\tdbConfig := config.DBConfig(dbPath)\n\tif dbConfig == nil {\n\t\treturn nil, fmt.Errorf(\"database not found in config: %s\", dbPath)\n\t}\n\tdb, err := NewDBFromConfig(dbConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Restore into original database path if not specified.\n\tif opt.OutputPath == \"\" {\n\t\topt.OutputPath = dbPath\n\t}\n\n\t// Exit successfully if the output file already exists.\n\tif _, err := os.Stat(opt.OutputPath); !os.IsNotExist(err) \u0026\u0026 ifDBNotExists {\n\t\treturn nil, errSkipDBExists\n\t}\n\n\treturn db.Replica, nil\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *RestoreCommand) Usage() {\n\tfmt.Printf(`\nThe restore command recovers a database from a previous snapshot and WAL.\n\nUsage:\n\n\tlitestream restore [arguments] DB_PATH\n\n\tlitestream restore [arguments] REPLICA_URL\n\nArguments:\n\n\t-config PATH\n\t Specifies the configuration file.\n\t Defaults to %s\n\n\t-no-expand-env\n\t Disables environment variable expansion in configuration file.\n\n\t-txid TXID\n\t Restore up to a specific hex-encoded transaction ID (inclusive).\n\t Defaults to use the highest available transaction.\n\n\t-timestamp TIMESTAMP\n\t Restore to a specific point-in-time.\n\t Defaults to use the latest available backup.\n\n\t-o PATH\n\t Output path of the restored database.\n\t Defaults to original DB path.\n\n\t-if-db-not-exists\n\t Returns exit code of 0 if the database already exists.\n\n\t-if-replica-exists\n\t Returns exit code of 0 if no backups found.\n\n\t-dry-run\n\t Print the restore plan and exit without writing a database.\n\n\t-force\n\t Overwrite an existing output database and SQLite sidecar files.\n\n\t-json\n\t Output raw JSON summary on successful restore.\n\n\t-f\n\t Follow mode. After restoring, continuously poll for and apply\n\t new changes. Similar to tail -f. The restored database should\n\t only be opened in read-only mode by consumers.\n\t Cannot be used with -txid or -timestamp.\n\n\t-follow-interval DURATION\n\t Polling interval for follow mode.\n\t Defaults to 1s.\n\n\t-parallelism NUM\n\t Determines the number of WAL files downloaded in parallel.\n\t Defaults to `+strconv.Itoa(litestream.DefaultRestoreParallelism)+`.\n\n\t-integrity-check MODE\n\t Run a post-restore integrity check on the database.\n\t MODE is one of: none, quick, full.\n\t Defaults to none.\n\n\nExamples:\n\n\t# Restore latest replica for database to original location.\n\t$ litestream restore /path/to/db\n\n\t# Restore replica for database to a given point in time.\n\t$ litestream restore -timestamp 2020-01-01T00:00:00Z /path/to/db\n\n\t# Restore latest replica for database to new /tmp directory\n\t$ litestream restore -o /tmp/db /path/to/db\n\n\t# Restore database from S3 replica URL.\n\t$ litestream restore -o /tmp/db s3://mybucket/db\n\n\t# Preview restore plan without writing a database.\n\t$ litestream restore -dry-run -o /tmp/db s3://mybucket/db\n\n\t# Continuously restore (follow) a database from a replica.\n\t$ litestream restore -f -o /tmp/read-replica.db s3://mybucket/db\n\n`[1:],\n\t\tDefaultConfigPath(),\n\t)\n}\n\nvar errSkipDBExists = errors.New(\"database already exists, skipping\")\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[34,50]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[51,60]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[61,68]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[69,75]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[76,87]}},{"name":"os","path":"os","span":{"start":[10,1],"end":[10,6],"bytes":[88,93]}},{"name":"strconv","path":"strconv","span":{"start":[11,1],"end":[11,11],"bytes":[94,104]}},{"name":"tabwriter","path":"text/tabwriter","span":{"start":[12,1],"end":[12,18],"bytes":[105,122]}},{"name":"time","path":"time","span":{"start":[13,1],"end":[13,8],"bytes":[123,130]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[15,1],"end":[15,27],"bytes":[132,158]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[17,1],"end":[17,37],"bytes":[160,196]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[18,1],"end":[18,46],"bytes":[197,242]}}],"types":{"RestoreCommand":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand","kind":"struct","span":{"start":[22,1],"end":[22,29],"bytes":[322,350]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","kind":"method","span":{"start":[25,1],"end":[168,2],"bytes":[381,4831]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[25,1],"end":[25,79],"bytes":[381,459]}},{"name":"args","type":"[]string","span":{"start":[25,1],"end":[25,79],"bytes":[381,459]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":35},"body":{"100:15":{"kind":"call","span":{"start":[100,15],"end":[100,66],"bytes":[3026,3077]},"callee":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL"},"100:34":{"kind":"call","span":{"start":[100,34],"end":[100,43],"bytes":[3045,3054]},"callee":null},"100:68":{"kind":"call","span":{"start":[100,68],"end":[100,99],"bytes":[3079,3110]},"callee":null},"101:4":{"kind":"call","span":{"start":[101,4],"end":[101,50],"bytes":[3116,3162]},"callee":null},"108:18":{"kind":"call","span":{"start":[108,18],"end":[108,37],"bytes":[3272,3291]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"},"110:15":{"kind":"call","span":{"start":[110,15],"end":[110,97],"bytes":[3310,3392]},"callee":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig"},"110:37":{"kind":"call","span":{"start":[110,37],"end":[110,46],"bytes":[3332,3341]},"callee":null},"110:99":{"kind":"call","span":{"start":[110,99],"end":[110,130],"bytes":[3394,3425]},"callee":null},"111:4":{"kind":"call","span":{"start":[111,4],"end":[111,50],"bytes":[3431,3477]},"callee":null},"119:16":{"kind":"call","span":{"start":[119,16],"end":[119,52],"bytes":[3568,3604]},"callee":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan"},"119:34":{"kind":"call","span":{"start":[119,34],"end":[119,43],"bytes":[3586,3595]},"callee":null},"120:6":{"kind":"call","span":{"start":[120,6],"end":[120,50],"bytes":[3610,3654]},"callee":null},"121:11":{"kind":"call","span":{"start":[121,11],"end":[121,59],"bytes":[3667,3715]},"callee":null},"126:19":{"kind":"call","span":{"start":[126,19],"end":[126,53],"bytes":[3796,3830]},"callee":null},"128:12":{"kind":"call","span":{"start":[128,12],"end":[128,60],"bytes":[3861,3909]},"callee":null},"130:16":{"kind":"call","span":{"start":[130,16],"end":[130,30],"bytes":[3930,3944]},"callee":null},"130:4":{"kind":"call","span":{"start":[130,4],"end":[130,31],"bytes":[3918,3945]},"callee":null},"133:3":{"kind":"call","span":{"start":[133,3],"end":[133,26],"bytes":[3966,3989]},"callee":null},"138:13":{"kind":"call","span":{"start":[138,13],"end":[138,56],"bytes":[4037,4080]},"callee":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.prepareOutputPath"},"143:10":{"kind":"call","span":{"start":[143,10],"end":[143,36],"bytes":[4126,4152]},"callee":null},"144:11":{"kind":"call","span":{"start":[144,11],"end":[144,21],"bytes":[4163,4173]},"callee":null},"145:12":{"kind":"call","span":{"start":[145,12],"end":[145,31],"bytes":[4185,4204]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore"},"145:33":{"kind":"call","span":{"start":[145,33],"end":[145,77],"bytes":[4206,4250]},"callee":null},"147:4":{"kind":"call","span":{"start":[147,4],"end":[147,42],"bytes":[4280,4318]},"callee":null},"150:10":{"kind":"call","span":{"start":[150,10],"end":[150,58],"bytes":[4346,4394]},"callee":null},"155:18":{"kind":"call","span":{"start":[155,18],"end":[161,15],"bytes":[4470,4703]},"callee":null},"157:20":{"kind":"call","span":{"start":[157,20],"end":[157,35],"bytes":[4558,4573]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type"},"159:20":{"kind":"call","span":{"start":[159,20],"end":[159,37],"bytes":[4619,4636]},"callee":null},"163:11":{"kind":"call","span":{"start":[163,11],"end":[163,59],"bytes":[4732,4780]},"callee":null},"165:15":{"kind":"call","span":{"start":[165,15],"end":[165,29],"bytes":[4799,4813]},"callee":null},"165:3":{"kind":"call","span":{"start":[165,3],"end":[165,30],"bytes":[4787,4814]},"callee":null},"26:9":{"kind":"call","span":{"start":[26,9],"end":[26,39],"bytes":[468,498]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions"},"28:8":{"kind":"call","span":{"start":[28,8],"end":[28,67],"bytes":[507,566]},"callee":null},"29:29":{"kind":"call","span":{"start":[29,29],"end":[29,51],"bytes":[595,617]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag"},"30:2":{"kind":"call","span":{"start":[30,2],"end":[30,55],"bytes":[619,672]},"callee":null},"31:2":{"kind":"call","span":{"start":[31,2],"end":[31,57],"bytes":[674,729]},"callee":null},"31:9":{"kind":"call","span":{"start":[31,9],"end":[31,30],"bytes":[681,702]},"callee":null},"32:2":{"kind":"call","span":{"start":[32,2],"end":[32,76],"bytes":[731,805]},"callee":null},"33:19":{"kind":"call","span":{"start":[33,19],"end":[33,57],"bytes":[824,862]},"callee":null},"34:21":{"kind":"call","span":{"start":[34,21],"end":[34,60],"bytes":[883,922]},"callee":null},"35:18":{"kind":"call","span":{"start":[35,18],"end":[35,57],"bytes":[940,979]},"callee":null},"36:12":{"kind":"call","span":{"start":[36,12],"end":[36,75],"bytes":[991,1054]},"callee":null},"37:11":{"kind":"call","span":{"start":[37,11],"end":[37,72],"bytes":[1065,1126]},"callee":null},"38:16":{"kind":"call","span":{"start":[38,16],"end":[38,57],"bytes":[1142,1183]},"callee":null},"39:2":{"kind":"call","span":{"start":[39,2],"end":[39,52],"bytes":[1185,1235]},"callee":null},"40:2":{"kind":"call","span":{"start":[40,2],"end":[40,112],"bytes":[1237,1347]},"callee":null},"41:20":{"kind":"call","span":{"start":[41,20],"end":[41,110],"bytes":[1367,1457]},"callee":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool"},"43:12":{"kind":"call","span":{"start":[43,12],"end":[43,26],"bytes":[1489,1503]},"callee":null},"45:12":{"kind":"call","span":{"start":[45,12],"end":[45,21],"bytes":[1542,1551]},"callee":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion"},"45:30":{"kind":"call","span":{"start":[45,30],"end":[45,39],"bytes":[1560,1569]},"callee":null},"50:12":{"kind":"call","span":{"start":[50,12],"end":[50,21],"bytes":[1736,1745]},"callee":null},"51:10":{"kind":"call","span":{"start":[51,10],"end":[51,42],"bytes":[1761,1793]},"callee":null},"58:2":{"kind":"call","span":{"start":[58,2],"end":[58,52],"bytes":[1868,1918]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"},"63:9":{"kind":"call","span":{"start":[63,9],"end":[63,21],"bytes":[2046,2058]},"callee":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.signalChan"},"64:24":{"kind":"call","span":{"start":[64,24],"end":[64,47],"bytes":[2082,2105]},"callee":null},"65:6":{"kind":"call","span":{"start":[65,6],"end":[71,6],"bytes":[2111,2197]},"callee":null,"is_goroutine":true},"73:9":{"kind":"call","span":{"start":[73,9],"end":[73,17],"bytes":[2224,2232]},"callee":null},"84:10":{"kind":"call","span":{"start":[84,10],"end":[84,75],"bytes":[2485,2550]},"callee":null},"89:27":{"kind":"call","span":{"start":[89,27],"end":[89,66],"bytes":[2642,2681]},"callee":null},"90:11":{"kind":"call","span":{"start":[90,11],"end":[90,105],"bytes":[2706,2800]},"callee":null},"96:22":{"kind":"call","span":{"start":[96,22],"end":[96,31],"bytes":[2896,2905]},"callee":null},"96:5":{"kind":"call","span":{"start":[96,5],"end":[96,32],"bytes":[2879,2906]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL"},"98:11":{"kind":"call","span":{"start":[98,11],"end":[98,74],"bytes":[2944,3007]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run.closure_1","kind":"function","span":{"start":[65,1],"end":[71,6],"bytes":[2106,2197]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run.closure_1","parameters":[],"body":{"68:5":{"kind":"call","span":{"start":[68,5],"end":[68,13],"bytes":[2150,2158]},"callee":null},"69:11":{"kind":"call","span":{"start":[69,11],"end":[69,27],"bytes":[2169,2185]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Usage":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Usage","kind":"method","span":{"start":[362,1],"end":[451,2],"bytes":[10842,13267]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"363:2":{"kind":"call","span":{"start":[363,2],"end":[450,3],"bytes":[10878,13265]},"callee":null},"420:20":{"kind":"call","span":{"start":[420,20],"end":[420,70],"bytes":[12363,12413]},"callee":null},"449:3":{"kind":"call","span":{"start":[449,3],"end":[449,22],"bytes":[13242,13261]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan","kind":"method","span":{"start":[196,1],"end":[228,2],"bytes":[5626,6684]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[196,1],"end":[196,149],"bytes":[5626,5774]}},{"name":"source","type":"string","span":{"start":[196,1],"end":[196,149],"bytes":[5626,5774]}},{"name":"r","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[196,1],"end":[196,149],"bytes":[5626,5774]}},{"name":"opt","type":"github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[196,1],"end":[196,149],"bytes":[5626,5774]}}],"return_type":"(github.com/benbjohnson/litestream/cmd/litestream.RestorePlan, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"198:25":{"kind":"call","span":{"start":[198,25],"end":[198,66],"bytes":[5816,5857]},"callee":null},"201:16":{"kind":"call","span":{"start":[201,16],"end":[201,94],"bytes":[5877,5955]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan"},"201:83":{"kind":"call","span":{"start":[201,83],"end":[201,93],"bytes":[5944,5954]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger"},"205:5":{"kind":"call","span":{"start":[205,5],"end":[205,15],"bytes":[6008,6018]},"callee":null},"212:15":{"kind":"call","span":{"start":[212,15],"end":[212,30],"bytes":[6171,6186]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type"},"213:15":{"kind":"call","span":{"start":[213,15],"end":[213,40],"bytes":[6202,6227]},"callee":null},"214:15":{"kind":"call","span":{"start":[214,15],"end":[214,51],"bytes":[6243,6279]},"callee":null},"214:21":{"kind":"call","span":{"start":[214,21],"end":[214,31],"bytes":[6249,6259]},"callee":null},"215:15":{"kind":"call","span":{"start":[215,15],"end":[215,53],"bytes":[6295,6333]},"callee":null},"215:42":{"kind":"call","span":{"start":[215,42],"end":[215,52],"bytes":[6322,6332]},"callee":null},"218:16":{"kind":"call","span":{"start":[218,16],"end":[225,5],"bytes":[6383,6661]},"callee":null},"220:15":{"kind":"call","span":{"start":[220,15],"end":[220,61],"bytes":[6459,6505]},"callee":null},"221:15":{"kind":"call","span":{"start":[221,15],"end":[221,36],"bytes":[6521,6542]},"callee":null},"222:15":{"kind":"call","span":{"start":[222,15],"end":[222,36],"bytes":[6558,6579]},"callee":null},"224:15":{"kind":"call","span":{"start":[224,15],"end":[224,50],"bytes":[6620,6655]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig","kind":"method","span":{"start":[328,1],"end":[359,2],"bytes":[9862,10797]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig","parameters":[{"name":"_","type":"context.Context","span":{"start":[328,1],"end":[328,180],"bytes":[9862,10041]}},{"name":"dbPath","type":"string","span":{"start":[328,1],"end":[328,180],"bytes":[9862,10041]}},{"name":"configPath","type":"string","span":{"start":[328,1],"end":[328,180],"bytes":[9862,10041]}},{"name":"expandEnv","type":"bool","span":{"start":[328,1],"end":[328,180],"bytes":[9862,10041]}},{"name":"ifDBNotExists","type":"bool","span":{"start":[328,1],"end":[328,180],"bytes":[9862,10041]}},{"name":"opt","type":"*github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[328,1],"end":[328,180],"bytes":[9862,10041]}}],"return_type":"(*github.com/benbjohnson/litestream.Replica, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"330:17":{"kind":"call","span":{"start":[330,17],"end":[330,54],"bytes":[10082,10119]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"336:19":{"kind":"call","span":{"start":[336,19],"end":[336,33],"bytes":[10230,10244]},"callee":null},"339:14":{"kind":"call","span":{"start":[339,14],"end":[339,37],"bytes":[10293,10316]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig"},"341:15":{"kind":"call","span":{"start":[341,15],"end":[341,69],"bytes":[10353,10407]},"callee":null},"343:13":{"kind":"call","span":{"start":[343,13],"end":[343,38],"bytes":[10423,10448]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"354:15":{"kind":"call","span":{"start":[354,15],"end":[354,38],"bytes":[10674,10697]},"callee":null},"354:41":{"kind":"call","span":{"start":[354,41],"end":[354,59],"bytes":[10700,10718]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL","kind":"method","span":{"start":[300,1],"end":[325,2],"bytes":[8949,9773]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[300,1],"end":[300,160],"bytes":[8949,9108]}},{"name":"replicaURL","type":"string","span":{"start":[300,1],"end":[300,160],"bytes":[8949,9108]}},{"name":"ifDBNotExists","type":"bool","span":{"start":[300,1],"end":[300,160],"bytes":[8949,9108]}},{"name":"opt","type":"*github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[300,1],"end":[300,160],"bytes":[8949,9108]}}],"return_type":"(*github.com/benbjohnson/litestream.Replica, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"304:13":{"kind":"call","span":{"start":[304,13],"end":[304,76],"bytes":[9239,9302]},"callee":null},"309:15":{"kind":"call","span":{"start":[309,15],"end":[309,38],"bytes":[9383,9406]},"callee":null},"309:41":{"kind":"call","span":{"start":[309,41],"end":[309,59],"bytes":[9409,9427]},"callee":null},"314:12":{"kind":"call","span":{"start":[314,12],"end":[319,9],"bytes":[9540,9677]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig"},"323:11":{"kind":"call","span":{"start":[323,11],"end":[323,41],"bytes":[9726,9756]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CalcRestoreTarget"}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.prepareOutputPath":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.prepareOutputPath","kind":"method","span":{"start":[268,1],"end":[297,2],"bytes":[7749,8864]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.prepareOutputPath","parameters":[{"name":"path","type":"string","span":{"start":[268,1],"end":[268,76],"bytes":[7749,7824]}},{"name":"force","type":"bool","span":{"start":[268,1],"end":[268,76],"bytes":[7749,7824]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"269:15":{"kind":"call","span":{"start":[269,15],"end":[269,28],"bytes":[7839,7852]},"callee":null},"270:5":{"kind":"call","span":{"start":[270,5],"end":[270,23],"bytes":[7857,7875]},"callee":null},"273:10":{"kind":"call","span":{"start":[273,10],"end":[273,58],"bytes":[7924,7972]},"callee":null},"275:5":{"kind":"call","span":{"start":[275,5],"end":[275,17],"bytes":[7980,7992]},"callee":null},"276:10":{"kind":"call","span":{"start":[276,10],"end":[276,76],"bytes":[8004,8070]},"callee":null},"279:5":{"kind":"call","span":{"start":[279,5],"end":[279,16],"bytes":[8079,8090]},"callee":null},"280:10":{"kind":"call","span":{"start":[280,10],"end":[280,118],"bytes":[8116,8224]},"callee":null},"284:16":{"kind":"call","span":{"start":[284,16],"end":[284,36],"bytes":[8333,8353]},"callee":null},"285:11":{"kind":"call","span":{"start":[285,11],"end":[285,117],"bytes":[8388,8494]},"callee":null},"286:28":{"kind":"call","span":{"start":[286,28],"end":[286,46],"bytes":[8522,8540]},"callee":null},"287:11":{"kind":"call","span":{"start":[287,11],"end":[287,67],"bytes":[8553,8609]},"callee":null},"292:13":{"kind":"call","span":{"start":[292,13],"end":[292,34],"bytes":[8724,8745]},"callee":null},"292:51":{"kind":"call","span":{"start":[292,51],"end":[292,69],"bytes":[8762,8780]},"callee":null},"293:11":{"kind":"call","span":{"start":[293,11],"end":[293,61],"bytes":[8793,8843]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.printDryRunPlan":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.printDryRunPlan","kind":"method","span":{"start":[230,1],"end":[252,2],"bytes":[6686,7351]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.printDryRunPlan","parameters":[{"name":"plan","type":"github.com/benbjohnson/litestream/cmd/litestream.RestorePlan","span":{"start":[230,1],"end":[230,61],"bytes":[6686,6746]}}],"metrics":{"cyclomatic":2},"body":{"231:2":{"kind":"call","span":{"start":[231,2],"end":[231,30],"bytes":[6748,6776]},"callee":null},"232:2":{"kind":"call","span":{"start":[232,2],"end":[232,43],"bytes":[6778,6819]},"callee":null},"233:2":{"kind":"call","span":{"start":[233,2],"end":[233,47],"bytes":[6821,6866]},"callee":null},"234:2":{"kind":"call","span":{"start":[234,2],"end":[234,45],"bytes":[6868,6911]},"callee":null},"235:2":{"kind":"call","span":{"start":[235,2],"end":[235,67],"bytes":[6913,6978]},"callee":null},"236:2":{"kind":"call","span":{"start":[236,2],"end":[236,15],"bytes":[6980,6993]},"callee":null},"237:2":{"kind":"call","span":{"start":[237,2],"end":[237,32],"bytes":[6995,7025]},"callee":null},"239:7":{"kind":"call","span":{"start":[239,7],"end":[239,54],"bytes":[7033,7080]},"callee":null},"240:8":{"kind":"call","span":{"start":[240,8],"end":[240,17],"bytes":[7088,7097]},"callee":null},"241:2":{"kind":"call","span":{"start":[241,2],"end":[241,69],"bytes":[7099,7166]},"callee":null},"243:3":{"kind":"call","span":{"start":[243,3],"end":[250,4],"bytes":[7204,7346]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.restoreTXID":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.restoreTXID","kind":"method","span":{"start":[254,1],"end":[266,2],"bytes":[7353,7747]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.restoreTXID","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[254,1],"end":[254,121],"bytes":[7353,7473]}},{"name":"r","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[254,1],"end":[254,121],"bytes":[7353,7473]}},{"name":"opt","type":"github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[254,1],"end":[254,121],"bytes":[7353,7473]}}],"return_type":"string","metrics":{"cyclomatic":4},"body":{"256:10":{"kind":"call","span":{"start":[256,10],"end":[256,27],"bytes":[7503,7520]},"callee":null},"261:16":{"kind":"call","span":{"start":[261,16],"end":[261,94],"bytes":[7571,7649]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan"},"261:83":{"kind":"call","span":{"start":[261,83],"end":[261,93],"bytes":[7638,7648]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger"},"262:19":{"kind":"call","span":{"start":[262,19],"end":[262,29],"bytes":[7668,7678]},"callee":null},"265:15":{"kind":"call","span":{"start":[265,15],"end":[265,25],"bytes":[7715,7725]},"callee":null},"265:9":{"kind":"call","span":{"start":[265,9],"end":[265,45],"bytes":[7709,7745]},"callee":null}}}}},"RestorePlan":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan","kind":"struct","span":{"start":[170,1],"end":[177,2],"bytes":[4833,5145]},"callables":{},"fields":{"Files":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan/Files","kind":"field","type":"[]github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile","span":{"start":[176,1],"end":[176,45],"bytes":[5099,5143]}},"MaxTXID":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan/MaxTXID","kind":"field","type":"string","span":{"start":[175,1],"end":[175,48],"bytes":[5051,5098]}},"MinTXID":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan/MinTXID","kind":"field","type":"string","span":{"start":[174,1],"end":[174,48],"bytes":[5003,5050]}},"Replica":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan/Replica","kind":"field","type":"string","span":{"start":[173,1],"end":[173,47],"bytes":[4956,5002]}},"Source":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan/Source","kind":"field","type":"string","span":{"start":[171,1],"end":[171,46],"bytes":[4859,4904]}},"TargetPath":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlan/TargetPath","kind":"field","type":"string","span":{"start":[172,1],"end":[172,51],"bytes":[4905,4955]}}}},"RestorePlanFile":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile","kind":"struct","span":{"start":[179,1],"end":[186,2],"bytes":[5147,5384]},"callables":{},"fields":{"Level":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile/Level","kind":"field","type":"int","span":{"start":[180,1],"end":[180,33],"bytes":[5177,5209]}},"MaxTXID":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile/MaxTXID","kind":"field","type":"string","span":{"start":[183,1],"end":[183,36],"bytes":[5278,5313]}},"MinTXID":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile/MinTXID","kind":"field","type":"string","span":{"start":[182,1],"end":[182,36],"bytes":[5242,5277]}},"Name":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile/Name","kind":"field","type":"string","span":{"start":[181,1],"end":[181,32],"bytes":[5210,5241]}},"Size":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile/Size","kind":"field","type":"int64","span":{"start":[184,1],"end":[184,32],"bytes":[5314,5345]}},"Timestamp":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestorePlanFile/Timestamp","kind":"field","type":"string","span":{"start":[185,1],"end":[185,37],"bytes":[5346,5382]}}}},"RestoreResult":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreResult","kind":"struct","span":{"start":[188,1],"end":[194,2],"bytes":[5386,5624]},"callables":{},"fields":{"DBPath":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreResult/DBPath","kind":"field","type":"string","span":{"start":[189,1],"end":[189,40],"bytes":[5414,5453]}},"DurationMS":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreResult/DurationMS","kind":"field","type":"int64","span":{"start":[192,1],"end":[192,44],"bytes":[5531,5574]}},"IntegrityCheck":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreResult/IntegrityCheck","kind":"field","type":"string","span":{"start":[193,1],"end":[193,48],"bytes":[5575,5622]}},"Replica":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreResult/Replica","kind":"field","type":"string","span":{"start":[190,1],"end":[190,40],"bytes":[5454,5493]}},"TXID":{"id":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreResult/TXID","kind":"field","type":"string","span":{"start":[191,1],"end":[191,37],"bytes":[5494,5530]}}}}},"functions":{},"content_hash":"86e46d8df1604840766b9f0392d007410769da6c8459a88d3b534e8a312e4373"},"cmd/litestream/start.go":{"id":"can://go/litestream/cmd/litestream/start.go","kind":"module","span":{"start":[1,1],"end":[159,1],"bytes":[0,4011]},"package":"main","source":"package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// StartCommand represents the command to start replication for a database.\ntype StartCommand struct{}\n\n// Run executes the start command.\nfunc (c *StartCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-start\", flag.ContinueOnError)\n\ttimeout := fs.Int(\"timeout\", 30, \"timeout in seconds\")\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() == 0 {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path required\",\n\t\t\thint: \"litestream start /path/to/db\",\n\t\t}\n\t}\n\tif fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tdbPath := fs.Arg(0)\n\n\t// Create HTTP client that connects via Unix socket with timeout\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\treq := litestream.StartRequest{\n\t\tPath: dbPath,\n\t\tTimeout: *timeout,\n\t}\n\treqBody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}\n\n\tresp, err := client.Post(\"http://localhost/start\", \"application/json\", bytes.NewReader(reqBody))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"start failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"start failed: %s\", string(body))\n\t}\n\n\tvar result litestream.StartResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tconfirmation := StartStopResult{\n\t\tStatus: result.Status,\n\t\tDBPath: result.Path,\n\t\tState: \"running\",\n\t\tTXID: result.TXID,\n\t\tSocket: *socketPath,\n\t}\n\tif err := printStartStopResult(confirmation, *jsonOutput); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\ntype StartStopResult struct {\n\tStatus string `json:\"status\"`\n\tDBPath string `json:\"db_path\"`\n\tState string `json:\"state\"`\n\tTXID uint64 `json:\"txid\"`\n\tSocket string `json:\"socket\"`\n}\n\nfunc printStartStopResult(result StartStopResult, jsonOutput bool) error {\n\tif jsonOutput {\n\t\toutput, err := json.MarshalIndent(result, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tfmt.Printf(\"status: %s\\n\", result.Status)\n\tfmt.Printf(\"db_path: %s\\n\", result.DBPath)\n\tfmt.Printf(\"state: %s\\n\", result.State)\n\tfmt.Printf(\"txid: %d\\n\", result.TXID)\n\tfmt.Printf(\"socket: %s\\n\", result.Socket)\n\n\treturn nil\n}\n\n// Usage prints the help text for the start command.\nfunc (c *StartCommand) Usage() {\n\tfmt.Println(`\nusage: litestream start [OPTIONS] DB_PATH\n\nStart replication for a database.\n\nOptions:\n -timeout SECONDS\n Maximum time to wait in seconds (default: 30).\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -json\n Output raw JSON instead of human-readable text.\n\nExamples:\n # Start replication for a database on the running daemon.\n $ litestream start /path/to/db\n\n # Start replication and emit a JSON confirmation.\n $ litestream start -json /path/to/db\n\n # Start replication using a non-default control socket.\n $ litestream start -socket /tmp/litestream.sock /path/to/db\n\n # Start replication and wait up to 10 seconds for the daemon response.\n $ litestream start -timeout 10 /path/to/db\n`[1:])\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"json","path":"encoding/json","span":{"start":[6,1],"end":[6,17],"bytes":[43,59]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[60,67]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[68,74]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[75,80]}},{"name":"net","path":"net","span":{"start":[10,1],"end":[10,7],"bytes":[81,87]}},{"name":"http","path":"net/http","span":{"start":[11,1],"end":[11,12],"bytes":[88,99]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[100,107]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[14,1],"end":[14,37],"bytes":[109,145]}}],"types":{"StartCommand":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartCommand","kind":"struct","span":{"start":[18,1],"end":[18,27],"bytes":[225,251]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartCommand/github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run","kind":"method","span":{"start":[21,1],"end":[99,2],"bytes":[288,2473]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[21,1],"end":[21,71],"bytes":[288,358]}},{"name":"args","type":"[]string","span":{"start":[21,1],"end":[21,71],"bytes":[288,358]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"22:8":{"kind":"call","span":{"start":[22,8],"end":[22,65],"bytes":[366,423]},"callee":null},"23:13":{"kind":"call","span":{"start":[23,13],"end":[23,56],"bytes":[436,479]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,86],"bytes":[495,565]},"callee":null},"25:16":{"kind":"call","span":{"start":[25,16],"end":[25,57],"bytes":[581,622]},"callee":null},"27:12":{"kind":"call","span":{"start":[27,12],"end":[27,26],"bytes":[654,668]},"callee":null},"31:5":{"kind":"call","span":{"start":[31,5],"end":[31,14],"bytes":[704,713]},"callee":null},"37:5":{"kind":"call","span":{"start":[37,5],"end":[37,14],"bytes":[836,845]},"callee":null},"38:10":{"kind":"call","span":{"start":[38,10],"end":[38,42],"bytes":[861,893]},"callee":null},"41:12":{"kind":"call","span":{"start":[41,12],"end":[41,21],"bytes":[909,918]},"callee":null},"44:19":{"kind":"call","span":{"start":[44,19],"end":[44,42],"bytes":[1004,1027]},"callee":null},"58:18":{"kind":"call","span":{"start":[58,18],"end":[58,35],"bytes":[1367,1384]},"callee":null},"60:10":{"kind":"call","span":{"start":[60,10],"end":[60,58],"bytes":[1411,1459]},"callee":null},"63:15":{"kind":"call","span":{"start":[63,15],"end":[63,98],"bytes":[1478,1561]},"callee":null},"63:73":{"kind":"call","span":{"start":[63,73],"end":[63,97],"bytes":[1536,1560]},"callee":null},"65:10":{"kind":"call","span":{"start":[65,10],"end":[65,68],"bytes":[1588,1646]},"callee":null},"67:8":{"kind":"call","span":{"start":[67,8],"end":[67,25],"bytes":[1657,1674]},"callee":null},"69:15":{"kind":"call","span":{"start":[69,15],"end":[69,36],"bytes":[1690,1711]},"callee":null},"71:10":{"kind":"call","span":{"start":[71,10],"end":[71,56],"bytes":[1738,1784]},"callee":null},"76:13":{"kind":"call","span":{"start":[76,13],"end":[76,43],"bytes":[1879,1909]},"callee":null},"77:11":{"kind":"call","span":{"start":[77,11],"end":[77,56],"bytes":[1957,2002]},"callee":null},"79:10":{"kind":"call","span":{"start":[79,10],"end":[79,54],"bytes":[2016,2060]},"callee":null},"79:41":{"kind":"call","span":{"start":[79,41],"end":[79,53],"bytes":[2047,2059]},"callee":null},"83:12":{"kind":"call","span":{"start":[83,12],"end":[83,41],"bytes":[2113,2142]},"callee":null},"84:10":{"kind":"call","span":{"start":[84,10],"end":[84,57],"bytes":[2166,2213]},"callee":null},"94:12":{"kind":"call","span":{"start":[94,12],"end":[94,59],"bytes":[2381,2428]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartCommand/github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run.closure_1","kind":"function","span":{"start":[48,1],"end":[50,6],"bytes":[1123,1264]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[48,1],"end":[48,73],"bytes":[1123,1195]}},{"name":"_","type":"string","span":{"start":[48,1],"end":[48,73],"bytes":[1123,1195]}},{"name":"_","type":"string","span":{"start":[48,1],"end":[48,73],"bytes":[1123,1195]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"49:12":{"kind":"call","span":{"start":[49,12],"end":[49,63],"bytes":[1207,1258]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Usage":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartCommand/github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Usage","kind":"method","span":{"start":[129,1],"end":[158,2],"bytes":[3214,4010]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"130:2":{"kind":"call","span":{"start":[130,2],"end":[157,7],"bytes":[3248,4008]},"callee":null}}}}},"StartStopResult":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartStopResult","kind":"struct","span":{"start":[101,1],"end":[107,2],"bytes":[2475,2659]},"callables":{},"fields":{"DBPath":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartStopResult/DBPath","kind":"field","type":"string","span":{"start":[103,1],"end":[103,32],"bytes":[2536,2567]}},"Socket":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartStopResult/Socket","kind":"field","type":"string","span":{"start":[106,1],"end":[106,31],"bytes":[2627,2657]}},"State":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartStopResult/State","kind":"field","type":"string","span":{"start":[104,1],"end":[104,30],"bytes":[2568,2597]}},"Status":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartStopResult/Status","kind":"field","type":"string","span":{"start":[102,1],"end":[102,31],"bytes":[2505,2535]}},"TXID":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartStopResult/TXID","kind":"field","type":"uint64","span":{"start":[105,1],"end":[105,29],"bytes":[2598,2626]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.printStartStopResult":{"id":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.printStartStopResult","kind":"function","span":{"start":[109,1],"end":[126,2],"bytes":[2661,3159]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.printStartStopResult","parameters":[{"name":"result","type":"github.com/benbjohnson/litestream/cmd/litestream.StartStopResult","span":{"start":[109,1],"end":[109,75],"bytes":[2661,2735]}},{"name":"jsonOutput","type":"bool","span":{"start":[109,1],"end":[109,75],"bytes":[2661,2735]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"111:18":{"kind":"call","span":{"start":[111,18],"end":[111,54],"bytes":[2770,2806]},"callee":null},"113:11":{"kind":"call","span":{"start":[113,11],"end":[113,59],"bytes":[2835,2883]},"callee":null},"115:15":{"kind":"call","span":{"start":[115,15],"end":[115,29],"bytes":[2902,2916]},"callee":null},"115:3":{"kind":"call","span":{"start":[115,3],"end":[115,30],"bytes":[2890,2917]},"callee":null},"119:2":{"kind":"call","span":{"start":[119,2],"end":[119,43],"bytes":[2936,2977]},"callee":null},"120:2":{"kind":"call","span":{"start":[120,2],"end":[120,44],"bytes":[2979,3021]},"callee":null},"121:2":{"kind":"call","span":{"start":[121,2],"end":[121,41],"bytes":[3023,3062]},"callee":null},"122:2":{"kind":"call","span":{"start":[122,2],"end":[122,39],"bytes":[3064,3101]},"callee":null},"123:2":{"kind":"call","span":{"start":[123,2],"end":[123,43],"bytes":[3103,3144]},"callee":null}}}},"content_hash":"84e42db46c78b62cc29bf708a9610b4ddb39e2e2ce292b681993a6b1316102d6"},"cmd/litestream/status.go":{"id":"can://go/litestream/cmd/litestream/status.go","kind":"module","span":{"start":[1,1],"end":[174,1],"bytes":[0,3920]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"text/tabwriter\"\n\n\t\"github.com/dustin/go-humanize\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// StatusCommand is a command for displaying replication status.\ntype StatusCommand struct{}\n\n// Run executes the command.\nfunc (c *StatusCommand) Run(ctx context.Context, args []string) (err error) {\n\tfs := flag.NewFlagSet(\"litestream-status\", flag.ContinueOnError)\n\tconfigPath, noExpandEnv := registerConfigFlag(fs)\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\t// Load configuration.\n\tif *configPath == \"\" {\n\t\t*configPath = DefaultConfigPath()\n\t}\n\tconfig, err := ReadConfigFile(*configPath, !*noExpandEnv)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// If a specific database path is provided, filter to just that one.\n\tvar filterPath string\n\tif fs.NArg() \u003e 0 {\n\t\tfilterPath = fs.Arg(0)\n\t}\n\n\tstatuses := make([]DBStatus, 0, len(config.DBs))\n\n\tfor _, dbConfig := range config.DBs {\n\t\tdb, err := NewDBFromConfig(dbConfig)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Filter if path specified.\n\t\tif filterPath != \"\" \u0026\u0026 db.Path() != filterPath {\n\t\t\tcontinue\n\t\t}\n\n\t\tstatuses = append(statuses, c.getDBStatus(db))\n\t}\n\n\tif *jsonOutput {\n\t\toutput, err := json.MarshalIndent(statuses, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)\n\tdefer w.Flush()\n\n\tfmt.Fprintln(w, \"database\\tstatus\\tlocal txid\\twal size\")\n\tfor _, status := range statuses {\n\t\tfmt.Fprintf(w, \"%s\\t%s\\t%s\\t%s\\n\",\n\t\t\tstatus.Database,\n\t\t\tstatus.Status,\n\t\t\tstatus.LocalTXID,\n\t\t\tstatus.WALSize,\n\t\t)\n\t}\n\n\treturn nil\n}\n\n// DBStatus holds the status information for a single database.\ntype DBStatus struct {\n\tDatabase string `json:\"database\"`\n\tStatus string `json:\"status\"`\n\tLocalTXID string `json:\"local_txid\"`\n\tWALSize string `json:\"wal_size\"`\n}\n\n// getDBStatus gathers status information for a database.\nfunc (c *StatusCommand) getDBStatus(db *litestream.DB) DBStatus {\n\tstatus := DBStatus{\n\t\tDatabase: db.Path(),\n\t\tStatus: \"unknown\",\n\t\tLocalTXID: \"-\",\n\t\tWALSize: \"-\",\n\t}\n\n\t// Check if database file exists.\n\tdbPath := db.Path()\n\tif _, err := os.Stat(dbPath); os.IsNotExist(err) {\n\t\tstatus.Status = \"no database\"\n\t\treturn status\n\t}\n\n\t// Get WAL file size.\n\twalPath := db.WALPath()\n\tif walInfo, err := os.Stat(walPath); err == nil {\n\t\tstatus.WALSize = humanize.Bytes(uint64(walInfo.Size()))\n\t} else if os.IsNotExist(err) {\n\t\tstatus.WALSize = \"0 B\"\n\t}\n\n\t// Get local TXID from L0 directory.\n\t_, maxTXID, err := db.MaxLTX()\n\tif err == nil \u0026\u0026 maxTXID \u003e 0 {\n\t\tstatus.LocalTXID = maxTXID.String()\n\t\tstatus.Status = \"ok\"\n\t} else if err == nil {\n\t\tstatus.Status = \"not initialized\"\n\t} else {\n\t\tstatus.Status = \"error\"\n\t}\n\n\treturn status\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *StatusCommand) Usage() {\n\tfmt.Printf(`\nThe status command displays the replication status of databases.\n\nUsage:\n\n\tlitestream status [arguments] [database path]\n\nArguments:\n\n\t-config PATH\n\t Specifies the configuration file.\n\t Defaults to %s\n\n\t-json\n\t Output raw JSON instead of human-readable text.\n\n\t-no-expand-env\n\t Disables environment variable expansion in configuration file.\n\nIf a database path is provided, only that database's status is shown.\nOtherwise, all configured databases are displayed.\n\nOutput columns:\n database Path to the SQLite database\n status Current status (ok, not initialized, no database, error)\n local txid Latest local transaction ID\n wal size Current WAL file size\n\nNote: To see replica TXID and sync status, inspect daemon diagnostics or logs\nwhile the replication daemon is running.\n\nExamples:\n\n\t$ litestream status\n\t$ litestream status /path/to/db\n\t$ litestream status -json\n\n`[1:],\n\t\tDefaultConfigPath(),\n\t)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[34,50]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[51,58]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[59,65]}},{"name":"os","path":"os","span":{"start":[8,1],"end":[8,6],"bytes":[66,71]}},{"name":"tabwriter","path":"text/tabwriter","span":{"start":[9,1],"end":[9,18],"bytes":[72,89]}},{"name":"go-humanize","path":"github.com/dustin/go-humanize","span":{"start":[11,1],"end":[11,33],"bytes":[91,123]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[13,1],"end":[13,37],"bytes":[125,161]}}],"types":{"DBStatus":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.DBStatus","kind":"struct","span":{"start":[86,1],"end":[91,2],"bytes":[1827,1995]},"callables":{},"fields":{"Database":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.DBStatus/Database","kind":"field","type":"string","span":{"start":[87,1],"end":[87,36],"bytes":[1850,1885]}},"LocalTXID":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.DBStatus/LocalTXID","kind":"field","type":"string","span":{"start":[89,1],"end":[89,38],"bytes":[1920,1957]}},"Status":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.DBStatus/Status","kind":"field","type":"string","span":{"start":[88,1],"end":[88,34],"bytes":[1886,1919]}},"WALSize":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.DBStatus/WALSize","kind":"field","type":"string","span":{"start":[90,1],"end":[90,36],"bytes":[1958,1993]}}}},"StatusCommand":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand","kind":"struct","span":{"start":[17,1],"end":[17,28],"bytes":[230,257]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","kind":"method","span":{"start":[20,1],"end":[83,2],"bytes":[288,1761]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[20,1],"end":[20,78],"bytes":[288,365]}},{"name":"args","type":"[]string","span":{"start":[20,1],"end":[20,78],"bytes":[288,365]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"21:8":{"kind":"call","span":{"start":[21,8],"end":[21,66],"bytes":[373,431]},"callee":null},"22:29":{"kind":"call","span":{"start":[22,29],"end":[22,51],"bytes":[460,482]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag"},"23:16":{"kind":"call","span":{"start":[23,16],"end":[23,57],"bytes":[498,539]},"callee":null},"25:12":{"kind":"call","span":{"start":[25,12],"end":[25,26],"bytes":[571,585]},"callee":null},"31:17":{"kind":"call","span":{"start":[31,17],"end":[31,36],"bytes":[681,700]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"33:17":{"kind":"call","span":{"start":[33,17],"end":[33,59],"bytes":[720,762]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile"},"40:5":{"kind":"call","span":{"start":[40,5],"end":[40,14],"bytes":[894,903]},"callee":null},"41:16":{"kind":"call","span":{"start":[41,16],"end":[41,25],"bytes":[925,934]},"callee":null},"44:14":{"kind":"call","span":{"start":[44,14],"end":[44,50],"bytes":[952,988]},"callee":null},"44:34":{"kind":"call","span":{"start":[44,34],"end":[44,49],"bytes":[972,987]},"callee":null},"47:14":{"kind":"call","span":{"start":[47,14],"end":[47,39],"bytes":[1042,1067]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig"},"53:26":{"kind":"call","span":{"start":[53,26],"end":[53,35],"bytes":[1161,1170]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"57:14":{"kind":"call","span":{"start":[57,14],"end":[57,49],"bytes":[1217,1252]},"callee":null},"57:31":{"kind":"call","span":{"start":[57,31],"end":[57,48],"bytes":[1234,1251]},"callee":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus"},"61:18":{"kind":"call","span":{"start":[61,18],"end":[61,56],"bytes":[1292,1330]},"callee":null},"63:11":{"kind":"call","span":{"start":[63,11],"end":[63,59],"bytes":[1359,1407]},"callee":null},"65:15":{"kind":"call","span":{"start":[65,15],"end":[65,29],"bytes":[1426,1440]},"callee":null},"65:3":{"kind":"call","span":{"start":[65,3],"end":[65,30],"bytes":[1414,1441]},"callee":null},"69:7":{"kind":"call","span":{"start":[69,7],"end":[69,54],"bytes":[1465,1512]},"callee":null},"70:8":{"kind":"call","span":{"start":[70,8],"end":[70,17],"bytes":[1520,1529]},"callee":null},"72:2":{"kind":"call","span":{"start":[72,2],"end":[72,59],"bytes":[1532,1589]},"callee":null},"74:3":{"kind":"call","span":{"start":[74,3],"end":[79,4],"bytes":[1627,1743]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Usage":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Usage","kind":"method","span":{"start":[132,1],"end":[173,2],"bytes":[2932,3919]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"133:2":{"kind":"call","span":{"start":[133,2],"end":[172,3],"bytes":[2967,3917]},"callee":null},"171:3":{"kind":"call","span":{"start":[171,3],"end":[171,22],"bytes":[3894,3913]},"callee":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath"}}},"github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus":{"id":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus","kind":"method","span":{"start":[94,1],"end":[129,2],"bytes":[2055,2887]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus","parameters":[{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[94,1],"end":[94,66],"bytes":[2055,2120]}}],"return_type":"github.com/benbjohnson/litestream/cmd/litestream.DBStatus","metrics":{"cyclomatic":6},"body":{"103:12":{"kind":"call","span":{"start":[103,12],"end":[103,21],"bytes":[2276,2285]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"104:15":{"kind":"call","span":{"start":[104,15],"end":[104,30],"bytes":[2300,2315]},"callee":null},"104:32":{"kind":"call","span":{"start":[104,32],"end":[104,50],"bytes":[2317,2335]},"callee":null},"110:13":{"kind":"call","span":{"start":[110,13],"end":[110,25],"bytes":[2425,2437]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"111:21":{"kind":"call","span":{"start":[111,21],"end":[111,37],"bytes":[2458,2474]},"callee":null},"112:20":{"kind":"call","span":{"start":[112,20],"end":[112,58],"bytes":[2508,2546]},"callee":null},"112:35":{"kind":"call","span":{"start":[112,35],"end":[112,57],"bytes":[2523,2545]},"callee":null},"112:42":{"kind":"call","span":{"start":[112,42],"end":[112,56],"bytes":[2530,2544]},"callee":null},"113:12":{"kind":"call","span":{"start":[113,12],"end":[113,30],"bytes":[2558,2576]},"callee":null},"118:21":{"kind":"call","span":{"start":[118,21],"end":[118,32],"bytes":[2666,2677]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX"},"120:22":{"kind":"call","span":{"start":[120,22],"end":[120,38],"bytes":[2731,2747]},"callee":null},"96:14":{"kind":"call","span":{"start":[96,14],"end":[96,23],"bytes":[2155,2164]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"}}}}}},"functions":{},"content_hash":"98a8f08a6eb5a3cbf7ad03e9f39b80995556107089918886c263347426f999a9"},"cmd/litestream/stop.go":{"id":"can://go/litestream/cmd/litestream/stop.go","kind":"module","span":{"start":[1,1],"end":[133,1],"bytes":[0,3330]},"package":"main","source":"package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// StopCommand represents the command to stop replication for a database.\ntype StopCommand struct{}\n\n// Run executes the stop command.\nfunc (c *StopCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-stop\", flag.ContinueOnError)\n\ttimeout := fs.Int(\"timeout\", 30, \"timeout in seconds\")\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() == 0 {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path required\",\n\t\t\thint: \"litestream stop /path/to/db\",\n\t\t}\n\t}\n\tif fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\n\tdbPath := fs.Arg(0)\n\n\t// Create HTTP client that connects via Unix socket with timeout\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\treq := litestream.StopRequest{\n\t\tPath: dbPath,\n\t\tTimeout: *timeout,\n\t}\n\treqBody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}\n\n\tresp, err := client.Post(\"http://localhost/stop\", \"application/json\", bytes.NewReader(reqBody))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"stop failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"stop failed: %s\", string(body))\n\t}\n\n\tvar result litestream.StopResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tconfirmation := StartStopResult{\n\t\tStatus: result.Status,\n\t\tDBPath: result.Path,\n\t\tState: \"stopped\",\n\t\tTXID: result.TXID,\n\t\tSocket: *socketPath,\n\t}\n\tif err := printStartStopResult(confirmation, *jsonOutput); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Usage prints the help text for the stop command.\nfunc (c *StopCommand) Usage() {\n\tfmt.Println(`\nusage: litestream stop [OPTIONS] DB_PATH\n\nStop replication for a database.\nStop always waits for shutdown and final sync.\n\nOptions:\n -timeout SECONDS\n Maximum time to wait in seconds (default: 30).\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -json\n Output raw JSON instead of human-readable text.\n\nExamples:\n # Stop replication for a database.\n $ litestream stop /path/to/db\n\n # Stop replication and emit a JSON confirmation.\n $ litestream stop -json /path/to/db\n\n # Stop replication using a non-default control socket.\n $ litestream stop -socket /tmp/litestream.sock /path/to/db\n\n # Stop replication and wait up to 10 seconds for final sync and shutdown.\n $ litestream stop -timeout 10 /path/to/db\n`[1:])\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"json","path":"encoding/json","span":{"start":[6,1],"end":[6,17],"bytes":[43,59]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[60,67]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[68,74]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[75,80]}},{"name":"net","path":"net","span":{"start":[10,1],"end":[10,7],"bytes":[81,87]}},{"name":"http","path":"net/http","span":{"start":[11,1],"end":[11,12],"bytes":[88,99]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[100,107]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[14,1],"end":[14,37],"bytes":[109,145]}}],"types":{"StopCommand":{"id":"can://go/litestream/cmd/litestream/stop.go/github.com/benbjohnson/litestream/cmd/litestream.StopCommand","kind":"struct","span":{"start":[18,1],"end":[18,26],"bytes":[223,248]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run":{"id":"can://go/litestream/cmd/litestream/stop.go/github.com/benbjohnson/litestream/cmd/litestream.StopCommand/github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run","kind":"method","span":{"start":[21,1],"end":[99,2],"bytes":[284,2461]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[21,1],"end":[21,70],"bytes":[284,353]}},{"name":"args","type":"[]string","span":{"start":[21,1],"end":[21,70],"bytes":[284,353]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"22:8":{"kind":"call","span":{"start":[22,8],"end":[22,64],"bytes":[361,417]},"callee":null},"23:13":{"kind":"call","span":{"start":[23,13],"end":[23,56],"bytes":[430,473]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,86],"bytes":[489,559]},"callee":null},"25:16":{"kind":"call","span":{"start":[25,16],"end":[25,57],"bytes":[575,616]},"callee":null},"27:12":{"kind":"call","span":{"start":[27,12],"end":[27,26],"bytes":[648,662]},"callee":null},"31:5":{"kind":"call","span":{"start":[31,5],"end":[31,14],"bytes":[698,707]},"callee":null},"37:5":{"kind":"call","span":{"start":[37,5],"end":[37,14],"bytes":[829,838]},"callee":null},"38:10":{"kind":"call","span":{"start":[38,10],"end":[38,42],"bytes":[854,886]},"callee":null},"41:12":{"kind":"call","span":{"start":[41,12],"end":[41,21],"bytes":[902,911]},"callee":null},"44:19":{"kind":"call","span":{"start":[44,19],"end":[44,42],"bytes":[997,1020]},"callee":null},"58:18":{"kind":"call","span":{"start":[58,18],"end":[58,35],"bytes":[1359,1376]},"callee":null},"60:10":{"kind":"call","span":{"start":[60,10],"end":[60,58],"bytes":[1403,1451]},"callee":null},"63:15":{"kind":"call","span":{"start":[63,15],"end":[63,97],"bytes":[1470,1552]},"callee":null},"63:72":{"kind":"call","span":{"start":[63,72],"end":[63,96],"bytes":[1527,1551]},"callee":null},"65:10":{"kind":"call","span":{"start":[65,10],"end":[65,68],"bytes":[1579,1637]},"callee":null},"67:8":{"kind":"call","span":{"start":[67,8],"end":[67,25],"bytes":[1648,1665]},"callee":null},"69:15":{"kind":"call","span":{"start":[69,15],"end":[69,36],"bytes":[1681,1702]},"callee":null},"71:10":{"kind":"call","span":{"start":[71,10],"end":[71,56],"bytes":[1729,1775]},"callee":null},"76:13":{"kind":"call","span":{"start":[76,13],"end":[76,43],"bytes":[1870,1900]},"callee":null},"77:11":{"kind":"call","span":{"start":[77,11],"end":[77,55],"bytes":[1948,1992]},"callee":null},"79:10":{"kind":"call","span":{"start":[79,10],"end":[79,53],"bytes":[2006,2049]},"callee":null},"79:40":{"kind":"call","span":{"start":[79,40],"end":[79,52],"bytes":[2036,2048]},"callee":null},"83:12":{"kind":"call","span":{"start":[83,12],"end":[83,41],"bytes":[2101,2130]},"callee":null},"84:10":{"kind":"call","span":{"start":[84,10],"end":[84,57],"bytes":[2154,2201]},"callee":null},"94:12":{"kind":"call","span":{"start":[94,12],"end":[94,59],"bytes":[2369,2416]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/stop.go/github.com/benbjohnson/litestream/cmd/litestream.StopCommand/github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run.closure_1","kind":"function","span":{"start":[48,1],"end":[50,6],"bytes":[1116,1257]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[48,1],"end":[48,73],"bytes":[1116,1188]}},{"name":"_","type":"string","span":{"start":[48,1],"end":[48,73],"bytes":[1116,1188]}},{"name":"_","type":"string","span":{"start":[48,1],"end":[48,73],"bytes":[1116,1188]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"49:12":{"kind":"call","span":{"start":[49,12],"end":[49,63],"bytes":[1200,1251]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Usage":{"id":"can://go/litestream/cmd/litestream/stop.go/github.com/benbjohnson/litestream/cmd/litestream.StopCommand/github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Usage","kind":"method","span":{"start":[102,1],"end":[132,2],"bytes":[2515,3329]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"103:2":{"kind":"call","span":{"start":[103,2],"end":[131,7],"bytes":[2548,3327]},"callee":null}}}}}},"functions":{},"content_hash":"7980bb929ad3d474f22b95549b12385368f7253b2704c25e3656b0c025b55625"},"cmd/litestream/sync.go":{"id":"can://go/litestream/cmd/litestream/sync.go","kind":"module","span":{"start":[1,1],"end":[162,1],"bytes":[0,4043]},"package":"main","source":"package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// SyncCommand represents the command to force an immediate sync for a database.\ntype SyncCommand struct{}\n\n// Run executes the sync command.\nfunc (c *SyncCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-sync\", flag.ContinueOnError)\n\ttimeout := fs.Int(\"timeout\", 30, \"timeout in seconds\")\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\twait := fs.Bool(\"wait\", false, \"block until sync completes\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() == 0 {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path required\",\n\t\t\thint: \"litestream sync /path/to/db\",\n\t\t}\n\t}\n\tif fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\tif *timeout \u003c= 0 {\n\t\treturn fmt.Errorf(\"timeout must be greater than 0\")\n\t}\n\n\tdbPath := fs.Arg(0)\n\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\treq := litestream.SyncRequest{\n\t\tPath: dbPath,\n\t\tWait: *wait,\n\t\tTimeout: *timeout,\n\t}\n\treqBody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}\n\n\tstart := time.Now()\n\tresp, err := client.Post(\"http://localhost/sync\", \"application/json\", bytes.NewReader(reqBody))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tdurationMS := time.Since(start).Milliseconds()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"sync failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"sync failed: %s\", string(body))\n\t}\n\n\tvar result litestream.SyncResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tconfirmation := SyncResult{\n\t\tDBPath: result.Path,\n\t\tTXID: result.TXID,\n\t\tDurationMS: durationMS,\n\t}\n\tif *wait {\n\t\tconfirmation.ReplicaTXID = \u0026result.ReplicatedTXID\n\t}\n\tif err := printSyncResult(confirmation, *jsonOutput); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\ntype SyncResult struct {\n\tDBPath string `json:\"db_path\"`\n\tTXID uint64 `json:\"txid\"`\n\tReplicaTXID *uint64 `json:\"replica_txid,omitempty\"`\n\tDurationMS int64 `json:\"duration_ms\"`\n}\n\nfunc printSyncResult(result SyncResult, jsonOutput bool) error {\n\tif jsonOutput {\n\t\toutput, err := json.MarshalIndent(result, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tfmt.Printf(\"db_path: %s\\n\", result.DBPath)\n\tfmt.Printf(\"txid: %d\\n\", result.TXID)\n\tif result.ReplicaTXID != nil {\n\t\tfmt.Printf(\"replica_txid: %d\\n\", *result.ReplicaTXID)\n\t}\n\tfmt.Printf(\"duration_ms: %d\\n\", result.DurationMS)\n\n\treturn nil\n}\n\n// Usage prints the help text for the sync command.\nfunc (c *SyncCommand) Usage() {\n\tfmt.Println(`\nusage: litestream sync [OPTIONS] DB_PATH\n\nForce an immediate sync for a database.\n\nOptions:\n -wait\n Block until sync completes including remote replication (default: false).\n\n -timeout SECONDS\n Maximum time to wait in seconds, best-effort (default: 30).\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -json\n Output raw JSON instead of human-readable text.\n\nExamples:\n $ litestream sync /path/to/db\n $ litestream sync -json /path/to/db\n $ litestream sync -wait /path/to/db\n $ litestream sync -wait -timeout 120 /path/to/db\n`[1:])\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"json","path":"encoding/json","span":{"start":[6,1],"end":[6,17],"bytes":[43,59]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[60,67]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[68,74]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[75,80]}},{"name":"net","path":"net","span":{"start":[10,1],"end":[10,7],"bytes":[81,87]}},{"name":"http","path":"net/http","span":{"start":[11,1],"end":[11,12],"bytes":[88,99]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[100,107]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[14,1],"end":[14,37],"bytes":[109,145]}}],"types":{"SyncCommand":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand","kind":"struct","span":{"start":[18,1],"end":[18,26],"bytes":[230,255]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run","kind":"method","span":{"start":[21,1],"end":[106,2],"bytes":[291,2650]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[21,1],"end":[21,70],"bytes":[291,360]}},{"name":"args","type":"[]string","span":{"start":[21,1],"end":[21,70],"bytes":[291,360]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"101:12":{"kind":"call","span":{"start":[101,12],"end":[101,54],"bytes":[2563,2605]},"callee":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.printSyncResult"},"22:8":{"kind":"call","span":{"start":[22,8],"end":[22,64],"bytes":[368,424]},"callee":null},"23:13":{"kind":"call","span":{"start":[23,13],"end":[23,56],"bytes":[437,480]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,86],"bytes":[496,566]},"callee":null},"25:10":{"kind":"call","span":{"start":[25,10],"end":[25,62],"bytes":[576,628]},"callee":null},"26:16":{"kind":"call","span":{"start":[26,16],"end":[26,57],"bytes":[644,685]},"callee":null},"28:12":{"kind":"call","span":{"start":[28,12],"end":[28,26],"bytes":[717,731]},"callee":null},"32:5":{"kind":"call","span":{"start":[32,5],"end":[32,14],"bytes":[767,776]},"callee":null},"38:5":{"kind":"call","span":{"start":[38,5],"end":[38,14],"bytes":[898,907]},"callee":null},"39:10":{"kind":"call","span":{"start":[39,10],"end":[39,42],"bytes":[923,955]},"callee":null},"42:10":{"kind":"call","span":{"start":[42,10],"end":[42,54],"bytes":[988,1032]},"callee":null},"45:12":{"kind":"call","span":{"start":[45,12],"end":[45,21],"bytes":[1048,1057]},"callee":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion"},"47:19":{"kind":"call","span":{"start":[47,19],"end":[47,42],"bytes":[1077,1100]},"callee":null},"62:18":{"kind":"call","span":{"start":[62,18],"end":[62,35],"bytes":[1457,1474]},"callee":null},"64:10":{"kind":"call","span":{"start":[64,10],"end":[64,58],"bytes":[1501,1549]},"callee":null},"67:11":{"kind":"call","span":{"start":[67,11],"end":[67,21],"bytes":[1564,1574]},"callee":null},"68:15":{"kind":"call","span":{"start":[68,15],"end":[68,97],"bytes":[1589,1671]},"callee":null},"68:72":{"kind":"call","span":{"start":[68,72],"end":[68,96],"bytes":[1646,1670]},"callee":null},"70:10":{"kind":"call","span":{"start":[70,10],"end":[70,68],"bytes":[1698,1756]},"callee":null},"72:8":{"kind":"call","span":{"start":[72,8],"end":[72,25],"bytes":[1767,1784]},"callee":null},"73:16":{"kind":"call","span":{"start":[73,16],"end":[73,33],"bytes":[1800,1817]},"callee":null},"75:15":{"kind":"call","span":{"start":[75,15],"end":[75,36],"bytes":[1848,1869]},"callee":null},"77:10":{"kind":"call","span":{"start":[77,10],"end":[77,56],"bytes":[1896,1942]},"callee":null},"82:13":{"kind":"call","span":{"start":[82,13],"end":[82,43],"bytes":[2037,2067]},"callee":null},"83:11":{"kind":"call","span":{"start":[83,11],"end":[83,55],"bytes":[2115,2159]},"callee":null},"85:10":{"kind":"call","span":{"start":[85,10],"end":[85,53],"bytes":[2173,2216]},"callee":null},"85:40":{"kind":"call","span":{"start":[85,40],"end":[85,52],"bytes":[2203,2215]},"callee":null},"89:12":{"kind":"call","span":{"start":[89,12],"end":[89,41],"bytes":[2268,2297]},"callee":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches"},"90:10":{"kind":"call","span":{"start":[90,10],"end":[90,57],"bytes":[2321,2368]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run.closure_1","kind":"function","span":{"start":[51,1],"end":[53,6],"bytes":[1196,1337]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[51,1],"end":[51,73],"bytes":[1196,1268]}},{"name":"_","type":"string","span":{"start":[51,1],"end":[51,73],"bytes":[1196,1268]}},{"name":"_","type":"string","span":{"start":[51,1],"end":[51,73],"bytes":[1196,1268]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"52:12":{"kind":"call","span":{"start":[52,12],"end":[52,63],"bytes":[1280,1331]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Usage":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Usage","kind":"method","span":{"start":[136,1],"end":[161,2],"bytes":[3406,4042]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"137:2":{"kind":"call","span":{"start":[137,2],"end":[160,7],"bytes":[3439,4040]},"callee":null}}}}},"SyncResult":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncResult","kind":"struct","span":{"start":[108,1],"end":[113,2],"bytes":[2652,2846]},"callables":{},"fields":{"DBPath":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncResult/DBPath","kind":"field","type":"string","span":{"start":[109,1],"end":[109,38],"bytes":[2677,2714]}},"DurationMS":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncResult/DurationMS","kind":"field","type":"int64","span":{"start":[112,1],"end":[112,42],"bytes":[2803,2844]}},"ReplicaTXID":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncResult/ReplicaTXID","kind":"field","type":"*uint64","span":{"start":[111,1],"end":[111,53],"bytes":[2750,2802]}},"TXID":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncResult/TXID","kind":"field","type":"uint64","span":{"start":[110,1],"end":[110,35],"bytes":[2715,2749]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.printSyncResult":{"id":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.printSyncResult","kind":"function","span":{"start":[115,1],"end":[133,2],"bytes":[2848,3352]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.printSyncResult","parameters":[{"name":"result","type":"github.com/benbjohnson/litestream/cmd/litestream.SyncResult","span":{"start":[115,1],"end":[115,65],"bytes":[2848,2912]}},{"name":"jsonOutput","type":"bool","span":{"start":[115,1],"end":[115,65],"bytes":[2848,2912]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"117:18":{"kind":"call","span":{"start":[117,18],"end":[117,54],"bytes":[2947,2983]},"callee":null},"119:11":{"kind":"call","span":{"start":[119,11],"end":[119,59],"bytes":[3012,3060]},"callee":null},"121:15":{"kind":"call","span":{"start":[121,15],"end":[121,29],"bytes":[3079,3093]},"callee":null},"121:3":{"kind":"call","span":{"start":[121,3],"end":[121,30],"bytes":[3067,3094]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"},"125:2":{"kind":"call","span":{"start":[125,2],"end":[125,44],"bytes":[3113,3155]},"callee":null},"126:2":{"kind":"call","span":{"start":[126,2],"end":[126,39],"bytes":[3157,3194]},"callee":null},"128:3":{"kind":"call","span":{"start":[128,3],"end":[128,56],"bytes":[3229,3282]},"callee":null},"130:2":{"kind":"call","span":{"start":[130,2],"end":[130,52],"bytes":[3287,3337]},"callee":null}}}},"content_hash":"f124c02d744ececee52a6c5a31922dc5e6a71a5b9fe8ad28262622a0785c0d9b"},"cmd/litestream/unregister.go":{"id":"can://go/litestream/cmd/litestream/unregister.go","kind":"module","span":{"start":[1,1],"end":[177,1],"bytes":[0,4764]},"package":"main","source":"package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\ntype UnregisterCommand struct{}\n\nfunc (c *UnregisterCommand) Run(ctx context.Context, args []string) error {\n\tfs := flag.NewFlagSet(\"litestream-unregister\", flag.ContinueOnError)\n\ttimeout := fs.Int(\"timeout\", 30, \"timeout in seconds\")\n\tsocketPath := fs.String(\"socket\", \"/var/run/litestream.sock\", \"control socket path\")\n\tdryRun := fs.Bool(\"dry-run\", false, \"print what would be unregistered without changing the daemon\")\n\tjsonOutput := fs.Bool(\"json\", false, \"output raw JSON\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif fs.NArg() == 0 {\n\t\treturn \u0026usageError{\n\t\t\tmessage: \"database path required\",\n\t\t\thint: \"litestream unregister /path/to/db\",\n\t\t}\n\t}\n\tif fs.NArg() \u003e 1 {\n\t\treturn fmt.Errorf(\"too many arguments\")\n\t}\n\tif *timeout \u003c= 0 {\n\t\treturn fmt.Errorf(\"timeout must be greater than 0\")\n\t}\n\n\tdbPath := fs.Arg(0)\n\n\tif *dryRun {\n\t\tfmt.Println(\"Dry run: unregister request preview\")\n\t\tfmt.Printf(\" database: %s\\n\", dbPath)\n\t\tfmt.Printf(\" socket: %s\\n\", *socketPath)\n\t\tfmt.Printf(\" replicas: daemon-managed replica for this database\\n\")\n\t\tfmt.Printf(\" final sync: daemon close will sync the database and replica before the command completes\\n\")\n\t\tfmt.Printf(\" timeout: %ds\\n\", *timeout)\n\t\tfmt.Println(\"No unregister request was sent.\")\n\t\treturn nil\n\t}\n\n\t// Create HTTP client that connects via Unix socket with timeout.\n\tclientTimeout := time.Duration(*timeout) * time.Second\n\tclient := \u0026http.Client{\n\t\tTimeout: clientTimeout,\n\t\tTransport: \u0026http.Transport{\n\t\t\tDialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\treturn net.DialTimeout(\"unix\", *socketPath, clientTimeout)\n\t\t\t},\n\t\t},\n\t}\n\n\treq := litestream.UnregisterDatabaseRequest{\n\t\tPath: dbPath,\n\t\tTimeout: *timeout,\n\t}\n\treqBody, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}\n\n\tresp, err := client.Post(\"http://localhost/unregister\", \"application/json\", bytes.NewReader(reqBody))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to control socket: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp litestream.ErrorResponse\n\t\tif err := json.Unmarshal(body, \u0026errResp); err == nil \u0026\u0026 errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"unregister failed: %s\", errResp.Error)\n\t\t}\n\t\treturn fmt.Errorf(\"unregister failed: %s\", string(body))\n\t}\n\n\tvar result litestream.UnregisterDatabaseResponse\n\tif err := json.Unmarshal(body, \u0026result); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse response: %w\", err)\n\t}\n\n\tconfirmation := UnregisterResult{\n\t\tStatus: result.Status,\n\t\tDBPath: result.Path,\n\t\tFinalTXID: result.TXID,\n\t\tSocket: *socketPath,\n\t}\n\tif err := printUnregisterResult(confirmation, *jsonOutput); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\ntype UnregisterResult struct {\n\tStatus string `json:\"status\"`\n\tDBPath string `json:\"db_path\"`\n\tFinalTXID uint64 `json:\"final_txid\"`\n\tSocket string `json:\"socket\"`\n}\n\nfunc printUnregisterResult(result UnregisterResult, jsonOutput bool) error {\n\tif jsonOutput {\n\t\toutput, err := json.MarshalIndent(result, \"\", \" \")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t}\n\t\tfmt.Println(string(output))\n\t\treturn nil\n\t}\n\n\tfmt.Printf(\"status: %s\\n\", result.Status)\n\tfmt.Printf(\"db_path: %s\\n\", result.DBPath)\n\tfmt.Printf(\"final_txid: %d\\n\", result.FinalTXID)\n\tfmt.Printf(\"socket: %s\\n\", result.Socket)\n\n\treturn nil\n}\n\nfunc (c *UnregisterCommand) Usage() {\n\tfmt.Println(`\nusage: litestream unregister [OPTIONS] DB_PATH\n\nUnregister a database from replication.\n\nArguments:\n DB_PATH Path to the SQLite database file.\n\nOptions:\n -timeout SECONDS\n Maximum time to wait in seconds (default: 30).\n\n -socket PATH\n Path to control socket (default: /var/run/litestream.sock).\n\n -dry-run\n Preview what would be unregistered without changing the daemon.\n\n -json\n Output raw JSON instead of human-readable text.\n\nExamples:\n # Unregister a database from the running daemon.\n $ litestream unregister /path/to/db\n\n # Unregister a database and emit a JSON confirmation.\n $ litestream unregister -json /path/to/db\n\n # Preview an unregister request without changing the daemon.\n $ litestream unregister -dry-run /path/to/db\n\n # Unregister a database using a non-default control socket.\n $ litestream unregister -socket /tmp/litestream.sock /path/to/db\n\n # Unregister a database and wait up to 10 seconds for shutdown.\n $ litestream unregister -timeout 10 /path/to/db\n`[1:])\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"json","path":"encoding/json","span":{"start":[6,1],"end":[6,17],"bytes":[43,59]}},{"name":"flag","path":"flag","span":{"start":[7,1],"end":[7,8],"bytes":[60,67]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[68,74]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[75,80]}},{"name":"net","path":"net","span":{"start":[10,1],"end":[10,7],"bytes":[81,87]}},{"name":"http","path":"net/http","span":{"start":[11,1],"end":[11,12],"bytes":[88,99]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[100,107]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[14,1],"end":[14,37],"bytes":[109,145]}}],"types":{"UnregisterCommand":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand","kind":"struct","span":{"start":[17,1],"end":[17,32],"bytes":[149,180]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run","kind":"method","span":{"start":[19,1],"end":[111,2],"bytes":[182,3036]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[19,1],"end":[19,76],"bytes":[182,257]}},{"name":"args","type":"[]string","span":{"start":[19,1],"end":[19,76],"bytes":[182,257]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"106:12":{"kind":"call","span":{"start":[106,12],"end":[106,60],"bytes":[2943,2991]},"callee":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.printUnregisterResult"},"20:8":{"kind":"call","span":{"start":[20,8],"end":[20,70],"bytes":[265,327]},"callee":null},"21:13":{"kind":"call","span":{"start":[21,13],"end":[21,56],"bytes":[340,383]},"callee":null},"22:16":{"kind":"call","span":{"start":[22,16],"end":[22,86],"bytes":[399,469]},"callee":null},"23:12":{"kind":"call","span":{"start":[23,12],"end":[23,101],"bytes":[481,570]},"callee":null},"24:16":{"kind":"call","span":{"start":[24,16],"end":[24,57],"bytes":[586,627]},"callee":null},"26:12":{"kind":"call","span":{"start":[26,12],"end":[26,26],"bytes":[659,673]},"callee":null},"30:5":{"kind":"call","span":{"start":[30,5],"end":[30,14],"bytes":[709,718]},"callee":null},"36:5":{"kind":"call","span":{"start":[36,5],"end":[36,14],"bytes":[846,855]},"callee":null},"37:10":{"kind":"call","span":{"start":[37,10],"end":[37,42],"bytes":[871,903]},"callee":null},"40:10":{"kind":"call","span":{"start":[40,10],"end":[40,54],"bytes":[936,980]},"callee":null},"43:12":{"kind":"call","span":{"start":[43,12],"end":[43,21],"bytes":[996,1005]},"callee":null},"46:3":{"kind":"call","span":{"start":[46,3],"end":[46,53],"bytes":[1023,1073]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog"},"47:3":{"kind":"call","span":{"start":[47,3],"end":[47,41],"bytes":[1076,1114]},"callee":null},"48:3":{"kind":"call","span":{"start":[48,3],"end":[48,44],"bytes":[1117,1158]},"callee":null},"49:3":{"kind":"call","span":{"start":[49,3],"end":[49,71],"bytes":[1161,1229]},"callee":null},"50:3":{"kind":"call","span":{"start":[50,3],"end":[50,109],"bytes":[1232,1338]},"callee":null},"51:3":{"kind":"call","span":{"start":[51,3],"end":[51,43],"bytes":[1341,1381]},"callee":null},"52:3":{"kind":"call","span":{"start":[52,3],"end":[52,49],"bytes":[1384,1430]},"callee":null},"57:19":{"kind":"call","span":{"start":[57,19],"end":[57,42],"bytes":[1533,1556]},"callee":null},"71:18":{"kind":"call","span":{"start":[71,18],"end":[71,35],"bytes":[1909,1926]},"callee":null},"73:10":{"kind":"call","span":{"start":[73,10],"end":[73,58],"bytes":[1953,2001]},"callee":null},"76:15":{"kind":"call","span":{"start":[76,15],"end":[76,103],"bytes":[2020,2108]},"callee":null},"76:78":{"kind":"call","span":{"start":[76,78],"end":[76,102],"bytes":[2083,2107]},"callee":null},"78:10":{"kind":"call","span":{"start":[78,10],"end":[78,68],"bytes":[2135,2193]},"callee":null},"80:8":{"kind":"call","span":{"start":[80,8],"end":[80,25],"bytes":[2204,2221]},"callee":null},"82:15":{"kind":"call","span":{"start":[82,15],"end":[82,36],"bytes":[2237,2258]},"callee":null},"84:10":{"kind":"call","span":{"start":[84,10],"end":[84,56],"bytes":[2285,2331]},"callee":null},"89:13":{"kind":"call","span":{"start":[89,13],"end":[89,43],"bytes":[2426,2456]},"callee":null},"90:11":{"kind":"call","span":{"start":[90,11],"end":[90,61],"bytes":[2504,2554]},"callee":null},"92:10":{"kind":"call","span":{"start":[92,10],"end":[92,59],"bytes":[2568,2617]},"callee":null},"92:46":{"kind":"call","span":{"start":[92,46],"end":[92,58],"bytes":[2604,2616]},"callee":null},"96:12":{"kind":"call","span":{"start":[96,12],"end":[96,41],"bytes":[2683,2712]},"callee":null},"97:10":{"kind":"call","span":{"start":[97,10],"end":[97,57],"bytes":[2736,2783]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run.closure_1","kind":"function","span":{"start":[61,1],"end":[63,6],"bytes":[1652,1793]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run.closure_1","parameters":[{"name":"_","type":"context.Context","span":{"start":[61,1],"end":[61,73],"bytes":[1652,1724]}},{"name":"_","type":"string","span":{"start":[61,1],"end":[61,73],"bytes":[1652,1724]}},{"name":"_","type":"string","span":{"start":[61,1],"end":[61,73],"bytes":[1652,1724]}}],"return_type":"(net.Conn, error)","error_channel":["error"],"body":{"62:12":{"kind":"call","span":{"start":[62,12],"end":[62,63],"bytes":[1736,1787]},"callee":null}}}}},"github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Usage":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Usage","kind":"method","span":{"start":[138,1],"end":[176,2],"bytes":[3685,4763]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"139:2":{"kind":"call","span":{"start":[139,2],"end":[175,7],"bytes":[3724,4761]},"callee":null}}}}},"UnregisterResult":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterResult","kind":"struct","span":{"start":[113,1],"end":[118,2],"bytes":[3038,3211]},"callables":{},"fields":{"DBPath":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterResult/DBPath","kind":"field","type":"string","span":{"start":[115,1],"end":[115,35],"bytes":[3103,3137]}},"FinalTXID":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterResult/FinalTXID","kind":"field","type":"uint64","span":{"start":[116,1],"end":[116,38],"bytes":[3138,3175]}},"Socket":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterResult/Socket","kind":"field","type":"string","span":{"start":[117,1],"end":[117,34],"bytes":[3176,3209]}},"Status":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterResult/Status","kind":"field","type":"string","span":{"start":[114,1],"end":[114,34],"bytes":[3069,3102]}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.printUnregisterResult":{"id":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.printUnregisterResult","kind":"function","span":{"start":[120,1],"end":[136,2],"bytes":[3213,3683]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.printUnregisterResult","parameters":[{"name":"result","type":"github.com/benbjohnson/litestream/cmd/litestream.UnregisterResult","span":{"start":[120,1],"end":[120,77],"bytes":[3213,3289]}},{"name":"jsonOutput","type":"bool","span":{"start":[120,1],"end":[120,77],"bytes":[3213,3289]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"122:18":{"kind":"call","span":{"start":[122,18],"end":[122,54],"bytes":[3324,3360]},"callee":null},"124:11":{"kind":"call","span":{"start":[124,11],"end":[124,59],"bytes":[3389,3437]},"callee":null},"126:15":{"kind":"call","span":{"start":[126,15],"end":[126,29],"bytes":[3456,3470]},"callee":null},"126:3":{"kind":"call","span":{"start":[126,3],"end":[126,30],"bytes":[3444,3471]},"callee":null},"130:2":{"kind":"call","span":{"start":[130,2],"end":[130,43],"bytes":[3490,3531]},"callee":null},"131:2":{"kind":"call","span":{"start":[131,2],"end":[131,44],"bytes":[3533,3575]},"callee":null},"132:2":{"kind":"call","span":{"start":[132,2],"end":[132,50],"bytes":[3577,3625]},"callee":null},"133:2":{"kind":"call","span":{"start":[133,2],"end":[133,43],"bytes":[3627,3668]},"callee":null}}}},"content_hash":"909fe9bc1d4b8b209baf657348f921a8297a26fbca8d7a9cae91f6b20174a455"},"cmd/litestream/version.go":{"id":"can://go/litestream/cmd/litestream/version.go","kind":"module","span":{"start":[1,1],"end":[110,1],"bytes":[0,2638]},"package":"main","source":"package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"runtime/debug\"\n)\n\n// defaultVersion is the placeholder used when no version was injected at\n// build time via -ldflags \"-X main.Version=...\".\nconst defaultVersion = \"(development build)\"\n\n// resolveVersion returns the most descriptive version string available:\n// the ldflags-injected value, the VCS-stamped module version, the VCS\n// revision, or the development-build placeholder, in that order.\nfunc resolveVersion(injected string, bi *debug.BuildInfo) string {\n\tif injected != \"\" \u0026\u0026 injected != defaultVersion {\n\t\treturn injected\n\t}\n\tif bi == nil {\n\t\treturn defaultVersion\n\t}\n\n\tif v := bi.Main.Version; v != \"\" \u0026\u0026 v != \"(devel)\" {\n\t\treturn v\n\t}\n\n\trevision, modified, _ := vcsSettings(bi)\n\tif revision == \"\" {\n\t\treturn defaultVersion\n\t}\n\tif len(revision) \u003e 12 {\n\t\trevision = revision[:12]\n\t}\n\tif modified {\n\t\trevision += \"-dirty\"\n\t}\n\treturn fmt.Sprintf(\"(development build %s)\", revision)\n}\n\n// vcsSettings extracts VCS metadata from embedded build info settings.\n// The vcs.time value is the commit timestamp, not the build time.\nfunc vcsSettings(bi *debug.BuildInfo) (revision string, modified bool, commitTime string) {\n\tfor _, s := range bi.Settings {\n\t\tswitch s.Key {\n\t\tcase \"vcs.revision\":\n\t\t\trevision = s.Value\n\t\tcase \"vcs.modified\":\n\t\t\tmodified = s.Value == \"true\"\n\t\tcase \"vcs.time\":\n\t\t\tcommitTime = s.Value\n\t\t}\n\t}\n\treturn revision, modified, commitTime\n}\n\n// VersionCommand represents a command to print the current version.\ntype VersionCommand struct{}\n\n// Run executes the command.\nfunc (c *VersionCommand) Run(_ context.Context, args []string) (err error) {\n\tfs := flag.NewFlagSet(\"litestream-version\", flag.ContinueOnError)\n\tverbose := fs.Bool(\"verbose\", false, \"print detailed build metadata\")\n\tfs.Usage = c.Usage\n\tif err := fs.Parse(args); err != nil {\n\t\treturn err\n\t}\n\n\tif !*verbose {\n\t\tfmt.Println(Version)\n\t\treturn nil\n\t}\n\n\tfmt.Printf(\"Version: %s\\n\", Version)\n\tfmt.Printf(\"Go Version: %s\\n\", runtime.Version())\n\tif bi, ok := debug.ReadBuildInfo(); ok {\n\t\trevision, modified, commitTime := vcsSettings(bi)\n\t\tif revision != \"\" {\n\t\t\tif modified {\n\t\t\t\trevision += \" (dirty)\"\n\t\t\t}\n\t\t\tfmt.Printf(\"Git Commit: %s\\n\", revision)\n\t\t}\n\t\tif commitTime != \"\" {\n\t\t\tfmt.Printf(\"Commit Time: %s\\n\", commitTime)\n\t\t}\n\t}\n\tfmt.Printf(\"OS/Arch: %s/%s\\n\", runtime.GOOS, runtime.GOARCH)\n\n\treturn nil\n}\n\n// Usage prints the help screen to STDOUT.\nfunc (c *VersionCommand) Usage() {\n\tfmt.Println(`\nPrints the version.\n\nUsage:\n\n\tlitestream version [arguments]\n\nArguments:\n\n\t-verbose\n\t Print detailed build metadata (Go version, commit, build time, OS/arch).\n`[1:])\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"flag","path":"flag","span":{"start":[5,1],"end":[5,8],"bytes":[34,41]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[42,48]}},{"name":"runtime","path":"runtime","span":{"start":[7,1],"end":[7,11],"bytes":[49,59]}},{"name":"debug","path":"runtime/debug","span":{"start":[8,1],"end":[8,17],"bytes":[60,76]}}],"types":{"VersionCommand":{"id":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand","kind":"struct","span":{"start":[60,1],"end":[60,29],"bytes":[1499,1527]},"callables":{"github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Run":{"id":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Run","kind":"method","span":{"start":[63,1],"end":[93,2],"bytes":[1558,2372]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Run","parameters":[{"name":"_","type":"context.Context","span":{"start":[63,1],"end":[63,77],"bytes":[1558,1634]}},{"name":"args","type":"[]string","span":{"start":[63,1],"end":[63,77],"bytes":[1558,1634]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"64:8":{"kind":"call","span":{"start":[64,8],"end":[64,67],"bytes":[1642,1701]},"callee":null},"65:13":{"kind":"call","span":{"start":[65,13],"end":[65,71],"bytes":[1714,1772]},"callee":null},"67:12":{"kind":"call","span":{"start":[67,12],"end":[67,26],"bytes":[1804,1818]},"callee":null},"72:3":{"kind":"call","span":{"start":[72,3],"end":[72,23],"bytes":[1868,1888]},"callee":null},"76:2":{"kind":"call","span":{"start":[76,2],"end":[76,42],"bytes":[1907,1947]},"callee":null},"77:2":{"kind":"call","span":{"start":[77,2],"end":[77,52],"bytes":[1949,1999]},"callee":null},"77:34":{"kind":"call","span":{"start":[77,34],"end":[77,51],"bytes":[1981,1998]},"callee":null},"78:15":{"kind":"call","span":{"start":[78,15],"end":[78,36],"bytes":[2014,2035]},"callee":null},"79:37":{"kind":"call","span":{"start":[79,37],"end":[79,52],"bytes":[2078,2093]},"callee":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.vcsSettings"},"84:4":{"kind":"call","span":{"start":[84,4],"end":[84,45],"bytes":[2168,2209]},"callee":null},"87:4":{"kind":"call","span":{"start":[87,4],"end":[87,47],"bytes":[2241,2284]},"callee":null},"90:2":{"kind":"call","span":{"start":[90,2],"end":[90,66],"bytes":[2293,2357]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Usage":{"id":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Usage","kind":"method","span":{"start":[96,1],"end":[109,2],"bytes":[2417,2637]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Usage","parameters":[],"metrics":{"cyclomatic":1},"body":{"97:2":{"kind":"call","span":{"start":[97,2],"end":[108,7],"bytes":[2453,2635]},"callee":null}}}}}},"functions":{"github.com/benbjohnson/litestream/cmd/litestream.resolveVersion":{"id":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion","kind":"function","span":{"start":[18,1],"end":[41,2],"bytes":[460,955]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.resolveVersion","parameters":[{"name":"injected","type":"string","span":{"start":[18,1],"end":[18,67],"bytes":[460,526]}},{"name":"bi","type":"*runtime/debug.BuildInfo","span":{"start":[18,1],"end":[18,67],"bytes":[460,526]}}],"return_type":"string","metrics":{"cyclomatic":7},"body":{"30:27":{"kind":"call","span":{"start":[30,27],"end":[30,42],"bytes":[738,753]},"callee":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.vcsSettings"},"34:5":{"kind":"call","span":{"start":[34,5],"end":[34,18],"bytes":[806,819]},"callee":null},"40:9":{"kind":"call","span":{"start":[40,9],"end":[40,56],"bytes":[906,953]},"callee":null}}},"github.com/benbjohnson/litestream/cmd/litestream.vcsSettings":{"id":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.vcsSettings","kind":"function","span":{"start":[45,1],"end":[57,2],"bytes":[1096,1428]},"signature":"github.com/benbjohnson/litestream/cmd/litestream.vcsSettings","parameters":[{"name":"bi","type":"*runtime/debug.BuildInfo","span":{"start":[45,1],"end":[45,92],"bytes":[1096,1187]}}],"return_type":"(string, bool, string)","metrics":{"cyclomatic":6},"body":{}}},"content_hash":"ef27995c6f376fd80906b31f6c239dde5ea3d88a99a771ec2962b75139f02da0"},"compaction_level.go":{"id":"can://go/litestream/compaction_level.go","kind":"module","span":{"start":[1,1],"end":[114,1],"bytes":[0,3545]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n// SnapshotLevel represents the level which full snapshots are held.\nconst SnapshotLevel = 9\n\n// DefaultCompactionLevels provides the canonical default compaction configuration.\n// Level 0 is raw LTX files, higher levels compact at increasing intervals.\n// These values are also used by cmd/litestream DefaultConfig().\nvar DefaultCompactionLevels = CompactionLevels{\n\t{Level: 0, Interval: 0},\n\t{Level: 1, Interval: 30 * time.Second},\n\t{Level: 2, Interval: 5 * time.Minute},\n\t{Level: 3, Interval: time.Hour},\n}\n\n// CompactionLevel represents a single part of a multi-level compaction.\n// Each level merges LTX files from the previous level into larger time granularities.\ntype CompactionLevel struct {\n\t// The numeric level. Must match the index in the list of levels.\n\tLevel int\n\n\t// The frequency that the level is compacted from the previous level.\n\tInterval time.Duration\n}\n\n// PrevCompactionAt returns the time when the last compaction occurred.\n// Returns the current time if it is exactly a multiple of the level interval.\nfunc (lvl *CompactionLevel) PrevCompactionAt(now time.Time) time.Time {\n\treturn now.Truncate(lvl.Interval).UTC()\n}\n\n// NextCompactionAt returns the time until the next compaction occurs.\n// Returns the current time if it is exactly a multiple of the level interval.\nfunc (lvl *CompactionLevel) NextCompactionAt(now time.Time) time.Time {\n\treturn lvl.PrevCompactionAt(now).Add(lvl.Interval)\n}\n\n// CompactionLevels represents a sorted slice of non-snapshot compaction levels.\ntype CompactionLevels []*CompactionLevel\n\n// Level returns the compaction level at the given index.\n// Returns an error if the index is a snapshot level or is out of bounds.\nfunc (a CompactionLevels) Level(level int) (*CompactionLevel, error) {\n\tif level == SnapshotLevel {\n\t\treturn nil, fmt.Errorf(\"invalid argument, snapshot level\")\n\t}\n\tif level \u003c 0 || level \u003e a.MaxLevel() {\n\t\treturn nil, fmt.Errorf(\"level out of bounds: %d\", level)\n\t}\n\treturn a[level], nil\n}\n\n// MaxLevel return the highest non-snapshot compaction level.\nfunc (a CompactionLevels) MaxLevel() int {\n\treturn len(a) - 1\n}\n\n// Validate returns an error if the levels are invalid.\nfunc (a CompactionLevels) Validate() error {\n\tif len(a) == 0 {\n\t\treturn fmt.Errorf(\"at least one compaction level is required\")\n\t}\n\n\tfor i, lvl := range a {\n\t\tif i != lvl.Level {\n\t\t\treturn fmt.Errorf(\"compaction level number out of order: %d, expected %d\", lvl.Level, i)\n\t\t} else if lvl.Level \u003e SnapshotLevel-1 {\n\t\t\treturn fmt.Errorf(\"compaction level cannot exceed %d\", SnapshotLevel-1)\n\t\t}\n\n\t\tif lvl.Level == 0 \u0026\u0026 lvl.Interval != 0 {\n\t\t\treturn fmt.Errorf(\"cannot set interval on compaction level zero\")\n\t\t}\n\n\t\tif lvl.Level != 0 \u0026\u0026 lvl.Interval \u003c= 0 {\n\t\t\treturn fmt.Errorf(\"interval required for level %d\", lvl.Level)\n\t\t}\n\t}\n\treturn nil\n}\n\n// IsValidLevel returns true if level is a valid compaction level number.\nfunc (a CompactionLevels) IsValidLevel(level int) bool {\n\tif level == SnapshotLevel {\n\t\treturn true\n\t}\n\treturn level \u003e= 0 \u0026\u0026 level \u003c len(a)\n}\n\n// PrevLevel returns the previous compaction level.\n// Returns -1 if there is no previous level.\nfunc (a CompactionLevels) PrevLevel(level int) int {\n\tif level == SnapshotLevel {\n\t\treturn a.MaxLevel()\n\t}\n\treturn level - 1\n}\n\n// NextLevel returns the next compaction level.\n// Returns -1 if there is no next level.\nfunc (a CompactionLevels) NextLevel(level int) int {\n\tif level == SnapshotLevel {\n\t\treturn -1\n\t} else if level == a.MaxLevel() {\n\t\treturn SnapshotLevel\n\t}\n\treturn level + 1\n}\n","imports":[{"name":"fmt","path":"fmt","span":{"start":[4,1],"end":[4,7],"bytes":[29,35]}},{"name":"time","path":"time","span":{"start":[5,1],"end":[5,8],"bytes":[36,43]}}],"types":{"CompactionLevel":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel","kind":"struct","span":{"start":[23,1],"end":[29,2],"bytes":[718,923]},"callables":{"github.com/benbjohnson/litestream.CompactionLevel.NextCompactionAt":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/github.com/benbjohnson/litestream.CompactionLevel.NextCompactionAt","kind":"method","span":{"start":[39,1],"end":[41,2],"bytes":[1342,1467]},"signature":"github.com/benbjohnson/litestream.CompactionLevel.NextCompactionAt","parameters":[{"name":"now","type":"time.Time","span":{"start":[39,1],"end":[39,72],"bytes":[1342,1413]}}],"return_type":"time.Time","metrics":{"cyclomatic":1},"body":{"40:9":{"kind":"call","span":{"start":[40,9],"end":[40,34],"bytes":[1422,1447]},"callee":null}}},"github.com/benbjohnson/litestream.CompactionLevel.PrevCompactionAt":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/github.com/benbjohnson/litestream.CompactionLevel.PrevCompactionAt","kind":"method","span":{"start":[33,1],"end":[35,2],"bytes":[1076,1190]},"signature":"github.com/benbjohnson/litestream.CompactionLevel.PrevCompactionAt","parameters":[{"name":"now","type":"time.Time","span":{"start":[33,1],"end":[33,72],"bytes":[1076,1147]}}],"return_type":"time.Time","metrics":{"cyclomatic":1},"body":{"34:9":{"kind":"call","span":{"start":[34,9],"end":[34,35],"bytes":[1156,1182]},"callee":null}}}},"fields":{"Interval":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/Interval","kind":"field","type":"time.Duration","span":{"start":[28,1],"end":[28,24],"bytes":[898,921]}},"Level":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/Level","kind":"field","type":"int","span":{"start":[25,1],"end":[25,11],"bytes":[815,825]}}}},"CompactionLevels":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels","kind":"struct","span":{"start":[44,1],"end":[44,41],"bytes":[1550,1590]},"callables":{"github.com/benbjohnson/litestream.CompactionLevels.IsValidLevel":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.IsValidLevel","kind":"method","span":{"start":[88,1],"end":[93,2],"bytes":[2913,3054]},"signature":"github.com/benbjohnson/litestream.CompactionLevels.IsValidLevel","parameters":[{"name":"level","type":"int","span":{"start":[88,1],"end":[88,57],"bytes":[2913,2969]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"92:31":{"kind":"call","span":{"start":[92,31],"end":[92,37],"bytes":[3046,3052]},"callee":null}}},"github.com/benbjohnson/litestream.CompactionLevels.Level":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.Level","kind":"method","span":{"start":[48,1],"end":[56,2],"bytes":[1724,2013]},"signature":"github.com/benbjohnson/litestream.CompactionLevels.Level","parameters":[{"name":"level","type":"int","span":{"start":[48,1],"end":[48,71],"bytes":[1724,1794]}}],"return_type":"(*github.com/benbjohnson/litestream.CompactionLevel, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"50:15":{"kind":"call","span":{"start":[50,15],"end":[50,61],"bytes":[1838,1884]},"callee":null},"52:26":{"kind":"call","span":{"start":[52,26],"end":[52,38],"bytes":[1913,1925]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel"},"53:15":{"kind":"call","span":{"start":[53,15],"end":[53,59],"bytes":[1942,1986]},"callee":null}}},"github.com/benbjohnson/litestream.CompactionLevels.MaxLevel":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel","kind":"method","span":{"start":[59,1],"end":[61,2],"bytes":[2077,2140]},"signature":"github.com/benbjohnson/litestream.CompactionLevels.MaxLevel","parameters":[],"return_type":"int","metrics":{"cyclomatic":1},"body":{"60:9":{"kind":"call","span":{"start":[60,9],"end":[60,15],"bytes":[2128,2134]},"callee":null}}},"github.com/benbjohnson/litestream.CompactionLevels.NextLevel":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.NextLevel","kind":"method","span":{"start":[106,1],"end":[113,2],"bytes":[3370,3544]},"signature":"github.com/benbjohnson/litestream.CompactionLevels.NextLevel","parameters":[{"name":"level","type":"int","span":{"start":[106,1],"end":[106,53],"bytes":[3370,3422]}}],"return_type":"int","metrics":{"cyclomatic":3},"body":{"109:21":{"kind":"call","span":{"start":[109,21],"end":[109,33],"bytes":[3484,3496]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel"}}},"github.com/benbjohnson/litestream.CompactionLevels.PrevLevel":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.PrevLevel","kind":"method","span":{"start":[97,1],"end":[102,2],"bytes":[3153,3279]},"signature":"github.com/benbjohnson/litestream.CompactionLevels.PrevLevel","parameters":[{"name":"level","type":"int","span":{"start":[97,1],"end":[97,53],"bytes":[3153,3205]}}],"return_type":"int","metrics":{"cyclomatic":2},"body":{"99:10":{"kind":"call","span":{"start":[99,10],"end":[99,22],"bytes":[3244,3256]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel"}}},"github.com/benbjohnson/litestream.CompactionLevels.Validate":{"id":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.Validate","kind":"method","span":{"start":[64,1],"end":[85,2],"bytes":[2198,2837]},"signature":"github.com/benbjohnson/litestream.CompactionLevels.Validate","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"65:5":{"kind":"call","span":{"start":[65,5],"end":[65,11],"bytes":[2247,2253]},"callee":null},"66:10":{"kind":"call","span":{"start":[66,10],"end":[66,65],"bytes":[2270,2325]},"callee":null},"71:11":{"kind":"call","span":{"start":[71,11],"end":[71,92],"bytes":[2387,2468]},"callee":null},"73:11":{"kind":"call","span":{"start":[73,11],"end":[73,75],"bytes":[2521,2585]},"callee":null},"77:11":{"kind":"call","span":{"start":[77,11],"end":[77,69],"bytes":[2644,2702]},"callee":null},"81:11":{"kind":"call","span":{"start":[81,11],"end":[81,66],"bytes":[2761,2816]},"callee":null}}}}}},"functions":{},"content_hash":"744ac7ec8fbcc2267945fbae48919c5b8a47f37cd2ea08b3a78c852dee2b895d"},"compactor.go":{"id":"can://go/litestream/compactor.go","kind":"module","span":{"start":[1,1],"end":[427,1],"bytes":[0,12016]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\n// Compactor handles compaction and retention for LTX files.\n// It operates solely through the ReplicaClient interface, making it\n// suitable for both DB (with local file caching) and VFS (remote-only).\ntype Compactor struct {\n\tclient ReplicaClient\n\tlogger *slog.Logger\n\n\t// VerifyCompaction enables post-compaction TXID consistency verification.\n\t// When enabled, verifies that files at the destination level have\n\t// contiguous TXID ranges after each compaction. Disabled by default.\n\tVerifyCompaction bool\n\n\t// RetentionEnabled controls whether Litestream actively deletes old files\n\t// during retention enforcement. When false, cloud provider lifecycle\n\t// policies handle retention instead. Local file cleanup still occurs.\n\tRetentionEnabled bool\n\n\t// CompactionVerifyErrorCounter is incremented when post-compaction\n\t// verification fails. Optional; if nil, no metric is recorded.\n\tCompactionVerifyErrorCounter prometheus.Counter\n\n\t// LocalFileOpener optionally opens a local LTX file for compaction.\n\t// If nil or returns os.ErrNotExist, falls back to remote.\n\t// This is used by DB to prefer local files over remote for consistency.\n\tLocalFileOpener func(level int, minTXID, maxTXID ltx.TXID) (io.ReadCloser, error)\n\n\t// LocalFileDeleter optionally deletes local LTX files after retention.\n\t// If nil, only remote files are deleted.\n\tLocalFileDeleter func(level int, minTXID, maxTXID ltx.TXID) error\n\n\t// CacheGetter optionally retrieves cached MaxLTXFileInfo for a level.\n\t// If nil, max file info is always fetched from remote.\n\tCacheGetter func(level int) (*ltx.FileInfo, bool)\n\n\t// CacheSetter optionally stores MaxLTXFileInfo for a level.\n\t// If nil, max file info is not cached.\n\tCacheSetter func(level int, info *ltx.FileInfo)\n}\n\n// NewCompactor creates a new Compactor with the given client and logger.\nfunc NewCompactor(client ReplicaClient, logger *slog.Logger) *Compactor {\n\tif logger == nil {\n\t\tlogger = slog.Default()\n\t}\n\treturn \u0026Compactor{\n\t\tclient: client,\n\t\tlogger: logger,\n\t\tRetentionEnabled: true,\n\t}\n}\n\nfunc (c *Compactor) setLogger(logger *slog.Logger) {\n\tc.logger = logger\n}\n\n// MaxLTXFileInfo returns metadata for the last LTX file in a level.\n// Uses cache if available, otherwise fetches from remote.\nfunc (c *Compactor) MaxLTXFileInfo(ctx context.Context, level int) (ltx.FileInfo, error) {\n\tif c.CacheGetter != nil {\n\t\tif info, ok := c.CacheGetter(level); ok {\n\t\t\treturn *info, nil\n\t\t}\n\t}\n\n\titr, err := c.client.LTXFiles(ctx, level, 0, false)\n\tif err != nil {\n\t\treturn ltx.FileInfo{}, err\n\t}\n\tdefer itr.Close()\n\n\tvar info ltx.FileInfo\n\tfor itr.Next() {\n\t\titem := itr.Item()\n\t\tif item.MaxTXID \u003e info.MaxTXID {\n\t\t\tinfo = *item\n\t\t}\n\t}\n\n\tif c.CacheSetter != nil \u0026\u0026 info.MaxTXID \u003e 0 {\n\t\tc.CacheSetter(level, \u0026info)\n\t}\n\n\treturn info, itr.Close()\n}\n\n// Compact compacts source level files into the destination level.\n// Returns ErrNoCompaction if there are no files to compact.\nfunc (c *Compactor) Compact(ctx context.Context, dstLevel int) (*ltx.FileInfo, error) {\n\tsrcLevel := dstLevel - 1\n\n\tprevMaxInfo, err := c.MaxLTXFileInfo(ctx, dstLevel)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot determine max ltx file for destination level: %w\", err)\n\t}\n\tseekTXID := prevMaxInfo.MaxTXID + 1\n\n\titr, err := c.client.LTXFiles(ctx, srcLevel, seekTXID, false)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"source ltx files after %s: %w\", seekTXID, err)\n\t}\n\tdefer itr.Close()\n\n\tvar rdrs []io.Reader\n\tdefer func() {\n\t\tfor _, rd := range rdrs {\n\t\t\tif closer, ok := rd.(io.Closer); ok {\n\t\t\t\t_ = closer.Close()\n\t\t\t}\n\t\t}\n\t}()\n\n\tvar minTXID, maxTXID ltx.TXID\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\n\t\tif minTXID == 0 || info.MinTXID \u003c minTXID {\n\t\t\tminTXID = info.MinTXID\n\t\t}\n\t\tif maxTXID == 0 || info.MaxTXID \u003e maxTXID {\n\t\t\tmaxTXID = info.MaxTXID\n\t\t}\n\n\t\tif c.LocalFileOpener != nil {\n\t\t\tif f, err := c.LocalFileOpener(srcLevel, info.MinTXID, info.MaxTXID); err == nil {\n\t\t\t\trdrs = append(rdrs, f)\n\t\t\t\tcontinue\n\t\t\t} else if !os.IsNotExist(err) {\n\t\t\t\treturn nil, fmt.Errorf(\"open local ltx file: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tf, err := c.client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"open ltx file: %w\", err)\n\t\t}\n\t\trdrs = append(rdrs, internal.NewResumableReader(ctx, c.client, info.Level, info.MinTXID, info.MaxTXID, info.Size, f, c.logger))\n\t}\n\tif len(rdrs) == 0 {\n\t\treturn nil, ErrNoCompaction\n\t}\n\n\tpr, pw := io.Pipe()\n\tgo func() {\n\t\tcomp, err := ltx.NewCompactor(pw, rdrs)\n\t\tif err != nil {\n\t\t\t_ = pw.CloseWithError(fmt.Errorf(\"new ltx compactor: %w\", err))\n\t\t\treturn\n\t\t}\n\t\tcomp.HeaderFlags = ltx.HeaderFlagNoChecksum\n\t\t_ = pw.CloseWithError(comp.Compact(ctx))\n\t}()\n\n\tinfo, err := c.client.WriteLTXFile(ctx, dstLevel, minTXID, maxTXID, pr)\n\t_ = pr.CloseWithError(err)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"write ltx file: %w\", err)\n\t}\n\n\tif c.CacheSetter != nil {\n\t\tc.CacheSetter(dstLevel, info)\n\t}\n\n\t// Verify level consistency if enabled\n\tif c.VerifyCompaction {\n\t\tif err := c.VerifyLevelConsistency(ctx, dstLevel); err != nil {\n\t\t\tc.logger.Warn(\"post-compaction verification failed\",\n\t\t\t\t\"level\", dstLevel,\n\t\t\t\t\"error\", err)\n\t\t\tif c.CompactionVerifyErrorCounter != nil {\n\t\t\t\tc.CompactionVerifyErrorCounter.Inc()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn info, nil\n}\n\n// VerifyLevelConsistency checks that LTX files at the given level have\n// contiguous TXID ranges (prevMaxTXID + 1 == currMinTXID for consecutive files).\n// Returns an error describing any gaps or overlaps found.\nfunc (c *Compactor) VerifyLevelConsistency(ctx context.Context, level int) error {\n\titr, err := c.client.LTXFiles(ctx, level, 0, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar prevInfo *ltx.FileInfo\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\n\t\t// Skip first file - nothing to compare against\n\t\tif prevInfo == nil {\n\t\t\tprevInfo = info\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for TXID contiguity: prev.MaxTXID + 1 should equal curr.MinTXID\n\t\texpectedMinTXID := prevInfo.MaxTXID + 1\n\t\tif info.MinTXID != expectedMinTXID {\n\t\t\tif info.MinTXID \u003e expectedMinTXID {\n\t\t\t\treturn fmt.Errorf(\"TXID gap detected: prev.MaxTXID=%s, next.MinTXID=%s (expected %s)\",\n\t\t\t\t\tprevInfo.MaxTXID, info.MinTXID, expectedMinTXID)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"TXID overlap detected: prev.MaxTXID=%s, next.MinTXID=%s\",\n\t\t\t\tprevInfo.MaxTXID, info.MinTXID)\n\t\t}\n\n\t\tprevInfo = info\n\t}\n\n\tif err := itr.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close iterator: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// EnforceSnapshotRetention enforces retention of snapshot level files by timestamp.\n// Files older than the retention duration are deleted (except the newest is always kept).\n// Returns the minimum snapshot TXID still retained (useful for cascading retention to lower levels).\nfunc (c *Compactor) EnforceSnapshotRetention(ctx context.Context, retention time.Duration) (ltx.TXID, error) {\n\ttimestamp := time.Now().Add(-retention)\n\tc.logger.Debug(\"enforcing snapshot retention\", \"timestamp\", timestamp)\n\n\titr, err := c.client.LTXFiles(ctx, SnapshotLevel, 0, false)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar deleted []*ltx.FileInfo\n\tvar lastInfo *ltx.FileInfo\n\tvar minSnapshotTXID ltx.TXID\n\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tlastInfo = info\n\n\t\tif info.CreatedAt.Before(timestamp) {\n\t\t\tdeleted = append(deleted, info)\n\t\t\tcontinue\n\t\t}\n\n\t\tif minSnapshotTXID == 0 || info.MaxTXID \u003c minSnapshotTXID {\n\t\t\tminSnapshotTXID = info.MaxTXID\n\t\t}\n\t}\n\n\tif len(deleted) \u003e 0 \u0026\u0026 deleted[len(deleted)-1] == lastInfo {\n\t\tdeleted = deleted[:len(deleted)-1]\n\t}\n\n\tif !c.RetentionEnabled {\n\t\tc.logger.Debug(\"skipping remote deletion (retention disabled)\", \"level\", SnapshotLevel, \"count\", len(deleted))\n\t} else if err := c.client.DeleteLTXFiles(ctx, deleted); err != nil {\n\t\treturn 0, fmt.Errorf(\"remove ltx files: %w\", err)\n\t}\n\n\tif c.LocalFileDeleter != nil {\n\t\tfor _, info := range deleted {\n\t\t\tc.logger.Debug(\"deleting local ltx file\",\n\t\t\t\t\"level\", SnapshotLevel,\n\t\t\t\t\"minTXID\", info.MinTXID,\n\t\t\t\t\"maxTXID\", info.MaxTXID)\n\t\t\tif err := c.LocalFileDeleter(SnapshotLevel, info.MinTXID, info.MaxTXID); err != nil {\n\t\t\t\tc.logger.Error(\"failed to remove local ltx file\", \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn minSnapshotTXID, nil\n}\n\n// EnforceRetentionByTXID deletes files at the given level with maxTXID below the target.\n// Always keeps at least one file.\nfunc (c *Compactor) EnforceRetentionByTXID(ctx context.Context, level int, txID ltx.TXID) error {\n\tc.logger.Debug(\"enforcing retention\", \"level\", level, \"txid\", txID)\n\n\titr, err := c.client.LTXFiles(ctx, level, 0, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar deleted []*ltx.FileInfo\n\tvar lastInfo *ltx.FileInfo\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tlastInfo = info\n\n\t\tif info.MaxTXID \u003c txID {\n\t\t\tdeleted = append(deleted, info)\n\t\t\tcontinue\n\t\t}\n\t}\n\n\tif len(deleted) \u003e 0 \u0026\u0026 deleted[len(deleted)-1] == lastInfo {\n\t\tdeleted = deleted[:len(deleted)-1]\n\t}\n\n\tif !c.RetentionEnabled {\n\t\tc.logger.Debug(\"skipping remote deletion (retention disabled)\", \"level\", level, \"count\", len(deleted))\n\t} else if err := c.client.DeleteLTXFiles(ctx, deleted); err != nil {\n\t\treturn fmt.Errorf(\"remove ltx files: %w\", err)\n\t}\n\n\tif c.LocalFileDeleter != nil {\n\t\tfor _, info := range deleted {\n\t\t\tc.logger.Debug(\"deleting local ltx file\",\n\t\t\t\t\"level\", level,\n\t\t\t\t\"minTXID\", info.MinTXID,\n\t\t\t\t\"maxTXID\", info.MaxTXID)\n\t\t\tif err := c.LocalFileDeleter(level, info.MinTXID, info.MaxTXID); err != nil {\n\t\t\t\tc.logger.Error(\"failed to remove local ltx file\", \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// EnforceL0Retention retains L0 files based on L1 compaction progress and time.\n// Files are only deleted if they have been compacted into L1 AND are older than retention.\n// This ensures contiguous L0 coverage for VFS reads.\nfunc (c *Compactor) EnforceL0Retention(ctx context.Context, retention time.Duration) error {\n\tif retention \u003c= 0 {\n\t\treturn nil\n\t}\n\n\tc.logger.Debug(\"enforcing l0 retention\", \"retention\", retention)\n\n\titr, err := c.client.LTXFiles(ctx, 1, 0, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch l1 files: %w\", err)\n\t}\n\tvar maxL1TXID ltx.TXID\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tif info.MaxTXID \u003e maxL1TXID {\n\t\t\tmaxL1TXID = info.MaxTXID\n\t\t}\n\t}\n\tif err := itr.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close l1 iterator: %w\", err)\n\t}\n\tif maxL1TXID == 0 {\n\t\treturn nil\n\t}\n\n\tthreshold := time.Now().Add(-retention)\n\titr, err = c.client.LTXFiles(ctx, 0, 0, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch l0 files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar (\n\t\tdeleted []*ltx.FileInfo\n\t\tlastInfo *ltx.FileInfo\n\t\tprocessedAll = true\n\t)\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tlastInfo = info\n\n\t\tcreatedAt := info.CreatedAt\n\t\tif createdAt.IsZero() {\n\t\t\tcreatedAt = threshold\n\t\t}\n\n\t\tif createdAt.After(threshold) {\n\t\t\tprocessedAll = false\n\t\t\tbreak\n\t\t}\n\n\t\tif info.MaxTXID \u003c= maxL1TXID {\n\t\t\tdeleted = append(deleted, info)\n\t\t}\n\t}\n\n\tif processedAll \u0026\u0026 len(deleted) \u003e 0 \u0026\u0026 lastInfo != nil \u0026\u0026 deleted[len(deleted)-1] == lastInfo {\n\t\tdeleted = deleted[:len(deleted)-1]\n\t}\n\n\tif len(deleted) == 0 {\n\t\treturn nil\n\t}\n\n\tif !c.RetentionEnabled {\n\t\tc.logger.Debug(\"skipping remote deletion (retention disabled)\", \"level\", 0, \"count\", len(deleted))\n\t} else if err := c.client.DeleteLTXFiles(ctx, deleted); err != nil {\n\t\treturn fmt.Errorf(\"remove expired l0 files: %w\", err)\n\t}\n\n\tif c.LocalFileDeleter != nil {\n\t\tfor _, info := range deleted {\n\t\t\tc.logger.Debug(\"deleting expired local l0 file\",\n\t\t\t\t\"minTXID\", info.MinTXID,\n\t\t\t\t\"maxTXID\", info.MaxTXID)\n\t\t\tif err := c.LocalFileDeleter(0, info.MinTXID, info.MaxTXID); err != nil {\n\t\t\t\tc.logger.Error(\"failed to remove local l0 file\", \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tc.logger.Info(\"l0 retention enforced\", \"deleted_count\", len(deleted), \"max_l1_txid\", maxL1TXID)\n\n\treturn nil\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"fmt","path":"fmt","span":{"start":[5,1],"end":[5,7],"bytes":[40,46]}},{"name":"io","path":"io","span":{"start":[6,1],"end":[6,6],"bytes":[47,52]}},{"name":"slog","path":"log/slog","span":{"start":[7,1],"end":[7,12],"bytes":[53,64]}},{"name":"os","path":"os","span":{"start":[8,1],"end":[8,6],"bytes":[65,70]}},{"name":"time","path":"time","span":{"start":[9,1],"end":[9,8],"bytes":[71,78]}},{"name":"prometheus","path":"github.com/prometheus/client_golang/prometheus","span":{"start":[11,1],"end":[11,50],"bytes":[80,129]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[12,1],"end":[12,27],"bytes":[130,156]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[14,1],"end":[14,46],"bytes":[158,203]}}],"types":{"Compactor":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor","kind":"struct","span":{"start":[20,1],"end":[54,2],"bytes":[410,1950]},"callables":{"github.com/benbjohnson/litestream.Compactor.Compact":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","kind":"method","span":{"start":[104,1],"end":[192,2],"bytes":[3132,5454]},"signature":"github.com/benbjohnson/litestream.Compactor.Compact","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[104,1],"end":[104,88],"bytes":[3132,3219]}},{"name":"dstLevel","type":"int","span":{"start":[104,1],"end":[104,88],"bytes":[3132,3219]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":19},"body":{"107:22":{"kind":"call","span":{"start":[107,22],"end":[107,53],"bytes":[3268,3299]},"callee":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.MaxLTXFileInfo"},"109:15":{"kind":"call","span":{"start":[109,15],"end":[109,89],"bytes":[3331,3405]},"callee":null},"113:14":{"kind":"call","span":{"start":[113,14],"end":[113,63],"bytes":[3460,3509]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"115:15":{"kind":"call","span":{"start":[115,15],"end":[115,73],"bytes":[3541,3599]},"callee":null},"117:8":{"kind":"call","span":{"start":[117,8],"end":[117,19],"bytes":[3610,3621]},"callee":null},"120:8":{"kind":"call","span":{"start":[120,8],"end":[126,5],"bytes":[3652,3766]},"callee":null},"129:6":{"kind":"call","span":{"start":[129,6],"end":[129,16],"bytes":[3804,3814]},"callee":null},"130:11":{"kind":"call","span":{"start":[130,11],"end":[130,21],"bytes":[3827,3837]},"callee":null},"140:17":{"kind":"call","span":{"start":[140,17],"end":[140,72],"bytes":[4040,4095]},"callee":null},"141:12":{"kind":"call","span":{"start":[141,12],"end":[141,27],"bytes":[4121,4136]},"callee":null},"143:15":{"kind":"call","span":{"start":[143,15],"end":[143,33],"bytes":[4164,4182]},"callee":null},"144:17":{"kind":"call","span":{"start":[144,17],"end":[144,59],"bytes":[4201,4243]},"callee":null},"148:13":{"kind":"call","span":{"start":[148,13],"end":[148,84],"bytes":[4266,4337]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile"},"150:16":{"kind":"call","span":{"start":[150,16],"end":[150,52],"bytes":[4371,4407]},"callee":null},"152:10":{"kind":"call","span":{"start":[152,10],"end":[152,130],"bytes":[4421,4541]},"callee":null},"152:23":{"kind":"call","span":{"start":[152,23],"end":[152,129],"bytes":[4434,4540]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.NewResumableReader"},"154:5":{"kind":"call","span":{"start":[154,5],"end":[154,14],"bytes":[4549,4558]},"callee":null},"158:12":{"kind":"call","span":{"start":[158,12],"end":[158,21],"bytes":[4611,4620]},"callee":null},"159:5":{"kind":"call","span":{"start":[159,5],"end":[167,5],"bytes":[4625,4868]},"callee":null,"is_goroutine":true},"169:15":{"kind":"call","span":{"start":[169,15],"end":[169,73],"bytes":[4884,4942]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile"},"170:6":{"kind":"call","span":{"start":[170,6],"end":[170,28],"bytes":[4948,4970]},"callee":null},"172:15":{"kind":"call","span":{"start":[172,15],"end":[172,52],"bytes":[5002,5039]},"callee":null},"176:3":{"kind":"call","span":{"start":[176,3],"end":[176,32],"bytes":[5073,5102]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"181:13":{"kind":"call","span":{"start":[181,13],"end":[181,52],"bytes":[5184,5223]},"callee":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.VerifyLevelConsistency"},"182:4":{"kind":"call","span":{"start":[182,4],"end":[184,18],"bytes":[5241,5334]},"callee":null},"186:5":{"kind":"call","span":{"start":[186,5],"end":[186,41],"bytes":[5385,5421]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact/github.com/benbjohnson/litestream.Compactor.Compact.closure_1","kind":"function","span":{"start":[120,1],"end":[126,5],"bytes":[3645,3766]},"signature":"github.com/benbjohnson/litestream.Compactor.Compact.closure_1","parameters":[],"body":{"123:9":{"kind":"call","span":{"start":[123,9],"end":[123,23],"bytes":[3738,3752]},"callee":null}}},"closure_2":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact/github.com/benbjohnson/litestream.Compactor.Compact.closure_2","kind":"function","span":{"start":[159,1],"end":[167,5],"bytes":[4621,4868]},"signature":"github.com/benbjohnson/litestream.Compactor.Compact.closure_2","parameters":[],"body":{"160:16":{"kind":"call","span":{"start":[160,16],"end":[160,42],"bytes":[4649,4675]},"callee":null},"162:26":{"kind":"call","span":{"start":[162,26],"end":[162,66],"bytes":[4719,4759]},"callee":null},"162:8":{"kind":"call","span":{"start":[162,8],"end":[162,67],"bytes":[4701,4760]},"callee":null},"166:25":{"kind":"call","span":{"start":[166,25],"end":[166,42],"bytes":[4845,4862]},"callee":null},"166:7":{"kind":"call","span":{"start":[166,7],"end":[166,43],"bytes":[4827,4863]},"callee":null}}}}},"github.com/benbjohnson/litestream.Compactor.EnforceL0Retention":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceL0Retention","kind":"method","span":{"start":[342,1],"end":[426,2],"bytes":[10004,12015]},"signature":"github.com/benbjohnson/litestream.Compactor.EnforceL0Retention","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[342,1],"end":[342,93],"bytes":[10004,10096]}},{"name":"retention","type":"time.Duration","span":{"start":[342,1],"end":[342,93],"bytes":[10004,10096]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":19},"body":{"347:2":{"kind":"call","span":{"start":[347,2],"end":[347,66],"bytes":[10136,10200]},"callee":null},"349:14":{"kind":"call","span":{"start":[349,14],"end":[349,49],"bytes":[10215,10250]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"351:10":{"kind":"call","span":{"start":[351,10],"end":[351,47],"bytes":[10277,10314]},"callee":null},"354:6":{"kind":"call","span":{"start":[354,6],"end":[354,16],"bytes":[10347,10357]},"callee":null},"355:11":{"kind":"call","span":{"start":[355,11],"end":[355,21],"bytes":[10370,10380]},"callee":null},"360:12":{"kind":"call","span":{"start":[360,12],"end":[360,23],"bytes":[10459,10470]},"callee":null},"361:10":{"kind":"call","span":{"start":[361,10],"end":[361,50],"bytes":[10494,10534]},"callee":null},"367:15":{"kind":"call","span":{"start":[367,15],"end":[367,25],"bytes":[10590,10600]},"callee":null},"368:13":{"kind":"call","span":{"start":[368,13],"end":[368,48],"bytes":[10629,10664]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"370:10":{"kind":"call","span":{"start":[370,10],"end":[370,47],"bytes":[10691,10728]},"callee":null},"372:8":{"kind":"call","span":{"start":[372,8],"end":[372,19],"bytes":[10739,10750]},"callee":null},"379:6":{"kind":"call","span":{"start":[379,6],"end":[379,16],"bytes":[10849,10859]},"callee":null},"380:11":{"kind":"call","span":{"start":[380,11],"end":[380,21],"bytes":[10872,10882]},"callee":null},"384:6":{"kind":"call","span":{"start":[384,6],"end":[384,24],"bytes":[10937,10955]},"callee":null},"388:6":{"kind":"call","span":{"start":[388,6],"end":[388,32],"bytes":[10993,11019]},"callee":null},"394:14":{"kind":"call","span":{"start":[394,14],"end":[394,35],"bytes":[11106,11127]},"callee":null},"398:21":{"kind":"call","span":{"start":[398,21],"end":[398,33],"bytes":[11156,11168]},"callee":null},"398:68":{"kind":"call","span":{"start":[398,68],"end":[398,80],"bytes":[11203,11215]},"callee":null},"399:22":{"kind":"call","span":{"start":[399,22],"end":[399,34],"bytes":[11254,11266]},"callee":null},"402:5":{"kind":"call","span":{"start":[402,5],"end":[402,17],"bytes":[11278,11290]},"callee":null},"407:3":{"kind":"call","span":{"start":[407,3],"end":[407,101],"bytes":[11343,11441]},"callee":null},"407:88":{"kind":"call","span":{"start":[407,88],"end":[407,100],"bytes":[11428,11440]},"callee":null},"408:19":{"kind":"call","span":{"start":[408,19],"end":[408,56],"bytes":[11460,11497]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles"},"409:10":{"kind":"call","span":{"start":[409,10],"end":[409,56],"bytes":[11521,11567]},"callee":null},"414:4":{"kind":"call","span":{"start":[414,4],"end":[416,29],"bytes":[11640,11746]},"callee":null},"417:14":{"kind":"call","span":{"start":[417,14],"end":[417,63],"bytes":[11760,11809]},"callee":null},"418:5":{"kind":"call","span":{"start":[418,5],"end":[418,67],"bytes":[11828,11890]},"callee":null},"423:2":{"kind":"call","span":{"start":[423,2],"end":[423,97],"bytes":[11905,12000]},"callee":null},"423:58":{"kind":"call","span":{"start":[423,58],"end":[423,70],"bytes":[11961,11973]},"callee":null}}},"github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID","kind":"method","span":{"start":[293,1],"end":[337,2],"bytes":[8549,9775]},"signature":"github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[293,1],"end":[293,98],"bytes":[8549,8646]}},{"name":"level","type":"int","span":{"start":[293,1],"end":[293,98],"bytes":[8549,8646]}},{"name":"txID","type":"github.com/superfly/ltx.TXID","span":{"start":[293,1],"end":[293,98],"bytes":[8549,8646]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"294:2":{"kind":"call","span":{"start":[294,2],"end":[294,69],"bytes":[8648,8715]},"callee":null},"296:14":{"kind":"call","span":{"start":[296,14],"end":[296,53],"bytes":[8730,8769]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"298:10":{"kind":"call","span":{"start":[298,10],"end":[298,48],"bytes":[8796,8834]},"callee":null},"300:8":{"kind":"call","span":{"start":[300,8],"end":[300,19],"bytes":[8845,8856]},"callee":null},"304:6":{"kind":"call","span":{"start":[304,6],"end":[304,16],"bytes":[8920,8930]},"callee":null},"305:11":{"kind":"call","span":{"start":[305,11],"end":[305,21],"bytes":[8943,8953]},"callee":null},"309:14":{"kind":"call","span":{"start":[309,14],"end":[309,35],"bytes":[9013,9034]},"callee":null},"314:33":{"kind":"call","span":{"start":[314,33],"end":[314,45],"bytes":[9087,9099]},"callee":null},"314:5":{"kind":"call","span":{"start":[314,5],"end":[314,17],"bytes":[9059,9071]},"callee":null},"315:22":{"kind":"call","span":{"start":[315,22],"end":[315,34],"bytes":[9138,9150]},"callee":null},"319:3":{"kind":"call","span":{"start":[319,3],"end":[319,105],"bytes":[9186,9288]},"callee":null},"319:92":{"kind":"call","span":{"start":[319,92],"end":[319,104],"bytes":[9275,9287]},"callee":null},"320:19":{"kind":"call","span":{"start":[320,19],"end":[320,56],"bytes":[9307,9344]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles"},"321:10":{"kind":"call","span":{"start":[321,10],"end":[321,49],"bytes":[9368,9407]},"callee":null},"326:4":{"kind":"call","span":{"start":[326,4],"end":[329,29],"bytes":[9480,9599]},"callee":null},"330:14":{"kind":"call","span":{"start":[330,14],"end":[330,67],"bytes":[9613,9666]},"callee":null},"331:5":{"kind":"call","span":{"start":[331,5],"end":[331,68],"bytes":[9685,9748]},"callee":null}}},"github.com/benbjohnson/litestream.Compactor.EnforceSnapshotRetention":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceSnapshotRetention","kind":"method","span":{"start":[238,1],"end":[289,2],"bytes":[6939,8422]},"signature":"github.com/benbjohnson/litestream.Compactor.EnforceSnapshotRetention","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[238,1],"end":[238,111],"bytes":[6939,7049]}},{"name":"retention","type":"time.Duration","span":{"start":[238,1],"end":[238,111],"bytes":[6939,7049]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"239:15":{"kind":"call","span":{"start":[239,15],"end":[239,25],"bytes":[7064,7074]},"callee":null},"240:2":{"kind":"call","span":{"start":[240,2],"end":[240,72],"bytes":[7092,7162]},"callee":null},"242:14":{"kind":"call","span":{"start":[242,14],"end":[242,61],"bytes":[7177,7224]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"244:13":{"kind":"call","span":{"start":[244,13],"end":[244,51],"bytes":[7254,7292]},"callee":null},"246:8":{"kind":"call","span":{"start":[246,8],"end":[246,19],"bytes":[7303,7314]},"callee":null},"252:6":{"kind":"call","span":{"start":[252,6],"end":[252,16],"bytes":[7409,7419]},"callee":null},"253:11":{"kind":"call","span":{"start":[253,11],"end":[253,21],"bytes":[7432,7442]},"callee":null},"256:6":{"kind":"call","span":{"start":[256,6],"end":[256,38],"bytes":[7467,7499]},"callee":null},"257:14":{"kind":"call","span":{"start":[257,14],"end":[257,35],"bytes":[7515,7536]},"callee":null},"266:33":{"kind":"call","span":{"start":[266,33],"end":[266,45],"bytes":[7690,7702]},"callee":null},"266:5":{"kind":"call","span":{"start":[266,5],"end":[266,17],"bytes":[7662,7674]},"callee":null},"267:22":{"kind":"call","span":{"start":[267,22],"end":[267,34],"bytes":[7741,7753]},"callee":null},"271:100":{"kind":"call","span":{"start":[271,100],"end":[271,112],"bytes":[7886,7898]},"callee":null},"271:3":{"kind":"call","span":{"start":[271,3],"end":[271,113],"bytes":[7789,7899]},"callee":null},"272:19":{"kind":"call","span":{"start":[272,19],"end":[272,56],"bytes":[7918,7955]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles"},"273:13":{"kind":"call","span":{"start":[273,13],"end":[273,52],"bytes":[7982,8021]},"callee":null},"278:4":{"kind":"call","span":{"start":[278,4],"end":[281,29],"bytes":[8094,8221]},"callee":null},"282:14":{"kind":"call","span":{"start":[282,14],"end":[282,75],"bytes":[8235,8296]},"callee":null},"283:5":{"kind":"call","span":{"start":[283,5],"end":[283,68],"bytes":[8315,8378]},"callee":null}}},"github.com/benbjohnson/litestream.Compactor.MaxLTXFileInfo":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.MaxLTXFileInfo","kind":"method","span":{"start":[74,1],"end":[100,2],"bytes":[2460,3002]},"signature":"github.com/benbjohnson/litestream.Compactor.MaxLTXFileInfo","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[74,1],"end":[74,91],"bytes":[2460,2550]}},{"name":"level","type":"int","span":{"start":[74,1],"end":[74,91],"bytes":[2460,2550]}}],"return_type":"(github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"76:18":{"kind":"call","span":{"start":[76,18],"end":[76,38],"bytes":[2595,2615]},"callee":null},"81:14":{"kind":"call","span":{"start":[81,14],"end":[81,53],"bytes":[2664,2703]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"85:8":{"kind":"call","span":{"start":[85,8],"end":[85,19],"bytes":[2760,2771]},"callee":null},"88:6":{"kind":"call","span":{"start":[88,6],"end":[88,16],"bytes":[2801,2811]},"callee":null},"89:11":{"kind":"call","span":{"start":[89,11],"end":[89,21],"bytes":[2824,2834]},"callee":null},"96:3":{"kind":"call","span":{"start":[96,3],"end":[96,30],"bytes":[2943,2970]},"callee":null},"99:15":{"kind":"call","span":{"start":[99,15],"end":[99,26],"bytes":[2989,3000]},"callee":null}}},"github.com/benbjohnson/litestream.Compactor.VerifyLevelConsistency":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.VerifyLevelConsistency","kind":"method","span":{"start":[197,1],"end":[233,2],"bytes":[5669,6659]},"signature":"github.com/benbjohnson/litestream.Compactor.VerifyLevelConsistency","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[197,1],"end":[197,83],"bytes":[5669,5751]}},{"name":"level","type":"int","span":{"start":[197,1],"end":[197,83],"bytes":[5669,5751]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"198:14":{"kind":"call","span":{"start":[198,14],"end":[198,53],"bytes":[5765,5804]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"200:10":{"kind":"call","span":{"start":[200,10],"end":[200,48],"bytes":[5831,5869]},"callee":null},"202:8":{"kind":"call","span":{"start":[202,8],"end":[202,19],"bytes":[5880,5891]},"callee":null},"205:6":{"kind":"call","span":{"start":[205,6],"end":[205,16],"bytes":[5926,5936]},"callee":null},"206:11":{"kind":"call","span":{"start":[206,11],"end":[206,21],"bytes":[5949,5959]},"callee":null},"218:12":{"kind":"call","span":{"start":[218,12],"end":[219,54],"bytes":[6276,6409]},"callee":null},"221:11":{"kind":"call","span":{"start":[221,11],"end":[222,36],"bytes":[6425,6530]},"callee":null},"228:12":{"kind":"call","span":{"start":[228,12],"end":[228,23],"bytes":[6569,6580]},"callee":null},"229:10":{"kind":"call","span":{"start":[229,10],"end":[229,47],"bytes":[6604,6641]},"callee":null}}},"github.com/benbjohnson/litestream.Compactor.setLogger":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.setLogger","kind":"method","span":{"start":[68,1],"end":[70,2],"bytes":[2257,2330]},"signature":"github.com/benbjohnson/litestream.Compactor.setLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[68,1],"end":[68,53],"bytes":[2257,2309]}}],"metrics":{"cyclomatic":1},"body":{}}},"fields":{"CacheGetter":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/CacheGetter","kind":"field","type":"func(level int) (*github.com/superfly/ltx.FileInfo, bool)","span":{"start":[49,1],"end":[49,51],"bytes":[1745,1795]}},"CacheSetter":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/CacheSetter","kind":"field","type":"func(level int, info *github.com/superfly/ltx.FileInfo)","span":{"start":[53,1],"end":[53,49],"bytes":[1900,1948]}},"CompactionVerifyErrorCounter":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/CompactionVerifyErrorCounter","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Counter","span":{"start":[36,1],"end":[36,49],"bytes":[1094,1142]}},"LocalFileDeleter":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/LocalFileDeleter","kind":"field","type":"func(level int, minTXID github.com/superfly/ltx.TXID, maxTXID github.com/superfly/ltx.TXID) error","span":{"start":[45,1],"end":[45,67],"bytes":[1548,1614]}},"LocalFileOpener":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/LocalFileOpener","kind":"field","type":"func(level int, minTXID github.com/superfly/ltx.TXID, maxTXID github.com/superfly/ltx.TXID) (io.ReadCloser, error)","span":{"start":[41,1],"end":[41,83],"bytes":[1348,1430]}},"RetentionEnabled":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/RetentionEnabled","kind":"field","type":"bool","span":{"start":[32,1],"end":[32,23],"bytes":[936,958]}},"VerifyCompaction":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/VerifyCompaction","kind":"field","type":"bool","span":{"start":[27,1],"end":[27,23],"bytes":[693,715]}},"client":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/client","kind":"field","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[21,1],"end":[21,22],"bytes":[434,455]}},"logger":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[22,1],"end":[22,21],"bytes":[456,476]}}}}},"functions":{"github.com/benbjohnson/litestream.NewCompactor":{"id":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.NewCompactor","kind":"function","span":{"start":[57,1],"end":[66,2],"bytes":[2026,2255]},"signature":"github.com/benbjohnson/litestream.NewCompactor","parameters":[{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[57,1],"end":[57,74],"bytes":[2026,2099]}},{"name":"logger","type":"*log/slog.Logger","span":{"start":[57,1],"end":[57,74],"bytes":[2026,2099]}}],"return_type":"*github.com/benbjohnson/litestream.Compactor","metrics":{"cyclomatic":2},"body":{"59:12":{"kind":"call","span":{"start":[59,12],"end":[59,26],"bytes":[2131,2145]},"callee":null}}}},"content_hash":"03305762391ff87adf9ad389d530c981333c4ad82035b223a3b612ac6612cb9b"},"db.go":{"id":"can://go/litestream/db.go","kind":"module","span":{"start":[1,1],"end":[3492,1],"bytes":[0,111886]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/crc64\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promauto\"\n\t\"github.com/superfly/ltx\"\n\t\"golang.org/x/sync/semaphore\"\n\t\"modernc.org/sqlite\"\n\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\n// Default DB settings.\nconst (\n\tDefaultMonitorInterval = 1 * time.Second\n\tDefaultCheckpointInterval = 1 * time.Minute\n\tDefaultBusyTimeout = 1 * time.Second\n\tDefaultMinCheckpointPageN = 1000\n\tDefaultTruncatePageN = 121359 // ~500MB with 4KB page size\n\tDefaultMaxSyncWALBytes = 64 \u003c\u003c 20\n\tDefaultShutdownSyncTimeout = 30 * time.Second\n\tDefaultShutdownSyncInterval = 500 * time.Millisecond\n\n\t// Sync error backoff configuration.\n\t// When sync errors occur repeatedly (e.g., disk full), backoff doubles each time.\n\tDefaultSyncBackoffMax = 5 * time.Minute // Maximum backoff between retries\n\tSyncErrorLogInterval = 30 * time.Second // Rate-limit repeated error logging\n)\n\n// DB represents a managed instance of a SQLite database in the file system.\n//\n// Checkpoint Strategy:\n// Litestream uses a progressive 3-tier checkpoint approach to balance WAL size\n// management with write availability:\n//\n// 1. MinCheckpointPageN (PASSIVE): Non-blocking checkpoint at ~1k pages (~4MB).\n// Attempts checkpoint but allows concurrent readers/writers.\n//\n// 2. CheckpointInterval (PASSIVE): Time-based non-blocking checkpoint.\n// Ensures regular checkpointing even with low write volume.\n//\n// 3. TruncatePageN (TRUNCATE): Blocking checkpoint at ~121k pages (~500MB).\n// Emergency brake for runaway WAL growth. Can block writes while waiting\n// for long-lived read transactions. Configurable with a default backstop.\n//\n// The RESTART checkpoint mode was permanently removed due to production issues\n// with indefinite write blocking (issue #724). All checkpoints now use either\n// PASSIVE (non-blocking) or TRUNCATE (emergency only) modes.\ntype DB struct {\n\tmu sync.RWMutex\n\texecSem *semaphore.Weighted\n\tpath string // part to database\n\tmetaPath string // Path to the database metadata.\n\tdb *sql.DB // target database\n\tf *os.File // long-running db file descriptor\n\trtx *sql.Tx // long running read transaction\n\tpageSize int // page size, in bytes\n\tnotify chan struct{} // closes on WAL change\n\tchkMu sync.RWMutex // checkpoint lock\n\topened bool // true if Open() was called and Close() not yet called\n\tsyncState syncState\n\tsyncDiag diagState\n\n\t// last file info for each level\n\tmaxLTXFileInfos struct {\n\t\tsync.Mutex\n\t\tm map[int]*ltx.FileInfo\n\t}\n\n\t// Cached position from the latest L0 LTX file.\n\t// nil means cache is invalid; non-nil is the cached position.\n\tpos struct {\n\t\tsync.Mutex\n\t\tvalue *ltx.Pos\n\t}\n\n\tfileInfo os.FileInfo // db info cached during init\n\tdirInfo os.FileInfo // parent dir info cached during init\n\n\t// openLTXFile opens LTX staging files; overridable in tests to\n\t// inject filesystem errors such as ENOSPC.\n\topenLTXFile func(name string, flag int, perm os.FileMode) (ltxStagingFile, error)\n\n\tctx context.Context\n\tcancel func()\n\twg sync.WaitGroup\n\tDone \u003c-chan struct{}\n\n\t// Metrics\n\tmetricsRegistered bool\n\tdbSizeGauge prometheus.Gauge\n\twalSizeGauge prometheus.Gauge\n\ttotalWALBytesCounter prometheus.Counter\n\ttxIDGauge prometheus.Gauge\n\tsyncNCounter prometheus.Counter\n\tsyncErrorNCounter prometheus.Counter\n\tsyncSecondsCounter prometheus.Counter\n\tdiskFullGauge prometheus.Gauge\n\tcheckpointNCounterVec *prometheus.CounterVec\n\tcheckpointErrorNCounterVec *prometheus.CounterVec\n\tcheckpointSecondsCounterVec *prometheus.CounterVec\n\n\t// Minimum threshold of WAL size, in pages, before a passive checkpoint.\n\t// A passive checkpoint will attempt a checkpoint but fail if there are\n\t// active transactions occurring at the same time.\n\t//\n\t// Uses PASSIVE checkpoint mode (non-blocking). Keeps WAL size manageable\n\t// for faster restores. Default: 1000 pages (~4MB with 4KB page size).\n\tMinCheckpointPageN int\n\n\t// Threshold of WAL size, in pages, before a forced truncation checkpoint.\n\t// A forced truncation checkpoint will block new transactions and wait for\n\t// existing transactions to finish before issuing a checkpoint and\n\t// truncating the WAL.\n\t//\n\t// Uses TRUNCATE checkpoint mode (blocking). Prevents unbounded WAL growth\n\t// from long-lived read transactions. Default: 121359 pages (~500MB with 4KB\n\t// page size). Set to 0 to retain the default emergency threshold.\n\tTruncatePageN int\n\n\t// Time between automatic checkpoints in the WAL. This is done to allow\n\t// more fine-grained WAL files so that restores can be performed with\n\t// better precision.\n\t//\n\t// Uses PASSIVE checkpoint mode (non-blocking). Default: 1 minute.\n\t// Set to 0 to disable time-based checkpoints.\n\tCheckpointInterval time.Duration\n\n\t// Frequency at which to perform db sync.\n\tMonitorInterval time.Duration\n\n\t// Maximum WAL bytes to process in a single sync executor run.\n\t// The limit is checked at commit boundaries so a single transaction\n\t// larger than this may exceed it. Set to zero to process all\n\t// committed WAL frames in one run.\n\tMaxSyncWALBytes int64\n\n\t// The timeout to wait for EBUSY from SQLite.\n\tBusyTimeout time.Duration\n\n\t// Minimum time to retain L0 files after they have been compacted into L1.\n\tL0Retention time.Duration\n\n\t// VerifyCompaction enables post-compaction TXID consistency verification.\n\t// When enabled, verifies that files at the destination level have\n\t// contiguous TXID ranges after each compaction.\n\tVerifyCompaction bool\n\n\t// RetentionEnabled controls whether Litestream actively deletes old files\n\t// during retention enforcement. When false, cloud provider lifecycle\n\t// policies handle retention instead. Local file cleanup still occurs.\n\tRetentionEnabled bool\n\n\t// Remote replica for the database.\n\t// Must be set before calling Open().\n\tReplica *Replica\n\n\t// Compactor handles shared compaction logic.\n\t// Created in NewDB with nil client; client set once in Open() from Replica.Client.\n\tcompactor *Compactor\n\n\t// Shutdown sync retry settings.\n\t// ShutdownSyncTimeout is the total time to retry syncing on shutdown.\n\t// ShutdownSyncInterval is the time between retry attempts.\n\tShutdownSyncTimeout time.Duration\n\tShutdownSyncInterval time.Duration\n\n\t// lastSuccessfulSyncAt tracks when replication last succeeded.\n\t// Used by heartbeat monitoring to determine if a ping should be sent.\n\tlastSuccessfulSyncMu sync.RWMutex\n\tlastSuccessfulSyncAt time.Time\n\n\t// Where to send log messages, defaults to global slog with database epath.\n\tLogger *slog.Logger\n}\n\n// syncState holds mutable sync-tracking fields extracted from DB.\n// These fields are threaded through sync/checkpoint methods via pointer.\ntype syncState struct {\n\ttruncatePassiveFailed bool\n\n\t// syncedSinceCheckpoint tracks whether any data has been synced since\n\t// the last checkpoint. Used to prevent time-based checkpoints from\n\t// triggering when there are no actual database changes, which would\n\t// otherwise create unnecessary LTX files. See issue #896.\n\tsyncedSinceCheckpoint bool\n\n\t// syncedToWALEnd tracks whether the last successful sync reached the\n\t// exact end of the WAL file. When true, a subsequent WAL truncation\n\t// (from checkpoint) is expected and should NOT trigger a full snapshot.\n\t// This prevents issue #927 where every checkpoint triggers unnecessary\n\t// full snapshots because verify() sees the old LTX position exceeds\n\t// the new (truncated) WAL size.\n\tsyncedToWALEnd bool\n\n\t// lastSyncedWALOffset tracks the logical end of the WAL content after\n\t// the last successful sync. This is the WALOffset + WALSize from the\n\t// last LTX file. Used for checkpoint threshold decisions instead of\n\t// file size, which may include stale frames with old salt values after\n\t// a checkpoint. This prevents issue #997 where PASSIVE checkpoints\n\t// trigger a feedback loop because stale file size exceeds threshold.\n\tlastSyncedWALOffset int64\n}\n\ntype syncExecutor struct {\n\tstate syncState\n\tpos ltx.Pos\n\tposChanged bool\n\tl0FileInfo *ltx.FileInfo\n\tsynced bool\n\tcheckpointAttempted bool\n}\n\ntype diagOp string\n\nconst (\n\tdiagOpSync diagOp = \"sync\"\n\tdiagOpCheckpoint diagOp = \"checkpoint\"\n)\n\ntype diagPhase string\n\nconst (\n\tdiagPhaseStarting diagPhase = \"starting\"\n\tdiagPhaseEnsureWAL diagPhase = \"ensure_wal\"\n\tdiagPhaseVerifyAndSync diagPhase = \"verify_and_sync\"\n\tdiagPhaseCheckpointIfNeeded diagPhase = \"checkpoint_if_needed\"\n\tdiagPhaseUpdateMetrics diagPhase = \"update_metrics\"\n\tdiagPhaseStatWAL diagPhase = \"stat_wal\"\n\tdiagPhaseVerify diagPhase = \"verify\"\n\tdiagPhaseSyncLTX diagPhase = \"sync_ltx\"\n\tdiagPhaseSyncOpenLTX diagPhase = \"sync_open_ltx\"\n\tdiagPhaseSyncPageMap diagPhase = \"sync_page_map\"\n\tdiagPhaseSyncPrepareLTX diagPhase = \"sync_prepare_ltx\"\n\tdiagPhaseWriteLTXFromDB diagPhase = \"write_ltx_from_db\"\n\tdiagPhaseWriteLTXFromWAL diagPhase = \"write_ltx_from_wal\"\n\tdiagPhaseCloseLTX diagPhase = \"close_ltx\"\n\tdiagPhaseFsyncLTX diagPhase = \"fsync_ltx\"\n\tdiagPhaseRenameLTX diagPhase = \"rename_ltx\"\n\tdiagPhaseSyncComplete diagPhase = \"sync_complete\"\n\tdiagPhaseCheckpointLock diagPhase = \"checkpoint_lock\"\n\tdiagPhaseCheckpointReadWALHeader diagPhase = \"checkpoint_read_wal_header\"\n\tdiagPhaseCheckpointCopyBefore diagPhase = \"checkpoint_copy_before\"\n\tdiagPhaseCheckpointExec diagPhase = \"checkpoint_exec\"\n\tdiagPhaseCheckpointVerifyRestart diagPhase = \"checkpoint_verify_restart\"\n\tdiagPhaseCheckpointSnapshotBoundaryLock diagPhase = \"checkpoint_snapshot_boundary_lock\"\n\tdiagPhaseCheckpointSnapshotBoundary diagPhase = \"checkpoint_snapshot_boundary\"\n)\n\ntype diagState struct {\n\tsync.RWMutex\n\tactive bool\n\toperation diagOp\n\tphase diagPhase\n\tstartedAt time.Time\n\tupdatedAt time.Time\n\ttxID ltx.TXID\n\twalSize int64\n\tlastSyncedWALOffset int64\n\tsnapshotting bool\n\tcheckpointMode string\n\treason string\n\terr string\n\texecutorWaiterCount int\n\texecutorWaitStarted time.Time\n}\n\n// SyncDiagnostic reports the latest DB sync/checkpoint activity.\ntype SyncDiagnostic struct {\n\tPath string `json:\"path\"`\n\tActive bool `json:\"active\"`\n\tOperation string `json:\"operation,omitempty\"`\n\tPhase string `json:\"phase,omitempty\"`\n\tStartedAt *time.Time `json:\"started_at,omitempty\"`\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\tElapsedSeconds float64 `json:\"elapsed_seconds,omitempty\"`\n\tTXID uint64 `json:\"txid,omitempty\"`\n\tWALSize int64 `json:\"wal_size,omitempty\"`\n\tLastSyncedWALOffset int64 `json:\"last_synced_wal_offset,omitempty\"`\n\tSnapshotting bool `json:\"snapshotting,omitempty\"`\n\tCheckpointMode string `json:\"checkpoint_mode,omitempty\"`\n\tReason string `json:\"reason,omitempty\"`\n\tError string `json:\"error,omitempty\"`\n\tExecutorWaiterCount int `json:\"executor_waiter_count,omitempty\"`\n\tExecutorWaitStarted *time.Time `json:\"executor_wait_started_at,omitempty\"`\n\tExecutorWaitSeconds float64 `json:\"executor_wait_seconds,omitempty\"`\n}\n\n// NewDB returns a new instance of DB for a given path.\nfunc NewDB(path string) *DB {\n\tdir, file := filepath.Split(path)\n\n\tdb := \u0026DB{\n\t\tpath: path,\n\t\tmetaPath: filepath.Join(dir, \".\"+file+MetaDirSuffix),\n\t\texecSem: semaphore.NewWeighted(1),\n\t\tnotify: make(chan struct{}),\n\n\t\tMinCheckpointPageN: DefaultMinCheckpointPageN,\n\t\tTruncatePageN: DefaultTruncatePageN,\n\t\tCheckpointInterval: DefaultCheckpointInterval,\n\t\tMonitorInterval: DefaultMonitorInterval,\n\t\tMaxSyncWALBytes: DefaultMaxSyncWALBytes,\n\t\tBusyTimeout: DefaultBusyTimeout,\n\t\tL0Retention: DefaultL0Retention,\n\t\tRetentionEnabled: true,\n\t\tShutdownSyncTimeout: DefaultShutdownSyncTimeout,\n\t\tShutdownSyncInterval: DefaultShutdownSyncInterval,\n\t\tLogger: slog.With(LogKeyDB, filepath.Base(path)),\n\t}\n\tdb.maxLTXFileInfos.m = make(map[int]*ltx.FileInfo)\n\tdb.openLTXFile = defaultOpenLTXFile\n\n\tdb.ctx, db.cancel = context.WithCancel(context.Background())\n\n\t// Initialize compactor with nil client (set once in Open() from Replica.Client).\n\tdb.compactor = NewCompactor(nil, db.Logger.With(LogKeySubsystem, LogSubsystemCompactor))\n\tdb.compactor.LocalFileOpener = db.openLocalLTXFile\n\tdb.compactor.LocalFileDeleter = db.deleteLocalLTXFile\n\tdb.initMetrics()\n\tdb.compactor.CacheGetter = func(level int) (*ltx.FileInfo, bool) {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdefer db.maxLTXFileInfos.Unlock()\n\t\tinfo, ok := db.maxLTXFileInfos.m[level]\n\t\treturn info, ok\n\t}\n\tdb.compactor.CacheSetter = func(level int, info *ltx.FileInfo) {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdefer db.maxLTXFileInfos.Unlock()\n\t\tdb.maxLTXFileInfos.m[level] = info\n\t}\n\n\treturn db\n}\n\n// initMetrics creates the per-database Prometheus series.\n// Open calls it again because Close deletes them.\nfunc (db *DB) initMetrics() {\n\tdbMetricOwners.Lock()\n\tdefer dbMetricOwners.Unlock()\n\tif db.metricsRegistered {\n\t\treturn\n\t}\n\tdb.metricsRegistered = true\n\tdbMetricOwners.paths[db.path]++\n\tdbMetricOwners.basenames[filepath.Base(db.path)]++\n\n\tdb.dbSizeGauge = dbSizeGaugeVec.WithLabelValues(db.path)\n\tdb.walSizeGauge = walSizeGaugeVec.WithLabelValues(db.path)\n\tdb.totalWALBytesCounter = totalWALBytesCounterVec.WithLabelValues(db.path)\n\tdb.txIDGauge = txIDIndexGaugeVec.WithLabelValues(db.path)\n\tdb.syncNCounter = syncNCounterVec.WithLabelValues(db.path)\n\tdb.syncErrorNCounter = syncErrorNCounterVec.WithLabelValues(db.path)\n\tdb.syncSecondsCounter = syncSecondsCounterVec.WithLabelValues(db.path)\n\tdb.diskFullGauge = diskFullGaugeVec.WithLabelValues(db.path)\n\tdb.checkpointNCounterVec = checkpointNCounterVec.MustCurryWith(prometheus.Labels{\"db\": db.path})\n\tdb.checkpointErrorNCounterVec = checkpointErrorNCounterVec.MustCurryWith(prometheus.Labels{\"db\": db.path})\n\tdb.checkpointSecondsCounterVec = checkpointSecondsCounterVec.MustCurryWith(prometheus.Labels{\"db\": db.path})\n\tdb.compactor.CompactionVerifyErrorCounter = compactionVerifyErrorCounterVec.WithLabelValues(db.path)\n}\n\n// deleteMetrics removes every per-database series so a process that opens many\n// databases over its lifetime does not accumulate series forever.\nfunc (db *DB) deleteMetrics() {\n\tdbMetricOwners.Lock()\n\tdefer dbMetricOwners.Unlock()\n\tif !db.metricsRegistered {\n\t\treturn\n\t}\n\tdb.metricsRegistered = false\n\n\tname := filepath.Base(db.path)\n\tdbMetricOwners.basenames[name]--\n\tif dbMetricOwners.basenames[name] == 0 {\n\t\tdelete(dbMetricOwners.basenames, name)\n\t\tinternal.L0RetentionGaugeVec.DeletePartialMatch(prometheus.Labels{\"db\": name})\n\t}\n\n\tdbMetricOwners.paths[db.path]--\n\tif dbMetricOwners.paths[db.path] != 0 {\n\t\treturn\n\t}\n\tdelete(dbMetricOwners.paths, db.path)\n\n\tlabels := prometheus.Labels{\"db\": db.path}\n\tdbSizeGaugeVec.DeletePartialMatch(labels)\n\twalSizeGaugeVec.DeletePartialMatch(labels)\n\ttotalWALBytesCounterVec.DeletePartialMatch(labels)\n\ttxIDIndexGaugeVec.DeletePartialMatch(labels)\n\tsyncNCounterVec.DeletePartialMatch(labels)\n\tsyncErrorNCounterVec.DeletePartialMatch(labels)\n\tsyncSecondsCounterVec.DeletePartialMatch(labels)\n\tdiskFullGaugeVec.DeletePartialMatch(labels)\n\tcompactionVerifyErrorCounterVec.DeletePartialMatch(labels)\n\tcheckpointNCounterVec.DeletePartialMatch(labels)\n\tcheckpointErrorNCounterVec.DeletePartialMatch(labels)\n\tcheckpointSecondsCounterVec.DeletePartialMatch(labels)\n}\n\n// SetLogger updates the database logger and propagates to subsystems.\nfunc (db *DB) SetLogger(logger *slog.Logger) {\n\tif logger == nil {\n\t\tlogger = slog.Default()\n\t}\n\tdb.Logger = logger\n\tif db.compactor != nil {\n\t\tdb.compactor.setLogger(logger.With(LogKeySubsystem, LogSubsystemCompactor))\n\t}\n\tif db.Replica != nil \u0026\u0026 db.Replica.Client != nil {\n\t\tdb.Replica.Client.SetLogger(db.Replica.Logger())\n\t}\n}\n\n// SQLDB returns a reference to the underlying sql.DB connection.\nfunc (db *DB) SQLDB() *sql.DB {\n\treturn db.db\n}\n\n// Path returns the path to the database.\nfunc (db *DB) Path() string {\n\treturn db.path\n}\n\n// SyncDiagnostic returns the latest sync/checkpoint diagnostic snapshot.\nfunc (db *DB) SyncDiagnostic() SyncDiagnostic {\n\tdb.syncDiag.RLock()\n\tdefer db.syncDiag.RUnlock()\n\n\tdiag := SyncDiagnostic{\n\t\tPath: db.path,\n\t\tActive: db.syncDiag.active,\n\t\tOperation: string(db.syncDiag.operation),\n\t\tPhase: string(db.syncDiag.phase),\n\t\tTXID: uint64(db.syncDiag.txID),\n\t\tWALSize: db.syncDiag.walSize,\n\t\tLastSyncedWALOffset: db.syncDiag.lastSyncedWALOffset,\n\t\tSnapshotting: db.syncDiag.snapshotting,\n\t\tCheckpointMode: db.syncDiag.checkpointMode,\n\t\tReason: db.syncDiag.reason,\n\t\tError: db.syncDiag.err,\n\t\tExecutorWaiterCount: db.syncDiag.executorWaiterCount,\n\t}\n\tif !db.syncDiag.executorWaitStarted.IsZero() {\n\t\tstartedAt := db.syncDiag.executorWaitStarted\n\t\tdiag.ExecutorWaitStarted = \u0026startedAt\n\t\tdiag.ExecutorWaitSeconds = time.Since(db.syncDiag.executorWaitStarted).Seconds()\n\t}\n\tif !db.syncDiag.startedAt.IsZero() {\n\t\tstartedAt := db.syncDiag.startedAt\n\t\tdiag.StartedAt = \u0026startedAt\n\t}\n\tif !db.syncDiag.updatedAt.IsZero() {\n\t\tupdatedAt := db.syncDiag.updatedAt\n\t\tdiag.UpdatedAt = \u0026updatedAt\n\t}\n\tif !db.syncDiag.startedAt.IsZero() {\n\t\tend := db.syncDiag.updatedAt\n\t\tif db.syncDiag.active || end.IsZero() {\n\t\t\tend = time.Now()\n\t\t}\n\t\tdiag.ElapsedSeconds = end.Sub(db.syncDiag.startedAt).Seconds()\n\t}\n\treturn diag\n}\n\nfunc (db *DB) beginSyncDiag(operation diagOp) {\n\tnow := time.Now()\n\twalSize, _ := db.walFileSize()\n\n\tdb.syncDiag.Lock()\n\tdb.syncDiag.active = true\n\tdb.syncDiag.operation = operation\n\tdb.syncDiag.phase = diagPhaseStarting\n\tdb.syncDiag.startedAt = now\n\tdb.syncDiag.updatedAt = now\n\tdb.syncDiag.txID = 0\n\tdb.syncDiag.walSize = walSize\n\tdb.syncDiag.lastSyncedWALOffset = db.syncState.lastSyncedWALOffset\n\tdb.syncDiag.snapshotting = false\n\tdb.syncDiag.checkpointMode = \"\"\n\tdb.syncDiag.reason = \"\"\n\tdb.syncDiag.err = \"\"\n\tdb.syncDiag.Unlock()\n}\n\nfunc (db *DB) setSyncDiagPhase(phase diagPhase, updates ...func(*diagState)) {\n\tdb.syncDiag.Lock()\n\tdefer db.syncDiag.Unlock()\n\tif !db.syncDiag.active {\n\t\treturn\n\t}\n\tdb.syncDiag.phase = phase\n\tdb.syncDiag.updatedAt = time.Now()\n\tfor _, update := range updates {\n\t\tupdate(\u0026db.syncDiag)\n\t}\n}\n\nfunc (db *DB) finishSyncDiag(err error) {\n\tdb.syncDiag.Lock()\n\tdefer db.syncDiag.Unlock()\n\tif !db.syncDiag.active {\n\t\treturn\n\t}\n\tdb.syncDiag.active = false\n\tdb.syncDiag.updatedAt = time.Now()\n\tif err != nil {\n\t\tdb.syncDiag.err = err.Error()\n\t}\n}\n\nfunc (db *DB) beginSyncExecutorWait() {\n\tdb.syncDiag.Lock()\n\tdefer db.syncDiag.Unlock()\n\tif db.syncDiag.executorWaiterCount == 0 {\n\t\tdb.syncDiag.executorWaitStarted = time.Now()\n\t}\n\tdb.syncDiag.executorWaiterCount++\n}\n\nfunc (db *DB) finishSyncExecutorWait() {\n\tdb.syncDiag.Lock()\n\tdefer db.syncDiag.Unlock()\n\tdb.syncDiag.executorWaiterCount--\n\tif db.syncDiag.executorWaiterCount == 0 {\n\t\tdb.syncDiag.executorWaitStarted = time.Time{}\n\t}\n}\n\n// IsOpen returns true if the database has been opened.\nfunc (db *DB) IsOpen() bool {\n\tdb.mu.RLock()\n\tdefer db.mu.RUnlock()\n\treturn db.opened\n}\n\n// WALPath returns the path to the database's WAL file.\nfunc (db *DB) WALPath() string {\n\treturn db.path + \"-wal\"\n}\n\n// MetaPath returns the path to the database metadata.\nfunc (db *DB) MetaPath() string {\n\treturn db.metaPath\n}\n\n// SetMetaPath sets the path to database metadata.\nfunc (db *DB) SetMetaPath(path string) {\n\tdb.metaPath = path\n}\n\n// LTXDir returns path of the root LTX directory.\nfunc (db *DB) LTXDir() string {\n\treturn filepath.Join(db.metaPath, \"ltx\")\n}\n\n// ResetLocalState removes local LTX files, forcing a fresh snapshot on next sync.\n// This is useful for recovering from corrupted or missing LTX files.\n// The database file itself is not modified.\nfunc (db *DB) ResetLocalState(ctx context.Context) error {\n\tdb.Logger.Info(\"resetting local litestream state\",\n\t\t\"meta_path\", db.metaPath,\n\t\t\"ltx_dir\", db.LTXDir())\n\n\t// Remove all LTX files\n\tif err := os.RemoveAll(db.LTXDir()); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"remove ltx directory: %w\", err)\n\t}\n\n\t// Clear cached LTX file info\n\tdb.maxLTXFileInfos.Lock()\n\tdb.maxLTXFileInfos.m = make(map[int]*ltx.FileInfo)\n\tdb.maxLTXFileInfos.Unlock()\n\n\tdb.invalidatePosCache()\n\n\tdb.Logger.Info(\"local state reset complete, next sync will create fresh snapshot\")\n\treturn nil\n}\n\n// LTXLevelDir returns path of the given LTX compaction level.\n// Panics if level is negative.\nfunc (db *DB) LTXLevelDir(level int) string {\n\treturn filepath.Join(db.LTXDir(), strconv.Itoa(level))\n}\n\n// LTXPath returns the local path of a single LTX file.\n// Panics if level or either txn ID is negative.\nfunc (db *DB) LTXPath(level int, minTXID, maxTXID ltx.TXID) string {\n\tassert(level \u003e= 0, \"level cannot be negative\")\n\treturn filepath.Join(db.LTXLevelDir(level), ltx.FormatFilename(minTXID, maxTXID))\n}\n\n// openLocalLTXFile opens a local LTX file for reading.\n// Used by the Compactor to prefer local files over remote.\nfunc (db *DB) openLocalLTXFile(level int, minTXID, maxTXID ltx.TXID) (io.ReadCloser, error) {\n\treturn os.Open(db.LTXPath(level, minTXID, maxTXID))\n}\n\n// deleteLocalLTXFile deletes a local LTX file.\n// Used by the Compactor for retention enforcement.\nfunc (db *DB) deleteLocalLTXFile(level int, minTXID, maxTXID ltx.TXID) error {\n\tpath := db.LTXPath(level, minTXID, maxTXID)\n\tif err := os.Remove(path); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\tif level == 0 {\n\t\tdb.invalidatePosCache()\n\t}\n\treturn nil\n}\n\n// MaxLTX returns the last LTX file written to level 0.\nfunc (db *DB) MaxLTX() (minTXID, maxTXID ltx.TXID, err error) {\n\tents, err := os.ReadDir(db.LTXLevelDir(0))\n\tif os.IsNotExist(err) {\n\t\treturn 0, 0, nil // no LTX files written\n\t} else if err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\t// Find highest txn ID.\n\tfor _, ent := range ents {\n\t\tif min, max, err := ltx.ParseFilename(ent.Name()); err != nil {\n\t\t\tcontinue // invalid LTX filename\n\t\t} else if max \u003e maxTXID {\n\t\t\tminTXID, maxTXID = min, max\n\t\t}\n\t}\n\treturn minTXID, maxTXID, nil\n}\n\n// FileInfo returns the cached file stats for the database file when it was initialized.\nfunc (db *DB) FileInfo() os.FileInfo {\n\treturn db.fileInfo\n}\n\n// DirInfo returns the cached file stats for the parent directory of the database file when it was initialized.\nfunc (db *DB) DirInfo() os.FileInfo {\n\treturn db.dirInfo\n}\n\n// Pos returns the current replication position of the database.\n// The result is cached and invalidated when L0 LTX files change.\nfunc (db *DB) Pos() (ltx.Pos, error) {\n\tdb.pos.Lock()\n\tdefer db.pos.Unlock()\n\n\tif db.pos.value != nil {\n\t\treturn *db.pos.value, nil\n\t}\n\n\tminTXID, maxTXID, err := db.MaxLTX()\n\tif err != nil {\n\t\treturn ltx.Pos{}, err\n\t} else if minTXID == 0 {\n\t\treturn ltx.Pos{}, nil // no replication yet\n\t}\n\n\tltxPath := db.LTXPath(0, minTXID, maxTXID)\n\tf, err := os.Open(ltxPath)\n\tif err != nil {\n\t\treturn ltx.Pos{}, NewLTXError(\"open\", ltxPath, 0, uint64(minTXID), uint64(maxTXID), err)\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tdec := ltx.NewDecoder(f)\n\tif err := dec.Verify(); err != nil {\n\t\treturn ltx.Pos{}, NewLTXError(\"verify\", ltxPath, 0, uint64(minTXID), uint64(maxTXID), fmt.Errorf(\"%w: %w\", ErrLTXCorrupted, err))\n\t}\n\n\tpos := dec.PostApplyPos()\n\tdb.pos.value = \u0026pos\n\n\treturn pos, nil\n}\n\n// invalidatePosCache clears the cached position so the next call to Pos()\n// recomputes it from disk. Call this when L0 LTX files are deleted or\n// when the L0 directory is cleared.\nfunc (db *DB) invalidatePosCache() {\n\tdb.pos.Lock()\n\tdb.pos.value = nil\n\tdb.pos.Unlock()\n}\n\n// Notify returns a channel that closes when the shadow WAL changes.\nfunc (db *DB) Notify() \u003c-chan struct{} {\n\tdb.mu.RLock()\n\tdefer db.mu.RUnlock()\n\treturn db.notify\n}\n\n// PageSize returns the page size of the underlying database.\n// Only valid after database exists \u0026 Init() has successfully run.\nfunc (db *DB) PageSize() int {\n\tdb.mu.RLock()\n\tdefer db.mu.RUnlock()\n\treturn db.pageSize\n}\n\n// RecordSuccessfulSync marks the current time as a successful sync.\n// Used by heartbeat monitoring to determine if a ping should be sent.\nfunc (db *DB) RecordSuccessfulSync() {\n\tdb.lastSuccessfulSyncMu.Lock()\n\tdefer db.lastSuccessfulSyncMu.Unlock()\n\tdb.lastSuccessfulSyncAt = time.Now()\n}\n\n// LastSuccessfulSyncAt returns the time of the last successful sync.\nfunc (db *DB) LastSuccessfulSyncAt() time.Time {\n\tdb.lastSuccessfulSyncMu.RLock()\n\tdefer db.lastSuccessfulSyncMu.RUnlock()\n\treturn db.lastSuccessfulSyncAt\n}\n\n// SyncStatus represents the current replication state of the database.\ntype SyncStatus struct {\n\tLocalTXID ltx.TXID\n\tRemoteTXID ltx.TXID\n\tInSync bool\n}\n\n// SyncStatus returns the current replication status of the database, comparing\n// the local transaction position against the remote replica position. The remote\n// position is queried from the replica storage, so this method may perform I/O.\nfunc (db *DB) SyncStatus(ctx context.Context) (SyncStatus, error) {\n\tif db.Replica == nil {\n\t\treturn SyncStatus{}, fmt.Errorf(\"no replica configured\")\n\t}\n\n\tlocalPos, err := db.Pos()\n\tif err != nil {\n\t\treturn SyncStatus{}, fmt.Errorf(\"local position: %w\", err)\n\t}\n\n\tremotePos, err := db.Replica.calcPos(ctx)\n\tif err != nil {\n\t\treturn SyncStatus{}, fmt.Errorf(\"remote position: %w\", err)\n\t}\n\n\treturn SyncStatus{\n\t\tLocalTXID: localPos.TXID,\n\t\tRemoteTXID: remotePos.TXID,\n\t\tInSync: localPos.TXID \u003e 0 \u0026\u0026 localPos.TXID == remotePos.TXID,\n\t}, nil\n}\n\n// SyncAndWait performs a full sync: WAL to LTX files, then LTX files to remote\n// replica. Blocks until both stages complete.\nfunc (db *DB) SyncAndWait(ctx context.Context) error {\n\tif db.Replica == nil {\n\t\treturn fmt.Errorf(\"no replica configured\")\n\t}\n\n\tif err := db.Sync(ctx); err != nil {\n\t\treturn fmt.Errorf(\"db sync: %w\", err)\n\t}\n\tif err := db.Replica.Sync(ctx); err != nil {\n\t\treturn fmt.Errorf(\"replica sync: %w\", err)\n\t}\n\treturn nil\n}\n\n// EnsureExists restores the database from the configured replica if the local\n// database file does not exist. If no backup is available, it returns nil and\n// a fresh database will be created on Open(). Must be called before Open().\nfunc (db *DB) EnsureExists(ctx context.Context) error {\n\tif db.Replica == nil {\n\t\treturn fmt.Errorf(\"no replica configured\")\n\t}\n\tif db.Replica.Client == nil {\n\t\treturn fmt.Errorf(\"no replica client configured\")\n\t}\n\n\tif _, err := os.Stat(db.Path()); err == nil {\n\t\treturn nil\n\t} else if !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"stat database: %w\", err)\n\t}\n\n\tif dir := filepath.Dir(db.Path()); dir != \".\" {\n\t\tif err := os.MkdirAll(dir, 0o750); err != nil {\n\t\t\treturn fmt.Errorf(\"create parent directory: %w\", err)\n\t\t}\n\t}\n\n\topt := NewRestoreOptions()\n\topt.OutputPath = db.Path()\n\topt.IntegrityCheck = IntegrityCheckQuick\n\n\tif err := db.Replica.Restore(ctx, opt); err != nil {\n\t\tif errors.Is(err, ErrTxNotAvailable) || errors.Is(err, ErrNoSnapshots) {\n\t\t\tdb.Logger.Debug(\"no backup found, will create fresh database\")\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"restore from backup: %w\", err)\n\t}\n\n\tdb.Logger.Info(\"database restored from backup\", \"path\", db.Path())\n\treturn nil\n}\n\n// Open initializes the background monitoring goroutine.\nfunc (db *DB) Open() (err error) {\n\tdb.mu.Lock()\n\tif db.opened {\n\t\tdb.mu.Unlock()\n\t\treturn nil // already open\n\t}\n\t// Recreate context for fresh start (handles reopen after close)\n\tdb.ctx, db.cancel = context.WithCancel(context.Background())\n\tdb.mu.Unlock()\n\n\t// Close deletes the per-database series, so recreate them.\n\tdb.initMetrics()\n\n\t// Validate fields on database.\n\tif db.Replica == nil {\n\t\treturn fmt.Errorf(\"replica required before opening database\")\n\t}\n\tif db.Replica.Client == nil {\n\t\treturn fmt.Errorf(\"replica client required before opening database\")\n\t}\n\tif db.MinCheckpointPageN \u003c= 0 {\n\t\treturn fmt.Errorf(\"minimum checkpoint page count required\")\n\t}\n\n\t// Clear old temporary files that my have been left from a crash.\n\tif err := removeTmpFiles(db.metaPath); err != nil {\n\t\treturn fmt.Errorf(\"cannot remove tmp files: %w\", err)\n\t}\n\n\t// Set the compactor client once before starting any goroutines.\n\tdb.compactor.VerifyCompaction = db.VerifyCompaction\n\tdb.compactor.RetentionEnabled = db.RetentionEnabled\n\tdb.compactor.client = db.Replica.Client\n\n\t// Start monitoring SQLite database in a separate goroutine.\n\tif db.MonitorInterval \u003e 0 {\n\t\tdb.wg.Add(1)\n\t\tgo func() { defer db.wg.Done(); db.monitor() }()\n\t}\n\n\t// Mark as opened only after successful initialization.\n\tdb.mu.Lock()\n\tdb.opened = true\n\tdb.mu.Unlock()\n\n\treturn nil\n}\n\n// Close flushes outstanding WAL writes to replicas, releases the read lock,\n// and closes the database. If Done is set, closing it interrupts the shutdown\n// sync retry loop and cancels any in-flight sync attempt.\nfunc (db *DB) Close(ctx context.Context) (err error) {\n\tdb.cancel()\n\tdb.wg.Wait()\n\n\t// Acquire without honoring caller cancellation: the cleanup below\n\t// (read lock release, handle closes, state reset) must always run or\n\t// the DB is left half-closed with its read lock held. Semaphore\n\t// acquisition fails immediately on an already-done context even when\n\t// the semaphore is free.\n\tif err := db.execSem.Acquire(context.WithoutCancel(ctx), 1); err != nil {\n\t\treturn err\n\t}\n\tdefer db.execSem.Release(1)\n\n\t// Perform a final db sync, if initialized.\n\tif db.db != nil {\n\t\tif _, e := db.syncLocked(ctx, 0); e != nil {\n\t\t\terr = e\n\t\t}\n\t}\n\n\t// Ensure replicas perform a final sync and stop replicating.\n\tif db.Replica != nil {\n\t\tif db.db != nil {\n\t\t\tif e := db.syncReplicaWithRetry(ctx); e != nil \u0026\u0026 err == nil {\n\t\t\t\terr = e\n\t\t\t}\n\t\t}\n\t\tdb.Replica.Stop(true)\n\t}\n\n\t// Release the read lock to allow other applications to handle checkpointing.\n\tif db.rtx != nil {\n\t\tif e := db.releaseReadLock(); e != nil \u0026\u0026 err == nil {\n\t\t\terr = e\n\t\t}\n\t}\n\n\tdb.mu.Lock()\n\tsqlDB := db.db\n\tf := db.f\n\tdb.db = nil\n\tdb.f = nil\n\tdb.opened = false\n\tdb.rtx = nil\n\tdb.mu.Unlock()\n\n\tif sqlDB != nil {\n\t\tif e := sqlDB.Close(); e != nil \u0026\u0026 err == nil {\n\t\t\terr = e\n\t\t}\n\t}\n\n\tif f != nil {\n\t\tif e := f.Close(); e != nil \u0026\u0026 err == nil {\n\t\t\terr = e\n\t\t}\n\t}\n\n\t// Remove this database's series now that the final sync has recorded\n\t// its last values.\n\tdb.deleteMetrics()\n\n\treturn err\n}\n\n// syncReplicaWithRetry attempts to sync the replica with retry logic for shutdown.\n// It retries until success, timeout, or context cancellation. If db.Done is non-nil,\n// closing it cancels any in-flight sync attempt and exits the retry loop.\n// If ShutdownSyncTimeout is 0, it performs a single sync attempt without retries.\nfunc (db *DB) syncReplicaWithRetry(ctx context.Context) error {\n\tif db.Replica == nil {\n\t\treturn nil\n\t}\n\n\ttimeout := db.ShutdownSyncTimeout\n\tinterval := db.ShutdownSyncInterval\n\n\t// If timeout is zero, just try once (no retry)\n\tif timeout == 0 {\n\t\treturn db.Replica.Sync(ctx)\n\t}\n\n\t// Use default interval if not set\n\tif interval == 0 {\n\t\tinterval = DefaultShutdownSyncInterval\n\t}\n\n\t// Create deadline context for total retry duration.\n\tdeadlineCtx, deadlineCancel := context.WithTimeout(ctx, timeout)\n\tdefer deadlineCancel()\n\n\t// If db.Done is set, derive a context that cancels when done is closed\n\t// so that in-flight Replica.Sync calls are interrupted immediately.\n\tsyncCtx := deadlineCtx\n\tif db.Done != nil {\n\t\tvar syncCancel context.CancelFunc\n\t\tsyncCtx, syncCancel = context.WithCancel(deadlineCtx)\n\t\tgo func() {\n\t\t\tselect {\n\t\t\tcase \u003c-db.Done:\n\t\t\t\tsyncCancel()\n\t\t\tcase \u003c-deadlineCtx.Done():\n\t\t\t\tsyncCancel()\n\t\t\t}\n\t\t}()\n\t}\n\n\tvar lastErr error\n\tattempt := 0\n\tstartTime := time.Now()\n\n\tfor {\n\t\t// Check if done is already closed before attempting sync\n\t\tif db.Done != nil {\n\t\t\tselect {\n\t\t\tcase \u003c-db.Done:\n\t\t\t\tdb.Logger.Warn(\"shutdown sync skipped, interrupted by signal\",\n\t\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\t\"duration\", time.Since(startTime))\n\t\t\t\treturn fmt.Errorf(\"after %d attempts: %w\", attempt, ErrShutdownInterrupted)\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\n\t\tattempt++\n\n\t\t// Try sync\n\t\tif err := db.Replica.Sync(syncCtx); err == nil {\n\t\t\tif attempt \u003e 1 {\n\t\t\t\tdb.Logger.Info(\"shutdown sync succeeded after retry\",\n\t\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\t\"duration\", time.Since(startTime))\n\t\t\t}\n\t\t\treturn nil\n\t\t} else {\n\t\t\tlastErr = err\n\t\t}\n\n\t\t// Check if we should stop retrying (done signal or timeout)\n\t\tselect {\n\t\tcase \u003c-deadlineCtx.Done():\n\t\t\tdb.Logger.Error(\"shutdown sync failed after timeout\",\n\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\"duration\", time.Since(startTime),\n\t\t\t\t\"error\", lastErr)\n\t\t\treturn fmt.Errorf(\"shutdown sync timeout after %d attempts: %w\", attempt, lastErr)\n\t\tcase \u003c-db.Done:\n\t\t\tdb.Logger.Warn(\"shutdown sync interrupted by signal\",\n\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\"duration\", time.Since(startTime),\n\t\t\t\t\"error\", lastErr)\n\t\t\treturn fmt.Errorf(\"after %d attempts: %w\", attempt, ErrShutdownInterrupted)\n\t\tdefault:\n\t\t}\n\n\t\t// Log retry with hint about second signal if interruptible\n\t\tif db.Done != nil {\n\t\t\tdb.Logger.Warn(\"shutdown sync failed, retrying (press Ctrl+C again to skip)\",\n\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\"error\", lastErr,\n\t\t\t\t\"elapsed\", time.Since(startTime),\n\t\t\t\t\"remaining\", time.Until(startTime.Add(timeout)))\n\t\t} else {\n\t\t\tdb.Logger.Warn(\"shutdown sync failed, retrying\",\n\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\"error\", lastErr,\n\t\t\t\t\"elapsed\", time.Since(startTime),\n\t\t\t\t\"remaining\", time.Until(startTime.Add(timeout)))\n\t\t}\n\n\t\t// Wait before retry, but also listen for done signal\n\t\tselect {\n\t\tcase \u003c-time.After(interval):\n\t\tcase \u003c-deadlineCtx.Done():\n\t\t\treturn fmt.Errorf(\"shutdown sync timeout after %d attempts: %w\", attempt, lastErr)\n\t\tcase \u003c-db.Done:\n\t\t\tdb.Logger.Warn(\"shutdown sync interrupted by signal\",\n\t\t\t\t\"attempts\", attempt,\n\t\t\t\t\"duration\", time.Since(startTime))\n\t\t\treturn fmt.Errorf(\"after %d attempts: %w\", attempt, ErrShutdownInterrupted)\n\t\t}\n\t}\n}\n\n// setPersistWAL sets the PERSIST_WAL file control on the database connection.\n// This prevents SQLite from removing the WAL file when connections close.\nfunc (db *DB) setPersistWAL(ctx context.Context) error {\n\tconn, err := db.db.Conn(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get connection: %w\", err)\n\t}\n\tdefer conn.Close()\n\n\treturn conn.Raw(func(driverConn interface{}) error {\n\t\tfc, ok := driverConn.(sqlite.FileControl)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"driver does not implement FileControl\")\n\t\t}\n\n\t\t_, err := fc.FileControlPersistWAL(\"main\", 1)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"FileControlPersistWAL: %w\", err)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// init initializes the connection to the database.\n// Skipped if already initialized or if the database file does not exist.\nfunc (db *DB) init(ctx context.Context) (err error) {\n\t// Exit if already initialized.\n\tif db.db != nil {\n\t\treturn nil\n\t}\n\n\t// Exit if no database file exists.\n\tfi, err := os.Stat(db.path)\n\tif os.IsNotExist(err) {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\tdb.fileInfo = fi\n\n\t// Obtain permissions for parent directory.\n\tif fi, err = os.Stat(filepath.Dir(db.path)); err != nil {\n\t\treturn err\n\t}\n\tdb.dirInfo = fi\n\n\tdsn := fmt.Sprintf(\"file:%s?_pragma=busy_timeout(%d)\u0026_pragma=wal_autocheckpoint(0)\",\n\t\tdb.path, db.BusyTimeout.Milliseconds())\n\n\tif db.db, err = sql.Open(\"sqlite\", dsn); err != nil {\n\t\treturn err\n\t}\n\n\t// Set PERSIST_WAL to prevent WAL file removal when database connections close.\n\tif err := db.setPersistWAL(ctx); err != nil {\n\t\treturn fmt.Errorf(\"set PERSIST_WAL: %w\", err)\n\t}\n\n\t// Open long-running database file descriptor. Required for non-OFD locks.\n\tif db.f, err = os.Open(db.path); err != nil {\n\t\treturn fmt.Errorf(\"open db file descriptor: %w\", err)\n\t}\n\n\t// Ensure database is closed if init fails.\n\t// Initialization can retry on next sync.\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = db.releaseReadLock()\n\t\t\tdb.db.Close()\n\t\t\tdb.f.Close()\n\t\t\tdb.db, db.f = nil, nil\n\t\t}\n\t}()\n\n\t// Enable WAL and ensure it is set. New mode should be returned on success:\n\t// https://www.sqlite.org/pragma.html#pragma_journal_mode\n\tvar mode string\n\tif err := db.db.QueryRowContext(ctx, `PRAGMA journal_mode = wal;`).Scan(\u0026mode); err != nil {\n\t\treturn err\n\t} else if mode != \"wal\" {\n\t\treturn fmt.Errorf(\"enable wal failed, mode=%q\", mode)\n\t}\n\n\t// Create a table to force writes to the WAL when empty.\n\t// There should only ever be one row with id=1.\n\tif _, err := db.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS _litestream_seq (id INTEGER PRIMARY KEY, seq INTEGER);`); err != nil {\n\t\treturn fmt.Errorf(\"create _litestream_seq table: %w\", err)\n\t}\n\n\t// Create a lock table to force write locks during sync.\n\t// The sync write transaction always rolls back so no data should be in this table.\n\tif _, err := db.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS _litestream_lock (id INTEGER);`); err != nil {\n\t\treturn fmt.Errorf(\"create _litestream_lock table: %w\", err)\n\t}\n\n\t// Start a long-running read transaction to prevent other transactions\n\t// from checkpointing.\n\tif err := db.acquireReadLock(ctx); err != nil {\n\t\treturn fmt.Errorf(\"acquire read lock: %w\", err)\n\t}\n\n\t// Read page size.\n\tif err := db.db.QueryRowContext(ctx, `PRAGMA page_size;`).Scan(\u0026db.pageSize); err != nil {\n\t\treturn fmt.Errorf(\"read page size: %w\", err)\n\t} else if db.pageSize \u003c= 0 {\n\t\treturn fmt.Errorf(\"invalid db page size: %d\", db.pageSize)\n\t}\n\n\t// Ensure meta directory structure exists.\n\tif err := internal.MkdirAll(db.metaPath, db.dirInfo); err != nil {\n\t\treturn err\n\t}\n\n\t// Ensure WAL has at least one frame in it.\n\tif err := db.ensureWALExists(ctx); err != nil {\n\t\treturn fmt.Errorf(\"ensure wal exists: %w\", err)\n\t}\n\n\t// Check if database is behind replica (issue #781).\n\t// This must happen before replica.Start() to detect restore scenarios.\n\tif db.Replica != nil {\n\t\tif err := db.checkDatabaseBehindReplica(ctx); err != nil {\n\t\t\treturn fmt.Errorf(\"check database behind replica: %w\", err)\n\t\t}\n\t}\n\n\t// If we have an existing replication files, ensure the headers match.\n\t// if err := db.verifyHeadersMatch(); err != nil {\n\t// \treturn fmt.Errorf(\"cannot determine last wal position: %w\", err)\n\t// }\n\n\t// TODO(gen): Generate diff of current LTX snapshot and save as next LTX file.\n\n\t// Start replication.\n\tif db.Replica != nil {\n\t\tdb.Replica.Start(db.ctx)\n\t}\n\n\treturn nil\n}\n\n/*\n// verifyHeadersMatch returns an error if\nfunc (db *DB) verifyHeadersMatch() error {\n\tpos, err := db.Pos()\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"cannot determine position: %w\", err)\n\t} else if pos.TXID == 0 {\n\t\treturn true, nil // no replication performed yet\n\t}\n\n\thdr0, err := readWALHeader(db.WALPath())\n\tif os.IsNotExist(err) {\n\t\treturn false, fmt.Errorf(\"no wal: %w\", err)\n\t} else if err != nil {\n\t\treturn false, fmt.Errorf(\"read wal header: %w\", err)\n\t}\n\tsalt1 := binary.BigEndian.Uint32(hdr0[16:])\n\tsalt2 := binary.BigEndian.Uint32(hdr0[20:])\n\n\tltxPath := db.LTXPath(0, pos.TXID, pos.TXID)\n\tf, err := os.Open(ltxPath)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"open ltx path: %w\", err)\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tdec := ltx.NewDecoder(f)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\treturn false, fmt.Errorf(\"decode ltx header: %w\", err)\n\t}\n\thdr1 := dec.Header()\n\tif salt1 != hdr1.WALSalt1 || salt2 != hdr1.WALSalt2 {\n\t\tdb.Logger.Log(internal.LevelTrace, \"salt mismatch\",\n\t\t\t\"path\", ltxPath,\n\t\t\t\"wal\", [2]uint32{salt1, salt2},\n\t\t\t\"ltx\", [2]uint32{hdr1.WALSalt1, hdr1.WALSalt2})\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\n*/\n\n// acquireReadLock begins a read transaction on the database to prevent checkpointing.\nfunc (db *DB) acquireReadLock(ctx context.Context) error {\n\tif db.rtx != nil {\n\t\treturn nil\n\t}\n\n\t// Start long running read-transaction to prevent checkpoints. The\n\t// transaction outlives this call, and database/sql rolls it back when its\n\t// BeginTx context is cancelled, so detach it from the caller's context\n\t// (e.g. a control socket request that is cancelled when it returns).\n\ttx, err := db.db.BeginTx(context.WithoutCancel(ctx), nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Execute read query to obtain read lock.\n\tif _, err := tx.ExecContext(ctx, `SELECT COUNT(1) FROM _litestream_seq;`); err != nil {\n\t\t_ = tx.Rollback()\n\t\treturn err\n\t}\n\n\t// Track transaction so we can release it later before checkpoint.\n\tdb.rtx = tx\n\treturn nil\n}\n\n// releaseReadLock rolls back the long-running read transaction.\nfunc (db *DB) releaseReadLock() error {\n\t// Ignore if we do not have a read lock.\n\tif db.rtx == nil {\n\t\treturn nil\n\t}\n\n\t// Rollback \u0026 clear read transaction.\n\t// Use rollback() helper to suppress \"already rolled back\" errors that can\n\t// occur during shutdown when concurrent checkpoint and close operations\n\t// both attempt to release the read lock. See issue #934.\n\terr := rollback(db.rtx)\n\tdb.rtx = nil\n\treturn err\n}\n\n// Sync copies pending data from the WAL to the shadow WAL.\nfunc (db *DB) Sync(ctx context.Context) error {\n\tfor {\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn context.Cause(ctx)\n\t\t}\n\n\t\tresult, err := db.syncOnce(ctx, db.MaxSyncWALBytes)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if !result.synced || !result.limited || result.syncedToWALEnd {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc (db *DB) syncOnce(ctx context.Context, maxSyncWALBytes int64) (syncResult, error) {\n\tif err := db.lockExec(ctx); err != nil {\n\t\treturn syncResult{}, err\n\t}\n\tdefer db.execSem.Release(1)\n\n\treturn db.syncLocked(ctx, maxSyncWALBytes)\n}\n\nfunc (db *DB) lockExec(ctx context.Context) error {\n\tif db.execSem.TryAcquire(1) {\n\t\treturn nil\n\t}\n\tdb.beginSyncExecutorWait()\n\tdefer db.finishSyncExecutorWait()\n\n\tif err := db.execSem.Acquire(ctx, 1); err != nil {\n\t\treturn fmt.Errorf(\"wait for db sync executor: %w\", context.Cause(ctx))\n\t}\n\treturn nil\n}\n\n// addDurationToCounter records positive durations without allowing a clock\n// adjustment to panic a Prometheus counter.\nfunc addDurationToCounter(counter prometheus.Counter, d time.Duration) {\n\tif d \u003e 0 {\n\t\tcounter.Add(d.Seconds())\n\t}\n}\n\nfunc (db *DB) syncLocked(ctx context.Context, maxSyncWALBytes int64) (result syncResult, err error) {\n\tdb.beginSyncDiag(diagOpSync)\n\tdefer func() { db.finishSyncDiag(err) }()\n\n\t// Track total sync metrics.\n\tt := time.Now()\n\tdefer func() {\n\t\tdb.syncNCounter.Inc()\n\t\tif err != nil {\n\t\t\tdb.syncErrorNCounter.Inc()\n\t\t}\n\t\tif isDiskFullError(err) {\n\t\t\tdb.diskFullGauge.Set(1)\n\t\t} else {\n\t\t\tdb.diskFullGauge.Set(0)\n\t\t}\n\t\taddDurationToCounter(db.syncSecondsCounter, time.Since(t))\n\t}()\n\n\texec, err := db.newSyncExecutor(ctx)\n\tif err != nil {\n\t\treturn result, err\n\t} else if exec == nil {\n\t\tdb.Logger.Debug(\"sync: no database found\")\n\t\treturn result, nil\n\t}\n\tdefer db.applySyncExecutor(exec, true)\n\n\t// Ensure WAL has at least one frame in it.\n\tdb.setSyncDiagPhase(diagPhaseEnsureWAL, func(s *diagState) {\n\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t})\n\tif err := db.ensureWALExists(ctx); err != nil {\n\t\treturn result, fmt.Errorf(\"ensure wal exists: %w\", err)\n\t}\n\n\tdb.setSyncDiagPhase(diagPhaseVerifyAndSync, func(s *diagState) {\n\t\ts.txID = exec.pos.TXID + 1\n\t})\n\tresult, err = db.verifyAndSyncWithExecutor(ctx, false, exec, maxSyncWALBytes)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\texec.applySyncResult(result)\n\n\t// Track that data was synced for time-based checkpoint decisions.\n\tif result.synced {\n\t\texec.state.syncedSinceCheckpoint = true\n\t}\n\n\t// Checkpoint checks normally wait until the WAL is fully synced, but the\n\t// emergency truncate threshold must be honored even mid catch-up:\n\t// sustained writes could otherwise keep every chunk limited and defer the\n\t// truncate checkpoint indefinitely, growing the WAL without bound.\n\tif !result.limited || result.syncedToWALEnd || db.exceedsTruncateThreshold(result.origWALSize) {\n\t\tdb.setSyncDiagPhase(diagPhaseCheckpointIfNeeded, func(s *diagState) {\n\t\t\ts.txID = exec.pos.TXID\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\t\tif err := db.checkpointIfNeeded(ctx, exec, result.origWALSize, result.newWALSize); err != nil {\n\t\t\treturn result, fmt.Errorf(\"checkpoint: %w\", err)\n\t\t}\n\t}\n\n\tdb.setSyncDiagPhase(diagPhaseUpdateMetrics, func(s *diagState) {\n\t\ts.txID = exec.pos.TXID\n\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t})\n\n\t// Compute current index and total shadow WAL size.\n\tdb.txIDGauge.Set(float64(exec.pos.TXID))\n\n\t// Update file size metrics.\n\tif fi, err := os.Stat(db.path); err == nil {\n\t\tdb.dbSizeGauge.Set(float64(fi.Size()))\n\t}\n\tdb.walSizeGauge.Set(float64(exec.state.lastSyncedWALOffset))\n\n\treturn result, nil\n}\n\nfunc (db *DB) verifyAndSync(ctx context.Context, checkpointing bool, state *syncState) (syncResult, error) {\n\tpos, err := db.Pos()\n\tif err != nil {\n\t\treturn syncResult{}, fmt.Errorf(\"pos: %w\", err)\n\t}\n\n\treturn db.verifyAndSyncWithExecutor(ctx, checkpointing, \u0026syncExecutor{\n\t\tstate: *state,\n\t\tpos: pos,\n\t}, 0)\n}\n\nfunc (db *DB) verifyAndSyncWithExecutor(ctx context.Context, checkpointing bool, exec *syncExecutor, maxSyncWALBytes int64) (syncResult, error) {\n\tdb.setSyncDiagPhase(diagPhaseStatWAL, func(s *diagState) {\n\t\ts.txID = exec.pos.TXID + 1\n\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t})\n\n\t// Use the last synced WAL offset as the logical size for checkpoint decisions.\n\t// This avoids using file size which may include stale frames with old salt\n\t// values after a checkpoint. See issue #997.\n\torigWALSize := exec.state.lastSyncedWALOffset\n\tif origWALSize == 0 {\n\t\t// First sync - use file size as fallback\n\t\tvar err error\n\t\torigWALSize, err = db.walFileSize()\n\t\tif err != nil {\n\t\t\treturn syncResult{}, fmt.Errorf(\"stat wal before sync: %w\", err)\n\t\t}\n\t}\n\n\t// Verify our last sync matches the current state of the WAL.\n\t// This ensures that the last sync position of the real WAL hasn't\n\t// been overwritten by another process.\n\tdb.setSyncDiagPhase(diagPhaseVerify)\n\tinfo, err := db.verifyWithExecutor(ctx, exec)\n\tif err != nil {\n\t\treturn syncResult{}, fmt.Errorf(\"cannot verify wal state: %w\", err)\n\t}\n\n\tdb.setSyncDiagPhase(diagPhaseSyncLTX, func(s *diagState) {\n\t\ts.txID = exec.pos.TXID + 1\n\t\ts.snapshotting = info.snapshotting\n\t\ts.reason = info.reason\n\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t})\n\tresult, err := db.sync(ctx, checkpointing, exec, info, maxSyncWALBytes)\n\tif err != nil {\n\t\treturn syncResult{}, fmt.Errorf(\"sync: %w\", err)\n\t}\n\n\tresult.origWALSize = origWALSize\n\treturn result, nil\n}\n\n// checkpointIfNeeded performs a checkpoint based on configured thresholds.\n// Checks thresholds in priority order: TruncatePageN → MinCheckpointPageN → CheckpointInterval.\n//\n// TruncatePageN uses TRUNCATE mode (blocking). Others use PASSIVE mode, which\n// briefly holds the write lock to seal the WAL and can be skipped when the\n// database is busy.\n//\n// Time-based checkpoints only trigger if exec.state.syncedSinceCheckpoint is true, indicating\n// that data has been synced since the last checkpoint. This prevents creating unnecessary\n// LTX files when the only WAL data is from internal bookkeeping (like _litestream_seq\n// updates from previous checkpoints). See issue #896.\nfunc (db *DB) checkpointIfNeeded(ctx context.Context, exec *syncExecutor, origWALSize, newWALSize int64) error {\n\tif db.pageSize == 0 {\n\t\treturn nil\n\t}\n\tif !db.exceedsTruncateThreshold(exec.state.lastSyncedWALOffset) {\n\t\texec.state.truncatePassiveFailed = false\n\t}\n\n\t// Priority 1: Emergency truncate checkpoint (TRUNCATE mode, blocking)\n\t// This prevents unbounded WAL growth from long-lived read transactions.\n\tif db.exceedsTruncateThreshold(origWALSize) {\n\t\ttruncateThreshold := calcWALSize(uint32(db.pageSize), uint32(db.effectiveTruncatePageN()))\n\n\t\tif !exec.state.truncatePassiveFailed {\n\t\t\t// Try a PASSIVE checkpoint first: if it restarts the WAL and brings\n\t\t\t// the synced offset below the threshold, the blocking TRUNCATE and\n\t\t\t// its mandatory boundary snapshot are skipped.\n\t\t\tif restarted, err := db.checkpointWithExecutor(ctx, CheckpointModePassive, exec); err != nil {\n\t\t\t\tif !isSQLiteBusyError(err) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\texec.state.truncatePassiveFailed = true\n\t\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"passive checkpoint skipped\", \"reason\", \"database busy\")\n\t\t\t} else if restarted {\n\t\t\t\texec.state.truncatePassiveFailed = false\n\t\t\t\tif !db.exceedsTruncateThreshold(exec.state.lastSyncedWALOffset) {\n\t\t\t\t\tdb.Logger.Info(\"wal restarted by passive checkpoint, skipping truncate checkpoint\",\n\t\t\t\t\t\t\"wal_size\", origWALSize,\n\t\t\t\t\t\t\"threshold\", truncateThreshold)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t} else if exec.checkpointAttempted {\n\t\t\t\texec.state.truncatePassiveFailed = true\n\t\t\t}\n\t\t}\n\n\t\tdb.Logger.Info(\"forcing truncate checkpoint\",\n\t\t\t\"wal_size\", origWALSize,\n\t\t\t\"threshold\", truncateThreshold)\n\t\trestarted, err := db.checkpointWithExecutor(ctx, CheckpointModeTruncate, exec)\n\t\tif restarted || !db.exceedsTruncateThreshold(exec.state.lastSyncedWALOffset) {\n\t\t\texec.state.truncatePassiveFailed = false\n\t\t}\n\t\treturn err\n\t}\n\n\t// Priority 2: Regular checkpoint at min threshold (PASSIVE mode)\n\tif newWALSize \u003e= calcWALSize(uint32(db.pageSize), uint32(db.MinCheckpointPageN)) {\n\t\tif _, err := db.checkpointWithExecutor(ctx, CheckpointModePassive, exec); err != nil {\n\t\t\t// PASSIVE checkpoints can fail with SQLITE_BUSY when database is locked.\n\t\t\t// This is expected behavior and not an error - just log and continue.\n\t\t\tif isSQLiteBusyError(err) {\n\t\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"passive checkpoint skipped\", \"reason\", \"database busy\")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Priority 3: Time-based checkpoint (PASSIVE mode)\n\t// Only trigger if there have been actual changes synced since the last\n\t// checkpoint. This prevents creating unnecessary LTX files when the only\n\t// WAL data is from internal bookkeeping (like _litestream_seq updates).\n\tif db.CheckpointInterval \u003e 0 \u0026\u0026 exec.state.syncedSinceCheckpoint {\n\t\t// Get database file modification time\n\t\tfi, err := db.f.Stat()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"stat database: %w\", err)\n\t\t}\n\n\t\t// Only checkpoint if enough time has passed and WAL has data\n\t\tif time.Since(fi.ModTime()) \u003e db.CheckpointInterval \u0026\u0026 newWALSize \u003e calcWALSize(uint32(db.pageSize), 1) {\n\t\t\tif _, err := db.checkpointWithExecutor(ctx, CheckpointModePassive, exec); err != nil {\n\t\t\t\t// PASSIVE checkpoints can fail with SQLITE_BUSY when database is locked.\n\t\t\t\t// This is expected behavior and not an error - just log and continue.\n\t\t\t\tif isSQLiteBusyError(err) {\n\t\t\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"passive checkpoint skipped\", \"reason\", \"database busy\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// exceedsTruncateThreshold returns true once walSize has grown past the\n// emergency truncate checkpoint threshold.\nfunc (db *DB) exceedsTruncateThreshold(walSize int64) bool {\n\ttruncatePageN := db.effectiveTruncatePageN()\n\treturn truncatePageN \u003e 0 \u0026\u0026 db.pageSize != 0 \u0026\u0026\n\t\twalSize \u003e= calcWALSize(uint32(db.pageSize), uint32(truncatePageN))\n}\n\nfunc (db *DB) effectiveTruncatePageN() int {\n\tif db.TruncatePageN == 0 {\n\t\treturn DefaultTruncatePageN\n\t}\n\treturn db.TruncatePageN\n}\n\n// isSQLiteBusyError returns true if the error is an SQLITE_BUSY error.\nfunc isSQLiteBusyError(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\t// Check for \"database is locked\" or \"SQLITE_BUSY\" in error message\n\terrStr := err.Error()\n\treturn strings.Contains(errStr, \"database is locked\") ||\n\t\tstrings.Contains(errStr, \"SQLITE_BUSY\")\n}\n\n// isDiskFullError returns true if the error indicates disk space issues.\n// This includes \"no space left on device\" (ENOSPC) and \"disk quota exceeded\" (EDQUOT).\nfunc isDiskFullError(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\tif errors.Is(err, ErrDiskFull) || errors.Is(err, syscall.ENOSPC) {\n\t\treturn true\n\t}\n\terrStr := strings.ToLower(err.Error())\n\treturn strings.Contains(errStr, \"no space left on device\") ||\n\t\tstrings.Contains(errStr, \"disk quota exceeded\") ||\n\t\tstrings.Contains(errStr, \"not enough space on the disk\") ||\n\t\tstrings.Contains(errStr, \"database or disk is full\") ||\n\t\tstrings.Contains(errStr, \"enospc\") ||\n\t\tstrings.Contains(errStr, \"edquot\")\n}\n\ntype ltxStagingFile interface {\n\tio.Writer\n\tSync() error\n\tClose() error\n}\n\nfunc defaultOpenLTXFile(name string, flag int, perm os.FileMode) (ltxStagingFile, error) {\n\treturn os.OpenFile(name, flag, perm)\n}\n\n// walFileSize returns the size of the WAL file in bytes.\nfunc (db *DB) walFileSize() (int64, error) {\n\tfi, err := os.Stat(db.WALPath())\n\tif os.IsNotExist(err) {\n\t\treturn 0, nil\n\t} else if err != nil {\n\t\treturn 0, err\n\t}\n\treturn fi.Size(), nil\n}\n\n// calcWALSize returns the size of the WAL for a given page size \u0026 count.\n// Casts to int64 before multiplication to prevent uint32 overflow with large page sizes.\nfunc calcWALSize(pageSize uint32, pageN uint32) int64 {\n\treturn int64(WALHeaderSize) + (int64(WALFrameHeaderSize+pageSize) * int64(pageN))\n}\n\nfunc (db *DB) bumpLitestreamSeq(ctx context.Context) error {\n\t_, err := db.db.ExecContext(ctx, `INSERT INTO _litestream_seq (id, seq) VALUES (1, 1) ON CONFLICT (id) DO UPDATE SET seq = seq + 1`)\n\treturn err\n}\n\n// ensureWALExists checks that the real WAL exists and has a header.\nfunc (db *DB) ensureWALExists(ctx context.Context) (err error) {\n\t// Exit early if WAL header exists.\n\tif fi, err := os.Stat(db.WALPath()); err == nil \u0026\u0026 fi.Size() \u003e= WALHeaderSize {\n\t\treturn nil\n\t}\n\n\t// Otherwise create transaction that updates the internal litestream table.\n\treturn db.bumpLitestreamSeq(ctx)\n}\n\n// checkDatabaseBehindReplica detects when a database has been restored to an\n// earlier state and the replica has a higher TXID. This handles issue #781.\n//\n// If detected, it clears local L0 files and fetches the latest L0 LTX file\n// from the replica to establish a baseline. The next DB.sync() will detect\n// the mismatch and trigger a snapshot at the current database state.\nfunc (db *DB) checkDatabaseBehindReplica(ctx context.Context) error {\n\t// Get database position from local L0 files\n\tdbPos, err := db.Pos()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get database position: %w\", err)\n\t}\n\n\t// Get replica position from remote\n\treplicaInfo, err := db.Replica.MaxLTXFileInfo(ctx, 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get replica position: %w\", err)\n\t} else if replicaInfo.MaxTXID == 0 {\n\t\treturn nil // No remote replica data yet\n\t}\n\n\t// Check if database is behind replica\n\tif dbPos.TXID \u003e= replicaInfo.MaxTXID {\n\t\treturn nil // Database is ahead or equal\n\t}\n\n\tdb.Logger.Info(\"detected database behind replica\",\n\t\t\"db_txid\", dbPos.TXID,\n\t\t\"replica_txid\", replicaInfo.MaxTXID)\n\n\t// Clear local L0 files\n\tl0Dir := db.LTXLevelDir(0)\n\tif err := os.RemoveAll(l0Dir); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"remove L0 directory: %w\", err)\n\t}\n\tdb.invalidatePosCache()\n\tif err := internal.MkdirAll(l0Dir, db.dirInfo); err != nil {\n\t\treturn fmt.Errorf(\"recreate L0 directory: %w\", err)\n\t}\n\n\t// Fetch latest L0 LTX file from replica\n\tminTXID, maxTXID := replicaInfo.MinTXID, replicaInfo.MaxTXID\n\treader, err := db.Replica.Client.OpenLTXFile(ctx, 0, minTXID, maxTXID, 0, 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open remote L0 file: %w\", err)\n\t}\n\tdefer func() { _ = reader.Close() }()\n\n\t// Write to temp file and atomically rename\n\tlocalPath := db.LTXPath(0, minTXID, maxTXID)\n\ttmpPath := localPath + \".tmp\"\n\n\ttmpFile, err := os.Create(tmpPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create temp L0 file: %w\", err)\n\t}\n\tdefer func() { _ = os.Remove(tmpPath) }() // Clean up temp file on error\n\n\tif _, err := io.Copy(tmpFile, reader); err != nil {\n\t\t_ = tmpFile.Close()\n\t\treturn fmt.Errorf(\"copy L0 file: %w\", err)\n\t}\n\n\tif err := tmpFile.Sync(); err != nil {\n\t\t_ = tmpFile.Close()\n\t\treturn fmt.Errorf(\"sync L0 file: %w\", err)\n\t}\n\n\tif err := tmpFile.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close L0 file: %w\", err)\n\t}\n\n\t// Atomically rename temp file to final path\n\tif err := os.Rename(tmpPath, localPath); err != nil {\n\t\treturn fmt.Errorf(\"rename L0 file: %w\", err)\n\t}\n\tdb.invalidatePosCache()\n\n\tdb.Logger.Info(\"fetched latest L0 file from replica\",\n\t\t\"min_txid\", minTXID,\n\t\t\"max_txid\", maxTXID)\n\n\treturn nil\n}\n\n// verify ensures the current LTX state matches where it left off from\n// the real WAL. Check info.ok if verification was successful.\nfunc (db *DB) verify(ctx context.Context, state *syncState) (info syncInfo, err error) {\n\tpos, err := db.Pos()\n\tif err != nil {\n\t\treturn info, fmt.Errorf(\"pos: %w\", err)\n\t}\n\n\treturn db.verifyWithExecutor(ctx, \u0026syncExecutor{\n\t\tstate: *state,\n\t\tpos: pos,\n\t})\n}\n\nfunc (db *DB) verifyWithExecutor(ctx context.Context, exec *syncExecutor) (info syncInfo, err error) {\n\tframeSize := int64(db.pageSize + WALFrameHeaderSize)\n\tinfo.snapshotting = true\n\n\tif exec.pos.TXID == 0 {\n\t\tinfo.offset = WALHeaderSize\n\t\treturn info, nil // first sync\n\t}\n\n\t// Determine last WAL offset we save from.\n\tltxPath := db.LTXPath(0, exec.pos.TXID, exec.pos.TXID)\n\tltxFile, err := os.Open(ltxPath)\n\tif err != nil {\n\t\treturn info, NewLTXError(\"open\", ltxPath, 0, uint64(exec.pos.TXID), uint64(exec.pos.TXID), err)\n\t}\n\tdefer func() { _ = ltxFile.Close() }()\n\n\tdec := ltx.NewDecoder(ltxFile)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\t// Decode failure indicates corruption\n\t\tltxErr := NewLTXError(\"decode\", ltxPath, 0, uint64(exec.pos.TXID), uint64(exec.pos.TXID), fmt.Errorf(\"%w: %w\", ErrLTXCorrupted, err))\n\t\treturn info, ltxErr\n\t}\n\tinfo.offset = dec.Header().WALOffset + dec.Header().WALSize\n\tinfo.salt1 = dec.Header().WALSalt1\n\tinfo.salt2 = dec.Header().WALSalt2\n\tinfo.prevCommit = dec.Header().Commit\n\n\t// If LTX WAL offset is larger than real WAL then the WAL has been truncated.\n\tif fi, err := os.Stat(db.WALPath()); err != nil {\n\t\treturn info, fmt.Errorf(\"open wal file: %w\", err)\n\t} else if info.offset \u003e fi.Size() {\n\t\texec.state.truncatePassiveFailed = false\n\n\t\t// If we previously synced to the exact end of the WAL, this truncation\n\t\t// is expected (normal checkpoint behavior). Reset position and continue\n\t\t// incrementally rather than triggering a full snapshot. See issue #927.\n\t\tif exec.state.syncedToWALEnd {\n\t\t\t// Read new WAL header to get current salt values\n\t\t\thdr, err := readWALHeader(db.WALPath())\n\t\t\tif err != nil {\n\t\t\t\treturn info, fmt.Errorf(\"read wal header after expected truncation: %w\", err)\n\t\t\t}\n\n\t\t\tinfo.offset = WALHeaderSize\n\t\t\tinfo.salt1 = binary.BigEndian.Uint32(hdr[16:])\n\t\t\tinfo.salt2 = binary.BigEndian.Uint32(hdr[20:])\n\t\t\tinfo.snapshotting = false\n\t\t\tinfo.reason = \"\"\n\t\t\tinfo.clearSyncedToWALEnd = true\n\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"wal truncated after sync to end (expected checkpoint)\",\n\t\t\t\t\"new_salt1\", info.salt1,\n\t\t\t\t\"new_salt2\", info.salt2)\n\n\t\t\treturn info, nil\n\t\t}\n\n\t\tinfo.reason = \"wal truncated by another process\"\n\t\treturn info, nil\n\t}\n\n\t// Compare WAL headers. Restart from beginning of WAL if different.\n\thdr0, err := readWALHeader(db.WALPath())\n\tif err != nil {\n\t\treturn info, fmt.Errorf(\"cannot read wal header: %w\", err)\n\t}\n\tsalt1 := binary.BigEndian.Uint32(hdr0[16:])\n\tsalt2 := binary.BigEndian.Uint32(hdr0[20:])\n\tsaltMatch := salt1 == dec.Header().WALSalt1 \u0026\u0026 salt2 == dec.Header().WALSalt2\n\tif !saltMatch {\n\t\texec.state.truncatePassiveFailed = false\n\t}\n\n\t// Handle edge case where we're at WAL header (WALOffset=32, WALSize=0).\n\t// This can happen when an LTX file represents a state at the beginning of the WAL\n\t// with no frames written yet. We must check this before computing prevWALOffset\n\t// to avoid underflow (32 - 4120 = -4088).\n\t// See: https://github.com/benbjohnson/litestream/issues/900\n\tif info.offset == WALHeaderSize {\n\t\tdb.Logger.Debug(\"verify\", \"saltMatch\", saltMatch, \"atWALHeader\", true)\n\t\tif saltMatch {\n\t\t\tinfo.snapshotting = false\n\t\t\treturn info, nil\n\t\t}\n\t\tinfo.reason = \"wal header salt reset, snapshotting\"\n\t\treturn info, nil\n\t}\n\n\t// If offset is at the beginning of the first page, we can't check for previous page.\n\tprevWALOffset := info.offset - frameSize\n\tdb.Logger.Debug(\"verify\", \"saltMatch\", saltMatch, \"prevWALOffset\", prevWALOffset)\n\n\tif prevWALOffset == WALHeaderSize {\n\t\tif saltMatch { // No writes occurred since last sync, salt still matches\n\t\t\tinfo.snapshotting = false\n\t\t\treturn info, nil\n\t\t}\n\t\t// Salt has changed but we don't know if writes occurred since last sync\n\t\tinfo.reason = \"wal header salt reset, snapshotting\"\n\t\treturn info, nil\n\t} else if prevWALOffset \u003c WALHeaderSize {\n\t\treturn info, fmt.Errorf(\"prev WAL offset is less than the header size: %d\", prevWALOffset)\n\t}\n\n\t// If we can't verify the last page is in the last LTX file, then we need to snapshot.\n\tlastPageMatch, err := db.lastPageMatch(ctx, dec, prevWALOffset, frameSize)\n\tif err != nil {\n\t\treturn info, fmt.Errorf(\"last page match: %w\", err)\n\t} else if !lastPageMatch {\n\t\t// A snapshot must hold every frame not yet in the database file,\n\t\t// including those before the stale offset.\n\t\tinfo.offset = WALHeaderSize\n\t\tinfo.reason = \"last page does not exist in last ltx file, wal overwritten by another process\"\n\t\treturn info, nil\n\t}\n\n\tdb.Logger.Debug(\"verify.2\", \"lastPageMatch\", lastPageMatch)\n\n\t// Salt has changed which could indicate a FULL checkpoint.\n\t// If we have a last page match, then we can assume that the WAL has not been overwritten.\n\tif !saltMatch {\n\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"wal restarted\",\n\t\t\t\"salt1\", salt1,\n\t\t\t\"salt2\", salt2)\n\n\t\tinfo.offset = WALHeaderSize\n\t\tinfo.salt1, info.salt2 = salt1, salt2\n\n\t\tif detected, err := db.detectFullCheckpoint(ctx, [][2]uint32{{salt1, salt2}, {dec.Header().WALSalt1, dec.Header().WALSalt2}}); err != nil {\n\t\t\treturn info, fmt.Errorf(\"detect full checkpoint: %w\", err)\n\t\t} else if detected {\n\t\t\tinfo.reason = \"full or restart checkpoint detected, snapshotting\"\n\t\t} else {\n\t\t\tinfo.snapshotting = false\n\t\t}\n\n\t\treturn info, nil\n\t}\n\n\tinfo.snapshotting = false\n\n\treturn info, nil\n}\n\n// lastPageMatch checks if the last page read in the WAL exists in the last LTX file.\nfunc (db *DB) lastPageMatch(ctx context.Context, dec *ltx.Decoder, prevWALOffset, frameSize int64) (bool, error) {\n\tif prevWALOffset \u003c= WALHeaderSize {\n\t\treturn false, nil\n\t}\n\n\tframe, err := readWALFileAt(db.WALPath(), prevWALOffset, frameSize)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"cannot read last synced wal page: %w\", err)\n\t}\n\tpgno := binary.BigEndian.Uint32(frame[0:])\n\tfsalt1 := binary.BigEndian.Uint32(frame[8:])\n\tfsalt2 := binary.BigEndian.Uint32(frame[12:])\n\tdata := frame[WALFrameHeaderSize:]\n\n\tif fsalt1 != dec.Header().WALSalt1 || fsalt2 != dec.Header().WALSalt2 {\n\t\treturn false, nil\n\t}\n\n\t// Verify that the last page in the WAL exists in the last LTX file.\n\tbuf := make([]byte, dec.Header().PageSize)\n\tfor {\n\t\tvar hdr ltx.PageHeader\n\t\tif err := dec.DecodePage(\u0026hdr, buf); errors.Is(err, io.EOF) {\n\t\t\treturn false, nil // page not found in LTX file\n\t\t} else if err != nil {\n\t\t\treturn false, fmt.Errorf(\"decode ltx page: %w\", err)\n\t\t}\n\n\t\tif pgno != hdr.Pgno {\n\t\t\tcontinue // page number doesn't match\n\t\t}\n\t\tif !bytes.Equal(data, buf) {\n\t\t\tcontinue // page data doesn't match\n\t\t}\n\t\treturn true, nil // Page matches\n\t}\n}\n\n// detectFullCheckpoint attempts to detect checks if a FULL or RESTART checkpoint\n// has occurred and we may have missed some frames.\nfunc (db *DB) detectFullCheckpoint(ctx context.Context, knownSalts [][2]uint32) (bool, error) {\n\twalFile, err := os.Open(db.WALPath())\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"open wal file: %w\", err)\n\t}\n\tdefer walFile.Close()\n\n\tvar lastKnownSalt [2]uint32\n\tif len(knownSalts) \u003e 0 {\n\t\tlastKnownSalt = knownSalts[len(knownSalts)-1]\n\t}\n\n\trd, err := NewWALReader(walFile, db.Logger.With(LogKeySubsystem, LogSubsystemWALReader))\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"new wal reader: %w\", err)\n\t}\n\tm, err := rd.FrameSaltsUntil(ctx, lastKnownSalt)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"frame salts until: %w\", err)\n\t}\n\n\t// Remove known salts from the map.\n\tfor _, salt := range knownSalts {\n\t\tdelete(m, salt)\n\t}\n\n\t// If we have more than one unknown salt, then we have a FULL or RESTART checkpoint.\n\treturn len(m) \u003e= 1, nil\n}\n\ntype syncInfo struct {\n\toffset int64 // end of the previous LTX read\n\tsalt1 uint32\n\tsalt2 uint32\n\tprevCommit uint32\n\tsnapshotting bool // if true, a full snapshot is required\n\treason string // reason for snapshot\n\tclearSyncedToWALEnd bool\n}\n\ntype syncResult struct {\n\torigWALSize int64\n\tnewWALSize int64\n\tsynced bool\n\tlimited bool\n\tsyncedToWALEnd bool\n\tpos *ltx.Pos\n\tl0FileInfo *ltx.FileInfo\n}\n\nfunc (db *DB) applySyncResult(state *syncState, result syncResult) {\n\tstate.lastSyncedWALOffset = result.newWALSize\n\tstate.syncedToWALEnd = result.syncedToWALEnd\n\tif result.pos != nil {\n\t\tdb.pos.Lock()\n\t\tdb.pos.value = result.pos\n\t\tdb.pos.Unlock()\n\t}\n\tif result.l0FileInfo != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdb.maxLTXFileInfos.m[0] = result.l0FileInfo\n\t\tdb.maxLTXFileInfos.Unlock()\n\t}\n}\n\nfunc (db *DB) newSyncExecutor(ctx context.Context) (*syncExecutor, error) {\n\tdb.mu.Lock()\n\tdefer db.mu.Unlock()\n\n\tif err := db.init(ctx); err != nil {\n\t\treturn nil, err\n\t} else if db.db == nil {\n\t\treturn nil, nil\n\t}\n\n\tpos, err := db.Pos()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"pos: %w\", err)\n\t}\n\n\treturn \u0026syncExecutor{\n\t\tstate: db.syncState,\n\t\tpos: pos,\n\t}, nil\n}\n\nfunc (db *DB) applySyncExecutor(exec *syncExecutor, notify bool) {\n\tif exec == nil {\n\t\treturn\n\t}\n\n\t// Only publish the position if this executor advanced it. Republishing\n\t// the snapshot taken at executor start would clobber concurrent cache\n\t// invalidation (e.g. ResetLocalState) with a stale position.\n\tif exec.posChanged {\n\t\tdb.pos.Lock()\n\t\tpos := exec.pos\n\t\tdb.pos.value = \u0026pos\n\t\tdb.pos.Unlock()\n\t}\n\n\tif exec.l0FileInfo != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tinfo := *exec.l0FileInfo\n\t\tdb.maxLTXFileInfos.m[0] = \u0026info\n\t\tdb.maxLTXFileInfos.Unlock()\n\t}\n\n\tdb.mu.Lock()\n\tdb.syncState = exec.state\n\tif notify \u0026\u0026 exec.synced {\n\t\tclose(db.notify)\n\t\tdb.notify = make(chan struct{})\n\t}\n\tdb.mu.Unlock()\n}\n\nfunc (exec *syncExecutor) applySyncResult(result syncResult) {\n\texec.state.lastSyncedWALOffset = result.newWALSize\n\texec.state.syncedToWALEnd = result.syncedToWALEnd\n\tif result.pos != nil {\n\t\texec.pos = *result.pos\n\t\texec.posChanged = true\n\t}\n\tif result.l0FileInfo != nil {\n\t\tinfo := *result.l0FileInfo\n\t\texec.l0FileInfo = \u0026info\n\t}\n\texec.synced = exec.synced || result.synced\n}\n\n// sync copies pending bytes from the real WAL to LTX.\n// Returns synced=true if an LTX file was created (i.e., there were new pages to sync).\nfunc (db *DB) sync(ctx context.Context, checkpointing bool, exec *syncExecutor, info syncInfo, maxSyncWALBytes int64) (result syncResult, err error) {\n\tresult.newWALSize = exec.state.lastSyncedWALOffset\n\tresult.syncedToWALEnd = exec.state.syncedToWALEnd\n\tif info.clearSyncedToWALEnd {\n\t\tresult.syncedToWALEnd = false\n\t}\n\n\t// Determine the next sequential transaction ID.\n\ttxID := exec.pos.TXID + 1\n\tdb.setSyncDiagPhase(diagPhaseSyncOpenLTX,\n\t\tfunc(s *diagState) {\n\t\t\ts.txID = txID\n\t\t\ts.snapshotting = info.snapshotting\n\t\t\ts.reason = info.reason\n\t\t})\n\n\tfilename := db.LTXPath(0, txID, txID)\n\n\tlogArgs := []any{\n\t\t\"txid\", txID.String(),\n\t\t\"offset\", info.offset,\n\t}\n\tif checkpointing {\n\t\tlogArgs = append(logArgs, \"chkpt\", \"true\")\n\t}\n\tif info.snapshotting {\n\t\tlogArgs = append(logArgs, \"snap\", \"true\")\n\t}\n\tif info.reason != \"\" {\n\t\tlogArgs = append(logArgs, \"reason\", info.reason)\n\t}\n\tdb.Logger.Debug(\"sync\", logArgs...)\n\n\t// Prevent internal checkpoints during sync. Ignore if already in a checkpoint.\n\tif !checkpointing {\n\t\tdb.chkMu.RLock()\n\t\tdefer db.chkMu.RUnlock()\n\t}\n\n\tfi, err := db.f.Stat()\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tmode := fi.Mode()\n\tcommit := uint32(fi.Size() / int64(db.pageSize))\n\n\twalFile, err := os.Open(db.WALPath())\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tdefer walFile.Close()\n\n\twalReaderLogger := db.Logger.With(LogKeySubsystem, LogSubsystemWALReader)\n\tvar rd *WALReader\n\tif info.offset == WALHeaderSize {\n\t\tif rd, err = NewWALReader(walFile, walReaderLogger); err != nil {\n\t\t\treturn result, fmt.Errorf(\"new wal reader: %w\", err)\n\t\t}\n\t} else {\n\t\t// If we cannot verify the previous frame\n\t\tvar pfmError *PrevFrameMismatchError\n\t\tif rd, err = NewWALReaderWithOffset(ctx, walFile, info.offset, info.salt1, info.salt2, walReaderLogger); errors.As(err, \u0026pfmError) {\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"prev frame mismatch, snapshotting\", \"err\", pfmError.Err)\n\t\t\tinfo.offset = WALHeaderSize\n\t\t\tif rd, err = NewWALReader(walFile, walReaderLogger); err != nil {\n\t\t\t\treturn result, fmt.Errorf(\"new wal reader, after reset\")\n\t\t\t}\n\t\t} else if err != nil {\n\t\t\treturn result, fmt.Errorf(\"new wal reader with offset: %w\", err)\n\t\t}\n\t}\n\n\t// Build a mapping of changed page numbers and their latest content.\n\tdb.setSyncDiagPhase(diagPhaseSyncPageMap,\n\t\tfunc(s *diagState) {\n\t\t\ts.txID = txID\n\t\t\ts.snapshotting = info.snapshotting\n\t\t\ts.reason = info.reason\n\t\t})\n\tif info.snapshotting {\n\t\tmaxSyncWALBytes = 0\n\t}\n\tpageMap, maxOffset, walCommit, limited, err := rd.pageMap(ctx, maxSyncWALBytes)\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"page map: %w\", err)\n\t}\n\tresult.limited = limited\n\tif walCommit \u003e 0 {\n\t\tcommit = walCommit\n\t}\n\tvar sz int64\n\tif maxOffset \u003e 0 {\n\t\tsz = maxOffset - info.offset\n\t}\n\tassert(sz \u003e= 0, fmt.Sprintf(\"wal size must be positive: sz=%d, maxOffset=%d, info.offset=%d\", sz, maxOffset, info.offset))\n\tdb.setSyncDiagPhase(diagPhaseSyncPrepareLTX,\n\t\tfunc(s *diagState) {\n\t\t\ts.txID = txID\n\t\t\ts.walSize = sz\n\t\t\ts.snapshotting = info.snapshotting\n\t\t\ts.reason = info.reason\n\t\t})\n\n\t// Track total WAL bytes synced.\n\tif sz \u003e 0 {\n\t\tdb.totalWALBytesCounter.Add(float64(sz))\n\t}\n\n\t// Exit if we have no new WAL pages and we aren't snapshotting.\n\tif !info.snapshotting \u0026\u0026 sz == 0 {\n\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"sync: skip\", \"reason\", \"no new wal pages\")\n\t\treturn result, nil\n\t}\n\n\ttmpFilename := filename + \".tmp\"\n\tif err := internal.MkdirAll(filepath.Dir(tmpFilename), db.dirInfo); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-mkdir\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, err\n\t}\n\n\tltxFile, err := db.openLTXFile(tmpFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)\n\tif err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-open\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"open temp ltx file: %w\", err)\n\t}\n\tdefer func() { _ = os.Remove(tmpFilename) }()\n\tdefer func() { _ = ltxFile.Close() }()\n\n\tuid, gid := internal.Fileinfo(db.fileInfo)\n\t_ = os.Chown(tmpFilename, uid, gid)\n\n\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode header\",\n\t\t\"txid\", txID.String(),\n\t\t\"commit\", commit,\n\t\t\"walOffset\", info.offset,\n\t\t\"walSize\", sz,\n\t\t\"salt1\", rd.salt1,\n\t\t\"salt2\", rd.salt2)\n\n\ttimestamp := time.Now()\n\tenc, err := ltx.NewEncoder(ltxFile)\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"new ltx encoder: %w\", err)\n\t}\n\tif err := enc.EncodeHeader(ltx.Header{\n\t\tVersion: ltx.Version,\n\t\tFlags: ltx.HeaderFlagNoChecksum,\n\t\tPageSize: uint32(db.pageSize),\n\t\tCommit: commit,\n\t\tMinTXID: txID,\n\t\tMaxTXID: txID,\n\t\tTimestamp: timestamp.UnixMilli(),\n\t\tWALOffset: info.offset,\n\t\tWALSize: sz,\n\t\tWALSalt1: rd.salt1,\n\t\tWALSalt2: rd.salt2,\n\t}); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-write\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"encode ltx header: %w\", err)\n\t}\n\n\t// If we need a full snapshot, then copy from the database \u0026 WAL.\n\t// Otherwise, just copy incrementally from the WAL.\n\tif info.snapshotting {\n\t\tdb.setSyncDiagPhase(diagPhaseWriteLTXFromDB,\n\t\t\tfunc(s *diagState) {\n\t\t\t\ts.txID = txID\n\t\t\t\ts.walSize = sz\n\t\t\t\ts.snapshotting = true\n\t\t\t\ts.reason = info.reason\n\t\t\t})\n\t\tif err := db.writeLTXFromDB(ctx, enc, walFile, commit, pageMap); err != nil {\n\t\t\tif isDiskFullError(err) {\n\t\t\t\treturn result, NewLTXError(\"stage-write\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t\t}\n\t\t\treturn result, fmt.Errorf(\"write ltx from db: %w\", err)\n\t\t}\n\t} else {\n\t\tdb.setSyncDiagPhase(diagPhaseWriteLTXFromWAL,\n\t\t\tfunc(s *diagState) {\n\t\t\t\ts.txID = txID\n\t\t\t\ts.walSize = sz\n\t\t\t\ts.snapshotting = false\n\t\t\t\ts.reason = info.reason\n\t\t\t})\n\t\tif err := db.writeLTXFromWAL(ctx, enc, walFile, info.prevCommit, commit, pageMap); err != nil {\n\t\t\tif isDiskFullError(err) {\n\t\t\t\treturn result, NewLTXError(\"stage-write\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t\t}\n\t\t\treturn result, fmt.Errorf(\"write ltx from wal: %w\", err)\n\t\t}\n\t}\n\n\t// Encode final trailer to the end of the LTX file.\n\tdb.setSyncDiagPhase(diagPhaseCloseLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := enc.Close(); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-write\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"close ltx encoder: %w\", err)\n\t}\n\n\t// Sync \u0026 close LTX file.\n\tdb.setSyncDiagPhase(diagPhaseFsyncLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := ltxFile.Sync(); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-sync\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"sync ltx file: %w\", err)\n\t}\n\tif err := ltxFile.Close(); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-close\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"close ltx file: %w\", err)\n\t}\n\n\t// Atomically rename file to final path.\n\tdb.setSyncDiagPhase(diagPhaseRenameLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := os.Rename(tmpFilename, filename); err != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdelete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state\n\t\tdb.maxLTXFileInfos.Unlock()\n\t\tdb.invalidatePosCache()\n\t\treturn result, fmt.Errorf(\"rename ltx file: %w\", err)\n\t}\n\tif err := internal.FsyncDir(filepath.Dir(filename)); err != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdelete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state\n\t\tdb.maxLTXFileInfos.Unlock()\n\t\tdb.invalidatePosCache()\n\t\treturn result, fmt.Errorf(\"sync ltx dir: %w\", err)\n\t}\n\n\tresult.synced = true\n\tresult.l0FileInfo = \u0026ltx.FileInfo{\n\t\tLevel: 0,\n\t\tMinTXID: txID,\n\t\tMaxTXID: txID,\n\t\tCreatedAt: time.Now(),\n\t\tSize: enc.N(),\n\t}\n\n\tencPos := enc.PostApplyPos()\n\tresult.pos = \u0026encPos\n\n\t// Track the logical end of WAL content for checkpoint decisions.\n\t// This is the WALOffset + WALSize from the LTX we just created.\n\t// Using this instead of file size prevents issue #997 where stale\n\t// frames with old salt values cause perpetual checkpoint triggering.\n\tfinalOffset := info.offset + sz\n\tresult.newWALSize = finalOffset\n\n\t// Track if we synced to the exact end of the WAL file.\n\t// This is used by verify() to distinguish expected checkpoint truncation\n\t// from unexpected external WAL modifications. See issue #927.\n\tif walSize, err := db.walFileSize(); err == nil {\n\t\tresult.syncedToWALEnd = finalOffset == walSize\n\t} else {\n\t\tresult.syncedToWALEnd = false\n\t}\n\tdb.setSyncDiagPhase(diagPhaseSyncComplete,\n\t\tfunc(s *diagState) {\n\t\t\ts.txID = txID\n\t\t\ts.walSize = sz\n\t\t\ts.lastSyncedWALOffset = finalOffset\n\t\t\ts.snapshotting = info.snapshotting\n\t\t\ts.reason = info.reason\n\t\t})\n\n\tdb.Logger.Debug(\"db sync\", \"status\", \"ok\")\n\n\treturn result, nil\n}\n\nfunc (db *DB) writeLTXFromDB(ctx context.Context, enc *ltx.Encoder, walFile *os.File, commit uint32, pageMap map[uint32]int64) error {\n\tlockPgno := ltx.LockPgno(uint32(db.pageSize))\n\tdata := make([]byte, db.pageSize)\n\n\tfor pgno := uint32(1); pgno \u003c= commit; pgno++ {\n\t\tif pgno == lockPgno {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check if the caller has canceled during processing.\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\treturn context.Cause(ctx)\n\t\tdefault:\n\t\t}\n\n\t\t// If page exists in the WAL, read from there.\n\t\tif offset, ok := pageMap[pgno]; ok {\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode page from wal\", \"txid\", enc.Header().MinTXID, \"offset\", offset, \"pgno\", pgno, \"type\", \"db+wal\")\n\n\t\t\tif n, err := walFile.ReadAt(data, offset+WALFrameHeaderSize); err != nil {\n\t\t\t\treturn fmt.Errorf(\"read page %d @ %d: %w\", pgno, offset, err)\n\t\t\t} else if n != len(data) {\n\t\t\t\treturn fmt.Errorf(\"short read page %d @ %d\", pgno, offset)\n\t\t\t}\n\n\t\t\tif err := enc.EncodePage(ltx.PageHeader{Pgno: pgno}, data); err != nil {\n\t\t\t\treturn fmt.Errorf(\"encode ltx frame (pgno=%d): %w\", pgno, err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\toffset := int64(pgno-1) * int64(db.pageSize)\n\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode page from database\", \"offset\", offset, \"pgno\", pgno)\n\n\t\t// Otherwise read directly from the database file.\n\t\tif _, err := db.f.ReadAt(data, offset); err != nil {\n\t\t\treturn fmt.Errorf(\"read database page %d: %w\", pgno, err)\n\t\t}\n\t\tif err := enc.EncodePage(ltx.PageHeader{Pgno: pgno}, data); err != nil {\n\t\t\treturn fmt.Errorf(\"encode ltx frame (pgno=%d): %w\", pgno, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (db *DB) writeLTXFromWAL(ctx context.Context, enc *ltx.Encoder, walFile *os.File, prevCommit, commit uint32, pageMap map[uint32]int64) error {\n\t// Create an ordered list of page numbers since the LTX encoder requires it.\n\tpgnos := make([]uint32, 0, len(pageMap))\n\tfor pgno := range pageMap {\n\t\tpgnos = append(pgnos, pgno)\n\t}\n\tlockPgno := ltx.LockPgno(uint32(db.pageSize))\n\tif commit \u003e prevCommit {\n\t\twalPgnoN := len(pgnos)\n\t\tfor pgno := prevCommit + 1; pgno \u003c= commit; pgno++ {\n\t\t\tif pgno == lockPgno {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, ok := pageMap[pgno]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpgnos = append(pgnos, pgno)\n\t\t}\n\t\tif growthPgnoN := len(pgnos) - walPgnoN; growthPgnoN \u003e 0 {\n\t\t\tdb.Logger.Debug(\"filling wal growth pages from database\",\n\t\t\t\t\"txid\", enc.Header().MinTXID,\n\t\t\t\t\"n\", growthPgnoN,\n\t\t\t\t\"prev_commit\", prevCommit,\n\t\t\t\t\"commit\", commit)\n\t\t}\n\t}\n\tslices.Sort(pgnos)\n\n\tdata := make([]byte, db.pageSize)\n\tfor _, pgno := range pgnos {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\treturn context.Cause(ctx)\n\t\tdefault:\n\t\t}\n\t\tif offset, ok := pageMap[pgno]; ok {\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode page from wal\", \"txid\", enc.Header().MinTXID, \"offset\", offset, \"pgno\", pgno, \"type\", \"walonly\")\n\n\t\t\tif n, err := walFile.ReadAt(data, offset+WALFrameHeaderSize); err != nil {\n\t\t\t\treturn fmt.Errorf(\"read page %d @ %d: %w\", pgno, offset, err)\n\t\t\t} else if n != len(data) {\n\t\t\t\treturn fmt.Errorf(\"short read page %d @ %d\", pgno, offset)\n\t\t\t}\n\t\t} else {\n\t\t\toffset := int64(pgno-1) * int64(db.pageSize)\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode page from database\", \"txid\", enc.Header().MinTXID, \"offset\", offset, \"pgno\", pgno, \"type\", \"walgrowth\")\n\n\t\t\tif _, err := db.f.ReadAt(data, offset); err != nil {\n\t\t\t\treturn fmt.Errorf(\"read database page %d: %w\", pgno, err)\n\t\t\t}\n\t\t}\n\n\t\tif err := enc.EncodePage(ltx.PageHeader{Pgno: pgno}, data); err != nil {\n\t\t\treturn fmt.Errorf(\"encode ltx frame (pgno=%d): %w\", pgno, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Checkpoint performs a checkpoint on the WAL file.\nfunc (db *DB) Checkpoint(ctx context.Context, mode string) (err error) {\n\tif err := db.lockExec(ctx); err != nil {\n\t\treturn err\n\t}\n\tdefer db.execSem.Release(1)\n\tdb.beginSyncDiag(diagOpCheckpoint)\n\tdefer func() { db.finishSyncDiag(err) }()\n\n\texec, err := db.newSyncExecutor(ctx)\n\tif err != nil {\n\t\treturn err\n\t} else if exec == nil {\n\t\treturn nil\n\t}\n\tdefer db.applySyncExecutor(exec, true)\n\n\t_, err = db.checkpointWithExecutor(ctx, mode, exec)\n\treturn err\n}\n\n// checkpoint performs a checkpoint on the WAL file and initializes a\n// new shadow WAL file.\nfunc (db *DB) checkpoint(ctx context.Context, mode string, state *syncState) error {\n\tpos, err := db.Pos()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"pos: %w\", err)\n\t}\n\n\texec := \u0026syncExecutor{\n\t\tstate: *state,\n\t\tpos: pos,\n\t}\n\tif _, err := db.checkpointWithExecutor(ctx, mode, exec); err != nil {\n\t\treturn err\n\t}\n\n\t*state = exec.state\n\tdb.pos.Lock()\n\tpos = exec.pos\n\tdb.pos.value = \u0026pos\n\tdb.pos.Unlock()\n\tif exec.l0FileInfo != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tinfo := *exec.l0FileInfo\n\t\tdb.maxLTXFileInfos.m[0] = \u0026info\n\t\tdb.maxLTXFileInfos.Unlock()\n\t}\n\treturn nil\n}\n\n// checkpointWithExecutor performs a checkpoint in the given mode and reports\n// whether the checkpoint restarted the WAL. It returns false without\n// checkpointing when the checkpoint lock is held by an in-progress snapshot.\nfunc (db *DB) checkpointWithExecutor(ctx context.Context, mode string, exec *syncExecutor) (walRestarted bool, err error) {\n\texec.checkpointAttempted = false\n\tdb.setSyncDiagPhase(diagPhaseCheckpointLock,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\t// Try getting a checkpoint lock, will fail during snapshots. Skipping is\n\t// safe since the next sync retries.\n\tif !db.chkMu.TryLock() {\n\t\tif mode == CheckpointModeTruncate {\n\t\t\tdb.Logger.Info(\"checkpoint skipped, snapshot in progress\", \"mode\", mode)\n\t\t} else {\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"checkpoint skipped, snapshot in progress\", \"mode\", mode)\n\t\t}\n\t\treturn false, nil\n\t}\n\tdefer db.chkMu.Unlock()\n\texec.checkpointAttempted = true\n\n\t// Read WAL header before checkpoint to check if it has been restarted.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointReadWALHeader,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\thdr, err := readWALHeader(db.WALPath())\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\t// Copy end of WAL before checkpoint to copy as much as possible.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointCopyBefore,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\tresult, err := db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"cannot copy wal before checkpoint: %w\", err)\n\t}\n\texec.applySyncResult(result)\n\n\tvar barrierTx *sql.Tx\n\tif mode == CheckpointModePassive {\n\t\tbarrierTx, err = db.db.BeginTx(ctx, nil)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"begin passive checkpoint barrier: %w\", err)\n\t\t}\n\t\tdefer func() {\n\t\t\tif barrierTx != nil {\n\t\t\t\t_ = rollback(barrierTx)\n\t\t\t}\n\t\t}()\n\n\t\tif _, err := barrierTx.ExecContext(ctx, `INSERT INTO _litestream_lock (id) VALUES (1);`); err != nil {\n\t\t\treturn false, fmt.Errorf(\"_litestream_lock: %w\", err)\n\t\t}\n\n\t\tresult, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"cannot seal wal before passive checkpoint: %w\", err)\n\t\t}\n\t\texec.applySyncResult(result)\n\t}\n\n\tframeSize := int64(db.pageSize + WALFrameHeaderSize)\n\tpreCheckpointFrameN := 0\n\tif exec.state.lastSyncedWALOffset \u003e WALHeaderSize {\n\t\tpreCheckpointFrameN = int((exec.state.lastSyncedWALOffset - WALHeaderSize) / frameSize)\n\t}\n\n\t// Execute checkpoint and immediately issue a write to the WAL to ensure\n\t// a new page is written.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointExec,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\twalFrameN, err := db.execCheckpoint(ctx, mode)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif barrierTx != nil {\n\t\tif err = rollback(barrierTx); err != nil {\n\t\t\treturn false, fmt.Errorf(\"rollback passive checkpoint barrier: %w\", err)\n\t\t}\n\t\tbarrierTx = nil\n\t}\n\n\tif err = db.bumpLitestreamSeq(ctx); err != nil {\n\t\treturn false, fmt.Errorf(\"bump litestream seq: %w\", err)\n\t}\n\n\t// If WAL hasn't been restarted, exit.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointVerifyRestart,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\tother, err := readWALHeader(db.WALPath())\n\tif err != nil {\n\t\treturn false, err\n\t} else if bytes.Equal(hdr, other) {\n\t\texec.state.syncedSinceCheckpoint = false\n\t\treturn false, nil\n\t}\n\texec.state.truncatePassiveFailed = false\n\n\tif mode == CheckpointModePassive {\n\t\tresult, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"cannot copy wal after passive checkpoint: %w\", err)\n\t\t}\n\t\texec.applySyncResult(result)\n\t\texec.state.syncedSinceCheckpoint = false\n\t\treturn true, nil\n\t}\n\n\t// A successful TRUNCATE checkpoint always reports zero frames because\n\t// the WAL is reset before the counters are read, so the comparison\n\t// below can never prove that no commits landed between the sealed\n\t// sync and the checkpoint taking the writer lock. Those commits are\n\t// backfilled and truncated unseen, so TRUNCATE must take the boundary\n\t// snapshot unconditionally.\n\tif mode != CheckpointModeTruncate \u0026\u0026 walFrameN \u003c= preCheckpointFrameN {\n\t\tresult, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"cannot copy wal after checkpoint: %w\", err)\n\t\t}\n\t\texec.applySyncResult(result)\n\t\texec.state.syncedSinceCheckpoint = false\n\t\treturn true, nil\n\t}\n\n\t// Start a transaction. This will be promoted immediately after.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointSnapshotBoundaryLock,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\ttx, err := db.db.BeginTx(ctx, nil)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"begin: %w\", err)\n\t}\n\tdefer func() { _ = rollback(tx) }()\n\n\t// Insert into the lock table to promote to a write tx. The lock table\n\t// insert will never actually occur because our tx will be rolled back,\n\t// however, it will ensure our tx grabs the write lock. Unfortunately,\n\t// we can't call \"BEGIN IMMEDIATE\" as we are already in a transaction.\n\tif _, err := tx.ExecContext(ctx, `INSERT INTO _litestream_lock (id) VALUES (1);`); err != nil {\n\t\treturn false, fmt.Errorf(\"_litestream_lock: %w\", err)\n\t}\n\n\t// Copy anything that may have occurred after the checkpoint.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointSnapshotBoundary,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\tsnapshotInfo := syncInfo{\n\t\toffset: WALHeaderSize,\n\t\tsalt1: binary.BigEndian.Uint32(other[16:]),\n\t\tsalt2: binary.BigEndian.Uint32(other[20:]),\n\t\tsnapshotting: true,\n\t\treason: \"checkpoint boundary snapshot\",\n\t}\n\tresult, err = db.sync(ctx, true, exec, snapshotInfo, 0)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"cannot snapshot after checkpoint: %w\", err)\n\t}\n\texec.applySyncResult(result)\n\n\t// Release write lock before exiting.\n\t// Use rollback() helper for consistency with releaseReadLock() and the\n\t// defer above. See issue #934.\n\tif err := rollback(tx); err != nil {\n\t\treturn false, fmt.Errorf(\"rollback post-checkpoint tx: %w\", err)\n\t}\n\n\texec.state.syncedSinceCheckpoint = false\n\treturn true, nil\n}\n\n// execCheckpoint issues a wal_checkpoint PRAGMA in the given mode and returns\n// the number of frames in the WAL as reported by the checkpoint.\nfunc (db *DB) execCheckpoint(ctx context.Context, mode string) (walFrameN int, err error) {\n\t// Ignore if there is no underlying database.\n\tif db.db == nil {\n\t\treturn 0, nil\n\t}\n\n\t// Track checkpoint metrics.\n\tt := time.Now()\n\tdefer func() {\n\t\tlabels := prometheus.Labels{\"mode\": mode}\n\t\tdb.checkpointNCounterVec.With(labels).Inc()\n\t\tif err != nil {\n\t\t\tdb.checkpointErrorNCounterVec.With(labels).Inc()\n\t\t}\n\t\taddDurationToCounter(db.checkpointSecondsCounterVec.With(labels), time.Since(t))\n\t}()\n\n\t// Ensure the read lock has been removed before issuing a checkpoint.\n\t// We defer the re-acquire to ensure it occurs even on an early return.\n\tif err := db.releaseReadLock(); err != nil {\n\t\treturn 0, fmt.Errorf(\"release read lock: %w\", err)\n\t}\n\tdefer func() { _ = db.acquireReadLock(ctx) }()\n\n\t// A non-forced checkpoint is issued as \"PASSIVE\". This will only checkpoint\n\t// if there are not pending transactions. A forced checkpoint (\"RESTART\")\n\t// will wait for pending transactions to end \u0026 block new transactions before\n\t// forcing the checkpoint and restarting the WAL.\n\t//\n\t// See: https://www.sqlite.org/pragma.html#pragma_wal_checkpoint\n\trawsql := `PRAGMA wal_checkpoint(` + mode + `);`\n\n\tvar row [3]int\n\tif err := db.db.QueryRowContext(ctx, rawsql).Scan(\u0026row[0], \u0026row[1], \u0026row[2]); err != nil {\n\t\treturn 0, err\n\t}\n\tdb.Logger.Debug(\"checkpoint\", \"mode\", mode, \"result\", fmt.Sprintf(\"%d,%d,%d\", row[0], row[1], row[2]))\n\n\t// Reacquire the read lock immediately after the checkpoint.\n\tif err := db.acquireReadLock(ctx); err != nil {\n\t\treturn 0, fmt.Errorf(\"reacquire read lock: %w\", err)\n\t}\n\n\treturn row[1], nil\n}\n\ntype snapshotReadPosition struct {\n\tpos ltx.Pos\n\tpageSize int\n\twalEndOffset int64\n\tdb *DB\n\tcloseOnce sync.Once\n}\n\nfunc (p *snapshotReadPosition) close() {\n\tp.closeOnce.Do(func() { p.db.chkMu.RUnlock() })\n}\n\ntype snapshotReadCloser struct {\n\t*io.PipeReader\n\tpos *snapshotReadPosition\n}\n\nfunc (r *snapshotReadCloser) Close() error {\n\tdefer r.pos.close()\n\treturn r.PipeReader.Close()\n}\n\n// SnapshotReader returns the current position of the database \u0026 a reader that contains a full database snapshot.\n// Internal checkpoints remain disabled until the reader reaches EOF or is\n// closed. Callers must close readers they do not fully consume; abandoning a\n// reader blocks checkpoints indefinitely.\nfunc (db *DB) SnapshotReader(ctx context.Context) (ltx.Pos, io.ReadCloser, error) {\n\tpos, err := db.snapshotPosition(ctx)\n\tif err != nil {\n\t\treturn ltx.Pos{}, nil, err\n\t}\n\n\tr, err := db.snapshotReader(ctx, pos)\n\tif err != nil {\n\t\tpos.close()\n\t\treturn pos.pos, nil, err\n\t}\n\treturn pos.pos, r, nil\n}\n\nfunc (db *DB) snapshotPosition(ctx context.Context) (*snapshotReadPosition, error) {\n\tif err := db.lockExec(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tdefer db.execSem.Release(1)\n\n\tpageSize := db.PageSize()\n\tpos, err := db.Pos()\n\n\tif pageSize == 0 {\n\t\tdb.Logger.Debug(\"page size not initialized yet\", \"pageSize\", 0)\n\t\treturn nil, \u0026DBNotReadyError{Reason: \"page size not initialized\"}\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"pos: %w\", err)\n\t}\n\n\twalEndOffset, err := db.snapshotWALEndOffset(pos)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif walEndOffset \u003c WALHeaderSize {\n\t\twalEndOffset = WALHeaderSize\n\t}\n\n\t// Acquire the checkpoint read lock while the executor semaphore is still\n\t// held (the deferred release runs after this function returns). Every\n\t// checkpoint takes chkMu under execSem, so this handoff guarantees no\n\t// checkpoint can run between capturing pos and locking chkMu — the\n\t// snapshot always matches the advertised position.\n\tdb.chkMu.RLock()\n\treturn \u0026snapshotReadPosition{\n\t\tpos: pos,\n\t\tpageSize: pageSize,\n\t\twalEndOffset: walEndOffset,\n\t\tdb: db,\n\t}, nil\n}\n\n// snapshotWALEndOffset returns the WAL offset a snapshot may read up to for\n// the given position. db.syncState is read without db.mu because every writer\n// mutates it while holding execSem, which the caller also holds.\nfunc (db *DB) snapshotWALEndOffset(pos ltx.Pos) (int64, error) {\n\tif db.syncState.lastSyncedWALOffset \u003e 0 {\n\t\treturn db.syncState.lastSyncedWALOffset, nil\n\t}\n\tif pos.TXID == 0 {\n\t\treturn WALHeaderSize, nil\n\t}\n\n\tltxPath := db.LTXPath(0, pos.TXID, pos.TXID)\n\tf, err := os.Open(ltxPath)\n\tif err != nil {\n\t\treturn 0, NewLTXError(\"open\", ltxPath, 0, uint64(pos.TXID), uint64(pos.TXID), err)\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tdec := ltx.NewDecoder(f)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\treturn 0, NewLTXError(\"decode\", ltxPath, 0, uint64(pos.TXID), uint64(pos.TXID), fmt.Errorf(\"%w: %w\", ErrLTXCorrupted, err))\n\t}\n\n\t// Compare WAL headers. If the WAL was restarted since this LTX file was\n\t// written, its salts no longer match and its recorded extent does not\n\t// apply to the current WAL.\n\thdr, err := readWALHeader(db.WALPath())\n\tif os.IsNotExist(err) || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n\t\treturn WALHeaderSize, nil\n\t} else if err != nil {\n\t\treturn 0, fmt.Errorf(\"cannot read wal header: %w\", err)\n\t}\n\tsalt1 := binary.BigEndian.Uint32(hdr[16:])\n\tsalt2 := binary.BigEndian.Uint32(hdr[20:])\n\tif salt1 != dec.Header().WALSalt1 || salt2 != dec.Header().WALSalt2 {\n\t\treturn WALHeaderSize, nil\n\t}\n\n\treturn dec.Header().WALOffset + dec.Header().WALSize, nil\n}\n\nfunc (db *DB) snapshotReader(ctx context.Context, pos *snapshotReadPosition) (io.ReadCloser, error) {\n\tdb.Logger.Debug(\"snapshot\", \"txid\", pos.pos.TXID.String(), \"walEndOffset\", pos.walEndOffset)\n\n\t// TODO(ltx): Read database size from database header.\n\n\tfi, err := db.f.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcommit := uint32(fi.Size() / int64(pos.pageSize))\n\n\t// Execute encoding in a separate goroutine so the caller can initialize before reading.\n\tpr, pw := io.Pipe()\n\tgo func() {\n\t\tdefer pos.close()\n\n\t\twalFile, err := os.Open(db.WALPath())\n\t\tif err != nil {\n\t\t\tpw.CloseWithError(err)\n\t\t\treturn\n\t\t}\n\t\tdefer walFile.Close()\n\n\t\trd, err := NewWALReader(walFile, db.Logger.With(LogKeySubsystem, LogSubsystemWALReader))\n\t\tif err != nil {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"new wal reader: %w\", err))\n\t\t\treturn\n\t\t}\n\n\t\t// Build a mapping of changed page numbers and their latest content.\n\t\tmaxBytes := pos.walEndOffset - WALHeaderSize\n\t\tpageMap := make(map[uint32]int64)\n\t\tvar maxOffset int64\n\t\tvar walCommit uint32\n\t\tif maxBytes \u003e 0 {\n\t\t\tpageMap, maxOffset, walCommit, _, err = rd.pageMap(ctx, maxBytes)\n\t\t\tif err != nil {\n\t\t\t\tpw.CloseWithError(fmt.Errorf(\"page map: %w\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif walCommit \u003e 0 {\n\t\t\tcommit = walCommit\n\t\t}\n\n\t\tif maxOffset \u003e pos.walEndOffset {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"snapshot wal read exceeded bound: max offset %d \u003e end offset %d\", maxOffset, pos.walEndOffset))\n\t\t\treturn\n\t\t}\n\t\twalOffset, walSize := snapshotHeaderWALRange(maxOffset, int64(WALFrameHeaderSize+pos.pageSize))\n\n\t\tdb.Logger.Debug(\"encode snapshot header\",\n\t\t\t\"txid\", pos.pos.TXID.String(),\n\t\t\t\"commit\", commit,\n\t\t\t\"walOffset\", walOffset,\n\t\t\t\"walSize\", walSize,\n\t\t\t\"walEndOffset\", pos.walEndOffset,\n\t\t\t\"salt1\", rd.salt1,\n\t\t\t\"salt2\", rd.salt2)\n\n\t\tenc, err := ltx.NewEncoder(pw)\n\t\tif err != nil {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"new ltx encoder: %w\", err))\n\t\t\treturn\n\t\t}\n\t\tif err := enc.EncodeHeader(ltx.Header{\n\t\t\tVersion: ltx.Version,\n\t\t\tFlags: ltx.HeaderFlagNoChecksum,\n\t\t\tPageSize: uint32(pos.pageSize),\n\t\t\tCommit: commit,\n\t\t\tMinTXID: 1,\n\t\t\tMaxTXID: pos.pos.TXID,\n\t\t\tTimestamp: time.Now().UnixMilli(),\n\t\t\tWALOffset: walOffset,\n\t\t\tWALSize: walSize,\n\t\t\tWALSalt1: rd.salt1,\n\t\t\tWALSalt2: rd.salt2,\n\t\t}); err != nil {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"encode ltx snapshot header: %w\", err))\n\t\t\treturn\n\t\t}\n\n\t\tif err := db.writeLTXFromDB(ctx, enc, walFile, commit, pageMap); err != nil {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"write snapshot ltx: %w\", err))\n\t\t\treturn\n\t\t}\n\n\t\tif err := enc.Close(); err != nil {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"close ltx snapshot encoder: %w\", err))\n\t\t\treturn\n\t\t}\n\t\t_ = pw.Close()\n\t}()\n\n\treturn \u0026snapshotReadCloser{PipeReader: pr, pos: pos}, nil\n}\n\nfunc snapshotHeaderWALRange(maxOffset, frameSize int64) (offset, size int64) {\n\tif maxOffset \u003c= WALHeaderSize || frameSize \u003c= 0 {\n\t\treturn WALHeaderSize, 0\n\t}\n\toffset = max(maxOffset-frameSize, WALHeaderSize)\n\treturn offset, maxOffset - offset\n}\n\n// Compact performs a compaction of the LTX file at the previous level into dstLevel.\n// Returns metadata for the newly written compaction file. Returns ErrNoCompaction\n// if no new files are available to be compacted.\nfunc (db *DB) Compact(ctx context.Context, dstLevel int) (*ltx.FileInfo, error) {\n\tinfo, err := db.compactor.Compact(ctx, dstLevel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// If this is L1, clean up L0 files using the time-based retention policy.\n\tif dstLevel == 1 {\n\t\tif err := db.EnforceL0RetentionByTime(ctx); err != nil {\n\t\t\t// Don't log context cancellation errors during shutdown\n\t\t\tif !isShutdownError(ctx, err) {\n\t\t\t\tdb.Logger.Error(\"enforce L0 time retention\", \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn info, nil\n}\n\n// Snapshot writes a snapshot to the replica for the current database position.\n// It always writes, even when a snapshot already exists at the current\n// position, so operators can force a re-upload (replicate -force-snapshot).\n// Callers that want to skip duplicates check first, as Store.CompactDB does.\nfunc (db *DB) Snapshot(ctx context.Context) (*ltx.FileInfo, error) {\n\tpos, r, err := db.SnapshotReader(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { _ = r.Close() }()\n\n\tinfo, err := db.Replica.Client.WriteLTXFile(ctx, SnapshotLevel, 1, pos.TXID, r)\n\tif err != nil {\n\t\treturn info, err\n\t}\n\n\tdb.maxLTXFileInfos.Lock()\n\tdb.maxLTXFileInfos.m[SnapshotLevel] = info\n\tdb.maxLTXFileInfos.Unlock()\n\n\treturn info, nil\n}\n\n// EnforceSnapshotRetention enforces retention of the snapshot level in the database by timestamp.\nfunc (db *DB) EnforceSnapshotRetention(ctx context.Context, timestamp time.Time) (minSnapshotTXID ltx.TXID, err error) {\n\tdb.Logger.Debug(\"enforcing snapshot retention\", \"timestamp\", timestamp)\n\n\t// Normal operation - use fast timestamps\n\titr, err := db.Replica.Client.LTXFiles(ctx, SnapshotLevel, 0, false)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar deleted []*ltx.FileInfo\n\tvar snapshots []*ltx.FileInfo\n\tvar lastInfo *ltx.FileInfo\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tsnapshots = append(snapshots, info)\n\t\tlastInfo = info\n\n\t\t// If this snapshot is before the retention timestamp, mark it for deletion.\n\t\tif info.CreatedAt.Before(timestamp) {\n\t\t\tdeleted = append(deleted, info)\n\t\t\tcontinue\n\t\t}\n\t}\n\n\t// If this is the snapshot level, we need to ensure that at least one snapshot exists.\n\tif len(deleted) \u003e 0 \u0026\u0026 deleted[len(deleted)-1] == lastInfo {\n\t\tdeleted = deleted[:len(deleted)-1]\n\t}\n\n\t// Use the last deleted snapshot's MaxTXID, rather than the first retained\n\t// snapshot's MaxTXID, to preserve lower-level files in an in-flight restore\n\t// plan. TestStore_EnforceSnapshotRetention_RetainsInFlightRestorePlanFiles\n\t// guards this conservative floor.\n\tfor i, info := range snapshots {\n\t\tif slices.Contains(deleted, info) {\n\t\t\tcontinue\n\t\t}\n\t\tif i \u003e 0 {\n\t\t\tminSnapshotTXID = snapshots[i-1].MaxTXID\n\t\t}\n\t\tbreak\n\t}\n\n\t// Remove files marked for deletion from remote storage (unless retention disabled).\n\tif !db.RetentionEnabled {\n\t\tdb.Logger.Debug(\"skipping remote deletion (retention disabled)\", \"level\", SnapshotLevel, \"count\", len(deleted))\n\t} else if err := db.Replica.Client.DeleteLTXFiles(ctx, deleted); err != nil {\n\t\treturn 0, fmt.Errorf(\"remove ltx files: %w\", err)\n\t}\n\n\t// Always clean up local files.\n\tfor _, info := range deleted {\n\t\tlocalPath := db.LTXPath(SnapshotLevel, info.MinTXID, info.MaxTXID)\n\t\tdb.Logger.Debug(\"deleting local ltx file\", \"level\", SnapshotLevel, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"path\", localPath)\n\n\t\tif err := os.Remove(localPath); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\tdb.Logger.Error(\"failed to remove local ltx file\", \"path\", localPath, \"error\", err)\n\t\t}\n\t}\n\n\treturn minSnapshotTXID, nil\n}\n\n// EnforceL0RetentionByTime retains L0 files until they have been compacted into\n// L1 and have existed for at least L0Retention.\nfunc (db *DB) EnforceL0RetentionByTime(ctx context.Context) error {\n\tif db.L0Retention \u003c= 0 {\n\t\treturn nil\n\t}\n\n\tdb.Logger.Debug(\"starting l0 retention enforcement\", \"retention\", db.L0Retention)\n\n\tdbName := filepath.Base(db.Path())\n\n\t// Determine the highest TXID that has been compacted into L1.\n\titr, err := db.Replica.Client.LTXFiles(ctx, 1, 0, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch l1 files: %w\", err)\n\t}\n\tvar maxL1TXID ltx.TXID\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tif info.MaxTXID \u003e maxL1TXID {\n\t\t\tmaxL1TXID = info.MaxTXID\n\t\t}\n\t}\n\tif err := itr.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close l1 iterator: %w\", err)\n\t}\n\tif maxL1TXID == 0 {\n\t\tinternal.L0RetentionGaugeVec.WithLabelValues(dbName, \"eligible\").Set(0)\n\t\tinternal.L0RetentionGaugeVec.WithLabelValues(dbName, \"not_compacted\").Set(0)\n\t\tinternal.L0RetentionGaugeVec.WithLabelValues(dbName, \"too_recent\").Set(0)\n\t\treturn nil\n\t}\n\n\tthreshold := time.Now().Add(-db.L0Retention)\n\titr, err = db.Replica.Client.LTXFiles(ctx, 0, 0, false)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch l0 files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar (\n\t\tdeleted []*ltx.FileInfo\n\t\tlastInfo *ltx.FileInfo\n\t\tprocessedAll = true\n\t\ttotalFiles int\n\t\tnotCompactedCount int\n\t\ttooRecentCount int\n\t)\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tlastInfo = info\n\t\ttotalFiles++\n\n\t\tcreatedAt := info.CreatedAt\n\t\tif createdAt.IsZero() {\n\t\t\tif fi, err := os.Stat(db.LTXPath(0, info.MinTXID, info.MaxTXID)); err == nil {\n\t\t\t\tcreatedAt = fi.ModTime().UTC()\n\t\t\t} else {\n\t\t\t\tcreatedAt = threshold\n\t\t\t}\n\t\t}\n\n\t\tif createdAt.After(threshold) {\n\t\t\t// L0 entries are ordered; once we reach a newer file we stop so we don't\n\t\t\t// create gaps between retained files. VFS expects contiguous coverage.\n\t\t\tprocessedAll = false\n\t\t\ttooRecentCount++\n\t\t\tbreak\n\t\t}\n\n\t\tif info.MaxTXID \u003c= maxL1TXID {\n\t\t\tdeleted = append(deleted, info)\n\t\t} else {\n\t\t\tnotCompactedCount++\n\t\t}\n\t}\n\n\t// Count remaining files as too_recent if we stopped early\n\tif !processedAll {\n\t\tfor itr.Next() {\n\t\t\ttooRecentCount++\n\t\t}\n\t}\n\n\t// Ensure we do not delete the newest L0 file only if we processed the entire level.\n\tif processedAll \u0026\u0026 len(deleted) \u003e 0 \u0026\u0026 lastInfo != nil \u0026\u0026 deleted[len(deleted)-1] == lastInfo {\n\t\tdeleted = deleted[:len(deleted)-1]\n\t}\n\n\tinternal.L0RetentionGaugeVec.WithLabelValues(dbName, \"eligible\").Set(float64(len(deleted)))\n\tinternal.L0RetentionGaugeVec.WithLabelValues(dbName, \"not_compacted\").Set(float64(notCompactedCount))\n\tinternal.L0RetentionGaugeVec.WithLabelValues(dbName, \"too_recent\").Set(float64(tooRecentCount))\n\n\tdb.Logger.Debug(\"l0 retention scan complete\",\n\t\t\"total_l0_files\", totalFiles,\n\t\t\"eligible_for_deletion\", len(deleted),\n\t\t\"not_compacted_yet\", notCompactedCount,\n\t\t\"too_recent\", tooRecentCount,\n\t\t\"max_l1_txid\", maxL1TXID)\n\n\tif len(deleted) == 0 {\n\t\treturn nil\n\t}\n\n\tif !db.RetentionEnabled {\n\t\tdb.Logger.Debug(\"skipping remote deletion (retention disabled)\", \"level\", 0, \"count\", len(deleted))\n\t} else if err := db.Replica.Client.DeleteLTXFiles(ctx, deleted); err != nil {\n\t\treturn fmt.Errorf(\"remove expired l0 files: %w\", err)\n\t}\n\n\tfor _, info := range deleted {\n\t\tlocalPath := db.LTXPath(0, info.MinTXID, info.MaxTXID)\n\t\tdb.Logger.Debug(\"deleting expired local l0 file\", \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"path\", localPath)\n\t\tif err := os.Remove(localPath); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\tdb.Logger.Error(\"failed to remove local l0 file\", \"path\", localPath, \"error\", err)\n\t\t}\n\t}\n\tif len(deleted) \u003e 0 {\n\t\tdb.invalidatePosCache()\n\t}\n\n\tdb.Logger.Info(\"l0 retention enforced\", \"deleted_count\", len(deleted), \"max_l1_txid\", maxL1TXID)\n\n\treturn nil\n}\n\n// EnforceRetentionByTXID enforces retention so that any LTX files below\n// the target TXID are deleted. Always keep at least one file.\nfunc (db *DB) EnforceRetentionByTXID(ctx context.Context, level int, txID ltx.TXID) (err error) {\n\treturn db.compactor.EnforceRetentionByTXID(ctx, level, txID)\n}\n\n// monitor runs in a separate goroutine and monitors the database \u0026 WAL.\n//\n// Implements exponential backoff on repeated sync errors to prevent disk churn\n// when persistent errors (like disk full) occur. See issue #927.\nfunc (db *DB) monitor() {\n\tticker := time.NewTicker(db.MonitorInterval)\n\tdefer ticker.Stop()\n\n\t// Backoff state for error handling.\n\tvar backoff time.Duration\n\tvar lastLogTime time.Time\n\tvar consecutiveErrs int\n\n\tfor {\n\t\t// Wait for ticker or context close.\n\t\tselect {\n\t\tcase \u003c-db.ctx.Done():\n\t\t\treturn\n\t\tcase \u003c-ticker.C:\n\t\t}\n\n\t\t// If in backoff mode, wait additional time before retrying.\n\t\tif backoff \u003e 0 {\n\t\t\tselect {\n\t\t\tcase \u003c-db.ctx.Done():\n\t\t\t\treturn\n\t\t\tcase \u003c-time.After(backoff):\n\t\t\t}\n\t\t}\n\n\t\t// Sync the database to the shadow WAL. Sync() loops over bounded\n\t\t// chunks, releasing the executor lock between each so checkpoints\n\t\t// and snapshots can interleave, but always catches up to the WAL\n\t\t// end so checkpointIfNeeded() runs. A single bounded chunk per\n\t\t// tick would cap drain throughput and starve the TruncatePageN\n\t\t// emergency checkpoint while behind, growing the WAL unbounded.\n\t\tif err := db.Sync(db.ctx); err != nil \u0026\u0026 !isShutdownError(db.ctx, err) {\n\t\t\tconsecutiveErrs++\n\n\t\t\t// Exponential backoff: MonitorInterval -\u003e 2x -\u003e 4x -\u003e ... -\u003e max\n\t\t\tif backoff == 0 {\n\t\t\t\tbackoff = db.MonitorInterval\n\t\t\t} else {\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff \u003e DefaultSyncBackoffMax {\n\t\t\t\t\tbackoff = DefaultSyncBackoffMax\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Log with rate limiting to avoid log spam during persistent errors.\n\t\t\tif time.Since(lastLogTime) \u003e= SyncErrorLogInterval {\n\t\t\t\tvar ltxErr *LTXError\n\t\t\t\tif errors.Is(err, ErrDiskFull) \u0026\u0026 errors.As(err, \u0026ltxErr) {\n\t\t\t\t\t// Recovery is automatic but can lag up to max_backoff\n\t\t\t\t\t// behind space being freed.\n\t\t\t\t\tdb.Logger.Error(\"disk full while staging ltx file, replication paused until space is freed\",\n\t\t\t\t\t\t\"error\", err,\n\t\t\t\t\t\t\"path\", ltxErr.Path,\n\t\t\t\t\t\t\"txid\", ltx.TXID(ltxErr.MaxTXID).String(),\n\t\t\t\t\t\t\"consecutive_errors\", consecutiveErrs,\n\t\t\t\t\t\t\"backoff\", backoff,\n\t\t\t\t\t\t\"max_backoff\", DefaultSyncBackoffMax)\n\t\t\t\t} else {\n\t\t\t\t\tdb.Logger.Error(\"sync error\",\n\t\t\t\t\t\t\"error\", err,\n\t\t\t\t\t\t\"consecutive_errors\", consecutiveErrs,\n\t\t\t\t\t\t\"backoff\", backoff)\n\t\t\t\t}\n\t\t\t\tlastLogTime = time.Now()\n\t\t\t}\n\n\t\t\t// Try to clean up stale temp files after persistent disk errors.\n\t\t\tif isDiskFullError(err) \u0026\u0026 consecutiveErrs \u003e= 3 {\n\t\t\t\tdb.Logger.Warn(\"attempting temp file cleanup due to persistent disk errors\")\n\t\t\t\tif cleanupErr := removeTmpFiles(db.metaPath); cleanupErr != nil {\n\t\t\t\t\tdb.Logger.Error(\"temp file cleanup failed\", \"error\", cleanupErr)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Success - reset backoff and error counter.\n\t\tif consecutiveErrs \u003e 0 {\n\t\t\tdb.Logger.Info(\"sync recovered\", \"previous_errors\", consecutiveErrs)\n\t\t}\n\t\tbackoff = 0\n\t\tconsecutiveErrs = 0\n\t}\n}\n\n// CRC64 returns a CRC-64 ISO checksum of the database and its current position.\n//\n// This function obtains a read lock so it prevents syncs from occurring until\n// the operation is complete. The database will still be usable but it will be\n// unable to checkpoint during this time.\n//\n// If dst is set, the database file is copied to that location before checksum.\nfunc (db *DB) CRC64(ctx context.Context) (uint64, ltx.Pos, error) {\n\tif err := db.lockExec(ctx); err != nil {\n\t\treturn 0, ltx.Pos{}, err\n\t}\n\tdefer db.execSem.Release(1)\n\n\texec, err := db.newSyncExecutor(ctx)\n\tif err != nil {\n\t\treturn 0, ltx.Pos{}, err\n\t} else if exec == nil {\n\t\treturn 0, ltx.Pos{}, os.ErrNotExist\n\t}\n\tdefer db.applySyncExecutor(exec, true)\n\n\t// Force a RESTART checkpoint to ensure the database is at the start of the WAL.\n\tif _, err := db.checkpointWithExecutor(ctx, CheckpointModeRestart, exec); err != nil {\n\t\treturn 0, ltx.Pos{}, err\n\t}\n\n\t// Seek to the beginning of the db file descriptor and checksum whole file.\n\th := crc64.New(crc64.MakeTable(crc64.ISO))\n\tif _, err := db.f.Seek(0, io.SeekStart); err != nil {\n\t\treturn 0, exec.pos, err\n\t} else if _, err := io.Copy(h, db.f); err != nil {\n\t\treturn 0, exec.pos, err\n\t}\n\treturn h.Sum64(), exec.pos, nil\n}\n\n// MaxLTXFileInfo returns the metadata for the last LTX file in a level.\n// If cached, it will returned the local copy. Otherwise, it fetches from the replica.\nfunc (db *DB) MaxLTXFileInfo(ctx context.Context, level int) (ltx.FileInfo, error) {\n\tdb.maxLTXFileInfos.Lock()\n\tdefer db.maxLTXFileInfos.Unlock()\n\n\tinfo, ok := db.maxLTXFileInfos.m[level]\n\tif ok {\n\t\treturn *info, nil\n\t}\n\n\tremoteInfo, err := db.Replica.MaxLTXFileInfo(ctx, level)\n\tif err != nil {\n\t\treturn ltx.FileInfo{}, fmt.Errorf(\"cannot determine L%d max ltx file for %q: %w\", level, db.Path(), err)\n\t}\n\n\tdb.maxLTXFileInfos.m[level] = \u0026remoteInfo\n\treturn remoteInfo, nil\n}\n\n// DefaultRestoreParallelism is the default parallelism when downloading WAL files.\nconst DefaultRestoreParallelism = 8\n\n// DefaultFollowInterval is the default polling interval for follow mode.\nconst DefaultFollowInterval = 1 * time.Second\n\n// IntegrityCheckMode specifies the level of integrity checking after restore.\ntype IntegrityCheckMode int\n\nconst (\n\tIntegrityCheckNone IntegrityCheckMode = iota\n\tIntegrityCheckQuick\n\tIntegrityCheckFull\n)\n\n// RestoreOptions represents options for DB.Restore().\ntype RestoreOptions struct {\n\t// Target path to restore into.\n\t// If blank, the original DB path is used.\n\tOutputPath string\n\n\t// Specific transaction to restore to.\n\t// If zero, TXID is ignored.\n\tTXID ltx.TXID\n\n\t// Point-in-time to restore database.\n\t// If zero, database restore to most recent state available.\n\tTimestamp time.Time\n\n\t// Specifies how many WAL files are downloaded in parallel during restore.\n\tParallelism int\n\n\t// Follow enables continuous restore mode, polling for new LTX files\n\t// and applying them to the restored database. Similar to tail -f.\n\tFollow bool\n\n\t// FollowInterval specifies how often to poll for new LTX files in follow mode.\n\tFollowInterval time.Duration\n\n\t// IntegrityCheck specifies the level of integrity checking after restore.\n\t// Zero value (IntegrityCheckNone) skips the check for backward compatibility.\n\tIntegrityCheck IntegrityCheckMode\n}\n\n// NewRestoreOptions returns a new instance of RestoreOptions with defaults.\nfunc NewRestoreOptions() RestoreOptions {\n\treturn RestoreOptions{\n\t\tParallelism: DefaultRestoreParallelism,\n\t\tFollowInterval: DefaultFollowInterval,\n\t}\n}\n\n// Database metrics.\nvar (\n\tdbMetricOwners = struct {\n\t\tsync.Mutex\n\t\tpaths map[string]int\n\t\tbasenames map[string]int\n\t}{\n\t\tpaths: make(map[string]int),\n\t\tbasenames: make(map[string]int),\n\t}\n\n\tdbSizeGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_db_size\",\n\t\tHelp: \"The current size of the real DB\",\n\t}, []string{\"db\"})\n\n\twalSizeGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_wal_size\",\n\t\tHelp: \"The current size of the real WAL\",\n\t}, []string{\"db\"})\n\n\ttotalWALBytesCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_total_wal_bytes\",\n\t\tHelp: \"Total number of bytes written to shadow WAL\",\n\t}, []string{\"db\"})\n\n\ttxIDIndexGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_txid\",\n\t\tHelp: \"The current transaction ID\",\n\t}, []string{\"db\"})\n\n\tsyncNCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_sync_count\",\n\t\tHelp: \"Number of sync operations performed\",\n\t}, []string{\"db\"})\n\n\tsyncErrorNCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_sync_error_count\",\n\t\tHelp: \"Number of sync errors that have occurred\",\n\t}, []string{\"db\"})\n\n\tsyncSecondsCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_sync_seconds\",\n\t\tHelp: \"Time spent syncing shadow WAL, in seconds\",\n\t}, []string{\"db\"})\n\n\tdiskFullGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_disk_full\",\n\t\tHelp: \"Whether replication is paused because the local disk is full\",\n\t}, []string{\"db\"})\n\n\tcheckpointNCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_checkpoint_count\",\n\t\tHelp: \"Number of checkpoint operations performed\",\n\t}, []string{\"db\", \"mode\"})\n\n\tcheckpointErrorNCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_checkpoint_error_count\",\n\t\tHelp: \"Number of checkpoint errors that have occurred\",\n\t}, []string{\"db\", \"mode\"})\n\n\tcheckpointSecondsCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_checkpoint_seconds\",\n\t\tHelp: \"Time spent checkpointing WAL, in seconds\",\n\t}, []string{\"db\", \"mode\"})\n\n\tcompactionVerifyErrorCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_compaction_verify_error_count\",\n\t\tHelp: \"Number of post-compaction verification failures\",\n\t}, []string{\"db\"})\n)\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[29,37]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[38,48]}},{"name":"sql","path":"database/sql","span":{"start":[6,1],"end":[6,16],"bytes":[49,64]}},{"name":"binary","path":"encoding/binary","span":{"start":[7,1],"end":[7,19],"bytes":[65,83]}},{"name":"errors","path":"errors","span":{"start":[8,1],"end":[8,10],"bytes":[84,93]}},{"name":"fmt","path":"fmt","span":{"start":[9,1],"end":[9,7],"bytes":[94,100]}},{"name":"crc64","path":"hash/crc64","span":{"start":[10,1],"end":[10,14],"bytes":[101,114]}},{"name":"io","path":"io","span":{"start":[11,1],"end":[11,6],"bytes":[115,120]}},{"name":"slog","path":"log/slog","span":{"start":[12,1],"end":[12,12],"bytes":[121,132]}},{"name":"os","path":"os","span":{"start":[13,1],"end":[13,6],"bytes":[133,138]}},{"name":"filepath","path":"path/filepath","span":{"start":[14,1],"end":[14,17],"bytes":[139,155]}},{"name":"slices","path":"slices","span":{"start":[15,1],"end":[15,10],"bytes":[156,165]}},{"name":"strconv","path":"strconv","span":{"start":[16,1],"end":[16,11],"bytes":[166,176]}},{"name":"strings","path":"strings","span":{"start":[17,1],"end":[17,11],"bytes":[177,187]}},{"name":"sync","path":"sync","span":{"start":[18,1],"end":[18,8],"bytes":[188,195]}},{"name":"syscall","path":"syscall","span":{"start":[19,1],"end":[19,11],"bytes":[196,206]}},{"name":"time","path":"time","span":{"start":[20,1],"end":[20,8],"bytes":[207,214]}},{"name":"prometheus","path":"github.com/prometheus/client_golang/prometheus","span":{"start":[22,1],"end":[22,50],"bytes":[216,265]}},{"name":"promauto","path":"github.com/prometheus/client_golang/prometheus/promauto","span":{"start":[23,1],"end":[23,59],"bytes":[266,324]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[24,1],"end":[24,27],"bytes":[325,351]}},{"name":"semaphore","path":"golang.org/x/sync/semaphore","span":{"start":[25,1],"end":[25,31],"bytes":[352,382]}},{"name":"sqlite","path":"modernc.org/sqlite","span":{"start":[26,1],"end":[26,22],"bytes":[383,404]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[28,1],"end":[28,46],"bytes":[406,451]}}],"types":{"DB":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB","kind":"struct","span":{"start":[67,1],"end":[193,2],"bytes":[2132,6925]},"callables":{"github.com/benbjohnson/litestream.DB.CRC64":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.CRC64","kind":"method","span":{"start":[3320,1],"end":[3347,2],"bytes":[106358,107235]},"signature":"github.com/benbjohnson/litestream.DB.CRC64","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[3320,1],"end":[3320,68],"bytes":[106358,106425]}}],"return_type":"(uint64, github.com/superfly/ltx.Pos, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"3321:12":{"kind":"call","span":{"start":[3321,12],"end":[3321,28],"bytes":[106437,106453]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec"},"3324:8":{"kind":"call","span":{"start":[3324,8],"end":[3324,29],"bytes":[106505,106526]},"callee":null},"3326:15":{"kind":"call","span":{"start":[3326,15],"end":[3326,38],"bytes":[106542,106565]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor"},"3332:8":{"kind":"call","span":{"start":[3332,8],"end":[3332,40],"bytes":[106683,106715]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor"},"3335:15":{"kind":"call","span":{"start":[3335,15],"end":[3335,74],"bytes":[106813,106872]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"},"3340:17":{"kind":"call","span":{"start":[3340,17],"end":[3340,43],"bytes":[107011,107037]},"callee":null},"3340:7":{"kind":"call","span":{"start":[3340,7],"end":[3340,44],"bytes":[107001,107038]},"callee":null},"3341:15":{"kind":"call","span":{"start":[3341,15],"end":[3341,41],"bytes":[107053,107079]},"callee":null},"3343:22":{"kind":"call","span":{"start":[3343,22],"end":[3343,38],"bytes":[107141,107157]},"callee":null},"3346:9":{"kind":"call","span":{"start":[3346,9],"end":[3346,18],"bytes":[107209,107218]},"callee":null}}},"github.com/benbjohnson/litestream.DB.Checkpoint":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint","kind":"method","span":{"start":[2469,1],"end":[2487,2],"bytes":[79355,79811]},"signature":"github.com/benbjohnson/litestream.DB.Checkpoint","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2469,1],"end":[2469,73],"bytes":[79355,79427]}},{"name":"mode","type":"string","span":{"start":[2469,1],"end":[2469,73],"bytes":[79355,79427]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"2470:12":{"kind":"call","span":{"start":[2470,12],"end":[2470,28],"bytes":[79439,79455]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec"},"2473:8":{"kind":"call","span":{"start":[2473,8],"end":[2473,29],"bytes":[79493,79514]},"callee":null},"2474:2":{"kind":"call","span":{"start":[2474,2],"end":[2474,36],"bytes":[79516,79550]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncDiag"},"2475:8":{"kind":"call","span":{"start":[2475,8],"end":[2475,43],"bytes":[79558,79593]},"callee":null},"2477:15":{"kind":"call","span":{"start":[2477,15],"end":[2477,38],"bytes":[79609,79632]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor"},"2483:8":{"kind":"call","span":{"start":[2483,8],"end":[2483,40],"bytes":[79711,79743]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor"},"2485:11":{"kind":"call","span":{"start":[2485,11],"end":[2485,53],"bytes":[79755,79797]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint/github.com/benbjohnson/litestream.DB.Checkpoint.closure_1","kind":"function","span":{"start":[2475,1],"end":[2475,43],"bytes":[79551,79593]},"signature":"github.com/benbjohnson/litestream.DB.Checkpoint.closure_1","parameters":[],"body":{"2475:17":{"kind":"call","span":{"start":[2475,17],"end":[2475,39],"bytes":[79567,79589]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.Close":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","kind":"method","span":{"start":[874,1],"end":[938,2],"bytes":[29441,30885]},"signature":"github.com/benbjohnson/litestream.DB.Close","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[874,1],"end":[874,55],"bytes":[29441,29495]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"875:2":{"kind":"call","span":{"start":[875,2],"end":[875,13],"bytes":[29497,29508]},"callee":null},"876:2":{"kind":"call","span":{"start":[876,2],"end":[876,14],"bytes":[29510,29522]},"callee":null},"883:12":{"kind":"call","span":{"start":[883,12],"end":[883,61],"bytes":[29838,29887]},"callee":null},"883:31":{"kind":"call","span":{"start":[883,31],"end":[883,57],"bytes":[29857,29883]},"callee":null},"886:8":{"kind":"call","span":{"start":[886,8],"end":[886,29],"bytes":[29925,29946]},"callee":null},"890:14":{"kind":"call","span":{"start":[890,14],"end":[890,35],"bytes":[30025,30046]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked"},"898:12":{"kind":"call","span":{"start":[898,12],"end":[898,40],"bytes":[30196,30224]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncReplicaWithRetry"},"902:3":{"kind":"call","span":{"start":[902,3],"end":[902,24],"bytes":[30274,30295]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Stop"},"907:11":{"kind":"call","span":{"start":[907,11],"end":[907,31],"bytes":[30409,30429]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.releaseReadLock"},"912:2":{"kind":"call","span":{"start":[912,2],"end":[912,14],"bytes":[30476,30488]},"callee":null},"919:2":{"kind":"call","span":{"start":[919,2],"end":[919,16],"bytes":[30575,30589]},"callee":null},"922:11":{"kind":"call","span":{"start":[922,11],"end":[922,24],"bytes":[30620,30633]},"callee":null},"928:11":{"kind":"call","span":{"start":[928,11],"end":[928,20],"bytes":[30704,30713]},"callee":null},"935:2":{"kind":"call","span":{"start":[935,2],"end":[935,20],"bytes":[30852,30870]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.deleteMetrics"}}},"github.com/benbjohnson/litestream.DB.Compact":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Compact","kind":"method","span":{"start":[2984,1],"end":[3001,2],"bytes":[95536,96052]},"signature":"github.com/benbjohnson/litestream.DB.Compact","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2984,1],"end":[2984,82],"bytes":[95536,95617]}},{"name":"dstLevel","type":"int","span":{"start":[2984,1],"end":[2984,82],"bytes":[95536,95617]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"2985:15":{"kind":"call","span":{"start":[2985,15],"end":[2985,50],"bytes":[95632,95667]},"callee":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact"},"2992:13":{"kind":"call","span":{"start":[2992,13],"end":[2992,45],"bytes":[95815,95847]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime"},"2994:8":{"kind":"call","span":{"start":[2994,8],"end":[2994,33],"bytes":[95929,95954]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"2995:5":{"kind":"call","span":{"start":[2995,5],"end":[2995,63],"bytes":[95961,96019]},"callee":null}}},"github.com/benbjohnson/litestream.DB.DirInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.DirInfo","kind":"method","span":{"start":[657,1],"end":[659,2],"bytes":[23069,23127]},"signature":"github.com/benbjohnson/litestream.DB.DirInfo","parameters":[],"return_type":"os.FileInfo","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","kind":"method","span":{"start":[3093,1],"end":[3215,2],"bytes":[99222,102862]},"signature":"github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[3093,1],"end":[3093,68],"bytes":[99222,99289]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":22},"body":{"3098:2":{"kind":"call","span":{"start":[3098,2],"end":[3098,83],"bytes":[99334,99415]},"callee":null},"3100:12":{"kind":"call","span":{"start":[3100,12],"end":[3100,36],"bytes":[99428,99452]},"callee":null},"3100:26":{"kind":"call","span":{"start":[3100,26],"end":[3100,35],"bytes":[99442,99451]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"3103:14":{"kind":"call","span":{"start":[3103,14],"end":[3103,58],"bytes":[99531,99575]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"3105:10":{"kind":"call","span":{"start":[3105,10],"end":[3105,47],"bytes":[99602,99639]},"callee":null},"3108:6":{"kind":"call","span":{"start":[3108,6],"end":[3108,16],"bytes":[99672,99682]},"callee":null},"3109:11":{"kind":"call","span":{"start":[3109,11],"end":[3109,21],"bytes":[99695,99705]},"callee":null},"3114:12":{"kind":"call","span":{"start":[3114,12],"end":[3114,23],"bytes":[99784,99795]},"callee":null},"3115:10":{"kind":"call","span":{"start":[3115,10],"end":[3115,50],"bytes":[99819,99859]},"callee":null},"3118:3":{"kind":"call","span":{"start":[3118,3],"end":[3118,67],"bytes":[99886,99950]},"callee":null},"3119:3":{"kind":"call","span":{"start":[3119,3],"end":[3119,72],"bytes":[99960,100029]},"callee":null},"3120:3":{"kind":"call","span":{"start":[3120,3],"end":[3120,69],"bytes":[100039,100105]},"callee":null},"3124:15":{"kind":"call","span":{"start":[3124,15],"end":[3124,25],"bytes":[100144,100154]},"callee":null},"3125:13":{"kind":"call","span":{"start":[3125,13],"end":[3125,57],"bytes":[100188,100232]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"3127:10":{"kind":"call","span":{"start":[3127,10],"end":[3127,47],"bytes":[100259,100296]},"callee":null},"3129:8":{"kind":"call","span":{"start":[3129,8],"end":[3129,19],"bytes":[100307,100318]},"callee":null},"3139:6":{"kind":"call","span":{"start":[3139,6],"end":[3139,16],"bytes":[100504,100514]},"callee":null},"3140:11":{"kind":"call","span":{"start":[3140,11],"end":[3140,21],"bytes":[100527,100537]},"callee":null},"3145:6":{"kind":"call","span":{"start":[3145,6],"end":[3145,24],"bytes":[100607,100625]},"callee":null},"3146:18":{"kind":"call","span":{"start":[3146,18],"end":[3146,68],"bytes":[100645,100695]},"callee":null},"3146:26":{"kind":"call","span":{"start":[3146,26],"end":[3146,67],"bytes":[100653,100694]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"3147:17":{"kind":"call","span":{"start":[3147,17],"end":[3147,29],"bytes":[100726,100738]},"callee":null},"3153:6":{"kind":"call","span":{"start":[3153,6],"end":[3153,32],"bytes":[100798,100824]},"callee":null},"3162:14":{"kind":"call","span":{"start":[3162,14],"end":[3162,35],"bytes":[101083,101104]},"callee":null},"3170:7":{"kind":"call","span":{"start":[3170,7],"end":[3170,17],"bytes":[101233,101243]},"callee":null},"3176:21":{"kind":"call","span":{"start":[3176,21],"end":[3176,33],"bytes":[101380,101392]},"callee":null},"3176:68":{"kind":"call","span":{"start":[3176,68],"end":[3176,80],"bytes":[101427,101439]},"callee":null},"3177:22":{"kind":"call","span":{"start":[3177,22],"end":[3177,34],"bytes":[101478,101490]},"callee":null},"3180:2":{"kind":"call","span":{"start":[3180,2],"end":[3180,66],"bytes":[101499,101563]},"callee":null},"3180:71":{"kind":"call","span":{"start":[3180,71],"end":[3180,92],"bytes":[101568,101589]},"callee":null},"3180:79":{"kind":"call","span":{"start":[3180,79],"end":[3180,91],"bytes":[101576,101588]},"callee":null},"3181:2":{"kind":"call","span":{"start":[3181,2],"end":[3181,71],"bytes":[101592,101661]},"callee":null},"3181:76":{"kind":"call","span":{"start":[3181,76],"end":[3181,102],"bytes":[101666,101692]},"callee":null},"3182:2":{"kind":"call","span":{"start":[3182,2],"end":[3182,68],"bytes":[101695,101761]},"callee":null},"3182:73":{"kind":"call","span":{"start":[3182,73],"end":[3182,96],"bytes":[101766,101789]},"callee":null},"3184:2":{"kind":"call","span":{"start":[3184,2],"end":[3189,28],"bytes":[101793,102013]},"callee":null},"3186:28":{"kind":"call","span":{"start":[3186,28],"end":[3186,40],"bytes":[101898,101910]},"callee":null},"3191:5":{"kind":"call","span":{"start":[3191,5],"end":[3191,17],"bytes":[102019,102031]},"callee":null},"3196:3":{"kind":"call","span":{"start":[3196,3],"end":[3196,102],"bytes":[102085,102184]},"callee":null},"3196:89":{"kind":"call","span":{"start":[3196,89],"end":[3196,101],"bytes":[102171,102183]},"callee":null},"3197:19":{"kind":"call","span":{"start":[3197,19],"end":[3197,65],"bytes":[102203,102249]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles"},"3198:10":{"kind":"call","span":{"start":[3198,10],"end":[3198,56],"bytes":[102273,102319]},"callee":null},"3202:16":{"kind":"call","span":{"start":[3202,16],"end":[3202,57],"bytes":[102371,102412]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"3203:3":{"kind":"call","span":{"start":[3203,3],"end":[3203,121],"bytes":[102415,102533]},"callee":null},"3204:13":{"kind":"call","span":{"start":[3204,13],"end":[3204,33],"bytes":[102546,102566]},"callee":null},"3204:50":{"kind":"call","span":{"start":[3204,50],"end":[3204,68],"bytes":[102583,102601]},"callee":null},"3205:4":{"kind":"call","span":{"start":[3205,4],"end":[3205,86],"bytes":[102607,102689]},"callee":null},"3208:5":{"kind":"call","span":{"start":[3208,5],"end":[3208,17],"bytes":[102701,102713]},"callee":null},"3209:3":{"kind":"call","span":{"start":[3209,3],"end":[3209,26],"bytes":[102722,102745]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"},"3212:2":{"kind":"call","span":{"start":[3212,2],"end":[3212,98],"bytes":[102751,102847]},"callee":null},"3212:59":{"kind":"call","span":{"start":[3212,59],"end":[3212,71],"bytes":[102808,102820]},"callee":null}}},"github.com/benbjohnson/litestream.DB.EnforceRetentionByTXID":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceRetentionByTXID","kind":"method","span":{"start":[3219,1],"end":[3221,2],"bytes":[103000,103161]},"signature":"github.com/benbjohnson/litestream.DB.EnforceRetentionByTXID","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[3219,1],"end":[3219,98],"bytes":[103000,103097]}},{"name":"level","type":"int","span":{"start":[3219,1],"end":[3219,98],"bytes":[103000,103097]}},{"name":"txID","type":"github.com/superfly/ltx.TXID","span":{"start":[3219,1],"end":[3219,98],"bytes":[103000,103097]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"3220:9":{"kind":"call","span":{"start":[3220,9],"end":[3220,62],"bytes":[103106,103159]},"callee":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID"}}},"github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention","kind":"method","span":{"start":[3027,1],"end":[3089,2],"bytes":[96884,99090]},"signature":"github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[3027,1],"end":[3027,121],"bytes":[96884,97004]}},{"name":"timestamp","type":"time.Time","span":{"start":[3027,1],"end":[3027,121],"bytes":[96884,97004]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"3028:2":{"kind":"call","span":{"start":[3028,2],"end":[3028,73],"bytes":[97006,97077]},"callee":null},"3031:14":{"kind":"call","span":{"start":[3031,14],"end":[3031,70],"bytes":[97135,97191]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"3033:13":{"kind":"call","span":{"start":[3033,13],"end":[3033,51],"bytes":[97221,97259]},"callee":null},"3035:8":{"kind":"call","span":{"start":[3035,8],"end":[3035,19],"bytes":[97270,97281]},"callee":null},"3040:6":{"kind":"call","span":{"start":[3040,6],"end":[3040,16],"bytes":[97376,97386]},"callee":null},"3041:11":{"kind":"call","span":{"start":[3041,11],"end":[3041,21],"bytes":[97399,97409]},"callee":null},"3042:15":{"kind":"call","span":{"start":[3042,15],"end":[3042,38],"bytes":[97424,97447]},"callee":null},"3046:6":{"kind":"call","span":{"start":[3046,6],"end":[3046,38],"bytes":[97551,97583]},"callee":null},"3047:14":{"kind":"call","span":{"start":[3047,14],"end":[3047,35],"bytes":[97599,97620]},"callee":null},"3053:33":{"kind":"call","span":{"start":[3053,33],"end":[3053,45],"bytes":[97761,97773]},"callee":null},"3053:5":{"kind":"call","span":{"start":[3053,5],"end":[3053,17],"bytes":[97733,97745]},"callee":null},"3054:22":{"kind":"call","span":{"start":[3054,22],"end":[3054,34],"bytes":[97812,97824]},"callee":null},"3062:6":{"kind":"call","span":{"start":[3062,6],"end":[3062,36],"bytes":[98138,98168]},"callee":null},"3073:101":{"kind":"call","span":{"start":[3073,101],"end":[3073,113],"bytes":[98473,98485]},"callee":null},"3073:3":{"kind":"call","span":{"start":[3073,3],"end":[3073,114],"bytes":[98375,98486]},"callee":null},"3074:19":{"kind":"call","span":{"start":[3074,19],"end":[3074,65],"bytes":[98505,98551]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles"},"3075:13":{"kind":"call","span":{"start":[3075,13],"end":[3075,52],"bytes":[98578,98617]},"callee":null},"3080:16":{"kind":"call","span":{"start":[3080,16],"end":[3080,69],"bytes":[98702,98755]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"3081:3":{"kind":"call","span":{"start":[3081,3],"end":[3081,138],"bytes":[98758,98893]},"callee":null},"3083:13":{"kind":"call","span":{"start":[3083,13],"end":[3083,33],"bytes":[98907,98927]},"callee":null},"3083:50":{"kind":"call","span":{"start":[3083,50],"end":[3083,68],"bytes":[98944,98962]},"callee":null},"3084:4":{"kind":"call","span":{"start":[3084,4],"end":[3084,87],"bytes":[98968,99051]},"callee":null}}},"github.com/benbjohnson/litestream.DB.EnsureExists":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnsureExists","kind":"method","span":{"start":[786,1],"end":[820,2],"bytes":[26853,27824]},"signature":"github.com/benbjohnson/litestream.DB.EnsureExists","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[786,1],"end":[786,56],"bytes":[26853,26908]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"788:10":{"kind":"call","span":{"start":[788,10],"end":[788,45],"bytes":[26942,26977]},"callee":null},"791:10":{"kind":"call","span":{"start":[791,10],"end":[791,52],"bytes":[27021,27063]},"callee":null},"794:15":{"kind":"call","span":{"start":[794,15],"end":[794,33],"bytes":[27082,27100]},"callee":null},"794:23":{"kind":"call","span":{"start":[794,23],"end":[794,32],"bytes":[27090,27099]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"796:13":{"kind":"call","span":{"start":[796,13],"end":[796,31],"bytes":[27140,27158]},"callee":null},"797:10":{"kind":"call","span":{"start":[797,10],"end":[797,46],"bytes":[27170,27206]},"callee":null},"800:12":{"kind":"call","span":{"start":[800,12],"end":[800,35],"bytes":[27222,27245]},"callee":null},"800:25":{"kind":"call","span":{"start":[800,25],"end":[800,34],"bytes":[27235,27244]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"801:13":{"kind":"call","span":{"start":[801,13],"end":[801,36],"bytes":[27272,27295]},"callee":null},"802:11":{"kind":"call","span":{"start":[802,11],"end":[802,57],"bytes":[27320,27366]},"callee":null},"806:9":{"kind":"call","span":{"start":[806,9],"end":[806,28],"bytes":[27383,27402]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions"},"807:19":{"kind":"call","span":{"start":[807,19],"end":[807,28],"bytes":[27421,27430]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"810:12":{"kind":"call","span":{"start":[810,12],"end":[810,40],"bytes":[27485,27513]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore"},"811:43":{"kind":"call","span":{"start":[811,43],"end":[811,73],"bytes":[27570,27600]},"callee":null},"811:6":{"kind":"call","span":{"start":[811,6],"end":[811,39],"bytes":[27533,27566]},"callee":null},"812:4":{"kind":"call","span":{"start":[812,4],"end":[812,66],"bytes":[27606,27668]},"callee":null},"815:10":{"kind":"call","span":{"start":[815,10],"end":[815,52],"bytes":[27696,27738]},"callee":null},"818:2":{"kind":"call","span":{"start":[818,2],"end":[818,68],"bytes":[27744,27810]},"callee":null},"818:58":{"kind":"call","span":{"start":[818,58],"end":[818,67],"bytes":[27800,27809]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"}}},"github.com/benbjohnson/litestream.DB.FileInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.FileInfo","kind":"method","span":{"start":[652,1],"end":[654,2],"bytes":[22895,22955]},"signature":"github.com/benbjohnson/litestream.DB.FileInfo","parameters":[],"return_type":"os.FileInfo","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.IsOpen":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","kind":"method","span":{"start":[549,1],"end":[553,2],"bytes":[19789,19876]},"signature":"github.com/benbjohnson/litestream.DB.IsOpen","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"550:2":{"kind":"call","span":{"start":[550,2],"end":[550,15],"bytes":[19820,19833]},"callee":null},"551:8":{"kind":"call","span":{"start":[551,8],"end":[551,23],"bytes":[19841,19856]},"callee":null}}},"github.com/benbjohnson/litestream.DB.LTXDir":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir","kind":"method","span":{"start":[571,1],"end":[573,2],"bytes":[20272,20347]},"signature":"github.com/benbjohnson/litestream.DB.LTXDir","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{"572:9":{"kind":"call","span":{"start":[572,9],"end":[572,42],"bytes":[20312,20345]},"callee":null}}},"github.com/benbjohnson/litestream.DB.LTXLevelDir":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir","kind":"method","span":{"start":[601,1],"end":[603,2],"bytes":[21229,21332]},"signature":"github.com/benbjohnson/litestream.DB.LTXLevelDir","parameters":[{"name":"level","type":"int","span":{"start":[601,1],"end":[601,46],"bytes":[21229,21274]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"602:23":{"kind":"call","span":{"start":[602,23],"end":[602,34],"bytes":[21297,21308]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir"},"602:36":{"kind":"call","span":{"start":[602,36],"end":[602,55],"bytes":[21310,21329]},"callee":null},"602:9":{"kind":"call","span":{"start":[602,9],"end":[602,56],"bytes":[21283,21330]},"callee":null}}},"github.com/benbjohnson/litestream.DB.LTXPath":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","kind":"method","span":{"start":[607,1],"end":[610,2],"bytes":[21439,21640]},"signature":"github.com/benbjohnson/litestream.DB.LTXPath","parameters":[{"name":"level","type":"int","span":{"start":[607,1],"end":[607,69],"bytes":[21439,21507]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[607,1],"end":[607,69],"bytes":[21439,21507]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[607,1],"end":[607,69],"bytes":[21439,21507]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"608:2":{"kind":"call","span":{"start":[608,2],"end":[608,48],"bytes":[21509,21555]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert"},"609:23":{"kind":"call","span":{"start":[609,23],"end":[609,44],"bytes":[21578,21599]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir"},"609:46":{"kind":"call","span":{"start":[609,46],"end":[609,82],"bytes":[21601,21637]},"callee":null},"609:9":{"kind":"call","span":{"start":[609,9],"end":[609,83],"bytes":[21564,21638]},"callee":null}}},"github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt","kind":"method","span":{"start":[729,1],"end":[733,2],"bytes":[25065,25221]},"signature":"github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt","parameters":[],"return_type":"time.Time","metrics":{"cyclomatic":1},"body":{"730:2":{"kind":"call","span":{"start":[730,2],"end":[730,33],"bytes":[25115,25146]},"callee":null},"731:8":{"kind":"call","span":{"start":[731,8],"end":[731,41],"bytes":[25154,25187]},"callee":null}}},"github.com/benbjohnson/litestream.DB.MaxLTX":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","kind":"method","span":{"start":[632,1],"end":[649,2],"bytes":[22329,22804]},"signature":"github.com/benbjohnson/litestream.DB.MaxLTX","parameters":[],"return_type":"(github.com/superfly/ltx.TXID, github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"633:15":{"kind":"call","span":{"start":[633,15],"end":[633,44],"bytes":[22407,22436]},"callee":null},"633:26":{"kind":"call","span":{"start":[633,26],"end":[633,43],"bytes":[22418,22435]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir"},"634:5":{"kind":"call","span":{"start":[634,5],"end":[634,23],"bytes":[22441,22459]},"callee":null},"642:23":{"kind":"call","span":{"start":[642,23],"end":[642,52],"bytes":[22627,22656]},"callee":null},"642:41":{"kind":"call","span":{"start":[642,41],"end":[642,51],"bytes":[22645,22655]},"callee":null}}},"github.com/benbjohnson/litestream.DB.MaxLTXFileInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTXFileInfo","kind":"method","span":{"start":[3351,1],"end":[3367,2],"bytes":[107397,107873]},"signature":"github.com/benbjohnson/litestream.DB.MaxLTXFileInfo","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[3351,1],"end":[3351,85],"bytes":[107397,107481]}},{"name":"level","type":"int","span":{"start":[3351,1],"end":[3351,85],"bytes":[107397,107481]}}],"return_type":"(github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"3352:2":{"kind":"call","span":{"start":[3352,2],"end":[3352,27],"bytes":[107483,107508]},"callee":null},"3353:8":{"kind":"call","span":{"start":[3353,8],"end":[3353,35],"bytes":[107516,107543]},"callee":null},"3360:21":{"kind":"call","span":{"start":[3360,21],"end":[3360,58],"bytes":[107639,107676]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo"},"3362:26":{"kind":"call","span":{"start":[3362,26],"end":[3362,107],"bytes":[107719,107800]},"callee":null},"3362:92":{"kind":"call","span":{"start":[3362,92],"end":[3362,101],"bytes":[107785,107794]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"}}},"github.com/benbjohnson/litestream.DB.MetaPath":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MetaPath","kind":"method","span":{"start":[561,1],"end":[563,2],"bytes":[20050,20105]},"signature":"github.com/benbjohnson/litestream.DB.MetaPath","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.Notify":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Notify","kind":"method","span":{"start":[706,1],"end":[710,2],"bytes":[24382,24480]},"signature":"github.com/benbjohnson/litestream.DB.Notify","parameters":[],"return_type":"\u003c-chan struct{}","metrics":{"cyclomatic":1},"body":{"707:2":{"kind":"call","span":{"start":[707,2],"end":[707,15],"bytes":[24424,24437]},"callee":null},"708:8":{"kind":"call","span":{"start":[708,8],"end":[708,23],"bytes":[24445,24460]},"callee":null}}},"github.com/benbjohnson/litestream.DB.Open":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open","kind":"method","span":{"start":[823,1],"end":[869,2],"bytes":[27883,29224]},"signature":"github.com/benbjohnson/litestream.DB.Open","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"824:2":{"kind":"call","span":{"start":[824,2],"end":[824,14],"bytes":[27919,27931]},"callee":null},"826:3":{"kind":"call","span":{"start":[826,3],"end":[826,17],"bytes":[27950,27964]},"callee":null},"830:22":{"kind":"call","span":{"start":[830,22],"end":[830,62],"bytes":[28084,28124]},"callee":null},"830:41":{"kind":"call","span":{"start":[830,41],"end":[830,61],"bytes":[28103,28123]},"callee":null},"831:2":{"kind":"call","span":{"start":[831,2],"end":[831,16],"bytes":[28126,28140]},"callee":null},"834:2":{"kind":"call","span":{"start":[834,2],"end":[834,18],"bytes":[28204,28220]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.initMetrics"},"838:10":{"kind":"call","span":{"start":[838,10],"end":[838,64],"bytes":[28288,28342]},"callee":null},"841:10":{"kind":"call","span":{"start":[841,10],"end":[841,71],"bytes":[28386,28447]},"callee":null},"844:10":{"kind":"call","span":{"start":[844,10],"end":[844,62],"bytes":[28493,28545]},"callee":null},"848:12":{"kind":"call","span":{"start":[848,12],"end":[848,39],"bytes":[28628,28655]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.removeTmpFiles"},"849:10":{"kind":"call","span":{"start":[849,10],"end":[849,56],"bytes":[28679,28725]},"callee":null},"859:3":{"kind":"call","span":{"start":[859,3],"end":[859,15],"bytes":[29037,29049]},"callee":null},"860:6":{"kind":"call","span":{"start":[860,6],"end":[860,51],"bytes":[29055,29100]},"callee":null,"is_goroutine":true},"864:2":{"kind":"call","span":{"start":[864,2],"end":[864,14],"bytes":[29163,29175]},"callee":null},"866:2":{"kind":"call","span":{"start":[866,2],"end":[866,16],"bytes":[29195,29209]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open/github.com/benbjohnson/litestream.DB.Open.closure_1","kind":"function","span":{"start":[860,1],"end":[860,51],"bytes":[29050,29100]},"signature":"github.com/benbjohnson/litestream.DB.Open.closure_1","parameters":[],"body":{"860:21":{"kind":"call","span":{"start":[860,21],"end":[860,33],"bytes":[29070,29082]},"callee":null},"860:35":{"kind":"call","span":{"start":[860,35],"end":[860,47],"bytes":[29084,29096]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.PageSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.PageSize","kind":"method","span":{"start":[714,1],"end":[718,2],"bytes":[24611,24701]},"signature":"github.com/benbjohnson/litestream.DB.PageSize","parameters":[],"return_type":"int","metrics":{"cyclomatic":1},"body":{"715:2":{"kind":"call","span":{"start":[715,2],"end":[715,15],"bytes":[24643,24656]},"callee":null},"716:8":{"kind":"call","span":{"start":[716,8],"end":[716,23],"bytes":[24664,24679]},"callee":null}}},"github.com/benbjohnson/litestream.DB.Path":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","kind":"method","span":{"start":[438,1],"end":[440,2],"bytes":[16743,16790]},"signature":"github.com/benbjohnson/litestream.DB.Path","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.Pos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","kind":"method","span":{"start":[663,1],"end":[694,2],"bytes":[23260,24036]},"signature":"github.com/benbjohnson/litestream.DB.Pos","parameters":[],"return_type":"(github.com/superfly/ltx.Pos, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"664:2":{"kind":"call","span":{"start":[664,2],"end":[664,15],"bytes":[23300,23313]},"callee":null},"665:8":{"kind":"call","span":{"start":[665,8],"end":[665,23],"bytes":[23321,23336]},"callee":null},"671:27":{"kind":"call","span":{"start":[671,27],"end":[671,38],"bytes":[23422,23433]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX"},"678:13":{"kind":"call","span":{"start":[678,13],"end":[678,44],"bytes":[23563,23594]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"679:12":{"kind":"call","span":{"start":[679,12],"end":[679,28],"bytes":[23606,23622]},"callee":null},"681:21":{"kind":"call","span":{"start":[681,21],"end":[681,91],"bytes":[23660,23730]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"681:53":{"kind":"call","span":{"start":[681,53],"end":[681,68],"bytes":[23692,23707]},"callee":null},"681:70":{"kind":"call","span":{"start":[681,70],"end":[681,85],"bytes":[23709,23724]},"callee":null},"683:8":{"kind":"call","span":{"start":[683,8],"end":[683,34],"bytes":[23741,23767]},"callee":null},"685:9":{"kind":"call","span":{"start":[685,9],"end":[685,26],"bytes":[23777,23794]},"callee":null},"686:12":{"kind":"call","span":{"start":[686,12],"end":[686,24],"bytes":[23806,23818]},"callee":null},"687:21":{"kind":"call","span":{"start":[687,21],"end":[687,132],"bytes":[23853,23964]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"687:55":{"kind":"call","span":{"start":[687,55],"end":[687,70],"bytes":[23887,23902]},"callee":null},"687:72":{"kind":"call","span":{"start":[687,72],"end":[687,87],"bytes":[23904,23919]},"callee":null},"687:89":{"kind":"call","span":{"start":[687,89],"end":[687,131],"bytes":[23921,23963]},"callee":null},"690:9":{"kind":"call","span":{"start":[690,9],"end":[690,27],"bytes":[23977,23995]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos/github.com/benbjohnson/litestream.DB.Pos.closure_1","kind":"function","span":{"start":[683,1],"end":[683,34],"bytes":[23734,23767]},"signature":"github.com/benbjohnson/litestream.DB.Pos.closure_1","parameters":[],"body":{"683:21":{"kind":"call","span":{"start":[683,21],"end":[683,30],"bytes":[23754,23763]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.RecordSuccessfulSync":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.RecordSuccessfulSync","kind":"method","span":{"start":[722,1],"end":[726,2],"bytes":[24843,24993]},"signature":"github.com/benbjohnson/litestream.DB.RecordSuccessfulSync","parameters":[],"metrics":{"cyclomatic":1},"body":{"723:2":{"kind":"call","span":{"start":[723,2],"end":[723,32],"bytes":[24883,24913]},"callee":null},"724:8":{"kind":"call","span":{"start":[724,8],"end":[724,40],"bytes":[24921,24953]},"callee":null},"725:28":{"kind":"call","span":{"start":[725,28],"end":[725,38],"bytes":[24981,24991]},"callee":null}}},"github.com/benbjohnson/litestream.DB.ResetLocalState":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState","kind":"method","span":{"start":[578,1],"end":[597,2],"bytes":[20547,21132]},"signature":"github.com/benbjohnson/litestream.DB.ResetLocalState","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[578,1],"end":[578,59],"bytes":[20547,20605]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"579:2":{"kind":"call","span":{"start":[579,2],"end":[581,26],"bytes":[20607,20711]},"callee":null},"581:14":{"kind":"call","span":{"start":[581,14],"end":[581,25],"bytes":[20699,20710]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir"},"584:12":{"kind":"call","span":{"start":[584,12],"end":[584,37],"bytes":[20749,20774]},"callee":null},"584:25":{"kind":"call","span":{"start":[584,25],"end":[584,36],"bytes":[20762,20773]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir"},"584:54":{"kind":"call","span":{"start":[584,54],"end":[584,72],"bytes":[20791,20809]},"callee":null},"585:10":{"kind":"call","span":{"start":[585,10],"end":[585,53],"bytes":[20821,20864]},"callee":null},"589:2":{"kind":"call","span":{"start":[589,2],"end":[589,27],"bytes":[20901,20926]},"callee":null},"590:25":{"kind":"call","span":{"start":[590,25],"end":[590,52],"bytes":[20951,20978]},"callee":null},"591:2":{"kind":"call","span":{"start":[591,2],"end":[591,29],"bytes":[20980,21007]},"callee":null},"593:2":{"kind":"call","span":{"start":[593,2],"end":[593,25],"bytes":[21010,21033]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"},"595:2":{"kind":"call","span":{"start":[595,2],"end":[595,84],"bytes":[21036,21118]},"callee":null}}},"github.com/benbjohnson/litestream.DB.SQLDB":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SQLDB","kind":"method","span":{"start":[433,1],"end":[435,2],"bytes":[16652,16699]},"signature":"github.com/benbjohnson/litestream.DB.SQLDB","parameters":[],"return_type":"*database/sql.DB","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.SetLogger":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetLogger","kind":"method","span":{"start":[419,1],"end":[430,2],"bytes":[16254,16584]},"signature":"github.com/benbjohnson/litestream.DB.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[419,1],"end":[419,47],"bytes":[16254,16300]}}],"metrics":{"cyclomatic":4},"body":{"421:12":{"kind":"call","span":{"start":[421,12],"end":[421,26],"bytes":[16332,16346]},"callee":null},"425:26":{"kind":"call","span":{"start":[425,26],"end":[425,77],"bytes":[16421,16472]},"callee":null},"425:3":{"kind":"call","span":{"start":[425,3],"end":[425,78],"bytes":[16398,16473]},"callee":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.setLogger"},"428:3":{"kind":"call","span":{"start":[428,3],"end":[428,51],"bytes":[16531,16579]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.SetLogger"},"428:31":{"kind":"call","span":{"start":[428,31],"end":[428,50],"bytes":[16559,16578]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger"}}},"github.com/benbjohnson/litestream.DB.SetMetaPath":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetMetaPath","kind":"method","span":{"start":[566,1],"end":[568,2],"bytes":[20158,20220]},"signature":"github.com/benbjohnson/litestream.DB.SetMetaPath","parameters":[{"name":"path","type":"string","span":{"start":[566,1],"end":[566,41],"bytes":[20158,20198]}}],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.Snapshot":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot","kind":"method","span":{"start":[3007,1],"end":[3024,2],"bytes":[96361,96783]},"signature":"github.com/benbjohnson/litestream.DB.Snapshot","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[3007,1],"end":[3007,69],"bytes":[96361,96429]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"3008:17":{"kind":"call","span":{"start":[3008,17],"end":[3008,39],"bytes":[96446,96468]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SnapshotReader"},"3012:8":{"kind":"call","span":{"start":[3012,8],"end":[3012,34],"bytes":[96514,96540]},"callee":null},"3014:15":{"kind":"call","span":{"start":[3014,15],"end":[3014,81],"bytes":[96556,96622]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile"},"3019:2":{"kind":"call","span":{"start":[3019,2],"end":[3019,27],"bytes":[96664,96689]},"callee":null},"3021:2":{"kind":"call","span":{"start":[3021,2],"end":[3021,29],"bytes":[96735,96762]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot/github.com/benbjohnson/litestream.DB.Snapshot.closure_1","kind":"function","span":{"start":[3012,1],"end":[3012,34],"bytes":[96507,96540]},"signature":"github.com/benbjohnson/litestream.DB.Snapshot.closure_1","parameters":[],"body":{"3012:21":{"kind":"call","span":{"start":[3012,21],"end":[3012,30],"bytes":[96527,96536]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.SnapshotReader":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SnapshotReader","kind":"method","span":{"start":[2782,1],"end":[2794,2],"bytes":[89442,89739]},"signature":"github.com/benbjohnson/litestream.DB.SnapshotReader","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2782,1],"end":[2782,84],"bytes":[89442,89525]}}],"return_type":"(github.com/superfly/ltx.Pos, io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"2783:14":{"kind":"call","span":{"start":[2783,14],"end":[2783,38],"bytes":[89539,89563]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition"},"2788:12":{"kind":"call","span":{"start":[2788,12],"end":[2788,39],"bytes":[89625,89652]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotReader"},"2790:3":{"kind":"call","span":{"start":[2790,3],"end":[2790,14],"bytes":[89672,89683]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/github.com/benbjohnson/litestream.snapshotReadPosition.close"}}},"github.com/benbjohnson/litestream.DB.Sync":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync","kind":"method","span":{"start":[1286,1],"end":[1299,2],"bytes":[41251,41557]},"signature":"github.com/benbjohnson/litestream.DB.Sync","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1286,1],"end":[1286,48],"bytes":[41251,41298]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"1288:13":{"kind":"call","span":{"start":[1288,13],"end":[1288,22],"bytes":[41318,41327]},"callee":null},"1289:11":{"kind":"call","span":{"start":[1289,11],"end":[1289,29],"bytes":[41352,41370]},"callee":null},"1292:18":{"kind":"call","span":{"start":[1292,18],"end":[1292,54],"bytes":[41393,41429]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncOnce"}}},"github.com/benbjohnson/litestream.DB.SyncAndWait":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncAndWait","kind":"method","span":{"start":[769,1],"end":[781,2],"bytes":[26300,26616]},"signature":"github.com/benbjohnson/litestream.DB.SyncAndWait","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[769,1],"end":[769,55],"bytes":[26300,26354]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"771:10":{"kind":"call","span":{"start":[771,10],"end":[771,45],"bytes":[26388,26423]},"callee":null},"774:12":{"kind":"call","span":{"start":[774,12],"end":[774,24],"bytes":[26439,26451]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync"},"775:10":{"kind":"call","span":{"start":[775,10],"end":[775,40],"bytes":[26475,26505]},"callee":null},"777:12":{"kind":"call","span":{"start":[777,12],"end":[777,32],"bytes":[26520,26540]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync"},"778:10":{"kind":"call","span":{"start":[778,10],"end":[778,45],"bytes":[26564,26599]},"callee":null}}},"github.com/benbjohnson/litestream.DB.SyncDiagnostic":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncDiagnostic","kind":"method","span":{"start":[443,1],"end":[482,2],"bytes":[16866,18214]},"signature":"github.com/benbjohnson/litestream.DB.SyncDiagnostic","parameters":[],"return_type":"github.com/benbjohnson/litestream.SyncDiagnostic","metrics":{"cyclomatic":6},"body":{"444:2":{"kind":"call","span":{"start":[444,2],"end":[444,21],"bytes":[16915,16934]},"callee":null},"445:8":{"kind":"call","span":{"start":[445,8],"end":[445,29],"bytes":[16942,16963]},"callee":null},"450:24":{"kind":"call","span":{"start":[450,24],"end":[450,53],"bytes":[17088,17117]},"callee":null},"451:24":{"kind":"call","span":{"start":[451,24],"end":[451,49],"bytes":[17142,17167]},"callee":null},"452:24":{"kind":"call","span":{"start":[452,24],"end":[452,48],"bytes":[17192,17216]},"callee":null},"461:6":{"kind":"call","span":{"start":[461,6],"end":[461,46],"bytes":[17565,17605]},"callee":null},"464:30":{"kind":"call","span":{"start":[464,30],"end":[464,73],"bytes":[17724,17767]},"callee":null},"466:6":{"kind":"call","span":{"start":[466,6],"end":[466,36],"bytes":[17786,17816]},"callee":null},"470:6":{"kind":"call","span":{"start":[470,6],"end":[470,36],"bytes":[17894,17924]},"callee":null},"474:6":{"kind":"call","span":{"start":[474,6],"end":[474,36],"bytes":[18002,18032]},"callee":null},"476:28":{"kind":"call","span":{"start":[476,28],"end":[476,40],"bytes":[18093,18105]},"callee":null},"477:10":{"kind":"call","span":{"start":[477,10],"end":[477,20],"bytes":[18117,18127]},"callee":null},"479:25":{"kind":"call","span":{"start":[479,25],"end":[479,55],"bytes":[18156,18186]},"callee":null}}},"github.com/benbjohnson/litestream.DB.SyncStatus":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncStatus","kind":"method","span":{"start":[745,1],"end":[765,2],"bytes":[25625,26171]},"signature":"github.com/benbjohnson/litestream.DB.SyncStatus","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[745,1],"end":[745,68],"bytes":[25625,25692]}}],"return_type":"(github.com/benbjohnson/litestream.SyncStatus, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"747:24":{"kind":"call","span":{"start":[747,24],"end":[747,59],"bytes":[25740,25775]},"callee":null},"750:19":{"kind":"call","span":{"start":[750,19],"end":[750,27],"bytes":[25798,25806]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"752:24":{"kind":"call","span":{"start":[752,24],"end":[752,61],"bytes":[25847,25884]},"callee":null},"755:20":{"kind":"call","span":{"start":[755,20],"end":[755,43],"bytes":[25908,25931]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.calcPos"},"757:24":{"kind":"call","span":{"start":[757,24],"end":[757,62],"bytes":[25972,26010]},"callee":null}}},"github.com/benbjohnson/litestream.DB.WALPath":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","kind":"method","span":{"start":[556,1],"end":[558,2],"bytes":[19934,19993]},"signature":"github.com/benbjohnson/litestream.DB.WALPath","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.DB.acquireReadLock":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.acquireReadLock","kind":"method","span":{"start":[1244,1],"end":[1267,2],"bytes":[39962,40703]},"signature":"github.com/benbjohnson/litestream.DB.acquireReadLock","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1244,1],"end":[1244,59],"bytes":[39962,40020]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1253:13":{"kind":"call","span":{"start":[1253,13],"end":[1253,59],"bytes":[40358,40404]},"callee":null},"1253:27":{"kind":"call","span":{"start":[1253,27],"end":[1253,53],"bytes":[40372,40398]},"callee":null},"1259:15":{"kind":"call","span":{"start":[1259,15],"end":[1259,75],"bytes":[40497,40557]},"callee":null},"1260:7":{"kind":"call","span":{"start":[1260,7],"end":[1260,20],"bytes":[40578,40591]},"callee":null}}},"github.com/benbjohnson/litestream.DB.applySyncExecutor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor","kind":"method","span":{"start":[2025,1],"end":[2054,2],"bytes":[65496,66196]},"signature":"github.com/benbjohnson/litestream.DB.applySyncExecutor","parameters":[{"name":"exec","type":"*github.com/benbjohnson/litestream.syncExecutor","span":{"start":[2025,1],"end":[2025,67],"bytes":[65496,65562]}},{"name":"notify","type":"bool","span":{"start":[2025,1],"end":[2025,67],"bytes":[65496,65562]}}],"metrics":{"cyclomatic":5},"body":{"2034:3":{"kind":"call","span":{"start":[2034,3],"end":[2034,16],"bytes":[65826,65839]},"callee":null},"2037:3":{"kind":"call","span":{"start":[2037,3],"end":[2037,18],"bytes":[65882,65897]},"callee":null},"2041:3":{"kind":"call","span":{"start":[2041,3],"end":[2041,28],"bytes":[65933,65958]},"callee":null},"2044:3":{"kind":"call","span":{"start":[2044,3],"end":[2044,30],"bytes":[66022,66049]},"callee":null},"2047:2":{"kind":"call","span":{"start":[2047,2],"end":[2047,14],"bytes":[66055,66067]},"callee":null},"2050:3":{"kind":"call","span":{"start":[2050,3],"end":[2050,19],"bytes":[66125,66141]},"callee":null},"2051:15":{"kind":"call","span":{"start":[2051,15],"end":[2051,34],"bytes":[66156,66175]},"callee":null},"2053:2":{"kind":"call","span":{"start":[2053,2],"end":[2053,16],"bytes":[66180,66194]},"callee":null}}},"github.com/benbjohnson/litestream.DB.applySyncResult":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncResult","kind":"method","span":{"start":[1989,1],"end":[2002,2],"bytes":[64732,65122]},"signature":"github.com/benbjohnson/litestream.DB.applySyncResult","parameters":[{"name":"state","type":"*github.com/benbjohnson/litestream.syncState","span":{"start":[1989,1],"end":[1989,69],"bytes":[64732,64800]}},{"name":"result","type":"github.com/benbjohnson/litestream.syncResult","span":{"start":[1989,1],"end":[1989,69],"bytes":[64732,64800]}}],"metrics":{"cyclomatic":3},"body":{"1993:3":{"kind":"call","span":{"start":[1993,3],"end":[1993,16],"bytes":[64920,64933]},"callee":null},"1995:3":{"kind":"call","span":{"start":[1995,3],"end":[1995,18],"bytes":[64964,64979]},"callee":null},"1998:3":{"kind":"call","span":{"start":[1998,3],"end":[1998,28],"bytes":[65016,65041]},"callee":null},"2000:3":{"kind":"call","span":{"start":[2000,3],"end":[2000,30],"bytes":[65090,65117]},"callee":null}}},"github.com/benbjohnson/litestream.DB.beginSyncDiag":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncDiag","kind":"method","span":{"start":[484,1],"end":[502,2],"bytes":[18216,18753]},"signature":"github.com/benbjohnson/litestream.DB.beginSyncDiag","parameters":[{"name":"operation","type":"github.com/benbjohnson/litestream.diagOp","span":{"start":[484,1],"end":[484,48],"bytes":[18216,18263]}}],"metrics":{"cyclomatic":1},"body":{"485:9":{"kind":"call","span":{"start":[485,9],"end":[485,19],"bytes":[18272,18282]},"callee":null},"486:16":{"kind":"call","span":{"start":[486,16],"end":[486,32],"bytes":[18298,18314]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize"},"488:2":{"kind":"call","span":{"start":[488,2],"end":[488,20],"bytes":[18317,18335]},"callee":null},"501:2":{"kind":"call","span":{"start":[501,2],"end":[501,22],"bytes":[18731,18751]},"callee":null}}},"github.com/benbjohnson/litestream.DB.beginSyncExecutorWait":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncExecutorWait","kind":"method","span":{"start":[530,1],"end":[537,2],"bytes":[19293,19510]},"signature":"github.com/benbjohnson/litestream.DB.beginSyncExecutorWait","parameters":[],"metrics":{"cyclomatic":2},"body":{"531:2":{"kind":"call","span":{"start":[531,2],"end":[531,20],"bytes":[19334,19352]},"callee":null},"532:8":{"kind":"call","span":{"start":[532,8],"end":[532,28],"bytes":[19360,19380]},"callee":null},"534:37":{"kind":"call","span":{"start":[534,37],"end":[534,47],"bytes":[19460,19470]},"callee":null}}},"github.com/benbjohnson/litestream.DB.bumpLitestreamSeq":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.bumpLitestreamSeq","kind":"method","span":{"start":[1638,1],"end":[1641,2],"bytes":[53165,53373]},"signature":"github.com/benbjohnson/litestream.DB.bumpLitestreamSeq","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1638,1],"end":[1638,61],"bytes":[53165,53225]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"1639:12":{"kind":"call","span":{"start":[1639,12],"end":[1639,134],"bytes":[53237,53359]},"callee":null}}},"github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","kind":"method","span":{"start":[1660,1],"end":[1737,2],"bytes":[54138,56383]},"signature":"github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1660,1],"end":[1660,70],"bytes":[54138,54207]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"1662:16":{"kind":"call","span":{"start":[1662,16],"end":[1662,24],"bytes":[54269,54277]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"1664:10":{"kind":"call","span":{"start":[1664,10],"end":[1664,54],"bytes":[54304,54348]},"callee":null},"1668:22":{"kind":"call","span":{"start":[1668,22],"end":[1668,55],"bytes":[54411,54444]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo"},"1670:10":{"kind":"call","span":{"start":[1670,10],"end":[1670,53],"bytes":[54471,54514]},"callee":null},"1680:2":{"kind":"call","span":{"start":[1680,2],"end":[1682,39],"bytes":[54728,54842]},"callee":null},"1685:11":{"kind":"call","span":{"start":[1685,11],"end":[1685,28],"bytes":[54879,54896]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir"},"1686:12":{"kind":"call","span":{"start":[1686,12],"end":[1686,31],"bytes":[54908,54927]},"callee":null},"1686:48":{"kind":"call","span":{"start":[1686,48],"end":[1686,66],"bytes":[54944,54962]},"callee":null},"1687:10":{"kind":"call","span":{"start":[1687,10],"end":[1687,52],"bytes":[54974,55016]},"callee":null},"1689:2":{"kind":"call","span":{"start":[1689,2],"end":[1689,25],"bytes":[55021,55044]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"},"1690:12":{"kind":"call","span":{"start":[1690,12],"end":[1690,48],"bytes":[55056,55092]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll"},"1691:10":{"kind":"call","span":{"start":[1691,10],"end":[1691,54],"bytes":[55116,55160]},"callee":null},"1696:17":{"kind":"call","span":{"start":[1696,17],"end":[1696,78],"bytes":[55285,55346]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile"},"1698:10":{"kind":"call","span":{"start":[1698,10],"end":[1698,52],"bytes":[55373,55415]},"callee":null},"1700:8":{"kind":"call","span":{"start":[1700,8],"end":[1700,39],"bytes":[55426,55457]},"callee":null},"1703:15":{"kind":"call","span":{"start":[1703,15],"end":[1703,46],"bytes":[55518,55549]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"1706:18":{"kind":"call","span":{"start":[1706,18],"end":[1706,36],"bytes":[55599,55617]},"callee":null},"1708:10":{"kind":"call","span":{"start":[1708,10],"end":[1708,52],"bytes":[55644,55686]},"callee":null},"1710:8":{"kind":"call","span":{"start":[1710,8],"end":[1710,43],"bytes":[55697,55732]},"callee":null},"1712:15":{"kind":"call","span":{"start":[1712,15],"end":[1712,39],"bytes":[55779,55803]},"callee":null},"1713:7":{"kind":"call","span":{"start":[1713,7],"end":[1713,22],"bytes":[55824,55839]},"callee":null},"1714:10":{"kind":"call","span":{"start":[1714,10],"end":[1714,45],"bytes":[55849,55884]},"callee":null},"1717:12":{"kind":"call","span":{"start":[1717,12],"end":[1717,26],"bytes":[55900,55914]},"callee":null},"1718:7":{"kind":"call","span":{"start":[1718,7],"end":[1718,22],"bytes":[55935,55950]},"callee":null},"1719:10":{"kind":"call","span":{"start":[1719,10],"end":[1719,45],"bytes":[55960,55995]},"callee":null},"1722:12":{"kind":"call","span":{"start":[1722,12],"end":[1722,27],"bytes":[56011,56026]},"callee":null},"1723:10":{"kind":"call","span":{"start":[1723,10],"end":[1723,46],"bytes":[56050,56086]},"callee":null},"1727:12":{"kind":"call","span":{"start":[1727,12],"end":[1727,41],"bytes":[56148,56177]},"callee":null},"1728:10":{"kind":"call","span":{"start":[1728,10],"end":[1728,47],"bytes":[56201,56238]},"callee":null},"1730:2":{"kind":"call","span":{"start":[1730,2],"end":[1730,25],"bytes":[56243,56266]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"},"1732:2":{"kind":"call","span":{"start":[1732,2],"end":[1734,23],"bytes":[56269,56368]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica.closure_1","kind":"function","span":{"start":[1700,1],"end":[1700,39],"bytes":[55419,55457]},"signature":"github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica.closure_1","parameters":[],"body":{"1700:21":{"kind":"call","span":{"start":[1700,21],"end":[1700,35],"bytes":[55439,55453]},"callee":null}}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica.closure_2","kind":"function","span":{"start":[1710,1],"end":[1710,74],"bytes":[55690,55763]},"signature":"github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica.closure_2","parameters":[],"body":{"1710:21":{"kind":"call","span":{"start":[1710,21],"end":[1710,39],"bytes":[55710,55728]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.checkpoint":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpoint","kind":"method","span":{"start":[2491,1],"end":[2517,2],"bytes":[79907,80471]},"signature":"github.com/benbjohnson/litestream.DB.checkpoint","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2491,1],"end":[2491,85],"bytes":[79907,79991]}},{"name":"mode","type":"string","span":{"start":[2491,1],"end":[2491,85],"bytes":[79907,79991]}},{"name":"state","type":"*github.com/benbjohnson/litestream.syncState","span":{"start":[2491,1],"end":[2491,85],"bytes":[79907,79991]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"2492:14":{"kind":"call","span":{"start":[2492,14],"end":[2492,22],"bytes":[80005,80013]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"2494:10":{"kind":"call","span":{"start":[2494,10],"end":[2494,36],"bytes":[80040,80066]},"callee":null},"2501:15":{"kind":"call","span":{"start":[2501,15],"end":[2501,57],"bytes":[80143,80185]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"},"2506:2":{"kind":"call","span":{"start":[2506,2],"end":[2506,15],"bytes":[80239,80252]},"callee":null},"2509:2":{"kind":"call","span":{"start":[2509,2],"end":[2509,17],"bytes":[80291,80306]},"callee":null},"2511:3":{"kind":"call","span":{"start":[2511,3],"end":[2511,28],"bytes":[80338,80363]},"callee":null},"2514:3":{"kind":"call","span":{"start":[2514,3],"end":[2514,30],"bytes":[80427,80454]},"callee":null}}},"github.com/benbjohnson/litestream.DB.checkpointIfNeeded":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","kind":"method","span":{"start":[1478,1],"end":[1565,2],"bytes":[47383,50904]},"signature":"github.com/benbjohnson/litestream.DB.checkpointIfNeeded","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1478,1],"end":[1478,113],"bytes":[47383,47495]}},{"name":"exec","type":"*github.com/benbjohnson/litestream.syncExecutor","span":{"start":[1478,1],"end":[1478,113],"bytes":[47383,47495]}},{"name":"origWALSize","type":"int64","span":{"start":[1478,1],"end":[1478,113],"bytes":[47383,47495]}},{"name":"newWALSize","type":"int64","span":{"start":[1478,1],"end":[1478,113],"bytes":[47383,47495]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":19},"body":{"1482:6":{"kind":"call","span":{"start":[1482,6],"end":[1482,65],"bytes":[47540,47599]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold"},"1488:5":{"kind":"call","span":{"start":[1488,5],"end":[1488,45],"bytes":[47799,47839]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold"},"1489:24":{"kind":"call","span":{"start":[1489,24],"end":[1489,93],"bytes":[47865,47934]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize"},"1489:36":{"kind":"call","span":{"start":[1489,36],"end":[1489,55],"bytes":[47877,47896]},"callee":null},"1489:57":{"kind":"call","span":{"start":[1489,57],"end":[1489,92],"bytes":[47898,47933]},"callee":null},"1489:64":{"kind":"call","span":{"start":[1489,64],"end":[1489,91],"bytes":[47905,47932]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.effectiveTruncatePageN"},"1495:25":{"kind":"call","span":{"start":[1495,25],"end":[1495,84],"bytes":[48195,48254]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"},"1496:9":{"kind":"call","span":{"start":[1496,9],"end":[1496,31],"bytes":[48277,48299]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isSQLiteBusyError"},"1500:5":{"kind":"call","span":{"start":[1500,5],"end":[1500,101],"bytes":[48372,48468]},"callee":null},"1503:9":{"kind":"call","span":{"start":[1503,9],"end":[1503,68],"bytes":[48547,48606]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold"},"1504:6":{"kind":"call","span":{"start":[1504,6],"end":[1506,38],"bytes":[48614,48766]},"callee":null},"1514:3":{"kind":"call","span":{"start":[1514,3],"end":[1516,35],"bytes":[48885,48993]},"callee":null},"1517:21":{"kind":"call","span":{"start":[1517,21],"end":[1517,81],"bytes":[49014,49074]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"},"1518:20":{"kind":"call","span":{"start":[1518,20],"end":[1518,79],"bytes":[49094,49153]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold"},"1525:19":{"kind":"call","span":{"start":[1525,19],"end":[1525,82],"bytes":[49306,49369]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize"},"1525:31":{"kind":"call","span":{"start":[1525,31],"end":[1525,50],"bytes":[49318,49337]},"callee":null},"1525:52":{"kind":"call","span":{"start":[1525,52],"end":[1525,81],"bytes":[49339,49368]},"callee":null},"1526:16":{"kind":"call","span":{"start":[1526,16],"end":[1526,75],"bytes":[49387,49446]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"},"1529:7":{"kind":"call","span":{"start":[1529,7],"end":[1529,29],"bytes":[49618,49640]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isSQLiteBusyError"},"1530:5":{"kind":"call","span":{"start":[1530,5],"end":[1530,101],"bytes":[49647,49743]},"callee":null},"1544:14":{"kind":"call","span":{"start":[1544,14],"end":[1544,25],"bytes":[50196,50207]},"callee":null},"1546:11":{"kind":"call","span":{"start":[1546,11],"end":[1546,47],"bytes":[50236,50272]},"callee":null},"1550:17":{"kind":"call","span":{"start":[1550,17],"end":[1550,29],"bytes":[50358,50370]},"callee":null},"1550:6":{"kind":"call","span":{"start":[1550,6],"end":[1550,30],"bytes":[50347,50371]},"callee":null},"1550:71":{"kind":"call","span":{"start":[1550,71],"end":[1550,106],"bytes":[50412,50447]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize"},"1550:83":{"kind":"call","span":{"start":[1550,83],"end":[1550,102],"bytes":[50424,50443]},"callee":null},"1551:17":{"kind":"call","span":{"start":[1551,17],"end":[1551,76],"bytes":[50466,50525]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor"},"1554:8":{"kind":"call","span":{"start":[1554,8],"end":[1554,30],"bytes":[50700,50722]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isSQLiteBusyError"},"1555:6":{"kind":"call","span":{"start":[1555,6],"end":[1555,102],"bytes":[50730,50826]},"callee":null}}},"github.com/benbjohnson/litestream.DB.checkpointWithExecutor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","kind":"method","span":{"start":[2522,1],"end":[2706,2],"bytes":[80699,86958]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2522,1],"end":[2522,124],"bytes":[80699,80822]}},{"name":"mode","type":"string","span":{"start":[2522,1],"end":[2522,124],"bytes":[80699,80822]}},{"name":"exec","type":"*github.com/benbjohnson/litestream.syncExecutor","span":{"start":[2522,1],"end":[2522,124],"bytes":[80699,80822]}}],"return_type":"(bool, error)","error_channel":["error"],"metrics":{"cyclomatic":25},"body":{"2524:2":{"kind":"call","span":{"start":[2524,2],"end":[2528,5],"bytes":[80858,81015]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2531:6":{"kind":"call","span":{"start":[2531,6],"end":[2531,24],"bytes":[81134,81152]},"callee":null},"2533:4":{"kind":"call","span":{"start":[2533,4],"end":[2533,76],"bytes":[81196,81268]},"callee":null},"2535:4":{"kind":"call","span":{"start":[2535,4],"end":[2535,101],"bytes":[81283,81380]},"callee":null},"2539:8":{"kind":"call","span":{"start":[2539,8],"end":[2539,25],"bytes":[81415,81432]},"callee":null},"2543:2":{"kind":"call","span":{"start":[2543,2],"end":[2547,5],"bytes":[81541,81707]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2548:14":{"kind":"call","span":{"start":[2548,14],"end":[2548,41],"bytes":[81721,81748]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader"},"2548:28":{"kind":"call","span":{"start":[2548,28],"end":[2548,40],"bytes":[81735,81747]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"2554:2":{"kind":"call","span":{"start":[2554,2],"end":[2558,5],"bytes":[81858,82021]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2559:17":{"kind":"call","span":{"start":[2559,17],"end":[2559,65],"bytes":[82038,82086]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor"},"2561:17":{"kind":"call","span":{"start":[2561,17],"end":[2561,73],"bytes":[82120,82176]},"callee":null},"2563:2":{"kind":"call","span":{"start":[2563,2],"end":[2563,30],"bytes":[82181,82209]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult"},"2567:20":{"kind":"call","span":{"start":[2567,20],"end":[2567,43],"bytes":[82289,82312]},"callee":null},"2569:18":{"kind":"call","span":{"start":[2569,18],"end":[2569,73],"bytes":[82348,82403]},"callee":null},"2571:9":{"kind":"call","span":{"start":[2571,9],"end":[2575,6],"bytes":[82416,82488]},"callee":null},"2577:16":{"kind":"call","span":{"start":[2577,16],"end":[2577,91],"bytes":[82505,82580]},"callee":null},"2578:18":{"kind":"call","span":{"start":[2578,18],"end":[2578,57],"bytes":[82612,82651]},"callee":null},"2581:17":{"kind":"call","span":{"start":[2581,17],"end":[2581,65],"bytes":[82673,82721]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor"},"2583:18":{"kind":"call","span":{"start":[2583,18],"end":[2583,82],"bytes":[82757,82821]},"callee":null},"2585:3":{"kind":"call","span":{"start":[2585,3],"end":[2585,31],"bytes":[82828,82856]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult"},"2588:15":{"kind":"call","span":{"start":[2588,15],"end":[2588,54],"bytes":[82875,82914]},"callee":null},"2591:25":{"kind":"call","span":{"start":[2591,25],"end":[2591,90],"bytes":[83018,83083]},"callee":null},"2596:2":{"kind":"call","span":{"start":[2596,2],"end":[2600,5],"bytes":[83190,83347]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2601:20":{"kind":"call","span":{"start":[2601,20],"end":[2601,48],"bytes":[83367,83395]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint"},"2607:12":{"kind":"call","span":{"start":[2607,12],"end":[2607,31],"bytes":[83471,83490]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.rollback"},"2608:18":{"kind":"call","span":{"start":[2608,18],"end":[2608,76],"bytes":[83522,83580]},"callee":null},"2613:11":{"kind":"call","span":{"start":[2613,11],"end":[2613,36],"bytes":[83617,83642]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.bumpLitestreamSeq"},"2614:17":{"kind":"call","span":{"start":[2614,17],"end":[2614,59],"bytes":[83673,83715]},"callee":null},"2618:2":{"kind":"call","span":{"start":[2618,2],"end":[2622,5],"bytes":[83761,83927]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2623:16":{"kind":"call","span":{"start":[2623,16],"end":[2623,43],"bytes":[83943,83970]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader"},"2623:30":{"kind":"call","span":{"start":[2623,30],"end":[2623,42],"bytes":[83957,83969]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"2626:12":{"kind":"call","span":{"start":[2626,12],"end":[2626,35],"bytes":[84019,84042]},"callee":null},"2633:17":{"kind":"call","span":{"start":[2633,17],"end":[2633,65],"bytes":[84206,84254]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor"},"2635:18":{"kind":"call","span":{"start":[2635,18],"end":[2635,81],"bytes":[84290,84353]},"callee":null},"2637:3":{"kind":"call","span":{"start":[2637,3],"end":[2637,31],"bytes":[84360,84388]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult"},"2649:17":{"kind":"call","span":{"start":[2649,17],"end":[2649,65],"bytes":[84925,84973]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor"},"2651:18":{"kind":"call","span":{"start":[2651,18],"end":[2651,73],"bytes":[85009,85064]},"callee":null},"2653:3":{"kind":"call","span":{"start":[2653,3],"end":[2653,31],"bytes":[85071,85099]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult"},"2659:2":{"kind":"call","span":{"start":[2659,2],"end":[2663,5],"bytes":[85233,85406]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2664:13":{"kind":"call","span":{"start":[2664,13],"end":[2664,36],"bytes":[85419,85442]},"callee":null},"2666:17":{"kind":"call","span":{"start":[2666,17],"end":[2666,45],"bytes":[85476,85504]},"callee":null},"2668:8":{"kind":"call","span":{"start":[2668,8],"end":[2668,37],"bytes":[85515,85544]},"callee":null},"2674:15":{"kind":"call","span":{"start":[2674,15],"end":[2674,83],"bytes":[85849,85917]},"callee":null},"2675:17":{"kind":"call","span":{"start":[2675,17],"end":[2675,56],"bytes":[85948,85987]},"callee":null},"2679:2":{"kind":"call","span":{"start":[2679,2],"end":[2683,5],"bytes":[86056,86225]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2686:17":{"kind":"call","span":{"start":[2686,17],"end":[2686,52],"bytes":[86300,86335]},"callee":null},"2687:17":{"kind":"call","span":{"start":[2687,17],"end":[2687,52],"bytes":[86353,86388]},"callee":null},"2691:16":{"kind":"call","span":{"start":[2691,16],"end":[2691,57],"bytes":[86478,86519]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync"},"2693:17":{"kind":"call","span":{"start":[2693,17],"end":[2693,72],"bytes":[86553,86608]},"callee":null},"2695:2":{"kind":"call","span":{"start":[2695,2],"end":[2695,30],"bytes":[86613,86641]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult"},"2700:12":{"kind":"call","span":{"start":[2700,12],"end":[2700,24],"bytes":[86799,86811]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.rollback"},"2701:17":{"kind":"call","span":{"start":[2701,17],"end":[2701,67],"bytes":[86842,86892]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_1","kind":"function","span":{"start":[2525,1],"end":[2528,5],"bytes":[80903,81015]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_1","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2525,1],"end":[2525,23],"bytes":[80903,80925]}}],"body":{}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_2","kind":"function","span":{"start":[2544,1],"end":[2547,5],"bytes":[81595,81707]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_2","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2544,1],"end":[2544,23],"bytes":[81595,81617]}}],"body":{}},"closure_3":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_3","kind":"function","span":{"start":[2555,1],"end":[2558,5],"bytes":[81909,82021]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_3","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2555,1],"end":[2555,23],"bytes":[81909,81931]}}],"body":{}},"closure_4":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_4","kind":"function","span":{"start":[2571,1],"end":[2575,6],"bytes":[82408,82488]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_4","parameters":[],"body":{"2573:9":{"kind":"call","span":{"start":[2573,9],"end":[2573,28],"bytes":[82458,82477]},"callee":null}}},"closure_5":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_5","kind":"function","span":{"start":[2597,1],"end":[2600,5],"bytes":[83235,83347]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_5","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2597,1],"end":[2597,23],"bytes":[83235,83257]}}],"body":{}},"closure_6":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_6","kind":"function","span":{"start":[2619,1],"end":[2622,5],"bytes":[83815,83927]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_6","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2619,1],"end":[2619,23],"bytes":[83815,83837]}}],"body":{}},"closure_7":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_7","kind":"function","span":{"start":[2660,1],"end":[2663,5],"bytes":[85294,85406]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_7","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2660,1],"end":[2660,23],"bytes":[85294,85316]}}],"body":{}},"closure_8":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_8","kind":"function","span":{"start":[2668,1],"end":[2668,37],"bytes":[85508,85544]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_8","parameters":[],"body":{"2668:21":{"kind":"call","span":{"start":[2668,21],"end":[2668,33],"bytes":[85528,85540]},"callee":null}}},"closure_9":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor/github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_9","kind":"function","span":{"start":[2680,1],"end":[2683,5],"bytes":[86113,86225]},"signature":"github.com/benbjohnson/litestream.DB.checkpointWithExecutor.closure_9","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2680,1],"end":[2680,23],"bytes":[86113,86135]}}],"body":{}}}},"github.com/benbjohnson/litestream.DB.deleteLocalLTXFile":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.deleteLocalLTXFile","kind":"method","span":{"start":[620,1],"end":[629,2],"bytes":[22008,22271]},"signature":"github.com/benbjohnson/litestream.DB.deleteLocalLTXFile","parameters":[{"name":"level","type":"int","span":{"start":[620,1],"end":[620,79],"bytes":[22008,22086]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[620,1],"end":[620,79],"bytes":[22008,22086]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[620,1],"end":[620,79],"bytes":[22008,22086]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"621:10":{"kind":"call","span":{"start":[621,10],"end":[621,45],"bytes":[22096,22131]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"622:12":{"kind":"call","span":{"start":[622,12],"end":[622,27],"bytes":[22143,22158]},"callee":null},"622:44":{"kind":"call","span":{"start":[622,44],"end":[622,62],"bytes":[22175,22193]},"callee":null},"626:3":{"kind":"call","span":{"start":[626,3],"end":[626,26],"bytes":[22231,22254]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"}}},"github.com/benbjohnson/litestream.DB.deleteMetrics":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.deleteMetrics","kind":"method","span":{"start":[382,1],"end":[416,2],"bytes":[15025,16181]},"signature":"github.com/benbjohnson/litestream.DB.deleteMetrics","parameters":[],"metrics":{"cyclomatic":4},"body":{"383:2":{"kind":"call","span":{"start":[383,2],"end":[383,23],"bytes":[15058,15079]},"callee":null},"384:8":{"kind":"call","span":{"start":[384,8],"end":[384,31],"bytes":[15087,15110]},"callee":null},"390:10":{"kind":"call","span":{"start":[390,10],"end":[390,32],"bytes":[15191,15213]},"callee":null},"393:3":{"kind":"call","span":{"start":[393,3],"end":[393,41],"bytes":[15292,15330]},"callee":null},"394:3":{"kind":"call","span":{"start":[394,3],"end":[394,81],"bytes":[15333,15411]},"callee":null},"401:2":{"kind":"call","span":{"start":[401,2],"end":[401,39],"bytes":[15503,15540]},"callee":null},"404:2":{"kind":"call","span":{"start":[404,2],"end":[404,43],"bytes":[15587,15628]},"callee":null},"405:2":{"kind":"call","span":{"start":[405,2],"end":[405,44],"bytes":[15630,15672]},"callee":null},"406:2":{"kind":"call","span":{"start":[406,2],"end":[406,52],"bytes":[15674,15724]},"callee":null},"407:2":{"kind":"call","span":{"start":[407,2],"end":[407,46],"bytes":[15726,15770]},"callee":null},"408:2":{"kind":"call","span":{"start":[408,2],"end":[408,44],"bytes":[15772,15814]},"callee":null},"409:2":{"kind":"call","span":{"start":[409,2],"end":[409,49],"bytes":[15816,15863]},"callee":null},"410:2":{"kind":"call","span":{"start":[410,2],"end":[410,50],"bytes":[15865,15913]},"callee":null},"411:2":{"kind":"call","span":{"start":[411,2],"end":[411,45],"bytes":[15915,15958]},"callee":null},"412:2":{"kind":"call","span":{"start":[412,2],"end":[412,60],"bytes":[15960,16018]},"callee":null},"413:2":{"kind":"call","span":{"start":[413,2],"end":[413,50],"bytes":[16020,16068]},"callee":null},"414:2":{"kind":"call","span":{"start":[414,2],"end":[414,55],"bytes":[16070,16123]},"callee":null},"415:2":{"kind":"call","span":{"start":[415,2],"end":[415,56],"bytes":[16125,16179]},"callee":null}}},"github.com/benbjohnson/litestream.DB.detectFullCheckpoint":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.detectFullCheckpoint","kind":"method","span":{"start":[1939,1],"end":[1967,2],"bytes":[63389,64226]},"signature":"github.com/benbjohnson/litestream.DB.detectFullCheckpoint","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1939,1],"end":[1939,96],"bytes":[63389,63484]}},{"name":"knownSalts","type":"[][2]uint32","span":{"start":[1939,1],"end":[1939,96],"bytes":[63389,63484]}}],"return_type":"(bool, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"1940:18":{"kind":"call","span":{"start":[1940,18],"end":[1940,39],"bytes":[63502,63523]},"callee":null},"1940:26":{"kind":"call","span":{"start":[1940,26],"end":[1940,38],"bytes":[63510,63522]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1942:17":{"kind":"call","span":{"start":[1942,17],"end":[1942,53],"bytes":[63557,63593]},"callee":null},"1944:8":{"kind":"call","span":{"start":[1944,8],"end":[1944,23],"bytes":[63604,63619]},"callee":null},"1947:5":{"kind":"call","span":{"start":[1947,5],"end":[1947,20],"bytes":[63654,63669]},"callee":null},"1948:30":{"kind":"call","span":{"start":[1948,30],"end":[1948,45],"bytes":[63705,63720]},"callee":null},"1951:13":{"kind":"call","span":{"start":[1951,13],"end":[1951,90],"bytes":[63740,63817]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader"},"1951:35":{"kind":"call","span":{"start":[1951,35],"end":[1951,89],"bytes":[63762,63816]},"callee":null},"1953:17":{"kind":"call","span":{"start":[1953,17],"end":[1953,54],"bytes":[63851,63888]},"callee":null},"1955:12":{"kind":"call","span":{"start":[1955,12],"end":[1955,50],"bytes":[63903,63941]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.FrameSaltsUntil"},"1957:17":{"kind":"call","span":{"start":[1957,17],"end":[1957,57],"bytes":[63975,64015]},"callee":null},"1962:3":{"kind":"call","span":{"start":[1962,3],"end":[1962,18],"bytes":[64094,64109]},"callee":null},"1966:9":{"kind":"call","span":{"start":[1966,9],"end":[1966,15],"bytes":[64208,64214]},"callee":null}}},"github.com/benbjohnson/litestream.DB.effectiveTruncatePageN":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.effectiveTruncatePageN","kind":"method","span":{"start":[1575,1],"end":[1580,2],"bytes":[51251,51383]},"signature":"github.com/benbjohnson/litestream.DB.effectiveTruncatePageN","parameters":[],"return_type":"int","metrics":{"cyclomatic":2},"body":{}},"github.com/benbjohnson/litestream.DB.ensureWALExists":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists","kind":"method","span":{"start":[1644,1],"end":[1652,2],"bytes":[53444,53756]},"signature":"github.com/benbjohnson/litestream.DB.ensureWALExists","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1644,1],"end":[1644,65],"bytes":[53444,53508]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1646:16":{"kind":"call","span":{"start":[1646,16],"end":[1646,37],"bytes":[53561,53582]},"callee":null},"1646:24":{"kind":"call","span":{"start":[1646,24],"end":[1646,36],"bytes":[53569,53581]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1646:53":{"kind":"call","span":{"start":[1646,53],"end":[1646,62],"bytes":[53598,53607]},"callee":null},"1651:9":{"kind":"call","span":{"start":[1651,9],"end":[1651,34],"bytes":[53729,53754]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.bumpLitestreamSeq"}}},"github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold","kind":"method","span":{"start":[1569,1],"end":[1573,2],"bytes":[51023,51249]},"signature":"github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold","parameters":[{"name":"walSize","type":"int64","span":{"start":[1569,1],"end":[1569,61],"bytes":[51023,51083]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"1570:19":{"kind":"call","span":{"start":[1570,19],"end":[1570,46],"bytes":[51102,51129]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.effectiveTruncatePageN"},"1572:14":{"kind":"call","span":{"start":[1572,14],"end":[1572,69],"bytes":[51192,51247]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize"},"1572:26":{"kind":"call","span":{"start":[1572,26],"end":[1572,45],"bytes":[51204,51223]},"callee":null},"1572:47":{"kind":"call","span":{"start":[1572,47],"end":[1572,68],"bytes":[51225,51246]},"callee":null}}},"github.com/benbjohnson/litestream.DB.execCheckpoint":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint","kind":"method","span":{"start":[2710,1],"end":[2754,2],"bytes":[87105,88720]},"signature":"github.com/benbjohnson/litestream.DB.execCheckpoint","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2710,1],"end":[2710,92],"bytes":[87105,87196]}},{"name":"mode","type":"string","span":{"start":[2710,1],"end":[2710,92],"bytes":[87105,87196]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"2717:7":{"kind":"call","span":{"start":[2717,7],"end":[2717,17],"bytes":[87319,87329]},"callee":null},"2718:8":{"kind":"call","span":{"start":[2718,8],"end":[2725,5],"bytes":[87337,87597]},"callee":null},"2729:12":{"kind":"call","span":{"start":[2729,12],"end":[2729,32],"bytes":[87754,87774]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.releaseReadLock"},"2730:13":{"kind":"call","span":{"start":[2730,13],"end":[2730,53],"bytes":[87801,87841]},"callee":null},"2732:8":{"kind":"call","span":{"start":[2732,8],"end":[2732,48],"bytes":[87852,87892]},"callee":null},"2743:12":{"kind":"call","span":{"start":[2743,12],"end":[2743,46],"bytes":[88324,88358]},"callee":null},"2746:2":{"kind":"call","span":{"start":[2746,2],"end":[2746,104],"bytes":[88425,88527]},"callee":null},"2746:56":{"kind":"call","span":{"start":[2746,56],"end":[2746,103],"bytes":[88479,88526]},"callee":null},"2749:12":{"kind":"call","span":{"start":[2749,12],"end":[2749,35],"bytes":[88602,88625]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.acquireReadLock"},"2750:13":{"kind":"call","span":{"start":[2750,13],"end":[2750,55],"bytes":[88652,88694]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint/github.com/benbjohnson/litestream.DB.execCheckpoint.closure_1","kind":"function","span":{"start":[2718,1],"end":[2725,5],"bytes":[87330,87597]},"signature":"github.com/benbjohnson/litestream.DB.execCheckpoint.closure_1","parameters":[],"body":{"2720:3":{"kind":"call","span":{"start":[2720,3],"end":[2720,40],"bytes":[87392,87429]},"callee":null},"2722:4":{"kind":"call","span":{"start":[2722,4],"end":[2722,46],"bytes":[87457,87499]},"callee":null},"2724:24":{"kind":"call","span":{"start":[2724,24],"end":[2724,67],"bytes":[87533,87576]},"callee":null},"2724:3":{"kind":"call","span":{"start":[2724,3],"end":[2724,83],"bytes":[87512,87592]},"callee":null},"2724:69":{"kind":"call","span":{"start":[2724,69],"end":[2724,82],"bytes":[87578,87591]},"callee":null}}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint/github.com/benbjohnson/litestream.DB.execCheckpoint.closure_2","kind":"function","span":{"start":[2732,1],"end":[2732,48],"bytes":[87845,87892]},"signature":"github.com/benbjohnson/litestream.DB.execCheckpoint.closure_2","parameters":[],"body":{"2732:21":{"kind":"call","span":{"start":[2732,21],"end":[2732,44],"bytes":[87865,87888]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.finishSyncDiag":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.finishSyncDiag","kind":"method","span":{"start":[517,1],"end":[528,2],"bytes":[19046,19291]},"signature":"github.com/benbjohnson/litestream.DB.finishSyncDiag","parameters":[{"name":"err","type":"error","span":{"start":[517,1],"end":[517,42],"bytes":[19046,19087]}}],"metrics":{"cyclomatic":3},"body":{"518:2":{"kind":"call","span":{"start":[518,2],"end":[518,20],"bytes":[19089,19107]},"callee":null},"519:8":{"kind":"call","span":{"start":[519,8],"end":[519,28],"bytes":[19115,19135]},"callee":null},"524:26":{"kind":"call","span":{"start":[524,26],"end":[524,36],"bytes":[19227,19237]},"callee":null},"526:21":{"kind":"call","span":{"start":[526,21],"end":[526,32],"bytes":[19275,19286]},"callee":null}}},"github.com/benbjohnson/litestream.DB.finishSyncExecutorWait":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.finishSyncExecutorWait","kind":"method","span":{"start":[539,1],"end":[546,2],"bytes":[19512,19731]},"signature":"github.com/benbjohnson/litestream.DB.finishSyncExecutorWait","parameters":[],"metrics":{"cyclomatic":2},"body":{"540:2":{"kind":"call","span":{"start":[540,2],"end":[540,20],"bytes":[19554,19572]},"callee":null},"541:8":{"kind":"call","span":{"start":[541,8],"end":[541,28],"bytes":[19580,19600]},"callee":null}}},"github.com/benbjohnson/litestream.DB.init":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","kind":"method","span":{"start":[1085,1],"end":[1199,2],"bytes":[35148,38717]},"signature":"github.com/benbjohnson/litestream.DB.init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1085,1],"end":[1085,54],"bytes":[35148,35201]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":21},"body":{"1092:13":{"kind":"call","span":{"start":[1092,13],"end":[1092,29],"bytes":[35320,35336]},"callee":null},"1093:5":{"kind":"call","span":{"start":[1093,5],"end":[1093,23],"bytes":[35341,35359]},"callee":null},"1101:15":{"kind":"call","span":{"start":[1101,15],"end":[1101,45],"bytes":[35493,35523]},"callee":null},"1101:23":{"kind":"call","span":{"start":[1101,23],"end":[1101,44],"bytes":[35501,35522]},"callee":null},"1106:9":{"kind":"call","span":{"start":[1106,9],"end":[1107,42],"bytes":[35580,35699]},"callee":null},"1107:12":{"kind":"call","span":{"start":[1107,12],"end":[1107,41],"bytes":[35669,35698]},"callee":null},"1109:18":{"kind":"call","span":{"start":[1109,18],"end":[1109,41],"bytes":[35718,35741]},"callee":null},"1114:12":{"kind":"call","span":{"start":[1114,12],"end":[1114,33],"bytes":[35865,35886]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setPersistWAL"},"1115:10":{"kind":"call","span":{"start":[1115,10],"end":[1115,48],"bytes":[35910,35948]},"callee":null},"1119:17":{"kind":"call","span":{"start":[1119,17],"end":[1119,33],"bytes":[36045,36061]},"callee":null},"1120:10":{"kind":"call","span":{"start":[1120,10],"end":[1120,56],"bytes":[36085,36131]},"callee":null},"1125:8":{"kind":"call","span":{"start":[1125,8],"end":[1132,5],"bytes":[36231,36353]},"callee":null},"1137:12":{"kind":"call","span":{"start":[1137,12],"end":[1137,68],"bytes":[36519,36575]},"callee":null},"1140:10":{"kind":"call","span":{"start":[1140,10],"end":[1140,56],"bytes":[36651,36697]},"callee":null},"1145:15":{"kind":"call","span":{"start":[1145,15],"end":[1145,122],"bytes":[36823,36930]},"callee":null},"1146:10":{"kind":"call","span":{"start":[1146,10],"end":[1146,61],"bytes":[36954,37005]},"callee":null},"1151:15":{"kind":"call","span":{"start":[1151,15],"end":[1151,98],"bytes":[37167,37250]},"callee":null},"1152:10":{"kind":"call","span":{"start":[1152,10],"end":[1152,62],"bytes":[37274,37326]},"callee":null},"1157:12":{"kind":"call","span":{"start":[1157,12],"end":[1157,35],"bytes":[37438,37461]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.acquireReadLock"},"1158:10":{"kind":"call","span":{"start":[1158,10],"end":[1158,50],"bytes":[37485,37525]},"callee":null},"1162:12":{"kind":"call","span":{"start":[1162,12],"end":[1162,59],"bytes":[37561,37608]},"callee":null},"1163:10":{"kind":"call","span":{"start":[1163,10],"end":[1163,47],"bytes":[37651,37688]},"callee":null},"1165:10":{"kind":"call","span":{"start":[1165,10],"end":[1165,61],"bytes":[37728,37779]},"callee":null},"1169:12":{"kind":"call","span":{"start":[1169,12],"end":[1169,54],"bytes":[37839,37881]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll"},"1174:12":{"kind":"call","span":{"start":[1174,12],"end":[1174,35],"bytes":[37969,37992]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists"},"1175:10":{"kind":"call","span":{"start":[1175,10],"end":[1175,50],"bytes":[38016,38056]},"callee":null},"1181:13":{"kind":"call","span":{"start":[1181,13],"end":[1181,47],"bytes":[38224,38258]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica"},"1182:11":{"kind":"call","span":{"start":[1182,11],"end":[1182,63],"bytes":[38283,38335]},"callee":null},"1195:3":{"kind":"call","span":{"start":[1195,3],"end":[1195,27],"bytes":[38675,38699]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Start"}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init/github.com/benbjohnson/litestream.DB.init.closure_1","kind":"function","span":{"start":[1125,1],"end":[1132,5],"bytes":[36224,36353]},"signature":"github.com/benbjohnson/litestream.DB.init.closure_1","parameters":[],"body":{"1127:8":{"kind":"call","span":{"start":[1127,8],"end":[1127,28],"bytes":[36265,36285]},"callee":null},"1128:4":{"kind":"call","span":{"start":[1128,4],"end":[1128,17],"bytes":[36289,36302]},"callee":null},"1129:4":{"kind":"call","span":{"start":[1129,4],"end":[1129,16],"bytes":[36306,36318]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.initMetrics":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.initMetrics","kind":"method","span":{"start":[356,1],"end":[378,2],"bytes":[13702,14876]},"signature":"github.com/benbjohnson/litestream.DB.initMetrics","parameters":[],"metrics":{"cyclomatic":2},"body":{"357:2":{"kind":"call","span":{"start":[357,2],"end":[357,23],"bytes":[13733,13754]},"callee":null},"358:8":{"kind":"call","span":{"start":[358,8],"end":[358,31],"bytes":[13762,13785]},"callee":null},"364:27":{"kind":"call","span":{"start":[364,27],"end":[364,49],"bytes":[13913,13935]},"callee":null},"366:19":{"kind":"call","span":{"start":[366,19],"end":[366,58],"bytes":[13958,13997]},"callee":null},"367:20":{"kind":"call","span":{"start":[367,20],"end":[367,60],"bytes":[14017,14057]},"callee":null},"368:28":{"kind":"call","span":{"start":[368,28],"end":[368,76],"bytes":[14085,14133]},"callee":null},"369:17":{"kind":"call","span":{"start":[369,17],"end":[369,59],"bytes":[14150,14192]},"callee":null},"370:20":{"kind":"call","span":{"start":[370,20],"end":[370,60],"bytes":[14212,14252]},"callee":null},"371:25":{"kind":"call","span":{"start":[371,25],"end":[371,70],"bytes":[14277,14322]},"callee":null},"372:26":{"kind":"call","span":{"start":[372,26],"end":[372,72],"bytes":[14348,14394]},"callee":null},"373:21":{"kind":"call","span":{"start":[373,21],"end":[373,62],"bytes":[14415,14456]},"callee":null},"374:29":{"kind":"call","span":{"start":[374,29],"end":[374,98],"bytes":[14485,14554]},"callee":null},"375:34":{"kind":"call","span":{"start":[375,34],"end":[375,108],"bytes":[14588,14662]},"callee":null},"376:35":{"kind":"call","span":{"start":[376,35],"end":[376,110],"bytes":[14697,14772]},"callee":null},"377:46":{"kind":"call","span":{"start":[377,46],"end":[377,102],"bytes":[14818,14874]},"callee":null}}},"github.com/benbjohnson/litestream.DB.invalidatePosCache":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache","kind":"method","span":{"start":[699,1],"end":[703,2],"bytes":[24221,24311]},"signature":"github.com/benbjohnson/litestream.DB.invalidatePosCache","parameters":[],"metrics":{"cyclomatic":1},"body":{"700:2":{"kind":"call","span":{"start":[700,2],"end":[700,15],"bytes":[24259,24272]},"callee":null},"702:2":{"kind":"call","span":{"start":[702,2],"end":[702,17],"bytes":[24294,24309]},"callee":null}}},"github.com/benbjohnson/litestream.DB.lastPageMatch":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lastPageMatch","kind":"method","span":{"start":[1899,1],"end":[1935,2],"bytes":[62116,63253]},"signature":"github.com/benbjohnson/litestream.DB.lastPageMatch","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1899,1],"end":[1899,115],"bytes":[62116,62230]}},{"name":"dec","type":"*github.com/superfly/ltx.Decoder","span":{"start":[1899,1],"end":[1899,115],"bytes":[62116,62230]}},{"name":"prevWALOffset","type":"int64","span":{"start":[1899,1],"end":[1899,115],"bytes":[62116,62230]}},{"name":"frameSize","type":"int64","span":{"start":[1899,1],"end":[1899,115],"bytes":[62116,62230]}}],"return_type":"(bool, error)","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"1904:16":{"kind":"call","span":{"start":[1904,16],"end":[1904,69],"bytes":[62307,62360]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALFileAt"},"1904:30":{"kind":"call","span":{"start":[1904,30],"end":[1904,42],"bytes":[62321,62333]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1906:17":{"kind":"call","span":{"start":[1906,17],"end":[1906,72],"bytes":[62394,62449]},"callee":null},"1908:10":{"kind":"call","span":{"start":[1908,10],"end":[1908,44],"bytes":[62462,62496]},"callee":null},"1909:12":{"kind":"call","span":{"start":[1909,12],"end":[1909,46],"bytes":[62508,62542]},"callee":null},"1910:12":{"kind":"call","span":{"start":[1910,12],"end":[1910,47],"bytes":[62554,62589]},"callee":null},"1913:15":{"kind":"call","span":{"start":[1913,15],"end":[1913,27],"bytes":[62641,62653]},"callee":null},"1913:50":{"kind":"call","span":{"start":[1913,50],"end":[1913,62],"bytes":[62676,62688]},"callee":null},"1918:22":{"kind":"call","span":{"start":[1918,22],"end":[1918,34],"bytes":[62815,62827]},"callee":null},"1918:9":{"kind":"call","span":{"start":[1918,9],"end":[1918,44],"bytes":[62802,62837]},"callee":null},"1921:13":{"kind":"call","span":{"start":[1921,13],"end":[1921,38],"bytes":[62882,62907]},"callee":null},"1921:40":{"kind":"call","span":{"start":[1921,40],"end":[1921,62],"bytes":[62909,62931]},"callee":null},"1924:18":{"kind":"call","span":{"start":[1924,18],"end":[1924,56],"bytes":[63027,63065]},"callee":null},"1930:7":{"kind":"call","span":{"start":[1930,7],"end":[1930,29],"bytes":[63146,63168]},"callee":null}}},"github.com/benbjohnson/litestream.DB.lockExec":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","kind":"method","span":{"start":[1310,1],"end":[1321,2],"bytes":[41797,42101]},"signature":"github.com/benbjohnson/litestream.DB.lockExec","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1310,1],"end":[1310,52],"bytes":[41797,41848]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"1311:5":{"kind":"call","span":{"start":[1311,5],"end":[1311,29],"bytes":[41853,41877]},"callee":null},"1314:2":{"kind":"call","span":{"start":[1314,2],"end":[1314,28],"bytes":[41897,41923]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncExecutorWait"},"1315:8":{"kind":"call","span":{"start":[1315,8],"end":[1315,35],"bytes":[41931,41958]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.finishSyncExecutorWait"},"1317:12":{"kind":"call","span":{"start":[1317,12],"end":[1317,38],"bytes":[41971,41997]},"callee":null},"1318:10":{"kind":"call","span":{"start":[1318,10],"end":[1318,73],"bytes":[42021,42084]},"callee":null},"1318:54":{"kind":"call","span":{"start":[1318,54],"end":[1318,72],"bytes":[42065,42083]},"callee":null}}},"github.com/benbjohnson/litestream.DB.monitor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.monitor","kind":"method","span":{"start":[3227,1],"end":[3311,2],"bytes":[103385,105989]},"signature":"github.com/benbjohnson/litestream.DB.monitor","parameters":[],"metrics":{"cyclomatic":17},"body":{"3228:12":{"kind":"call","span":{"start":[3228,12],"end":[3228,46],"bytes":[103422,103456]},"callee":null},"3229:8":{"kind":"call","span":{"start":[3229,8],"end":[3229,21],"bytes":[103464,103477]},"callee":null},"3239:10":{"kind":"call","span":{"start":[3239,10],"end":[3239,23],"bytes":[103663,103676]},"callee":null},"3247:11":{"kind":"call","span":{"start":[3247,11],"end":[3247,24],"bytes":[103816,103829]},"callee":null},"3249:11":{"kind":"call","span":{"start":[3249,11],"end":[3249,30],"bytes":[103852,103871]},"callee":null},"3259:13":{"kind":"call","span":{"start":[3259,13],"end":[3259,28],"bytes":[104299,104314]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync"},"3259:45":{"kind":"call","span":{"start":[3259,45],"end":[3259,73],"bytes":[104331,104359]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"3273:7":{"kind":"call","span":{"start":[3273,7],"end":[3273,30],"bytes":[104705,104728]},"callee":null},"3275:39":{"kind":"call","span":{"start":[3275,39],"end":[3275,62],"bytes":[104818,104841]},"callee":null},"3275:8":{"kind":"call","span":{"start":[3275,8],"end":[3275,35],"bytes":[104787,104814]},"callee":null},"3278:6":{"kind":"call","span":{"start":[3278,6],"end":[3284,44],"bytes":[104943,105246]},"callee":null},"3281:15":{"kind":"call","span":{"start":[3281,15],"end":[3281,39],"bytes":[105097,105121]},"callee":null},"3286:6":{"kind":"call","span":{"start":[3286,6],"end":[3289,26],"bytes":[105265,105385]},"callee":null},"3291:19":{"kind":"call","span":{"start":[3291,19],"end":[3291,29],"bytes":[105410,105420]},"callee":null},"3295:7":{"kind":"call","span":{"start":[3295,7],"end":[3295,27],"bytes":[105502,105522]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"3296:5":{"kind":"call","span":{"start":[3296,5],"end":[3296,81],"bytes":[105553,105629]},"callee":null},"3297:22":{"kind":"call","span":{"start":[3297,22],"end":[3297,49],"bytes":[105651,105678]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.removeTmpFiles"},"3298:6":{"kind":"call","span":{"start":[3298,6],"end":[3298,70],"bytes":[105705,105769]},"callee":null},"3306:4":{"kind":"call","span":{"start":[3306,4],"end":[3306,72],"bytes":[105876,105944]},"callee":null}}},"github.com/benbjohnson/litestream.DB.newSyncExecutor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor","kind":"method","span":{"start":[2004,1],"end":[2023,2],"bytes":[65124,65494]},"signature":"github.com/benbjohnson/litestream.DB.newSyncExecutor","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2004,1],"end":[2004,76],"bytes":[65124,65199]}}],"return_type":"(*github.com/benbjohnson/litestream.syncExecutor, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"2005:2":{"kind":"call","span":{"start":[2005,2],"end":[2005,14],"bytes":[65201,65213]},"callee":null},"2006:8":{"kind":"call","span":{"start":[2006,8],"end":[2006,22],"bytes":[65221,65235]},"callee":null},"2008:12":{"kind":"call","span":{"start":[2008,12],"end":[2008,24],"bytes":[65248,65260]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init"},"2014:14":{"kind":"call","span":{"start":[2014,14],"end":[2014,22],"bytes":[65354,65362]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"2016:15":{"kind":"call","span":{"start":[2016,15],"end":[2016,41],"bytes":[65394,65420]},"callee":null}}},"github.com/benbjohnson/litestream.DB.openLocalLTXFile":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.openLocalLTXFile","kind":"method","span":{"start":[614,1],"end":[616,2],"bytes":[21758,21906]},"signature":"github.com/benbjohnson/litestream.DB.openLocalLTXFile","parameters":[{"name":"level","type":"int","span":{"start":[614,1],"end":[614,94],"bytes":[21758,21851]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[614,1],"end":[614,94],"bytes":[21758,21851]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[614,1],"end":[614,94],"bytes":[21758,21851]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"615:17":{"kind":"call","span":{"start":[615,17],"end":[615,52],"bytes":[21868,21903]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"615:9":{"kind":"call","span":{"start":[615,9],"end":[615,53],"bytes":[21860,21904]},"callee":null}}},"github.com/benbjohnson/litestream.DB.releaseReadLock":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.releaseReadLock","kind":"method","span":{"start":[1270,1],"end":[1283,2],"bytes":[40770,41189]},"signature":"github.com/benbjohnson/litestream.DB.releaseReadLock","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1280:9":{"kind":"call","span":{"start":[1280,9],"end":[1280,25],"bytes":[41145,41161]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.rollback"}}},"github.com/benbjohnson/litestream.DB.setPersistWAL":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setPersistWAL","kind":"method","span":{"start":[1061,1],"end":[1081,2],"bytes":[34525,35020]},"signature":"github.com/benbjohnson/litestream.DB.setPersistWAL","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1061,1],"end":[1061,57],"bytes":[34525,34581]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1062:15":{"kind":"call","span":{"start":[1062,15],"end":[1062,30],"bytes":[34596,34611]},"callee":null},"1064:10":{"kind":"call","span":{"start":[1064,10],"end":[1064,47],"bytes":[34638,34675]},"callee":null},"1066:8":{"kind":"call","span":{"start":[1066,8],"end":[1066,20],"bytes":[34686,34698]},"callee":null},"1068:9":{"kind":"call","span":{"start":[1068,9],"end":[1080,4],"bytes":[34708,35018]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setPersistWAL/github.com/benbjohnson/litestream.DB.setPersistWAL.closure_1","kind":"function","span":{"start":[1068,1],"end":[1080,4],"bytes":[34700,35018]},"signature":"github.com/benbjohnson/litestream.DB.setPersistWAL.closure_1","parameters":[{"name":"driverConn","type":"interface{}","span":{"start":[1068,1],"end":[1068,54],"bytes":[34700,34753]}}],"return_type":"error","error_channel":["error"],"body":{"1071:11":{"kind":"call","span":{"start":[1071,11],"end":[1071,62],"bytes":[34819,34870]},"callee":null},"1074:13":{"kind":"call","span":{"start":[1074,13],"end":[1074,48],"bytes":[34888,34923]},"callee":null},"1076:11":{"kind":"call","span":{"start":[1076,11],"end":[1076,55],"bytes":[34952,34996]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.setSyncDiagPhase":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase","kind":"method","span":{"start":[504,1],"end":[515,2],"bytes":[18755,19044]},"signature":"github.com/benbjohnson/litestream.DB.setSyncDiagPhase","parameters":[{"name":"phase","type":"github.com/benbjohnson/litestream.diagPhase","span":{"start":[504,1],"end":[504,79],"bytes":[18755,18833]}},{"name":"updates","type":"[]func(*github.com/benbjohnson/litestream.diagState)","span":{"start":[504,1],"end":[504,79],"bytes":[18755,18833]},"is_variadic":true}],"metrics":{"cyclomatic":3},"body":{"505:2":{"kind":"call","span":{"start":[505,2],"end":[505,20],"bytes":[18835,18853]},"callee":null},"506:8":{"kind":"call","span":{"start":[506,8],"end":[506,28],"bytes":[18861,18881]},"callee":null},"511:26":{"kind":"call","span":{"start":[511,26],"end":[511,36],"bytes":[18972,18982]},"callee":null},"513:3":{"kind":"call","span":{"start":[513,3],"end":[513,23],"bytes":[19019,19039]},"callee":null}}},"github.com/benbjohnson/litestream.DB.snapshotPosition":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition","kind":"method","span":{"start":[2796,1],"end":[2833,2],"bytes":[89741,90845]},"signature":"github.com/benbjohnson/litestream.DB.snapshotPosition","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2796,1],"end":[2796,85],"bytes":[89741,89825]}}],"return_type":"(*github.com/benbjohnson/litestream.snapshotReadPosition, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"2797:12":{"kind":"call","span":{"start":[2797,12],"end":[2797,28],"bytes":[89837,89853]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec"},"2800:8":{"kind":"call","span":{"start":[2800,8],"end":[2800,29],"bytes":[89896,89917]},"callee":null},"2802:14":{"kind":"call","span":{"start":[2802,14],"end":[2802,27],"bytes":[89932,89945]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.PageSize"},"2803:14":{"kind":"call","span":{"start":[2803,14],"end":[2803,22],"bytes":[89959,89967]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"2806:3":{"kind":"call","span":{"start":[2806,3],"end":[2806,66],"bytes":[89991,90054]},"callee":null},"2810:15":{"kind":"call","span":{"start":[2810,15],"end":[2810,41],"bytes":[90157,90183]},"callee":null},"2813:23":{"kind":"call","span":{"start":[2813,23],"end":[2813,51],"bytes":[90210,90238]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset"},"2826:2":{"kind":"call","span":{"start":[2826,2],"end":[2826,18],"bytes":[90691,90707]},"callee":null}}},"github.com/benbjohnson/litestream.DB.snapshotReader":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotReader","kind":"method","span":{"start":[2876,1],"end":[2971,2],"bytes":[92366,95068]},"signature":"github.com/benbjohnson/litestream.DB.snapshotReader","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2876,1],"end":[2876,102],"bytes":[92366,92467]}},{"name":"pos","type":"*github.com/benbjohnson/litestream.snapshotReadPosition","span":{"start":[2876,1],"end":[2876,102],"bytes":[92366,92467]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"2877:2":{"kind":"call","span":{"start":[2877,2],"end":[2877,94],"bytes":[92469,92561]},"callee":null},"2877:38":{"kind":"call","span":{"start":[2877,38],"end":[2877,59],"bytes":[92505,92526]},"callee":null},"2881:13":{"kind":"call","span":{"start":[2881,13],"end":[2881,24],"bytes":[92632,92643]},"callee":null},"2885:12":{"kind":"call","span":{"start":[2885,12],"end":[2885,51],"bytes":[92693,92732]},"callee":null},"2885:19":{"kind":"call","span":{"start":[2885,19],"end":[2885,28],"bytes":[92700,92709]},"callee":null},"2885:31":{"kind":"call","span":{"start":[2885,31],"end":[2885,50],"bytes":[92712,92731]},"callee":null},"2888:12":{"kind":"call","span":{"start":[2888,12],"end":[2888,21],"bytes":[92835,92844]},"callee":null},"2889:5":{"kind":"call","span":{"start":[2889,5],"end":[2968,5],"bytes":[92849,95006]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotReader/github.com/benbjohnson/litestream.DB.snapshotReader.closure_1","kind":"function","span":{"start":[2889,1],"end":[2968,5],"bytes":[92845,95006]},"signature":"github.com/benbjohnson/litestream.DB.snapshotReader.closure_1","parameters":[],"body":{"2890:9":{"kind":"call","span":{"start":[2890,9],"end":[2890,20],"bytes":[92866,92877]},"callee":null},"2892:19":{"kind":"call","span":{"start":[2892,19],"end":[2892,40],"bytes":[92897,92918]},"callee":null},"2892:27":{"kind":"call","span":{"start":[2892,27],"end":[2892,39],"bytes":[92905,92917]},"callee":null},"2894:4":{"kind":"call","span":{"start":[2894,4],"end":[2894,26],"bytes":[92940,92962]},"callee":null},"2897:9":{"kind":"call","span":{"start":[2897,9],"end":[2897,24],"bytes":[92985,93000]},"callee":null},"2899:14":{"kind":"call","span":{"start":[2899,14],"end":[2899,91],"bytes":[93015,93092]},"callee":null},"2899:36":{"kind":"call","span":{"start":[2899,36],"end":[2899,90],"bytes":[93037,93091]},"callee":null},"2901:22":{"kind":"call","span":{"start":[2901,22],"end":[2901,59],"bytes":[93132,93169]},"callee":null},"2901:4":{"kind":"call","span":{"start":[2901,4],"end":[2901,60],"bytes":[93114,93170]},"callee":null},"2907:14":{"kind":"call","span":{"start":[2907,14],"end":[2907,36],"bytes":[93317,93339]},"callee":null},"2911:44":{"kind":"call","span":{"start":[2911,44],"end":[2911,69],"bytes":[93448,93473]},"callee":null},"2913:23":{"kind":"call","span":{"start":[2913,23],"end":[2913,54],"bytes":[93515,93546]},"callee":null},"2913:5":{"kind":"call","span":{"start":[2913,5],"end":[2913,55],"bytes":[93497,93547]},"callee":null},"2922:22":{"kind":"call","span":{"start":[2922,22],"end":[2922,128],"bytes":[93673,93779]},"callee":null},"2922:4":{"kind":"call","span":{"start":[2922,4],"end":[2922,129],"bytes":[93655,93780]},"callee":null},"2925:25":{"kind":"call","span":{"start":[2925,25],"end":[2925,98],"bytes":[93819,93892]},"callee":null},"2925:59":{"kind":"call","span":{"start":[2925,59],"end":[2925,97],"bytes":[93853,93891]},"callee":null},"2927:3":{"kind":"call","span":{"start":[2927,3],"end":[2934,22],"bytes":[93896,94123]},"callee":null},"2928:12":{"kind":"call","span":{"start":[2928,12],"end":[2928,33],"bytes":[93949,93970]},"callee":null},"2936:15":{"kind":"call","span":{"start":[2936,15],"end":[2936,33],"bytes":[94139,94157]},"callee":null},"2938:22":{"kind":"call","span":{"start":[2938,22],"end":[2938,60],"bytes":[94197,94235]},"callee":null},"2938:4":{"kind":"call","span":{"start":[2938,4],"end":[2938,61],"bytes":[94179,94236]},"callee":null},"2941:13":{"kind":"call","span":{"start":[2941,13],"end":[2953,5],"bytes":[94263,94600]},"callee":null},"2944:15":{"kind":"call","span":{"start":[2944,15],"end":[2944,35],"bytes":[94373,94393]},"callee":null},"2948:15":{"kind":"call","span":{"start":[2948,15],"end":[2948,25],"bytes":[94476,94486]},"callee":null},"2954:22":{"kind":"call","span":{"start":[2954,22],"end":[2954,71],"bytes":[94636,94685]},"callee":null},"2954:4":{"kind":"call","span":{"start":[2954,4],"end":[2954,72],"bytes":[94618,94686]},"callee":null},"2958:13":{"kind":"call","span":{"start":[2958,13],"end":[2958,66],"bytes":[94714,94767]},"callee":null},"2959:22":{"kind":"call","span":{"start":[2959,22],"end":[2959,63],"bytes":[94803,94844]},"callee":null},"2959:4":{"kind":"call","span":{"start":[2959,4],"end":[2959,64],"bytes":[94785,94845]},"callee":null},"2963:13":{"kind":"call","span":{"start":[2963,13],"end":[2963,24],"bytes":[94873,94884]},"callee":null},"2964:22":{"kind":"call","span":{"start":[2964,22],"end":[2964,71],"bytes":[94920,94969]},"callee":null},"2964:4":{"kind":"call","span":{"start":[2964,4],"end":[2964,72],"bytes":[94902,94970]},"callee":null},"2967:7":{"kind":"call","span":{"start":[2967,7],"end":[2967,17],"bytes":[94991,95001]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.snapshotWALEndOffset":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","kind":"method","span":{"start":[2838,1],"end":[2874,2],"bytes":[91069,92364]},"signature":"github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","parameters":[{"name":"pos","type":"github.com/superfly/ltx.Pos","span":{"start":[2838,1],"end":[2838,65],"bytes":[91069,91133]}}],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"2846:13":{"kind":"call","span":{"start":[2846,13],"end":[2846,46],"bytes":[91291,91324]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"2847:12":{"kind":"call","span":{"start":[2847,12],"end":[2847,28],"bytes":[91336,91352]},"callee":null},"2849:13":{"kind":"call","span":{"start":[2849,13],"end":[2849,85],"bytes":[91382,91454]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2849:45":{"kind":"call","span":{"start":[2849,45],"end":[2849,61],"bytes":[91414,91430]},"callee":null},"2849:63":{"kind":"call","span":{"start":[2849,63],"end":[2849,79],"bytes":[91432,91448]},"callee":null},"2851:8":{"kind":"call","span":{"start":[2851,8],"end":[2851,34],"bytes":[91465,91491]},"callee":null},"2853:9":{"kind":"call","span":{"start":[2853,9],"end":[2853,26],"bytes":[91501,91518]},"callee":null},"2854:12":{"kind":"call","span":{"start":[2854,12],"end":[2854,30],"bytes":[91530,91548]},"callee":null},"2855:13":{"kind":"call","span":{"start":[2855,13],"end":[2855,126],"bytes":[91575,91688]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2855:47":{"kind":"call","span":{"start":[2855,47],"end":[2855,63],"bytes":[91609,91625]},"callee":null},"2855:65":{"kind":"call","span":{"start":[2855,65],"end":[2855,81],"bytes":[91627,91643]},"callee":null},"2855:83":{"kind":"call","span":{"start":[2855,83],"end":[2855,125],"bytes":[91645,91687]},"callee":null},"2861:14":{"kind":"call","span":{"start":[2861,14],"end":[2861,41],"bytes":[91882,91909]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader"},"2861:28":{"kind":"call","span":{"start":[2861,28],"end":[2861,40],"bytes":[91896,91908]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"2862:27":{"kind":"call","span":{"start":[2862,27],"end":[2862,49],"bytes":[91936,91958]},"callee":null},"2862:5":{"kind":"call","span":{"start":[2862,5],"end":[2862,23],"bytes":[91914,91932]},"callee":null},"2862:53":{"kind":"call","span":{"start":[2862,53],"end":[2862,88],"bytes":[91962,91997]},"callee":null},"2865:13":{"kind":"call","span":{"start":[2865,13],"end":[2865,58],"bytes":[92064,92109]},"callee":null},"2867:11":{"kind":"call","span":{"start":[2867,11],"end":[2867,44],"bytes":[92123,92156]},"callee":null},"2868:11":{"kind":"call","span":{"start":[2868,11],"end":[2868,44],"bytes":[92167,92200]},"callee":null},"2869:14":{"kind":"call","span":{"start":[2869,14],"end":[2869,26],"bytes":[92214,92226]},"callee":null},"2869:48":{"kind":"call","span":{"start":[2869,48],"end":[2869,60],"bytes":[92248,92260]},"callee":null},"2873:34":{"kind":"call","span":{"start":[2873,34],"end":[2873,46],"bytes":[92337,92349]},"callee":null},"2873:9":{"kind":"call","span":{"start":[2873,9],"end":[2873,21],"bytes":[92312,92324]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset.closure_1","kind":"function","span":{"start":[2851,1],"end":[2851,34],"bytes":[91458,91491]},"signature":"github.com/benbjohnson/litestream.DB.snapshotWALEndOffset.closure_1","parameters":[],"body":{"2851:21":{"kind":"call","span":{"start":[2851,21],"end":[2851,30],"bytes":[91478,91487]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.sync":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","kind":"method","span":{"start":[2072,1],"end":[2360,2],"bytes":[66720,75776]},"signature":"github.com/benbjohnson/litestream.DB.sync","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2072,1],"end":[2072,151],"bytes":[66720,66870]}},{"name":"checkpointing","type":"bool","span":{"start":[2072,1],"end":[2072,151],"bytes":[66720,66870]}},{"name":"exec","type":"*github.com/benbjohnson/litestream.syncExecutor","span":{"start":[2072,1],"end":[2072,151],"bytes":[66720,66870]}},{"name":"info","type":"github.com/benbjohnson/litestream.syncInfo","span":{"start":[2072,1],"end":[2072,151],"bytes":[66720,66870]}},{"name":"maxSyncWALBytes","type":"int64","span":{"start":[2072,1],"end":[2072,151],"bytes":[66720,66870]}}],"return_type":"(github.com/benbjohnson/litestream.syncResult, error)","error_channel":["error"],"metrics":{"cyclomatic":40},"body":{"2081:2":{"kind":"call","span":{"start":[2081,2],"end":[2086,5],"bytes":[67119,67269]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2088:14":{"kind":"call","span":{"start":[2088,14],"end":[2088,39],"bytes":[67284,67309]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"2091:11":{"kind":"call","span":{"start":[2091,11],"end":[2091,24],"bytes":[67340,67353]},"callee":null},"2095:13":{"kind":"call","span":{"start":[2095,13],"end":[2095,45],"bytes":[67415,67447]},"callee":null},"2098:13":{"kind":"call","span":{"start":[2098,13],"end":[2098,44],"bytes":[67487,67518]},"callee":null},"2101:13":{"kind":"call","span":{"start":[2101,13],"end":[2101,51],"bytes":[67558,67596]},"callee":null},"2103:2":{"kind":"call","span":{"start":[2103,2],"end":[2103,37],"bytes":[67601,67636]},"callee":null},"2107:3":{"kind":"call","span":{"start":[2107,3],"end":[2107,19],"bytes":[67742,67758]},"callee":null},"2108:9":{"kind":"call","span":{"start":[2108,9],"end":[2108,27],"bytes":[67767,67785]},"callee":null},"2111:13":{"kind":"call","span":{"start":[2111,13],"end":[2111,24],"bytes":[67802,67813]},"callee":null},"2115:10":{"kind":"call","span":{"start":[2115,10],"end":[2115,19],"bytes":[67864,67873]},"callee":null},"2116:12":{"kind":"call","span":{"start":[2116,12],"end":[2116,50],"bytes":[67885,67923]},"callee":null},"2116:19":{"kind":"call","span":{"start":[2116,19],"end":[2116,28],"bytes":[67892,67901]},"callee":null},"2116:31":{"kind":"call","span":{"start":[2116,31],"end":[2116,49],"bytes":[67904,67922]},"callee":null},"2118:18":{"kind":"call","span":{"start":[2118,18],"end":[2118,39],"bytes":[67942,67963]},"callee":null},"2118:26":{"kind":"call","span":{"start":[2118,26],"end":[2118,38],"bytes":[67950,67962]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"2122:8":{"kind":"call","span":{"start":[2122,8],"end":[2122,23],"bytes":[68012,68027]},"callee":null},"2124:21":{"kind":"call","span":{"start":[2124,21],"end":[2124,75],"bytes":[68049,68103]},"callee":null},"2127:16":{"kind":"call","span":{"start":[2127,16],"end":[2127,54],"bytes":[68173,68211]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader"},"2128:19":{"kind":"call","span":{"start":[2128,19],"end":[2128,56],"bytes":[68244,68281]},"callee":null},"2133:108":{"kind":"call","span":{"start":[2133,108],"end":[2133,133],"bytes":[68486,68511]},"callee":null},"2133:16":{"kind":"call","span":{"start":[2133,16],"end":[2133,106],"bytes":[68394,68484]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReaderWithOffset"},"2134:4":{"kind":"call","span":{"start":[2134,4],"end":[2134,101],"bytes":[68517,68614]},"callee":null},"2136:17":{"kind":"call","span":{"start":[2136,17],"end":[2136,55],"bytes":[68662,68700]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader"},"2137:20":{"kind":"call","span":{"start":[2137,20],"end":[2137,61],"bytes":[68734,68775]},"callee":null},"2140:19":{"kind":"call","span":{"start":[2140,19],"end":[2140,68],"bytes":[68824,68873]},"callee":null},"2145:2":{"kind":"call","span":{"start":[2145,2],"end":[2150,5],"bytes":[68953,69103]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2154:49":{"kind":"call","span":{"start":[2154,49],"end":[2154,81],"bytes":[69201,69233]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap"},"2156:18":{"kind":"call","span":{"start":[2156,18],"end":[2156,49],"bytes":[69268,69299]},"callee":null},"2166:18":{"kind":"call","span":{"start":[2166,18],"end":[2166,123],"bytes":[69458,69563]},"callee":null},"2166:2":{"kind":"call","span":{"start":[2166,2],"end":[2166,124],"bytes":[69442,69564]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert"},"2167:2":{"kind":"call","span":{"start":[2167,2],"end":[2173,5],"bytes":[69566,69737]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2177:3":{"kind":"call","span":{"start":[2177,3],"end":[2177,43],"bytes":[69788,69828]},"callee":null},"2177:31":{"kind":"call","span":{"start":[2177,31],"end":[2177,42],"bytes":[69816,69827]},"callee":null},"2182:3":{"kind":"call","span":{"start":[2182,3],"end":[2182,86],"bytes":[69936,70019]},"callee":null},"2187:12":{"kind":"call","span":{"start":[2187,12],"end":[2187,68],"bytes":[70090,70146]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll"},"2187:30":{"kind":"call","span":{"start":[2187,30],"end":[2187,55],"bytes":[70108,70133]},"callee":null},"2188:6":{"kind":"call","span":{"start":[2188,6],"end":[2188,26],"bytes":[70166,70186]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2189:19":{"kind":"call","span":{"start":[2189,19],"end":[2189,129],"bytes":[70207,70317]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2189:62":{"kind":"call","span":{"start":[2189,62],"end":[2189,74],"bytes":[70250,70262]},"callee":null},"2189:76":{"kind":"call","span":{"start":[2189,76],"end":[2189,88],"bytes":[70264,70276]},"callee":null},"2189:90":{"kind":"call","span":{"start":[2189,90],"end":[2189,128],"bytes":[70278,70316]},"callee":null},"2194:18":{"kind":"call","span":{"start":[2194,18],"end":[2194,85],"bytes":[70364,70431]},"callee":null},"2196:6":{"kind":"call","span":{"start":[2196,6],"end":[2196,26],"bytes":[70454,70474]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2197:19":{"kind":"call","span":{"start":[2197,19],"end":[2197,128],"bytes":[70495,70604]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2197:61":{"kind":"call","span":{"start":[2197,61],"end":[2197,73],"bytes":[70537,70549]},"callee":null},"2197:75":{"kind":"call","span":{"start":[2197,75],"end":[2197,87],"bytes":[70551,70563]},"callee":null},"2197:89":{"kind":"call","span":{"start":[2197,89],"end":[2197,127],"bytes":[70565,70603]},"callee":null},"2199:18":{"kind":"call","span":{"start":[2199,18],"end":[2199,59],"bytes":[70626,70667]},"callee":null},"2201:8":{"kind":"call","span":{"start":[2201,8],"end":[2201,47],"bytes":[70678,70717]},"callee":null},"2202:8":{"kind":"call","span":{"start":[2202,8],"end":[2202,40],"bytes":[70725,70757]},"callee":null},"2204:14":{"kind":"call","span":{"start":[2204,14],"end":[2204,44],"bytes":[70772,70802]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo"},"2205:6":{"kind":"call","span":{"start":[2205,6],"end":[2205,37],"bytes":[70808,70839]},"callee":null},"2207:2":{"kind":"call","span":{"start":[2207,2],"end":[2213,21],"bytes":[70842,71030]},"callee":null},"2208:11":{"kind":"call","span":{"start":[2208,11],"end":[2208,24],"bytes":[70909,70922]},"callee":null},"2215:15":{"kind":"call","span":{"start":[2215,15],"end":[2215,25],"bytes":[71046,71056]},"callee":null},"2216:14":{"kind":"call","span":{"start":[2216,14],"end":[2216,37],"bytes":[71070,71093]},"callee":null},"2218:18":{"kind":"call","span":{"start":[2218,18],"end":[2218,56],"bytes":[71128,71166]},"callee":null},"2220:12":{"kind":"call","span":{"start":[2220,12],"end":[2232,4],"bytes":[71181,71496]},"callee":null},"2223:14":{"kind":"call","span":{"start":[2223,14],"end":[2223,33],"bytes":[71288,71307]},"callee":null},"2227:14":{"kind":"call","span":{"start":[2227,14],"end":[2227,35],"bytes":[71381,71402]},"callee":null},"2233:6":{"kind":"call","span":{"start":[2233,6],"end":[2233,26],"bytes":[71516,71536]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2234:19":{"kind":"call","span":{"start":[2234,19],"end":[2234,129],"bytes":[71557,71667]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2234:62":{"kind":"call","span":{"start":[2234,62],"end":[2234,74],"bytes":[71600,71612]},"callee":null},"2234:76":{"kind":"call","span":{"start":[2234,76],"end":[2234,88],"bytes":[71614,71626]},"callee":null},"2234:90":{"kind":"call","span":{"start":[2234,90],"end":[2234,128],"bytes":[71628,71666]},"callee":null},"2236:18":{"kind":"call","span":{"start":[2236,18],"end":[2236,58],"bytes":[71689,71729]},"callee":null},"2242:3":{"kind":"call","span":{"start":[2242,3],"end":[2248,6],"bytes":[71880,72044]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2249:13":{"kind":"call","span":{"start":[2249,13],"end":[2249,66],"bytes":[72057,72110]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.writeLTXFromDB"},"2250:7":{"kind":"call","span":{"start":[2250,7],"end":[2250,27],"bytes":[72131,72151]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2251:20":{"kind":"call","span":{"start":[2251,20],"end":[2251,130],"bytes":[72173,72283]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2251:63":{"kind":"call","span":{"start":[2251,63],"end":[2251,75],"bytes":[72216,72228]},"callee":null},"2251:77":{"kind":"call","span":{"start":[2251,77],"end":[2251,89],"bytes":[72230,72242]},"callee":null},"2251:91":{"kind":"call","span":{"start":[2251,91],"end":[2251,129],"bytes":[72244,72282]},"callee":null},"2253:19":{"kind":"call","span":{"start":[2253,19],"end":[2253,59],"bytes":[72307,72347]},"callee":null},"2256:3":{"kind":"call","span":{"start":[2256,3],"end":[2262,6],"bytes":[72364,72530]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2263:13":{"kind":"call","span":{"start":[2263,13],"end":[2263,84],"bytes":[72543,72614]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.writeLTXFromWAL"},"2264:7":{"kind":"call","span":{"start":[2264,7],"end":[2264,27],"bytes":[72635,72655]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2265:20":{"kind":"call","span":{"start":[2265,20],"end":[2265,130],"bytes":[72677,72787]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2265:63":{"kind":"call","span":{"start":[2265,63],"end":[2265,75],"bytes":[72720,72732]},"callee":null},"2265:77":{"kind":"call","span":{"start":[2265,77],"end":[2265,89],"bytes":[72734,72746]},"callee":null},"2265:91":{"kind":"call","span":{"start":[2265,91],"end":[2265,129],"bytes":[72748,72786]},"callee":null},"2267:19":{"kind":"call","span":{"start":[2267,19],"end":[2267,60],"bytes":[72811,72852]},"callee":null},"2272:2":{"kind":"call","span":{"start":[2272,2],"end":[2275,4],"bytes":[72915,73011]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2276:12":{"kind":"call","span":{"start":[2276,12],"end":[2276,23],"bytes":[73023,73034]},"callee":null},"2277:6":{"kind":"call","span":{"start":[2277,6],"end":[2277,26],"bytes":[73054,73074]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2278:19":{"kind":"call","span":{"start":[2278,19],"end":[2278,129],"bytes":[73095,73205]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2278:62":{"kind":"call","span":{"start":[2278,62],"end":[2278,74],"bytes":[73138,73150]},"callee":null},"2278:76":{"kind":"call","span":{"start":[2278,76],"end":[2278,88],"bytes":[73152,73164]},"callee":null},"2278:90":{"kind":"call","span":{"start":[2278,90],"end":[2278,128],"bytes":[73166,73204]},"callee":null},"2280:18":{"kind":"call","span":{"start":[2280,18],"end":[2280,58],"bytes":[73227,73267]},"callee":null},"2284:2":{"kind":"call","span":{"start":[2284,2],"end":[2287,4],"bytes":[73300,73396]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2288:12":{"kind":"call","span":{"start":[2288,12],"end":[2288,26],"bytes":[73408,73422]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile/github.com/benbjohnson/litestream.ltxStagingFile.Sync"},"2289:6":{"kind":"call","span":{"start":[2289,6],"end":[2289,26],"bytes":[73442,73462]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2290:19":{"kind":"call","span":{"start":[2290,19],"end":[2290,128],"bytes":[73483,73592]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2290:61":{"kind":"call","span":{"start":[2290,61],"end":[2290,73],"bytes":[73525,73537]},"callee":null},"2290:75":{"kind":"call","span":{"start":[2290,75],"end":[2290,87],"bytes":[73539,73551]},"callee":null},"2290:89":{"kind":"call","span":{"start":[2290,89],"end":[2290,127],"bytes":[73553,73591]},"callee":null},"2292:18":{"kind":"call","span":{"start":[2292,18],"end":[2292,54],"bytes":[73614,73650]},"callee":null},"2294:12":{"kind":"call","span":{"start":[2294,12],"end":[2294,27],"bytes":[73665,73680]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile/github.com/benbjohnson/litestream.ltxStagingFile.Close"},"2295:6":{"kind":"call","span":{"start":[2295,6],"end":[2295,26],"bytes":[73700,73720]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError"},"2296:19":{"kind":"call","span":{"start":[2296,19],"end":[2296,129],"bytes":[73741,73851]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"2296:62":{"kind":"call","span":{"start":[2296,62],"end":[2296,74],"bytes":[73784,73796]},"callee":null},"2296:76":{"kind":"call","span":{"start":[2296,76],"end":[2296,88],"bytes":[73798,73810]},"callee":null},"2296:90":{"kind":"call","span":{"start":[2296,90],"end":[2296,128],"bytes":[73812,73850]},"callee":null},"2298:18":{"kind":"call","span":{"start":[2298,18],"end":[2298,55],"bytes":[73873,73910]},"callee":null},"2302:2":{"kind":"call","span":{"start":[2302,2],"end":[2305,4],"bytes":[73958,74055]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2306:12":{"kind":"call","span":{"start":[2306,12],"end":[2306,44],"bytes":[74067,74099]},"callee":null},"2307:3":{"kind":"call","span":{"start":[2307,3],"end":[2307,28],"bytes":[74116,74141]},"callee":null},"2308:3":{"kind":"call","span":{"start":[2308,3],"end":[2308,34],"bytes":[74144,74175]},"callee":null},"2309:3":{"kind":"call","span":{"start":[2309,3],"end":[2309,30],"bytes":[74213,74240]},"callee":null},"2310:3":{"kind":"call","span":{"start":[2310,3],"end":[2310,26],"bytes":[74243,74266]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"},"2311:18":{"kind":"call","span":{"start":[2311,18],"end":[2311,56],"bytes":[74284,74322]},"callee":null},"2313:12":{"kind":"call","span":{"start":[2313,12],"end":[2313,53],"bytes":[74337,74378]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir"},"2313:30":{"kind":"call","span":{"start":[2313,30],"end":[2313,52],"bytes":[74355,74377]},"callee":null},"2314:3":{"kind":"call","span":{"start":[2314,3],"end":[2314,28],"bytes":[74395,74420]},"callee":null},"2315:3":{"kind":"call","span":{"start":[2315,3],"end":[2315,34],"bytes":[74423,74454]},"callee":null},"2316:3":{"kind":"call","span":{"start":[2316,3],"end":[2316,30],"bytes":[74492,74519]},"callee":null},"2317:3":{"kind":"call","span":{"start":[2317,3],"end":[2317,26],"bytes":[74522,74545]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache"},"2318:18":{"kind":"call","span":{"start":[2318,18],"end":[2318,53],"bytes":[74563,74598]},"callee":null},"2326:14":{"kind":"call","span":{"start":[2326,14],"end":[2326,24],"bytes":[74728,74738]},"callee":null},"2327:14":{"kind":"call","span":{"start":[2327,14],"end":[2327,21],"bytes":[74753,74760]},"callee":null},"2330:12":{"kind":"call","span":{"start":[2330,12],"end":[2330,30],"bytes":[74777,74795]},"callee":null},"2343:21":{"kind":"call","span":{"start":[2343,21],"end":[2343,37],"bytes":[75374,75390]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize"},"2348:2":{"kind":"call","span":{"start":[2348,2],"end":[2355,5],"bytes":[75500,75708]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"2357:2":{"kind":"call","span":{"start":[2357,2],"end":[2357,44],"bytes":[75711,75753]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_1","kind":"function","span":{"start":[2082,1],"end":[2086,5],"bytes":[67161,67269]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_1","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2082,1],"end":[2082,23],"bytes":[67161,67183]}}],"body":{}},"closure_10":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_10","kind":"function","span":{"start":[2302,1],"end":[2305,4],"bytes":[73957,74055]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_10","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2302,1],"end":[2302,62],"bytes":[73957,74018]}}],"body":{}},"closure_11":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_11","kind":"function","span":{"start":[2349,1],"end":[2355,5],"bytes":[75543,75708]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_11","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2349,1],"end":[2349,23],"bytes":[75543,75565]}}],"body":{}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_2","kind":"function","span":{"start":[2146,1],"end":[2150,5],"bytes":[68995,69103]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_2","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2146,1],"end":[2146,23],"bytes":[68995,69017]}}],"body":{}},"closure_3":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_3","kind":"function","span":{"start":[2168,1],"end":[2173,5],"bytes":[69611,69737]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_3","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2168,1],"end":[2168,23],"bytes":[69611,69633]}}],"body":{}},"closure_4":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_4","kind":"function","span":{"start":[2201,1],"end":[2201,47],"bytes":[70671,70717]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_4","parameters":[],"body":{"2201:21":{"kind":"call","span":{"start":[2201,21],"end":[2201,43],"bytes":[70691,70713]},"callee":null}}},"closure_5":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_5","kind":"function","span":{"start":[2202,1],"end":[2202,40],"bytes":[70718,70757]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_5","parameters":[],"body":{"2202:21":{"kind":"call","span":{"start":[2202,21],"end":[2202,36],"bytes":[70738,70753]},"callee":null}}},"closure_6":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_6","kind":"function","span":{"start":[2243,1],"end":[2248,6],"bytes":[71925,72044]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_6","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2243,1],"end":[2243,24],"bytes":[71925,71948]}}],"body":{}},"closure_7":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_7","kind":"function","span":{"start":[2257,1],"end":[2262,6],"bytes":[72410,72530]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_7","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2257,1],"end":[2257,24],"bytes":[72410,72433]}}],"body":{}},"closure_8":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_8","kind":"function","span":{"start":[2272,1],"end":[2275,4],"bytes":[72914,73011]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_8","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2272,1],"end":[2272,61],"bytes":[72914,72974]}}],"body":{}},"closure_9":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync/github.com/benbjohnson/litestream.DB.sync.closure_9","kind":"function","span":{"start":[2284,1],"end":[2287,4],"bytes":[73299,73396]},"signature":"github.com/benbjohnson/litestream.DB.sync.closure_9","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[2284,1],"end":[2284,61],"bytes":[73299,73359]}}],"body":{}}}},"github.com/benbjohnson/litestream.DB.syncLocked":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","kind":"method","span":{"start":[1331,1],"end":[1410,2],"bytes":[42342,44853]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1331,1],"end":[1331,102],"bytes":[42342,42443]}},{"name":"maxSyncWALBytes","type":"int64","span":{"start":[1331,1],"end":[1331,102],"bytes":[42342,42443]}}],"return_type":"(github.com/benbjohnson/litestream.syncResult, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"1332:2":{"kind":"call","span":{"start":[1332,2],"end":[1332,30],"bytes":[42445,42473]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncDiag"},"1333:8":{"kind":"call","span":{"start":[1333,8],"end":[1333,43],"bytes":[42481,42516]},"callee":null},"1336:7":{"kind":"call","span":{"start":[1336,7],"end":[1336,17],"bytes":[42554,42564]},"callee":null},"1337:8":{"kind":"call","span":{"start":[1337,8],"end":[1348,5],"bytes":[42572,42819]},"callee":null},"1350:15":{"kind":"call","span":{"start":[1350,15],"end":[1350,38],"bytes":[42835,42858]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor"},"1354:3":{"kind":"call","span":{"start":[1354,3],"end":[1354,45],"bytes":[42924,42966]},"callee":null},"1357:8":{"kind":"call","span":{"start":[1357,8],"end":[1357,40],"bytes":[42998,43030]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor"},"1360:2":{"kind":"call","span":{"start":[1360,2],"end":[1362,4],"bytes":[43078,43199]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1363:12":{"kind":"call","span":{"start":[1363,12],"end":[1363,35],"bytes":[43211,43234]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists"},"1364:18":{"kind":"call","span":{"start":[1364,18],"end":[1364,58],"bytes":[43266,43306]},"callee":null},"1367:2":{"kind":"call","span":{"start":[1367,2],"end":[1369,4],"bytes":[43312,43409]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1370:16":{"kind":"call","span":{"start":[1370,16],"end":[1370,79],"bytes":[43425,43488]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor"},"1374:2":{"kind":"call","span":{"start":[1374,2],"end":[1374,30],"bytes":[43531,43559]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult"},"1385:49":{"kind":"call","span":{"start":[1385,49],"end":[1385,96],"bytes":[44031,44078]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold"},"1386:3":{"kind":"call","span":{"start":[1386,3],"end":[1389,5],"bytes":[44083,44241]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1390:13":{"kind":"call","span":{"start":[1390,13],"end":[1390,84],"bytes":[44254,44325]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded"},"1391:19":{"kind":"call","span":{"start":[1391,19],"end":[1391,52],"bytes":[44358,44391]},"callee":null},"1395:2":{"kind":"call","span":{"start":[1395,2],"end":[1398,4],"bytes":[44401,44551]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1401:19":{"kind":"call","span":{"start":[1401,19],"end":[1401,41],"bytes":[44624,44646]},"callee":null},"1401:2":{"kind":"call","span":{"start":[1401,2],"end":[1401,42],"bytes":[44607,44647]},"callee":null},"1404:16":{"kind":"call","span":{"start":[1404,16],"end":[1404,32],"bytes":[44694,44710]},"callee":null},"1405:22":{"kind":"call","span":{"start":[1405,22],"end":[1405,40],"bytes":[44746,44764]},"callee":null},"1405:3":{"kind":"call","span":{"start":[1405,3],"end":[1405,41],"bytes":[44727,44765]},"callee":null},"1405:30":{"kind":"call","span":{"start":[1405,30],"end":[1405,39],"bytes":[44754,44763]},"callee":null},"1407:2":{"kind":"call","span":{"start":[1407,2],"end":[1407,62],"bytes":[44770,44830]},"callee":null},"1407:22":{"kind":"call","span":{"start":[1407,22],"end":[1407,61],"bytes":[44790,44829]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked/github.com/benbjohnson/litestream.DB.syncLocked.closure_1","kind":"function","span":{"start":[1333,1],"end":[1333,43],"bytes":[42474,42516]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked.closure_1","parameters":[],"body":{"1333:17":{"kind":"call","span":{"start":[1333,17],"end":[1333,39],"bytes":[42490,42512]},"callee":null}}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked/github.com/benbjohnson/litestream.DB.syncLocked.closure_2","kind":"function","span":{"start":[1337,1],"end":[1348,5],"bytes":[42565,42819]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked.closure_2","parameters":[],"body":{"1338:3":{"kind":"call","span":{"start":[1338,3],"end":[1338,24],"bytes":[42583,42604]},"callee":null},"1340:4":{"kind":"call","span":{"start":[1340,4],"end":[1340,30],"bytes":[42626,42652]},"callee":null},"1342:6":{"kind":"call","span":{"start":[1342,6],"end":[1342,26],"bytes":[42662,42682]},"callee":null},"1343:4":{"kind":"call","span":{"start":[1343,4],"end":[1343,27],"bytes":[42688,42711]},"callee":null},"1345:4":{"kind":"call","span":{"start":[1345,4],"end":[1345,27],"bytes":[42726,42749]},"callee":null},"1347:3":{"kind":"call","span":{"start":[1347,3],"end":[1347,61],"bytes":[42756,42814]},"callee":null},"1347:47":{"kind":"call","span":{"start":[1347,47],"end":[1347,60],"bytes":[42800,42813]},"callee":null}}},"closure_3":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked/github.com/benbjohnson/litestream.DB.syncLocked.closure_3","kind":"function","span":{"start":[1360,1],"end":[1362,4],"bytes":[43077,43199]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked.closure_3","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[1360,1],"end":[1360,62],"bytes":[43077,43138]}}],"body":{}},"closure_4":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked/github.com/benbjohnson/litestream.DB.syncLocked.closure_4","kind":"function","span":{"start":[1367,1],"end":[1369,4],"bytes":[43311,43409]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked.closure_4","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[1367,1],"end":[1367,66],"bytes":[43311,43376]}}],"body":{}},"closure_5":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked/github.com/benbjohnson/litestream.DB.syncLocked.closure_5","kind":"function","span":{"start":[1386,1],"end":[1389,5],"bytes":[44081,44241]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked.closure_5","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[1386,1],"end":[1386,72],"bytes":[44081,44152]}}],"body":{}},"closure_6":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked/github.com/benbjohnson/litestream.DB.syncLocked.closure_6","kind":"function","span":{"start":[1395,1],"end":[1398,4],"bytes":[44400,44551]},"signature":"github.com/benbjohnson/litestream.DB.syncLocked.closure_6","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[1395,1],"end":[1395,66],"bytes":[44400,44465]}}],"body":{}}}},"github.com/benbjohnson/litestream.DB.syncOnce":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncOnce","kind":"method","span":{"start":[1301,1],"end":[1308,2],"bytes":[41559,41795]},"signature":"github.com/benbjohnson/litestream.DB.syncOnce","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1301,1],"end":[1301,89],"bytes":[41559,41647]}},{"name":"maxSyncWALBytes","type":"int64","span":{"start":[1301,1],"end":[1301,89],"bytes":[41559,41647]}}],"return_type":"(github.com/benbjohnson/litestream.syncResult, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1302:12":{"kind":"call","span":{"start":[1302,12],"end":[1302,28],"bytes":[41659,41675]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec"},"1305:8":{"kind":"call","span":{"start":[1305,8],"end":[1305,29],"bytes":[41727,41748]},"callee":null},"1307:9":{"kind":"call","span":{"start":[1307,9],"end":[1307,44],"bytes":[41758,41793]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked"}}},"github.com/benbjohnson/litestream.DB.syncReplicaWithRetry":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncReplicaWithRetry","kind":"method","span":{"start":[944,1],"end":[1057,2],"bytes":[31215,34369]},"signature":"github.com/benbjohnson/litestream.DB.syncReplicaWithRetry","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[944,1],"end":[944,64],"bytes":[31215,31278]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":24},"body":{"1002:13":{"kind":"call","span":{"start":[1002,13],"end":[1002,37],"bytes":[32598,32622]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync"},"1004:5":{"kind":"call","span":{"start":[1004,5],"end":[1006,40],"bytes":[32661,32780]},"callee":null},"1006:18":{"kind":"call","span":{"start":[1006,18],"end":[1006,39],"bytes":[32758,32779]},"callee":null},"1015:10":{"kind":"call","span":{"start":[1015,10],"end":[1015,28],"bytes":[32916,32934]},"callee":null},"1016:4":{"kind":"call","span":{"start":[1016,4],"end":[1019,22],"bytes":[32939,33078]},"callee":null},"1018:17":{"kind":"call","span":{"start":[1018,17],"end":[1018,38],"bytes":[33034,33055]},"callee":null},"1020:11":{"kind":"call","span":{"start":[1020,11],"end":[1020,86],"bytes":[33089,33164]},"callee":null},"1022:4":{"kind":"call","span":{"start":[1022,4],"end":[1025,22],"bytes":[33186,33325]},"callee":null},"1024:17":{"kind":"call","span":{"start":[1024,17],"end":[1024,38],"bytes":[33281,33302]},"callee":null},"1026:11":{"kind":"call","span":{"start":[1026,11],"end":[1026,79],"bytes":[33336,33404]},"callee":null},"1032:4":{"kind":"call","span":{"start":[1032,4],"end":[1036,53],"bytes":[33508,33723]},"callee":null},"1035:16":{"kind":"call","span":{"start":[1035,16],"end":[1035,37],"bytes":[33648,33669]},"callee":null},"1036:18":{"kind":"call","span":{"start":[1036,18],"end":[1036,52],"bytes":[33688,33722]},"callee":null},"1036:29":{"kind":"call","span":{"start":[1036,29],"end":[1036,51],"bytes":[33699,33721]},"callee":null},"1038:4":{"kind":"call","span":{"start":[1038,4],"end":[1042,53],"bytes":[33738,33924]},"callee":null},"1041:16":{"kind":"call","span":{"start":[1041,16],"end":[1041,37],"bytes":[33849,33870]},"callee":null},"1042:18":{"kind":"call","span":{"start":[1042,18],"end":[1042,52],"bytes":[33889,33923]},"callee":null},"1042:29":{"kind":"call","span":{"start":[1042,29],"end":[1042,51],"bytes":[33900,33922]},"callee":null},"1047:10":{"kind":"call","span":{"start":[1047,10],"end":[1047,30],"bytes":[34006,34026]},"callee":null},"1048:10":{"kind":"call","span":{"start":[1048,10],"end":[1048,28],"bytes":[34037,34055]},"callee":null},"1049:11":{"kind":"call","span":{"start":[1049,11],"end":[1049,86],"bytes":[34067,34142]},"callee":null},"1051:4":{"kind":"call","span":{"start":[1051,4],"end":[1053,39],"bytes":[34164,34281]},"callee":null},"1053:17":{"kind":"call","span":{"start":[1053,17],"end":[1053,38],"bytes":[34259,34280]},"callee":null},"1054:11":{"kind":"call","span":{"start":[1054,11],"end":[1054,79],"bytes":[34292,34360]},"callee":null},"954:10":{"kind":"call","span":{"start":[954,10],"end":[954,30],"bytes":[31470,31490]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync"},"963:33":{"kind":"call","span":{"start":[963,33],"end":[963,66],"bytes":[31682,31715]},"callee":null},"964:8":{"kind":"call","span":{"start":[964,8],"end":[964,24],"bytes":[31723,31739]},"callee":null},"971:25":{"kind":"call","span":{"start":[971,25],"end":[971,56],"bytes":[31989,32020]},"callee":null},"972:6":{"kind":"call","span":{"start":[972,6],"end":[979,6],"bytes":[32026,32140]},"callee":null,"is_goroutine":true},"984:15":{"kind":"call","span":{"start":[984,15],"end":[984,25],"bytes":[32192,32202]},"callee":null},"991:5":{"kind":"call","span":{"start":[991,5],"end":[993,40],"bytes":[32328,32456]},"callee":null},"993:18":{"kind":"call","span":{"start":[993,18],"end":[993,39],"bytes":[32434,32455]},"callee":null},"994:12":{"kind":"call","span":{"start":[994,12],"end":[994,80],"bytes":[32468,32536]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncReplicaWithRetry/github.com/benbjohnson/litestream.DB.syncReplicaWithRetry.closure_1","kind":"function","span":{"start":[972,1],"end":[979,6],"bytes":[32021,32140]},"signature":"github.com/benbjohnson/litestream.DB.syncReplicaWithRetry.closure_1","parameters":[],"body":{"975:5":{"kind":"call","span":{"start":[975,5],"end":[975,17],"bytes":[32070,32082]},"callee":null},"976:11":{"kind":"call","span":{"start":[976,11],"end":[976,29],"bytes":[32093,32111]},"callee":null},"977:5":{"kind":"call","span":{"start":[977,5],"end":[977,17],"bytes":[32117,32129]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.verify":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verify","kind":"method","span":{"start":[1741,1],"end":[1751,2],"bytes":[56519,56779]},"signature":"github.com/benbjohnson/litestream.DB.verify","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1741,1],"end":[1741,89],"bytes":[56519,56607]}},{"name":"state","type":"*github.com/benbjohnson/litestream.syncState","span":{"start":[1741,1],"end":[1741,89],"bytes":[56519,56607]}}],"return_type":"(github.com/benbjohnson/litestream.syncInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1742:14":{"kind":"call","span":{"start":[1742,14],"end":[1742,22],"bytes":[56621,56629]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"1744:16":{"kind":"call","span":{"start":[1744,16],"end":[1744,42],"bytes":[56662,56688]},"callee":null},"1747:9":{"kind":"call","span":{"start":[1747,9],"end":[1750,4],"bytes":[56701,56777]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor"}}},"github.com/benbjohnson/litestream.DB.verifyAndSync":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSync","kind":"method","span":{"start":[1412,1],"end":[1422,2],"bytes":[44855,45168]},"signature":"github.com/benbjohnson/litestream.DB.verifyAndSync","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1412,1],"end":[1412,109],"bytes":[44855,44963]}},{"name":"checkpointing","type":"bool","span":{"start":[1412,1],"end":[1412,109],"bytes":[44855,44963]}},{"name":"state","type":"*github.com/benbjohnson/litestream.syncState","span":{"start":[1412,1],"end":[1412,109],"bytes":[44855,44963]}}],"return_type":"(github.com/benbjohnson/litestream.syncResult, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1413:14":{"kind":"call","span":{"start":[1413,14],"end":[1413,22],"bytes":[44977,44985]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"1415:24":{"kind":"call","span":{"start":[1415,24],"end":[1415,50],"bytes":[45026,45052]},"callee":null},"1418:9":{"kind":"call","span":{"start":[1418,9],"end":[1421,7],"bytes":[45065,45166]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor"}}},"github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","kind":"method","span":{"start":[1424,1],"end":[1465,2],"bytes":[45170,46694]},"signature":"github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1424,1],"end":[1424,146],"bytes":[45170,45315]}},{"name":"checkpointing","type":"bool","span":{"start":[1424,1],"end":[1424,146],"bytes":[45170,45315]}},{"name":"exec","type":"*github.com/benbjohnson/litestream.syncExecutor","span":{"start":[1424,1],"end":[1424,146],"bytes":[45170,45315]}},{"name":"maxSyncWALBytes","type":"int64","span":{"start":[1424,1],"end":[1424,146],"bytes":[45170,45315]}}],"return_type":"(github.com/benbjohnson/litestream.syncResult, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"1425:2":{"kind":"call","span":{"start":[1425,2],"end":[1428,4],"bytes":[45317,45465]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1437:22":{"kind":"call","span":{"start":[1437,22],"end":[1437,38],"bytes":[45823,45839]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize"},"1439:25":{"kind":"call","span":{"start":[1439,25],"end":[1439,68],"bytes":[45882,45925]},"callee":null},"1446:2":{"kind":"call","span":{"start":[1446,2],"end":[1446,38],"bytes":[46107,46143]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1447:15":{"kind":"call","span":{"start":[1447,15],"end":[1447,47],"bytes":[46158,46190]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor"},"1449:24":{"kind":"call","span":{"start":[1449,24],"end":[1449,70],"bytes":[46231,46277]},"callee":null},"1452:2":{"kind":"call","span":{"start":[1452,2],"end":[1457,4],"bytes":[46283,46493]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase"},"1458:17":{"kind":"call","span":{"start":[1458,17],"end":[1458,73],"bytes":[46510,46566]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync"},"1460:24":{"kind":"call","span":{"start":[1460,24],"end":[1460,51],"bytes":[46607,46634]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor.closure_1","kind":"function","span":{"start":[1425,1],"end":[1428,4],"bytes":[45316,45465]},"signature":"github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor.closure_1","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[1425,1],"end":[1425,60],"bytes":[45316,45375]}}],"body":{}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor.closure_2","kind":"function","span":{"start":[1452,1],"end":[1457,4],"bytes":[46282,46493]},"signature":"github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor.closure_2","parameters":[{"name":"s","type":"*github.com/benbjohnson/litestream.diagState","span":{"start":[1452,1],"end":[1452,60],"bytes":[46282,46341]}}],"body":{}}}},"github.com/benbjohnson/litestream.DB.verifyWithExecutor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","kind":"method","span":{"start":[1753,1],"end":[1896,2],"bytes":[56781,62028]},"signature":"github.com/benbjohnson/litestream.DB.verifyWithExecutor","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1753,1],"end":[1753,103],"bytes":[56781,56883]}},{"name":"exec","type":"*github.com/benbjohnson/litestream.syncExecutor","span":{"start":[1753,1],"end":[1753,103],"bytes":[56781,56883]}}],"return_type":"(github.com/benbjohnson/litestream.syncInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":20},"body":{"1754:15":{"kind":"call","span":{"start":[1754,15],"end":[1754,54],"bytes":[56898,56937]},"callee":null},"1763:13":{"kind":"call","span":{"start":[1763,13],"end":[1763,56],"bytes":[57113,57156]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"1764:18":{"kind":"call","span":{"start":[1764,18],"end":[1764,34],"bytes":[57174,57190]},"callee":null},"1766:16":{"kind":"call","span":{"start":[1766,16],"end":[1766,98],"bytes":[57223,57305]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"1766:48":{"kind":"call","span":{"start":[1766,48],"end":[1766,69],"bytes":[57255,57276]},"callee":null},"1766:71":{"kind":"call","span":{"start":[1766,71],"end":[1766,92],"bytes":[57278,57299]},"callee":null},"1768:8":{"kind":"call","span":{"start":[1768,8],"end":[1768,40],"bytes":[57316,57348]},"callee":null},"1770:9":{"kind":"call","span":{"start":[1770,9],"end":[1770,32],"bytes":[57358,57381]},"callee":null},"1771:12":{"kind":"call","span":{"start":[1771,12],"end":[1771,30],"bytes":[57393,57411]},"callee":null},"1773:13":{"kind":"call","span":{"start":[1773,13],"end":[1773,136],"bytes":[57479,57602]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"1773:47":{"kind":"call","span":{"start":[1773,47],"end":[1773,68],"bytes":[57513,57534]},"callee":null},"1773:70":{"kind":"call","span":{"start":[1773,70],"end":[1773,91],"bytes":[57536,57557]},"callee":null},"1773:93":{"kind":"call","span":{"start":[1773,93],"end":[1773,135],"bytes":[57559,57601]},"callee":null},"1776:16":{"kind":"call","span":{"start":[1776,16],"end":[1776,28],"bytes":[57643,57655]},"callee":null},"1776:41":{"kind":"call","span":{"start":[1776,41],"end":[1776,53],"bytes":[57668,57680]},"callee":null},"1777:15":{"kind":"call","span":{"start":[1777,15],"end":[1777,27],"bytes":[57703,57715]},"callee":null},"1778:15":{"kind":"call","span":{"start":[1778,15],"end":[1778,27],"bytes":[57739,57751]},"callee":null},"1779:20":{"kind":"call","span":{"start":[1779,20],"end":[1779,32],"bytes":[57780,57792]},"callee":null},"1782:16":{"kind":"call","span":{"start":[1782,16],"end":[1782,37],"bytes":[57895,57916]},"callee":null},"1782:24":{"kind":"call","span":{"start":[1782,24],"end":[1782,36],"bytes":[57903,57915]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1783:16":{"kind":"call","span":{"start":[1783,16],"end":[1783,52],"bytes":[57946,57982]},"callee":null},"1784:26":{"kind":"call","span":{"start":[1784,26],"end":[1784,35],"bytes":[58008,58017]},"callee":null},"1792:16":{"kind":"call","span":{"start":[1792,16],"end":[1792,43],"bytes":[58389,58416]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader"},"1792:30":{"kind":"call","span":{"start":[1792,30],"end":[1792,42],"bytes":[58403,58415]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1794:18":{"kind":"call","span":{"start":[1794,18],"end":[1794,82],"bytes":[58453,58517]},"callee":null},"1798:17":{"kind":"call","span":{"start":[1798,17],"end":[1798,50],"bytes":[58571,58604]},"callee":null},"1799:17":{"kind":"call","span":{"start":[1799,17],"end":[1799,50],"bytes":[58621,58654]},"callee":null},"1804:4":{"kind":"call","span":{"start":[1804,4],"end":[1806,29],"bytes":[58743,58897]},"callee":null},"1816:15":{"kind":"call","span":{"start":[1816,15],"end":[1816,42],"bytes":[59081,59108]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader"},"1816:29":{"kind":"call","span":{"start":[1816,29],"end":[1816,41],"bytes":[59095,59107]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1818:16":{"kind":"call","span":{"start":[1818,16],"end":[1818,61],"bytes":[59141,59186]},"callee":null},"1820:11":{"kind":"call","span":{"start":[1820,11],"end":[1820,45],"bytes":[59200,59234]},"callee":null},"1821:11":{"kind":"call","span":{"start":[1821,11],"end":[1821,45],"bytes":[59245,59279]},"callee":null},"1822:24":{"kind":"call","span":{"start":[1822,24],"end":[1822,36],"bytes":[59303,59315]},"callee":null},"1822:58":{"kind":"call","span":{"start":[1822,58],"end":[1822,70],"bytes":[59337,59349]},"callee":null},"1833:3":{"kind":"call","span":{"start":[1833,3],"end":[1833,73],"bytes":[59806,59876]},"callee":null},"1844:2":{"kind":"call","span":{"start":[1844,2],"end":[1844,83],"bytes":[60154,60235]},"callee":null},"1855:16":{"kind":"call","span":{"start":[1855,16],"end":[1855,93],"bytes":[60608,60685]},"callee":null},"1859:24":{"kind":"call","span":{"start":[1859,24],"end":[1859,76],"bytes":[60801,60853]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lastPageMatch"},"1861:16":{"kind":"call","span":{"start":[1861,16],"end":[1861,54],"bytes":[60886,60924]},"callee":null},"1870:2":{"kind":"call","span":{"start":[1870,2],"end":[1870,61],"bytes":[61217,61276]},"callee":null},"1875:3":{"kind":"call","span":{"start":[1875,3],"end":[1877,19],"bytes":[61450,61544]},"callee":null},"1882:104":{"kind":"call","span":{"start":[1882,104],"end":[1882,116],"bytes":[61720,61732]},"callee":null},"1882:23":{"kind":"call","span":{"start":[1882,23],"end":[1882,128],"bytes":[61639,61744]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.detectFullCheckpoint"},"1882:81":{"kind":"call","span":{"start":[1882,81],"end":[1882,93],"bytes":[61697,61709]},"callee":null},"1883:17":{"kind":"call","span":{"start":[1883,17],"end":[1883,62],"bytes":[61775,61820]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor/github.com/benbjohnson/litestream.DB.verifyWithExecutor.closure_1","kind":"function","span":{"start":[1768,1],"end":[1768,40],"bytes":[57309,57348]},"signature":"github.com/benbjohnson/litestream.DB.verifyWithExecutor.closure_1","parameters":[],"body":{"1768:21":{"kind":"call","span":{"start":[1768,21],"end":[1768,36],"bytes":[57329,57344]},"callee":null}}}}},"github.com/benbjohnson/litestream.DB.walFileSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize","kind":"method","span":{"start":[1622,1],"end":[1630,2],"bytes":[52670,52857]},"signature":"github.com/benbjohnson/litestream.DB.walFileSize","parameters":[],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"1623:13":{"kind":"call","span":{"start":[1623,13],"end":[1623,34],"bytes":[52727,52748]},"callee":null},"1623:21":{"kind":"call","span":{"start":[1623,21],"end":[1623,33],"bytes":[52735,52747]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath"},"1624:5":{"kind":"call","span":{"start":[1624,5],"end":[1624,23],"bytes":[52753,52771]},"callee":null},"1629:9":{"kind":"call","span":{"start":[1629,9],"end":[1629,18],"bytes":[52841,52850]},"callee":null}}},"github.com/benbjohnson/litestream.DB.writeLTXFromDB":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.writeLTXFromDB","kind":"method","span":{"start":[2362,1],"end":[2407,2],"bytes":[75778,77350]},"signature":"github.com/benbjohnson/litestream.DB.writeLTXFromDB","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2362,1],"end":[2362,135],"bytes":[75778,75912]}},{"name":"enc","type":"*github.com/superfly/ltx.Encoder","span":{"start":[2362,1],"end":[2362,135],"bytes":[75778,75912]}},{"name":"walFile","type":"*os.File","span":{"start":[2362,1],"end":[2362,135],"bytes":[75778,75912]}},{"name":"commit","type":"uint32","span":{"start":[2362,1],"end":[2362,135],"bytes":[75778,75912]}},{"name":"pageMap","type":"map[uint32]int64","span":{"start":[2362,1],"end":[2362,135],"bytes":[75778,75912]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"2363:14":{"kind":"call","span":{"start":[2363,14],"end":[2363,47],"bytes":[75926,75959]},"callee":null},"2363:27":{"kind":"call","span":{"start":[2363,27],"end":[2363,46],"bytes":[75939,75958]},"callee":null},"2364:10":{"kind":"call","span":{"start":[2364,10],"end":[2364,35],"bytes":[75969,75994]},"callee":null},"2366:14":{"kind":"call","span":{"start":[2366,14],"end":[2366,23],"bytes":[76009,76018]},"callee":null},"2373:10":{"kind":"call","span":{"start":[2373,10],"end":[2373,20],"bytes":[76163,76173]},"callee":null},"2374:11":{"kind":"call","span":{"start":[2374,11],"end":[2374,29],"bytes":[76185,76203]},"callee":null},"2380:4":{"kind":"call","span":{"start":[2380,4],"end":[2380,147],"bytes":[76311,76454]},"callee":null},"2380:76":{"kind":"call","span":{"start":[2380,76],"end":[2380,88],"bytes":[76383,76395]},"callee":null},"2382:17":{"kind":"call","span":{"start":[2382,17],"end":[2382,64],"bytes":[76472,76519]},"callee":null},"2383:12":{"kind":"call","span":{"start":[2383,12],"end":[2383,66],"bytes":[76545,76599]},"callee":null},"2384:19":{"kind":"call","span":{"start":[2384,19],"end":[2384,28],"bytes":[76618,76627]},"callee":null},"2385:12":{"kind":"call","span":{"start":[2385,12],"end":[2385,63],"bytes":[76641,76692]},"callee":null},"2388:14":{"kind":"call","span":{"start":[2388,14],"end":[2388,62],"bytes":[76712,76760]},"callee":null},"2389:12":{"kind":"call","span":{"start":[2389,12],"end":[2389,67],"bytes":[76786,76841]},"callee":null},"2394:13":{"kind":"call","span":{"start":[2394,13],"end":[2394,26],"bytes":[76876,76889]},"callee":null},"2394:29":{"kind":"call","span":{"start":[2394,29],"end":[2394,47],"bytes":[76892,76910]},"callee":null},"2395:3":{"kind":"call","span":{"start":[2395,3],"end":[2395,103],"bytes":[76913,77013]},"callee":null},"2398:16":{"kind":"call","span":{"start":[2398,16],"end":[2398,41],"bytes":[77083,77108]},"callee":null},"2399:11":{"kind":"call","span":{"start":[2399,11],"end":[2399,61],"bytes":[77133,77183]},"callee":null},"2401:13":{"kind":"call","span":{"start":[2401,13],"end":[2401,61],"bytes":[77200,77248]},"callee":null},"2402:11":{"kind":"call","span":{"start":[2402,11],"end":[2402,66],"bytes":[77273,77328]},"callee":null}}},"github.com/benbjohnson/litestream.DB.writeLTXFromWAL":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.writeLTXFromWAL","kind":"method","span":{"start":[2409,1],"end":[2466,2],"bytes":[77352,79300]},"signature":"github.com/benbjohnson/litestream.DB.writeLTXFromWAL","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[2409,1],"end":[2409,148],"bytes":[77352,77499]}},{"name":"enc","type":"*github.com/superfly/ltx.Encoder","span":{"start":[2409,1],"end":[2409,148],"bytes":[77352,77499]}},{"name":"walFile","type":"*os.File","span":{"start":[2409,1],"end":[2409,148],"bytes":[77352,77499]}},{"name":"prevCommit","type":"uint32","span":{"start":[2409,1],"end":[2409,148],"bytes":[77352,77499]}},{"name":"commit","type":"uint32","span":{"start":[2409,1],"end":[2409,148],"bytes":[77352,77499]}},{"name":"pageMap","type":"map[uint32]int64","span":{"start":[2409,1],"end":[2409,148],"bytes":[77352,77499]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":16},"body":{"2411:11":{"kind":"call","span":{"start":[2411,11],"end":[2411,42],"bytes":[77588,77619]},"callee":null},"2411:29":{"kind":"call","span":{"start":[2411,29],"end":[2411,41],"bytes":[77606,77618]},"callee":null},"2413:11":{"kind":"call","span":{"start":[2413,11],"end":[2413,30],"bytes":[77659,77678]},"callee":null},"2415:14":{"kind":"call","span":{"start":[2415,14],"end":[2415,47],"bytes":[77695,77728]},"callee":null},"2415:27":{"kind":"call","span":{"start":[2415,27],"end":[2415,46],"bytes":[77708,77727]},"callee":null},"2417:15":{"kind":"call","span":{"start":[2417,15],"end":[2417,25],"bytes":[77769,77779]},"callee":null},"2425:12":{"kind":"call","span":{"start":[2425,12],"end":[2425,31],"bytes":[77942,77961]},"callee":null},"2427:21":{"kind":"call","span":{"start":[2427,21],"end":[2427,31],"bytes":[77986,77996]},"callee":null},"2428:4":{"kind":"call","span":{"start":[2428,4],"end":[2432,22],"bytes":[78030,78196]},"callee":null},"2429:13":{"kind":"call","span":{"start":[2429,13],"end":[2429,25],"bytes":[78100,78112]},"callee":null},"2435:2":{"kind":"call","span":{"start":[2435,2],"end":[2435,20],"bytes":[78205,78223]},"callee":null},"2437:10":{"kind":"call","span":{"start":[2437,10],"end":[2437,35],"bytes":[78234,78259]},"callee":null},"2440:10":{"kind":"call","span":{"start":[2440,10],"end":[2440,20],"bytes":[78310,78320]},"callee":null},"2441:11":{"kind":"call","span":{"start":[2441,11],"end":[2441,29],"bytes":[78332,78350]},"callee":null},"2445:4":{"kind":"call","span":{"start":[2445,4],"end":[2445,148],"bytes":[78408,78552]},"callee":null},"2445:76":{"kind":"call","span":{"start":[2445,76],"end":[2445,88],"bytes":[78480,78492]},"callee":null},"2447:17":{"kind":"call","span":{"start":[2447,17],"end":[2447,64],"bytes":[78570,78617]},"callee":null},"2448:12":{"kind":"call","span":{"start":[2448,12],"end":[2448,66],"bytes":[78643,78697]},"callee":null},"2449:19":{"kind":"call","span":{"start":[2449,19],"end":[2449,28],"bytes":[78716,78725]},"callee":null},"2450:12":{"kind":"call","span":{"start":[2450,12],"end":[2450,63],"bytes":[78739,78790]},"callee":null},"2453:14":{"kind":"call","span":{"start":[2453,14],"end":[2453,27],"bytes":[78820,78833]},"callee":null},"2453:30":{"kind":"call","span":{"start":[2453,30],"end":[2453,48],"bytes":[78836,78854]},"callee":null},"2454:4":{"kind":"call","span":{"start":[2454,4],"end":[2454,155],"bytes":[78858,79009]},"callee":null},"2454:81":{"kind":"call","span":{"start":[2454,81],"end":[2454,93],"bytes":[78935,78947]},"callee":null},"2456:17":{"kind":"call","span":{"start":[2456,17],"end":[2456,42],"bytes":[79027,79052]},"callee":null},"2457:12":{"kind":"call","span":{"start":[2457,12],"end":[2457,62],"bytes":[79078,79128]},"callee":null},"2461:13":{"kind":"call","span":{"start":[2461,13],"end":[2461,61],"bytes":[79151,79199]},"callee":null},"2462:11":{"kind":"call","span":{"start":[2462,11],"end":[2462,66],"bytes":[79224,79279]},"callee":null}}}},"fields":{"BusyTimeout":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/BusyTimeout","kind":"field","type":"time.Duration","span":{"start":[157,1],"end":[157,27],"bytes":[5538,5564]}},"CheckpointInterval":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/CheckpointInterval","kind":"field","type":"time.Duration","span":{"start":[145,1],"end":[145,34],"bytes":[5123,5156]}},"Done":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/Done","kind":"field","type":"\u003c-chan struct{}","span":{"start":[105,1],"end":[105,24],"bytes":[3368,3391]}},"L0Retention":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/L0Retention","kind":"field","type":"time.Duration","span":{"start":[160,1],"end":[160,27],"bytes":[5642,5668]}},"Logger":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/Logger","kind":"field","type":"*log/slog.Logger","span":{"start":[192,1],"end":[192,21],"bytes":[6903,6923]}},"MaxSyncWALBytes":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/MaxSyncWALBytes","kind":"field","type":"int64","span":{"start":[154,1],"end":[154,23],"bytes":[5467,5489]}},"MinCheckpointPageN":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/MinCheckpointPageN","kind":"field","type":"int","span":{"start":[127,1],"end":[127,24],"bytes":[4322,4345]}},"MonitorInterval":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/MonitorInterval","kind":"field","type":"time.Duration","span":{"start":[148,1],"end":[148,31],"bytes":[5201,5231]}},"Replica":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/Replica","kind":"field","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[174,1],"end":[174,18],"bytes":[6207,6224]}},"RetentionEnabled":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/RetentionEnabled","kind":"field","type":"bool","span":{"start":[170,1],"end":[170,23],"bytes":[6107,6129]}},"ShutdownSyncInterval":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/ShutdownSyncInterval","kind":"field","type":"time.Duration","span":{"start":[184,1],"end":[184,36],"bytes":[6584,6619]}},"ShutdownSyncTimeout":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/ShutdownSyncTimeout","kind":"field","type":"time.Duration","span":{"start":[183,1],"end":[183,36],"bytes":[6548,6583]}},"TruncatePageN":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/TruncatePageN","kind":"field","type":"int","span":{"start":[137,1],"end":[137,19],"bytes":[4817,4835]}},"VerifyCompaction":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/VerifyCompaction","kind":"field","type":"bool","span":{"start":[165,1],"end":[165,23],"bytes":[5864,5886]}},"cancel":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/cancel","kind":"field","type":"func()","span":{"start":[103,1],"end":[103,15],"bytes":[3330,3344]}},"checkpointErrorNCounterVec":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/checkpointErrorNCounterVec","kind":"field","type":"*github.com/prometheus/client_golang/prometheus.CounterVec","span":{"start":[118,1],"end":[118,52],"bytes":[3867,3918]}},"checkpointNCounterVec":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/checkpointNCounterVec","kind":"field","type":"*github.com/prometheus/client_golang/prometheus.CounterVec","span":{"start":[117,1],"end":[117,52],"bytes":[3815,3866]}},"checkpointSecondsCounterVec":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/checkpointSecondsCounterVec","kind":"field","type":"*github.com/prometheus/client_golang/prometheus.CounterVec","span":{"start":[119,1],"end":[119,52],"bytes":[3919,3970]}},"chkMu":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/chkMu","kind":"field","type":"sync.RWMutex","span":{"start":[77,1],"end":[77,44],"bytes":[2567,2610]}},"compactor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/compactor","kind":"field","type":"*github.com/benbjohnson/litestream.Compactor","span":{"start":[178,1],"end":[178,22],"bytes":[6358,6379]}},"ctx":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/ctx","kind":"field","type":"context.Context","span":{"start":[102,1],"end":[102,24],"bytes":[3306,3329]}},"db":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/db","kind":"field","type":"*database/sql.DB","span":{"start":[72,1],"end":[72,44],"bytes":[2308,2351]}},"dbSizeGauge":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/dbSizeGauge","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Gauge","span":{"start":[109,1],"end":[109,46],"bytes":[3439,3484]}},"dirInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/dirInfo","kind":"field","type":"os.FileInfo","span":{"start":[96,1],"end":[96,60],"bytes":[3051,3110]}},"diskFullGauge":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/diskFullGauge","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Gauge","span":{"start":[116,1],"end":[116,46],"bytes":[3769,3814]}},"execSem":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/execSem","kind":"field","type":"*golang.org/x/sync/semaphore.Weighted","span":{"start":[69,1],"end":[69,31],"bytes":[2173,2203]}},"f":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/f","kind":"field","type":"*os.File","span":{"start":[73,1],"end":[73,60],"bytes":[2352,2411]}},"fileInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/fileInfo","kind":"field","type":"os.FileInfo","span":{"start":[95,1],"end":[95,52],"bytes":[2999,3050]}},"lastSuccessfulSyncAt":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/lastSuccessfulSyncAt","kind":"field","type":"time.Time","span":{"start":[189,1],"end":[189,32],"bytes":[6793,6824]}},"lastSuccessfulSyncMu":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/lastSuccessfulSyncMu","kind":"field","type":"sync.RWMutex","span":{"start":[188,1],"end":[188,35],"bytes":[6758,6792]}},"maxLTXFileInfos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/maxLTXFileInfos","kind":"field","type":"struct{sync.Mutex; m map[int]*github.com/superfly/ltx.FileInfo}","span":{"start":[83,1],"end":[86,3],"bytes":[2769,2836]}},"metaPath":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/metaPath","kind":"field","type":"string","span":{"start":[71,1],"end":[71,59],"bytes":[2249,2307]}},"metricsRegistered":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/metricsRegistered","kind":"field","type":"bool","span":{"start":[108,1],"end":[108,34],"bytes":[3405,3438]}},"mu":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/mu","kind":"field","type":"sync.RWMutex","span":{"start":[68,1],"end":[68,24],"bytes":[2149,2172]}},"notify":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/notify","kind":"field","type":"chan struct{}","span":{"start":[76,1],"end":[76,49],"bytes":[2518,2566]}},"openLTXFile":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/openLTXFile","kind":"field","type":"func(name string, flag int, perm os.FileMode) (github.com/benbjohnson/litestream.ltxStagingFile, error)","span":{"start":[100,1],"end":[100,83],"bytes":[3222,3304]}},"opened":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/opened","kind":"field","type":"bool","span":{"start":[78,1],"end":[78,81],"bytes":[2611,2691]}},"pageSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/pageSize","kind":"field","type":"int","span":{"start":[75,1],"end":[75,48],"bytes":[2470,2517]}},"path":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/path","kind":"field","type":"string","span":{"start":[70,1],"end":[70,45],"bytes":[2204,2248]}},"pos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/pos","kind":"field","type":"struct{sync.Mutex; value *github.com/superfly/ltx.Pos}","span":{"start":[90,1],"end":[93,3],"bytes":[2951,2997]}},"rtx":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/rtx","kind":"field","type":"*database/sql.Tx","span":{"start":[74,1],"end":[74,58],"bytes":[2412,2469]}},"syncDiag":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/syncDiag","kind":"field","type":"github.com/benbjohnson/litestream.diagState","span":{"start":[80,1],"end":[80,21],"bytes":[2713,2733]}},"syncErrorNCounter":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/syncErrorNCounter","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Counter","span":{"start":[114,1],"end":[114,48],"bytes":[3673,3720]}},"syncNCounter":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/syncNCounter","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Counter","span":{"start":[113,1],"end":[113,48],"bytes":[3625,3672]}},"syncSecondsCounter":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/syncSecondsCounter","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Counter","span":{"start":[115,1],"end":[115,48],"bytes":[3721,3768]}},"syncState":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/syncState","kind":"field","type":"github.com/benbjohnson/litestream.syncState","span":{"start":[79,1],"end":[79,21],"bytes":[2692,2712]}},"totalWALBytesCounter":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/totalWALBytesCounter","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Counter","span":{"start":[111,1],"end":[111,48],"bytes":[3531,3578]}},"txIDGauge":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/txIDGauge","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Gauge","span":{"start":[112,1],"end":[112,46],"bytes":[3579,3624]}},"walSizeGauge":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/walSizeGauge","kind":"field","type":"github.com/prometheus/client_golang/prometheus.Gauge","span":{"start":[110,1],"end":[110,46],"bytes":[3485,3530]}},"wg":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/wg","kind":"field","type":"sync.WaitGroup","span":{"start":[104,1],"end":[104,23],"bytes":[3345,3367]}}}},"IntegrityCheckMode":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.IntegrityCheckMode","kind":"struct","span":{"start":[3376,1],"end":[3376,28],"bytes":[108196,108223]},"callables":{}},"RestoreOptions":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions","kind":"struct","span":{"start":[3385,1],"end":[3411,2],"bytes":[108378,109263]},"callables":{},"fields":{"Follow":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/Follow","kind":"field","type":"bool","span":{"start":[3403,1],"end":[3403,13],"bytes":[108945,108957]}},"FollowInterval":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/FollowInterval","kind":"field","type":"time.Duration","span":{"start":[3406,1],"end":[3406,30],"bytes":[109040,109069]}},"IntegrityCheck":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/IntegrityCheck","kind":"field","type":"github.com/benbjohnson/litestream.IntegrityCheckMode","span":{"start":[3410,1],"end":[3410,35],"bytes":[109227,109261]}},"OutputPath":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/OutputPath","kind":"field","type":"string","span":{"start":[3388,1],"end":[3388,19],"bytes":[108484,108502]}},"Parallelism":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/Parallelism","kind":"field","type":"int","span":{"start":[3399,1],"end":[3399,17],"bytes":[108789,108805]}},"TXID":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/TXID","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[3392,1],"end":[3392,15],"bytes":[108574,108588]}},"Timestamp":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.RestoreOptions/Timestamp","kind":"field","type":"time.Time","span":{"start":[3396,1],"end":[3396,21],"bytes":[108691,108711]}}}},"SyncDiagnostic":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic","kind":"struct","span":{"start":[287,1],"end":[305,2],"bytes":[10852,11943]},"callables":{},"fields":{"Active":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Active","kind":"field","type":"bool","span":{"start":[289,1],"end":[289,48],"bytes":[10927,10974]}},"CheckpointMode":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/CheckpointMode","kind":"field","type":"string","span":{"start":[299,1],"end":[299,67],"bytes":[11538,11604]}},"ElapsedSeconds":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/ElapsedSeconds","kind":"field","type":"float64","span":{"start":[294,1],"end":[294,67],"bytes":[11217,11283]}},"Error":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Error","kind":"field","type":"string","span":{"start":[301,1],"end":[301,57],"bytes":[11663,11719]}},"ExecutorWaitSeconds":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/ExecutorWaitSeconds","kind":"field","type":"float64","span":{"start":[304,1],"end":[304,73],"bytes":[11869,11941]}},"ExecutorWaitStarted":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/ExecutorWaitStarted","kind":"field","type":"*time.Time","span":{"start":[303,1],"end":[303,76],"bytes":[11793,11868]}},"ExecutorWaiterCount":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/ExecutorWaiterCount","kind":"field","type":"int","span":{"start":[302,1],"end":[302,73],"bytes":[11720,11792]}},"LastSyncedWALOffset":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/LastSyncedWALOffset","kind":"field","type":"int64","span":{"start":[297,1],"end":[297,74],"bytes":[11400,11473]}},"Operation":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Operation","kind":"field","type":"string","span":{"start":[290,1],"end":[290,61],"bytes":[10975,11035]}},"Path":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Path","kind":"field","type":"string","span":{"start":[288,1],"end":[288,46],"bytes":[10881,10926]}},"Phase":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Phase","kind":"field","type":"string","span":{"start":[291,1],"end":[291,57],"bytes":[11036,11092]}},"Reason":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Reason","kind":"field","type":"string","span":{"start":[300,1],"end":[300,58],"bytes":[11605,11662]}},"Snapshotting":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/Snapshotting","kind":"field","type":"bool","span":{"start":[298,1],"end":[298,64],"bytes":[11474,11537]}},"StartedAt":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/StartedAt","kind":"field","type":"*time.Time","span":{"start":[292,1],"end":[292,62],"bytes":[11093,11154]}},"TXID":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/TXID","kind":"field","type":"uint64","span":{"start":[295,1],"end":[295,56],"bytes":[11284,11339]}},"UpdatedAt":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/UpdatedAt","kind":"field","type":"*time.Time","span":{"start":[293,1],"end":[293,62],"bytes":[11155,11216]}},"WALSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncDiagnostic/WALSize","kind":"field","type":"int64","span":{"start":[296,1],"end":[296,60],"bytes":[11340,11399]}}}},"SyncStatus":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncStatus","kind":"struct","span":{"start":[736,1],"end":[740,2],"bytes":[25295,25380]},"callables":{},"fields":{"InSync":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncStatus/InSync","kind":"field","type":"bool","span":{"start":[739,1],"end":[739,17],"bytes":[25362,25378]}},"LocalTXID":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncStatus/LocalTXID","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[737,1],"end":[737,21],"bytes":[25320,25340]}},"RemoteTXID":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.SyncStatus/RemoteTXID","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[738,1],"end":[738,21],"bytes":[25341,25361]}}}},"diagOp":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagOp","kind":"struct","span":{"start":[232,1],"end":[232,19],"bytes":[8494,8512]},"callables":{}},"diagPhase":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagPhase","kind":"struct","span":{"start":[239,1],"end":[239,22],"bytes":[8599,8620]},"callables":{}},"diagState":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState","kind":"struct","span":{"start":[268,1],"end":[284,2],"bytes":[10348,10784]},"base_types":["sync.RWMutex"],"callables":{},"fields":{"active":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/active","kind":"field","type":"bool","span":{"start":[270,1],"end":[270,26],"bytes":[10386,10411]}},"checkpointMode":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/checkpointMode","kind":"field","type":"string","span":{"start":[279,1],"end":[279,28],"bytes":[10643,10670]}},"err":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/err","kind":"field","type":"string","span":{"start":[281,1],"end":[281,28],"bytes":[10699,10726]}},"executorWaitStarted":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/executorWaitStarted","kind":"field","type":"time.Time","span":{"start":[283,1],"end":[283,31],"bytes":[10752,10782]}},"executorWaiterCount":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/executorWaiterCount","kind":"field","type":"int","span":{"start":[282,1],"end":[282,25],"bytes":[10727,10751]}},"lastSyncedWALOffset":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/lastSyncedWALOffset","kind":"field","type":"int64","span":{"start":[277,1],"end":[277,27],"bytes":[10590,10616]}},"operation":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/operation","kind":"field","type":"github.com/benbjohnson/litestream.diagOp","span":{"start":[271,1],"end":[271,28],"bytes":[10412,10439]}},"phase":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/phase","kind":"field","type":"github.com/benbjohnson/litestream.diagPhase","span":{"start":[272,1],"end":[272,31],"bytes":[10440,10470]}},"reason":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/reason","kind":"field","type":"string","span":{"start":[280,1],"end":[280,28],"bytes":[10671,10698]}},"snapshotting":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/snapshotting","kind":"field","type":"bool","span":{"start":[278,1],"end":[278,26],"bytes":[10617,10642]}},"startedAt":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/startedAt","kind":"field","type":"time.Time","span":{"start":[273,1],"end":[273,31],"bytes":[10471,10501]}},"sync.RWMutex":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/sync.RWMutex","kind":"field","type":"sync.RWMutex","span":{"start":[269,1],"end":[269,14],"bytes":[10372,10385]}},"txID":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/txID","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[275,1],"end":[275,30],"bytes":[10533,10562]}},"updatedAt":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/updatedAt","kind":"field","type":"time.Time","span":{"start":[274,1],"end":[274,31],"bytes":[10502,10532]}},"walSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.diagState/walSize","kind":"field","type":"int64","span":{"start":[276,1],"end":[276,27],"bytes":[10563,10589]}}}},"ltxStagingFile":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile","kind":"interface","span":{"start":[1611,1],"end":[1615,2],"bytes":[52405,52478]},"base_types":["io.Writer"],"callables":{"github.com/benbjohnson/litestream.ltxStagingFile.Close":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile/github.com/benbjohnson/litestream.ltxStagingFile.Close","kind":"method","span":{"start":[1614,1],"end":[1614,15],"bytes":[52462,52476]},"signature":"github.com/benbjohnson/litestream.ltxStagingFile.Close","parameters":[],"return_type":"error","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ltxStagingFile.Sync":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile/github.com/benbjohnson/litestream.ltxStagingFile.Sync","kind":"method","span":{"start":[1613,1],"end":[1613,14],"bytes":[52448,52461]},"signature":"github.com/benbjohnson/litestream.ltxStagingFile.Sync","parameters":[],"return_type":"error","error_channel":["error"],"body":{}}}},"snapshotReadCloser":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadCloser","kind":"struct","span":{"start":[2768,1],"end":[2771,2],"bytes":[88955,89032]},"base_types":["*io.PipeReader"],"callables":{"github.com/benbjohnson/litestream.snapshotReadCloser.Close":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadCloser/github.com/benbjohnson/litestream.snapshotReadCloser.Close","kind":"method","span":{"start":[2773,1],"end":[2776,2],"bytes":[89034,89130]},"signature":"github.com/benbjohnson/litestream.snapshotReadCloser.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"2774:8":{"kind":"call","span":{"start":[2774,8],"end":[2774,21],"bytes":[89086,89099]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/github.com/benbjohnson/litestream.snapshotReadPosition.close"},"2775:9":{"kind":"call","span":{"start":[2775,9],"end":[2775,29],"bytes":[89108,89128]},"callee":null}}}},"fields":{"*io.PipeReader":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadCloser/*io.PipeReader","kind":"field","type":"*io.PipeReader","span":{"start":[2769,1],"end":[2769,16],"bytes":[88988,89003]}},"pos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadCloser/pos","kind":"field","type":"*github.com/benbjohnson/litestream.snapshotReadPosition","span":{"start":[2770,1],"end":[2770,27],"bytes":[89004,89030]}}}},"snapshotReadPosition":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition","kind":"struct","span":{"start":[2756,1],"end":[2762,2],"bytes":[88722,88860]},"callables":{"github.com/benbjohnson/litestream.snapshotReadPosition.close":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/github.com/benbjohnson/litestream.snapshotReadPosition.close","kind":"method","span":{"start":[2764,1],"end":[2766,2],"bytes":[88862,88953]},"signature":"github.com/benbjohnson/litestream.snapshotReadPosition.close","parameters":[],"metrics":{"cyclomatic":1},"body":{"2765:2":{"kind":"call","span":{"start":[2765,2],"end":[2765,49],"bytes":[88904,88951]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/github.com/benbjohnson/litestream.snapshotReadPosition.close/github.com/benbjohnson/litestream.snapshotReadPosition.close.closure_1","kind":"function","span":{"start":[2765,1],"end":[2765,49],"bytes":[88903,88951]},"signature":"github.com/benbjohnson/litestream.snapshotReadPosition.close.closure_1","parameters":[],"body":{"2765:26":{"kind":"call","span":{"start":[2765,26],"end":[2765,46],"bytes":[88928,88948]},"callee":null}}}}}},"fields":{"closeOnce":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/closeOnce","kind":"field","type":"sync.Once","span":{"start":[2761,1],"end":[2761,24],"bytes":[88835,88858]}},"db":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/db","kind":"field","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[2760,1],"end":[2760,18],"bytes":[88817,88834]}},"pageSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/pageSize","kind":"field","type":"int","span":{"start":[2758,1],"end":[2758,18],"bytes":[88779,88796]}},"pos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/pos","kind":"field","type":"github.com/superfly/ltx.Pos","span":{"start":[2757,1],"end":[2757,22],"bytes":[88757,88778]}},"walEndOffset":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/walEndOffset","kind":"field","type":"int64","span":{"start":[2759,1],"end":[2759,20],"bytes":[88797,88816]}}}},"syncExecutor":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor","kind":"struct","span":{"start":[223,1],"end":[230,2],"bytes":[8291,8492]},"callables":{"github.com/benbjohnson/litestream.syncExecutor.applySyncResult":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult","kind":"method","span":{"start":[2056,1],"end":[2068,2],"bytes":[66198,66575]},"signature":"github.com/benbjohnson/litestream.syncExecutor.applySyncResult","parameters":[{"name":"result","type":"github.com/benbjohnson/litestream.syncResult","span":{"start":[2056,1],"end":[2056,63],"bytes":[66198,66260]}}],"metrics":{"cyclomatic":3},"body":{}}},"fields":{"checkpointAttempted":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/checkpointAttempted","kind":"field","type":"bool","span":{"start":[229,1],"end":[229,26],"bytes":[8465,8490]}},"l0FileInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/l0FileInfo","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[227,1],"end":[227,35],"bytes":[8404,8438]}},"pos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/pos","kind":"field","type":"github.com/superfly/ltx.Pos","span":{"start":[225,1],"end":[225,29],"bytes":[8349,8377]}},"posChanged":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/posChanged","kind":"field","type":"bool","span":{"start":[226,1],"end":[226,26],"bytes":[8378,8403]}},"state":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/state","kind":"field","type":"github.com/benbjohnson/litestream.syncState","span":{"start":[224,1],"end":[224,31],"bytes":[8318,8348]}},"synced":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/synced","kind":"field","type":"bool","span":{"start":[228,1],"end":[228,26],"bytes":[8439,8464]}}}},"syncInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo","kind":"struct","span":{"start":[1969,1],"end":[1977,2],"bytes":[64228,64540]},"callables":{},"fields":{"clearSyncedToWALEnd":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/clearSyncedToWALEnd","kind":"field","type":"bool","span":{"start":[1976,1],"end":[1976,26],"bytes":[64513,64538]}},"offset":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/offset","kind":"field","type":"int64","span":{"start":[1970,1],"end":[1970,59],"bytes":[64251,64309]}},"prevCommit":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/prevCommit","kind":"field","type":"uint32","span":{"start":[1973,1],"end":[1973,28],"bytes":[64366,64393]}},"reason":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/reason","kind":"field","type":"string","span":{"start":[1975,1],"end":[1975,51],"bytes":[64462,64512]}},"salt1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/salt1","kind":"field","type":"uint32","span":{"start":[1971,1],"end":[1971,28],"bytes":[64310,64337]}},"salt2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/salt2","kind":"field","type":"uint32","span":{"start":[1972,1],"end":[1972,28],"bytes":[64338,64365]}},"snapshotting":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncInfo/snapshotting","kind":"field","type":"bool","span":{"start":[1974,1],"end":[1974,68],"bytes":[64394,64461]}}}},"syncResult":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult","kind":"struct","span":{"start":[1979,1],"end":[1987,2],"bytes":[64542,64730]},"callables":{},"fields":{"l0FileInfo":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/l0FileInfo","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1986,1],"end":[1986,30],"bytes":[64699,64728]}},"limited":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/limited","kind":"field","type":"bool","span":{"start":[1983,1],"end":[1983,21],"bytes":[64632,64652]}},"newWALSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/newWALSize","kind":"field","type":"int64","span":{"start":[1981,1],"end":[1981,22],"bytes":[64589,64610]}},"origWALSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/origWALSize","kind":"field","type":"int64","span":{"start":[1980,1],"end":[1980,22],"bytes":[64567,64588]}},"pos":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/pos","kind":"field","type":"*github.com/superfly/ltx.Pos","span":{"start":[1985,1],"end":[1985,25],"bytes":[64674,64698]}},"synced":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/synced","kind":"field","type":"bool","span":{"start":[1982,1],"end":[1982,21],"bytes":[64611,64631]}},"syncedToWALEnd":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncResult/syncedToWALEnd","kind":"field","type":"bool","span":{"start":[1984,1],"end":[1984,21],"bytes":[64653,64673]}}}},"syncState":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncState","kind":"struct","span":{"start":[197,1],"end":[221,2],"bytes":[7068,8289]},"callables":{},"fields":{"lastSyncedWALOffset":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncState/lastSyncedWALOffset","kind":"field","type":"int64","span":{"start":[220,1],"end":[220,27],"bytes":[8261,8287]}},"syncedSinceCheckpoint":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncState/syncedSinceCheckpoint","kind":"field","type":"bool","span":{"start":[204,1],"end":[204,28],"bytes":[7392,7419]}},"syncedToWALEnd":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncState/syncedToWALEnd","kind":"field","type":"bool","span":{"start":[212,1],"end":[212,21],"bytes":[7813,7833]}},"truncatePassiveFailed":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncState/truncatePassiveFailed","kind":"field","type":"bool","span":{"start":[198,1],"end":[198,28],"bytes":[7092,7119]}}}}},"functions":{"github.com/benbjohnson/litestream.NewDB":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","kind":"function","span":{"start":[308,1],"end":[352,2],"bytes":[12001,13590]},"signature":"github.com/benbjohnson/litestream.NewDB","parameters":[{"name":"path","type":"string","span":{"start":[308,1],"end":[308,30],"bytes":[12001,12030]}}],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":1},"body":{"309:15":{"kind":"call","span":{"start":[309,15],"end":[309,35],"bytes":[12045,12065]},"callee":null},"313:13":{"kind":"call","span":{"start":[313,13],"end":[313,55],"bytes":[12109,12151]},"callee":null},"314:13":{"kind":"call","span":{"start":[314,13],"end":[314,37],"bytes":[12165,12189]},"callee":null},"315:13":{"kind":"call","span":{"start":[315,13],"end":[315,32],"bytes":[12203,12222]},"callee":null},"327:25":{"kind":"call","span":{"start":[327,25],"end":[327,65],"bytes":[12716,12756]},"callee":null},"327:45":{"kind":"call","span":{"start":[327,45],"end":[327,64],"bytes":[12736,12755]},"callee":null},"329:25":{"kind":"call","span":{"start":[329,25],"end":[329,52],"bytes":[12785,12812]},"callee":null},"332:22":{"kind":"call","span":{"start":[332,22],"end":[332,62],"bytes":[12872,12912]},"callee":null},"332:41":{"kind":"call","span":{"start":[332,41],"end":[332,61],"bytes":[12891,12911]},"callee":null},"335:17":{"kind":"call","span":{"start":[335,17],"end":[335,90],"bytes":[13013,13086]},"callee":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.NewCompactor"},"335:35":{"kind":"call","span":{"start":[335,35],"end":[335,89],"bytes":[13031,13085]},"callee":null},"338:2":{"kind":"call","span":{"start":[338,2],"end":[338,18],"bytes":[13195,13211]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.initMetrics"}},"callables":{"closure_1":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB/github.com/benbjohnson/litestream.NewDB.closure_1","kind":"function","span":{"start":[339,1],"end":[344,3],"bytes":[13212,13406]},"signature":"github.com/benbjohnson/litestream.NewDB.closure_1","parameters":[{"name":"level","type":"int","span":{"start":[339,1],"end":[339,68],"bytes":[13212,13279]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, bool)","body":{"340:3":{"kind":"call","span":{"start":[340,3],"end":[340,28],"bytes":[13282,13307]},"callee":null},"341:9":{"kind":"call","span":{"start":[341,9],"end":[341,36],"bytes":[13316,13343]},"callee":null}}},"closure_2":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB/github.com/benbjohnson/litestream.NewDB.closure_2","kind":"function","span":{"start":[345,1],"end":[349,3],"bytes":[13407,13576]},"signature":"github.com/benbjohnson/litestream.NewDB.closure_2","parameters":[{"name":"level","type":"int","span":{"start":[345,1],"end":[345,66],"bytes":[13407,13472]}},{"name":"info","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[345,1],"end":[345,66],"bytes":[13407,13472]}}],"body":{"346:3":{"kind":"call","span":{"start":[346,3],"end":[346,28],"bytes":[13475,13500]},"callee":null},"347:9":{"kind":"call","span":{"start":[347,9],"end":[347,36],"bytes":[13509,13536]},"callee":null}}}}},"github.com/benbjohnson/litestream.NewRestoreOptions":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions","kind":"function","span":{"start":[3414,1],"end":[3419,2],"bytes":[109342,109498]},"signature":"github.com/benbjohnson/litestream.NewRestoreOptions","parameters":[],"return_type":"github.com/benbjohnson/litestream.RestoreOptions","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.addDurationToCounter":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.addDurationToCounter","kind":"function","span":{"start":[1325,1],"end":[1329,2],"bytes":[42224,42340]},"signature":"github.com/benbjohnson/litestream.addDurationToCounter","parameters":[{"name":"counter","type":"github.com/prometheus/client_golang/prometheus.Counter","span":{"start":[1325,1],"end":[1325,73],"bytes":[42224,42296]}},{"name":"d","type":"time.Duration","span":{"start":[1325,1],"end":[1325,73],"bytes":[42224,42296]}}],"metrics":{"cyclomatic":2},"body":{"1327:15":{"kind":"call","span":{"start":[1327,15],"end":[1327,26],"bytes":[42323,42334]},"callee":null},"1327:3":{"kind":"call","span":{"start":[1327,3],"end":[1327,27],"bytes":[42311,42335]},"callee":null}}},"github.com/benbjohnson/litestream.calcWALSize":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize","kind":"function","span":{"start":[1634,1],"end":[1636,2],"bytes":[53023,53163]},"signature":"github.com/benbjohnson/litestream.calcWALSize","parameters":[{"name":"pageSize","type":"uint32","span":{"start":[1634,1],"end":[1634,56],"bytes":[53023,53078]}},{"name":"pageN","type":"uint32","span":{"start":[1634,1],"end":[1634,56],"bytes":[53023,53078]}}],"return_type":"int64","metrics":{"cyclomatic":1},"body":{"1635:33":{"kind":"call","span":{"start":[1635,33],"end":[1635,67],"bytes":[53111,53145]},"callee":null},"1635:70":{"kind":"call","span":{"start":[1635,70],"end":[1635,82],"bytes":[53148,53160]},"callee":null},"1635:9":{"kind":"call","span":{"start":[1635,9],"end":[1635,29],"bytes":[53087,53107]},"callee":null}}},"github.com/benbjohnson/litestream.defaultOpenLTXFile":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.defaultOpenLTXFile","kind":"function","span":{"start":[1617,1],"end":[1619,2],"bytes":[52480,52610]},"signature":"github.com/benbjohnson/litestream.defaultOpenLTXFile","parameters":[{"name":"name","type":"string","span":{"start":[1617,1],"end":[1617,91],"bytes":[52480,52570]}},{"name":"flag","type":"int","span":{"start":[1617,1],"end":[1617,91],"bytes":[52480,52570]}},{"name":"perm","type":"os.FileMode","span":{"start":[1617,1],"end":[1617,91],"bytes":[52480,52570]}}],"return_type":"(github.com/benbjohnson/litestream.ltxStagingFile, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"1618:9":{"kind":"call","span":{"start":[1618,9],"end":[1618,38],"bytes":[52579,52608]},"callee":null}}},"github.com/benbjohnson/litestream.isDiskFullError":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError","kind":"function","span":{"start":[1595,1],"end":[1609,2],"bytes":[51890,52403]},"signature":"github.com/benbjohnson/litestream.isDiskFullError","parameters":[{"name":"err","type":"error","span":{"start":[1595,1],"end":[1595,39],"bytes":[51890,51928]}}],"return_type":"bool","metrics":{"cyclomatic":3},"body":{"1599:36":{"kind":"call","span":{"start":[1599,36],"end":[1599,66],"bytes":[51999,52029]},"callee":null},"1599:5":{"kind":"call","span":{"start":[1599,5],"end":[1599,32],"bytes":[51968,51995]},"callee":null},"1602:12":{"kind":"call","span":{"start":[1602,12],"end":[1602,40],"bytes":[52060,52088]},"callee":null},"1602:28":{"kind":"call","span":{"start":[1602,28],"end":[1602,39],"bytes":[52076,52087]},"callee":null},"1603:9":{"kind":"call","span":{"start":[1603,9],"end":[1603,60],"bytes":[52097,52148]},"callee":null},"1604:3":{"kind":"call","span":{"start":[1604,3],"end":[1604,50],"bytes":[52154,52201]},"callee":null},"1605:3":{"kind":"call","span":{"start":[1605,3],"end":[1605,59],"bytes":[52207,52263]},"callee":null},"1606:3":{"kind":"call","span":{"start":[1606,3],"end":[1606,55],"bytes":[52269,52321]},"callee":null},"1607:3":{"kind":"call","span":{"start":[1607,3],"end":[1607,37],"bytes":[52327,52361]},"callee":null},"1608:3":{"kind":"call","span":{"start":[1608,3],"end":[1608,37],"bytes":[52367,52401]},"callee":null}}},"github.com/benbjohnson/litestream.isSQLiteBusyError":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isSQLiteBusyError","kind":"function","span":{"start":[1583,1],"end":[1591,2],"bytes":[51457,51726]},"signature":"github.com/benbjohnson/litestream.isSQLiteBusyError","parameters":[{"name":"err","type":"error","span":{"start":[1583,1],"end":[1583,41],"bytes":[51457,51497]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"1588:12":{"kind":"call","span":{"start":[1588,12],"end":[1588,23],"bytes":[51613,51624]},"callee":null},"1589:9":{"kind":"call","span":{"start":[1589,9],"end":[1589,55],"bytes":[51633,51679]},"callee":null},"1590:3":{"kind":"call","span":{"start":[1590,3],"end":[1590,42],"bytes":[51685,51724]},"callee":null}}},"github.com/benbjohnson/litestream.snapshotHeaderWALRange":{"id":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotHeaderWALRange","kind":"function","span":{"start":[2973,1],"end":[2979,2],"bytes":[95070,95315]},"signature":"github.com/benbjohnson/litestream.snapshotHeaderWALRange","parameters":[{"name":"maxOffset","type":"int64","span":{"start":[2973,1],"end":[2973,79],"bytes":[95070,95148]}},{"name":"frameSize","type":"int64","span":{"start":[2973,1],"end":[2973,79],"bytes":[95070,95148]}}],"return_type":"(int64, int64)","metrics":{"cyclomatic":2},"body":{"2977:11":{"kind":"call","span":{"start":[2977,11],"end":[2977,50],"bytes":[95239,95278]},"callee":null}}}},"content_hash":"95d34cde5d76f1f6f683c38ec01b4dcf5e5167059935a0816889d1c72c7944fd"},"file/replica_client.go":{"id":"can://go/litestream/file/replica_client.go","kind":"module","span":{"start":[1,1],"end":[379,1],"bytes":[0,10820]},"package":"file","source":"package file\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"file\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"file\"\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\nvar _ litestream.ReplicaClientV3 = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files to disk.\ntype ReplicaClient struct {\n\tpath string // destination path\n\n\tReplica *litestream.Replica\n\tlogger *slog.Logger\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient(path string) *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t\tpath: path,\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\t// For file URLs, the path is the full path\n\tif urlPath == \"\" {\n\t\treturn nil, fmt.Errorf(\"file replica path required\")\n\t}\n\treturn NewReplicaClient(urlPath), nil\n}\n\n// db returns the database, if available.\nfunc (c *ReplicaClient) db() *litestream.DB {\n\tif c.Replica == nil {\n\t\treturn nil\n\t}\n\treturn c.Replica.DB()\n}\n\n// Type returns \"file\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init is a no-op for file replica client as no initialization is required.\nfunc (c *ReplicaClient) Init(ctx context.Context) error {\n\treturn nil\n}\n\n// Path returns the destination path to replicate the database to.\nfunc (c *ReplicaClient) Path() string {\n\treturn c.path\n}\n\n// LTXLevelDir returns the path to a given level.\nfunc (c *ReplicaClient) LTXLevelDir(level int) string {\n\treturn filepath.FromSlash(litestream.LTXLevelDir(c.path, level))\n}\n\n// LTXFilePath returns the path to an LTX file.\nfunc (c *ReplicaClient) LTXFilePath(level int, minTXID, maxTXID ltx.TXID) string {\n\treturn filepath.FromSlash(litestream.LTXFilePath(c.path, level, minTXID, maxTXID))\n}\n\n// LTXFiles returns an iterator over all LTX files on the replica for the given level.\n// The useMetadata parameter is ignored for file backend as ModTime is always available from readdir.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\tf, err := os.Open(c.LTXLevelDir(level))\n\tif os.IsNotExist(err) {\n\t\treturn ltx.NewFileInfoSliceIterator(nil), nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tfis, err := f.Readdir(-1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Iterate over every file and convert to metadata.\n\t// ModTime contains the accurate timestamp set by Chtimes in WriteLTXFile.\n\tinfos := make([]*ltx.FileInfo, 0, len(fis))\n\tfor _, fi := range fis {\n\t\tminTXID, maxTXID, err := ltx.ParseFilename(fi.Name())\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t} else if minTXID \u003c seek {\n\t\t\tcontinue\n\t\t}\n\n\t\tinfos = append(infos, \u0026ltx.FileInfo{\n\t\t\tLevel: level,\n\t\t\tMinTXID: minTXID,\n\t\t\tMaxTXID: maxTXID,\n\t\t\tSize: fi.Size(),\n\t\t\tCreatedAt: fi.ModTime().UTC(),\n\t\t})\n\t}\n\n\treturn ltx.NewFileInfoSliceIterator(infos), nil\n}\n\n// OpenLTXFile returns a reader for an LTX file at the given position.\n// Returns os.ErrNotExist if no matching index/offset is found.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\tpath := c.LTXFilePath(level, minTXID, maxTXID)\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, litestream.NewLTXError(\"open\", path, level, uint64(minTXID), uint64(maxTXID), err)\n\t}\n\n\tif offset \u003e 0 {\n\t\tif _, err := f.Seek(offset, io.SeekStart); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif size \u003e 0 {\n\t\treturn internal.LimitReadCloser(f, size), nil\n\t}\n\n\treturn f, nil\n}\n\n// WriteLTXFile writes an LTX file to the replica.\n// Extracts timestamp from LTX header and sets it as the file's ModTime to preserve original creation time.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {\n\tvar fileInfo, dirInfo os.FileInfo\n\tif db := c.db(); db != nil {\n\t\tfileInfo, dirInfo = db.FileInfo(), db.DirInfo()\n\t}\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(rd, \u0026buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\tfullReader := io.MultiReader(\u0026buf, rd)\n\n\t// Ensure parent directory exists.\n\tfilename := c.LTXFilePath(level, minTXID, maxTXID)\n\tif err := internal.MkdirAll(filepath.Dir(filename), dirInfo); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Write LTX file to temporary file next to destination path.\n\ttmpFilename := filename + \".tmp\"\n\tf, err := internal.CreateFile(tmpFilename, fileInfo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Clean up temp file on error. On successful rename, the temp file\n\t// becomes the final file and should not be removed.\n\tdefer func() {\n\t\t_ = f.Close()\n\t\tif err != nil {\n\t\t\t_ = os.Remove(tmpFilename)\n\t\t}\n\t}()\n\n\tif _, err := io.Copy(f, fullReader); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := f.Sync(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Build metadata.\n\tfi, err := f.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinfo = \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: fi.Size(),\n\t\tCreatedAt: timestamp,\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Move LTX file to final path when it has been written \u0026 synced to disk.\n\tif err := os.Rename(tmpFilename, filename); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := internal.FsyncDir(filepath.Dir(filename)); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Set file ModTime to preserve original timestamp\n\tif err := os.Chtimes(filename, timestamp, timestamp); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn info, nil\n}\n\n// DeleteLTXFiles deletes LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tfor _, info := range a {\n\t\tfilename := c.LTXFilePath(info.Level, info.MinTXID, info.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"path\", filename)\n\n\t\tif err := os.Remove(filename); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// DeleteAll deletes all LTX files.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tif err := os.RemoveAll(c.path); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// GenerationsV3 returns a list of v0.3.x generation IDs in the replica.\nfunc (c *ReplicaClient) GenerationsV3(ctx context.Context) ([]string, error) {\n\tgenPath := filepath.Join(c.path, litestream.GenerationsDirV3)\n\tentries, err := os.ReadDir(genPath)\n\tif os.IsNotExist(err) {\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar generations []string\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() \u0026\u0026 litestream.IsGenerationIDV3(entry.Name()) {\n\t\t\tgenerations = append(generations, entry.Name())\n\t\t}\n\t}\n\tslices.Sort(generations)\n\treturn generations, nil\n}\n\n// SnapshotsV3 returns snapshots for a generation, sorted by index.\nfunc (c *ReplicaClient) SnapshotsV3(ctx context.Context, generation string) ([]litestream.SnapshotInfoV3, error) {\n\tsnapshotsPath := filepath.Join(c.path, litestream.GenerationsDirV3, generation, litestream.SnapshotsDirV3)\n\tentries, err := os.ReadDir(snapshotsPath)\n\tif os.IsNotExist(err) {\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar snapshots []litestream.SnapshotInfoV3\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tindex, err := litestream.ParseSnapshotFilenameV3(entry.Name())\n\t\tif err != nil {\n\t\t\tcontinue // skip invalid filenames\n\t\t}\n\t\tinfo, err := entry.Info()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsnapshots = append(snapshots, litestream.SnapshotInfoV3{\n\t\t\tGeneration: generation,\n\t\t\tIndex: index,\n\t\t\tSize: info.Size(),\n\t\t\tCreatedAt: info.ModTime(),\n\t\t})\n\t}\n\tslices.SortFunc(snapshots, func(a, b litestream.SnapshotInfoV3) int {\n\t\treturn a.Index - b.Index\n\t})\n\treturn snapshots, nil\n}\n\n// WALSegmentsV3 returns WAL segments for a generation, sorted by index then offset.\nfunc (c *ReplicaClient) WALSegmentsV3(ctx context.Context, generation string) ([]litestream.WALSegmentInfoV3, error) {\n\twalPath := filepath.Join(c.path, litestream.GenerationsDirV3, generation, litestream.WALDirV3)\n\tentries, err := os.ReadDir(walPath)\n\tif os.IsNotExist(err) {\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar segments []litestream.WALSegmentInfoV3\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tindex, offset, err := litestream.ParseWALSegmentFilenameV3(entry.Name())\n\t\tif err != nil {\n\t\t\tcontinue // skip invalid filenames\n\t\t}\n\t\tinfo, err := entry.Info()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsegments = append(segments, litestream.WALSegmentInfoV3{\n\t\t\tGeneration: generation,\n\t\t\tIndex: index,\n\t\t\tOffset: offset,\n\t\t\tSize: info.Size(),\n\t\t\tCreatedAt: info.ModTime(),\n\t\t})\n\t}\n\tslices.SortFunc(segments, func(a, b litestream.WALSegmentInfoV3) int {\n\t\tif a.Index != b.Index {\n\t\t\treturn a.Index - b.Index\n\t\t}\n\t\treturn cmp.Compare(a.Offset, b.Offset)\n\t})\n\treturn segments, nil\n}\n\n// OpenSnapshotV3 opens a v0.3.x snapshot file for reading.\n// The returned reader provides LZ4-decompressed data.\nfunc (c *ReplicaClient) OpenSnapshotV3(ctx context.Context, generation string, index int) (io.ReadCloser, error) {\n\tpath := filepath.Join(c.path, litestream.GenerationsDirV3, generation, litestream.SnapshotsDirV3, litestream.FormatSnapshotFilenameV3(index))\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn internal.NewLZ4Reader(f), nil\n}\n\n// OpenWALSegmentV3 opens a v0.3.x WAL segment file for reading.\n// The returned reader provides LZ4-decompressed data.\nfunc (c *ReplicaClient) OpenWALSegmentV3(ctx context.Context, generation string, index int, offset int64) (io.ReadCloser, error) {\n\tpath := filepath.Join(c.path, litestream.GenerationsDirV3, generation, litestream.WALDirV3, litestream.FormatWALSegmentFilenameV3(index, offset))\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn internal.NewLZ4Reader(f), nil\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"cmp","path":"cmp","span":{"start":[5,1],"end":[5,7],"bytes":[32,38]}},{"name":"context","path":"context","span":{"start":[6,1],"end":[6,11],"bytes":[39,49]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[50,56]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[57,62]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[63,74]}},{"name":"url","path":"net/url","span":{"start":[10,1],"end":[10,11],"bytes":[75,85]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[86,91]}},{"name":"filepath","path":"path/filepath","span":{"start":[12,1],"end":[12,17],"bytes":[92,108]}},{"name":"slices","path":"slices","span":{"start":[13,1],"end":[13,10],"bytes":[109,118]}},{"name":"time","path":"time","span":{"start":[14,1],"end":[14,8],"bytes":[119,126]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[16,1],"end":[16,27],"bytes":[128,154]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[18,1],"end":[18,37],"bytes":[156,192]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[19,1],"end":[19,46],"bytes":[193,238]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient","kind":"struct","span":{"start":[33,1],"end":[38,2],"bytes":[598,712]},"callables":{"github.com/benbjohnson/litestream/file.ReplicaClient.DeleteAll":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.DeleteAll","kind":"method","span":{"start":[255,1],"end":[260,2],"bytes":[6926,7087]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[255,1],"end":[255,63],"bytes":[6926,6988]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"256:12":{"kind":"call","span":{"start":[256,12],"end":[256,32],"bytes":[7000,7020]},"callee":null},"256:49":{"kind":"call","span":{"start":[256,49],"end":[256,67],"bytes":[7037,7055]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[241,1],"end":[252,2],"bytes":[6475,6888]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[241,1],"end":[241,87],"bytes":[6475,6561]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[241,1],"end":[241,87],"bytes":[6475,6561]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"243:15":{"kind":"call","span":{"start":[243,15],"end":[243,68],"bytes":[6602,6655]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath"},"245:3":{"kind":"call","span":{"start":[245,3],"end":[245,127],"bytes":[6659,6783]},"callee":null},"247:13":{"kind":"call","span":{"start":[247,13],"end":[247,32],"bytes":[6797,6816]},"callee":null},"247:49":{"kind":"call","span":{"start":[247,49],"end":[247,67],"bytes":[6833,6851]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.GenerationsV3":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.GenerationsV3","kind":"method","span":{"start":[263,1],"end":[280,2],"bytes":[7162,7665]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.GenerationsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[263,1],"end":[263,79],"bytes":[7162,7240]}}],"return_type":"([]string, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"264:13":{"kind":"call","span":{"start":[264,13],"end":[264,63],"bytes":[7253,7303]},"callee":null},"265:18":{"kind":"call","span":{"start":[265,18],"end":[265,37],"bytes":[7321,7340]},"callee":null},"266:5":{"kind":"call","span":{"start":[266,5],"end":[266,23],"bytes":[7345,7363]},"callee":null},"274:23":{"kind":"call","span":{"start":[274,23],"end":[274,64],"bytes":[7511,7552]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.IsGenerationIDV3"},"274:51":{"kind":"call","span":{"start":[274,51],"end":[274,63],"bytes":[7539,7551]},"callee":null},"274:6":{"kind":"call","span":{"start":[274,6],"end":[274,19],"bytes":[7494,7507]},"callee":null},"275:18":{"kind":"call","span":{"start":[275,18],"end":[275,51],"bytes":[7572,7605]},"callee":null},"275:38":{"kind":"call","span":{"start":[275,38],"end":[275,50],"bytes":[7592,7604]},"callee":null},"278:2":{"kind":"call","span":{"start":[278,2],"end":[278,26],"bytes":[7614,7638]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.Init":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.Init","kind":"method","span":{"start":[76,1],"end":[78,2],"bytes":[1816,1887]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[76,1],"end":[76,58],"bytes":[1816,1873]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath","kind":"method","span":{"start":[91,1],"end":[93,2],"bytes":[2237,2405]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath","parameters":[{"name":"level","type":"int","span":{"start":[91,1],"end":[91,83],"bytes":[2237,2319]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[91,1],"end":[91,83],"bytes":[2237,2319]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[91,1],"end":[91,83],"bytes":[2237,2319]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"92:28":{"kind":"call","span":{"start":[92,28],"end":[92,83],"bytes":[2347,2402]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"92:9":{"kind":"call","span":{"start":[92,9],"end":[92,84],"bytes":[2328,2403]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.LTXFiles":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFiles","kind":"method","span":{"start":[97,1],"end":[132,2],"bytes":[2596,3521]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[97,1],"end":[97,126],"bytes":[2596,2721]}},{"name":"level","type":"int","span":{"start":[97,1],"end":[97,126],"bytes":[2596,2721]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[97,1],"end":[97,126],"bytes":[2596,2721]}},{"name":"useMetadata","type":"bool","span":{"start":[97,1],"end":[97,126],"bytes":[2596,2721]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"100:10":{"kind":"call","span":{"start":[100,10],"end":[100,43],"bytes":[2797,2830]},"callee":null},"104:8":{"kind":"call","span":{"start":[104,8],"end":[104,17],"bytes":[2888,2897]},"callee":null},"106:14":{"kind":"call","span":{"start":[106,14],"end":[106,27],"bytes":[2912,2925]},"callee":null},"113:11":{"kind":"call","span":{"start":[113,11],"end":[113,45],"bytes":[3104,3138]},"callee":null},"113:36":{"kind":"call","span":{"start":[113,36],"end":[113,44],"bytes":[3129,3137]},"callee":null},"115:28":{"kind":"call","span":{"start":[115,28],"end":[115,56],"bytes":[3192,3220]},"callee":null},"115:46":{"kind":"call","span":{"start":[115,46],"end":[115,55],"bytes":[3210,3219]},"callee":null},"122:11":{"kind":"call","span":{"start":[122,11],"end":[128,5],"bytes":[3307,3466]},"callee":null},"126:15":{"kind":"call","span":{"start":[126,15],"end":[126,24],"bytes":[3417,3426]},"callee":null},"127:15":{"kind":"call","span":{"start":[127,15],"end":[127,27],"bytes":[3442,3454]},"callee":null},"131:9":{"kind":"call","span":{"start":[131,9],"end":[131,44],"bytes":[3479,3514]},"callee":null},"98:12":{"kind":"call","span":{"start":[98,12],"end":[98,41],"bytes":[2733,2762]},"callee":null},"98:20":{"kind":"call","span":{"start":[98,20],"end":[98,40],"bytes":[2741,2761]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXLevelDir"},"99:5":{"kind":"call","span":{"start":[99,5],"end":[99,23],"bytes":[2767,2785]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.LTXLevelDir":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXLevelDir","kind":"method","span":{"start":[86,1],"end":[88,2],"bytes":[2064,2187]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.LTXLevelDir","parameters":[{"name":"level","type":"int","span":{"start":[86,1],"end":[86,56],"bytes":[2064,2119]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"87:28":{"kind":"call","span":{"start":[87,28],"end":[87,65],"bytes":[2147,2184]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"87:9":{"kind":"call","span":{"start":[87,9],"end":[87,66],"bytes":[2128,2185]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[136,1],"end":[154,2],"bytes":[3658,4174]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[136,1],"end":[136,140],"bytes":[3658,3797]}},{"name":"level","type":"int","span":{"start":[136,1],"end":[136,140],"bytes":[3658,3797]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[136,1],"end":[136,140],"bytes":[3658,3797]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[136,1],"end":[136,140],"bytes":[3658,3797]}},{"name":"offset","type":"int64","span":{"start":[136,1],"end":[136,140],"bytes":[3658,3797]}},{"name":"size","type":"int64","span":{"start":[136,1],"end":[136,140],"bytes":[3658,3797]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"137:10":{"kind":"call","span":{"start":[137,10],"end":[137,48],"bytes":[3807,3845]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath"},"138:12":{"kind":"call","span":{"start":[138,12],"end":[138,25],"bytes":[3857,3870]},"callee":null},"140:15":{"kind":"call","span":{"start":[140,15],"end":[140,97],"bytes":[3902,3984]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"140:59":{"kind":"call","span":{"start":[140,59],"end":[140,74],"bytes":[3946,3961]},"callee":null},"140:76":{"kind":"call","span":{"start":[140,76],"end":[140,91],"bytes":[3963,3978]},"callee":null},"144:16":{"kind":"call","span":{"start":[144,16],"end":[144,44],"bytes":[4021,4049]},"callee":null},"150:10":{"kind":"call","span":{"start":[150,10],"end":[150,43],"bytes":[4115,4148]},"callee":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser"}}},"github.com/benbjohnson/litestream/file.ReplicaClient.OpenSnapshotV3":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenSnapshotV3","kind":"method","span":{"start":[360,1],"end":[367,2],"bytes":[9957,10317]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.OpenSnapshotV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[360,1],"end":[360,115],"bytes":[9957,10071]}},{"name":"generation","type":"string","span":{"start":[360,1],"end":[360,115],"bytes":[9957,10071]}},{"name":"index","type":"int","span":{"start":[360,1],"end":[360,115],"bytes":[9957,10071]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"361:10":{"kind":"call","span":{"start":[361,10],"end":[361,143],"bytes":[10081,10214]},"callee":null},"361:100":{"kind":"call","span":{"start":[361,100],"end":[361,142],"bytes":[10171,10213]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatSnapshotFilenameV3"},"362:12":{"kind":"call","span":{"start":[362,12],"end":[362,25],"bytes":[10226,10239]},"callee":null},"366:9":{"kind":"call","span":{"start":[366,9],"end":[366,33],"bytes":[10286,10310]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader"}}},"github.com/benbjohnson/litestream/file.ReplicaClient.OpenWALSegmentV3":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenWALSegmentV3","kind":"method","span":{"start":[371,1],"end":[378,2],"bytes":[10439,10819]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.OpenWALSegmentV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[371,1],"end":[371,131],"bytes":[10439,10569]}},{"name":"generation","type":"string","span":{"start":[371,1],"end":[371,131],"bytes":[10439,10569]}},{"name":"index","type":"int","span":{"start":[371,1],"end":[371,131],"bytes":[10439,10569]}},{"name":"offset","type":"int64","span":{"start":[371,1],"end":[371,131],"bytes":[10439,10569]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"372:10":{"kind":"call","span":{"start":[372,10],"end":[372,147],"bytes":[10579,10716]},"callee":null},"372:94":{"kind":"call","span":{"start":[372,94],"end":[372,146],"bytes":[10663,10715]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3"},"373:12":{"kind":"call","span":{"start":[373,12],"end":[373,25],"bytes":[10728,10741]},"callee":null},"377:9":{"kind":"call","span":{"start":[377,9],"end":[377,33],"bytes":[10788,10812]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader"}}},"github.com/benbjohnson/litestream/file.ReplicaClient.Path":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.Path","kind":"method","span":{"start":[81,1],"end":[83,2],"bytes":[1956,2012]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.Path","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/file.ReplicaClient.SetLogger":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.SetLogger","kind":"method","span":{"start":[48,1],"end":[50,2],"bytes":[928,1034]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[48,1],"end":[48,57],"bytes":[928,984]}}],"metrics":{"cyclomatic":1},"body":{"49:13":{"kind":"call","span":{"start":[49,13],"end":[49,48],"bytes":[997,1032]},"callee":null}}},"github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3","kind":"method","span":{"start":[283,1],"end":[316,2],"bytes":[7735,8698]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[283,1],"end":[283,115],"bytes":[7735,7849]}},{"name":"generation","type":"string","span":{"start":[283,1],"end":[283,115],"bytes":[7735,7849]}}],"return_type":"([]github.com/benbjohnson/litestream.SnapshotInfoV3, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"284:19":{"kind":"call","span":{"start":[284,19],"end":[284,108],"bytes":[7868,7957]},"callee":null},"285:18":{"kind":"call","span":{"start":[285,18],"end":[285,43],"bytes":[7975,8000]},"callee":null},"286:5":{"kind":"call","span":{"start":[286,5],"end":[286,23],"bytes":[8005,8023]},"callee":null},"294:6":{"kind":"call","span":{"start":[294,6],"end":[294,19],"bytes":[8171,8184]},"callee":null},"297:17":{"kind":"call","span":{"start":[297,17],"end":[297,65],"bytes":[8219,8267]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseSnapshotFilenameV3"},"297:52":{"kind":"call","span":{"start":[297,52],"end":[297,64],"bytes":[8254,8266]},"callee":null},"301:16":{"kind":"call","span":{"start":[301,16],"end":[301,28],"bytes":[8343,8355]},"callee":null},"305:15":{"kind":"call","span":{"start":[305,15],"end":[310,5],"bytes":[8411,8568]},"callee":null},"308:16":{"kind":"call","span":{"start":[308,16],"end":[308,27],"bytes":[8520,8531]},"callee":null},"309:16":{"kind":"call","span":{"start":[309,16],"end":[309,30],"bytes":[8548,8562]},"callee":null},"312:2":{"kind":"call","span":{"start":[312,2],"end":[314,4],"bytes":[8573,8673]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3/github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3.closure_1","kind":"function","span":{"start":[312,1],"end":[314,4],"bytes":[8572,8673]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3.closure_1","parameters":[{"name":"a","type":"github.com/benbjohnson/litestream.SnapshotInfoV3","span":{"start":[312,1],"end":[312,71],"bytes":[8572,8642]}},{"name":"b","type":"github.com/benbjohnson/litestream.SnapshotInfoV3","span":{"start":[312,1],"end":[312,71],"bytes":[8572,8642]}}],"return_type":"int","body":{}}}},"github.com/benbjohnson/litestream/file.ReplicaClient.Type":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.Type","kind":"method","span":{"start":[71,1],"end":[73,2],"bytes":[1670,1737]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3","kind":"method","span":{"start":[319,1],"end":[356,2],"bytes":[8785,9840]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[319,1],"end":[319,119],"bytes":[8785,8903]}},{"name":"generation","type":"string","span":{"start":[319,1],"end":[319,119],"bytes":[8785,8903]}}],"return_type":"([]github.com/benbjohnson/litestream.WALSegmentInfoV3, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"320:13":{"kind":"call","span":{"start":[320,13],"end":[320,96],"bytes":[8916,8999]},"callee":null},"321:18":{"kind":"call","span":{"start":[321,18],"end":[321,37],"bytes":[9017,9036]},"callee":null},"322:5":{"kind":"call","span":{"start":[322,5],"end":[322,23],"bytes":[9041,9059]},"callee":null},"330:6":{"kind":"call","span":{"start":[330,6],"end":[330,19],"bytes":[9208,9221]},"callee":null},"333:25":{"kind":"call","span":{"start":[333,25],"end":[333,75],"bytes":[9264,9314]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3"},"333:62":{"kind":"call","span":{"start":[333,62],"end":[333,74],"bytes":[9301,9313]},"callee":null},"337:16":{"kind":"call","span":{"start":[337,16],"end":[337,28],"bytes":[9390,9402]},"callee":null},"341:14":{"kind":"call","span":{"start":[341,14],"end":[347,5],"bytes":[9457,9638]},"callee":null},"345:16":{"kind":"call","span":{"start":[345,16],"end":[345,27],"bytes":[9590,9601]},"callee":null},"346:16":{"kind":"call","span":{"start":[346,16],"end":[346,30],"bytes":[9618,9632]},"callee":null},"349:2":{"kind":"call","span":{"start":[349,2],"end":[354,4],"bytes":[9643,9816]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3/github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3.closure_1","kind":"function","span":{"start":[349,1],"end":[354,4],"bytes":[9642,9816]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3.closure_1","parameters":[{"name":"a","type":"github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[349,1],"end":[349,72],"bytes":[9642,9713]}},{"name":"b","type":"github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[349,1],"end":[349,72],"bytes":[9642,9713]}}],"return_type":"int","body":{"353:10":{"kind":"call","span":{"start":[353,10],"end":[353,41],"bytes":[9781,9812]},"callee":null}}}}},"github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[158,1],"end":[238,2],"bytes":[4335,6436]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[158,1],"end":[158,144],"bytes":[4335,4478]}},{"name":"level","type":"int","span":{"start":[158,1],"end":[158,144],"bytes":[4335,4478]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[158,1],"end":[158,144],"bytes":[4335,4478]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[158,1],"end":[158,144],"bytes":[4335,4478]}},{"name":"rd","type":"io.Reader","span":{"start":[158,1],"end":[158,144],"bytes":[4335,4478]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"160:11":{"kind":"call","span":{"start":[160,11],"end":[160,17],"bytes":[4524,4530]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.db"},"161:23":{"kind":"call","span":{"start":[161,23],"end":[161,36],"bytes":[4566,4579]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.FileInfo"},"161:38":{"kind":"call","span":{"start":[161,38],"end":[161,50],"bytes":[4581,4593]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.DirInfo"},"166:15":{"kind":"call","span":{"start":[166,15],"end":[166,37],"bytes":[4707,4729]},"callee":null},"169:17":{"kind":"call","span":{"start":[169,17],"end":[169,42],"bytes":[4785,4810]},"callee":null},"171:15":{"kind":"call","span":{"start":[171,15],"end":[171,71],"bytes":[4842,4898]},"callee":null},"173:15":{"kind":"call","span":{"start":[173,15],"end":[173,44],"bytes":[4916,4945]},"callee":null},"176:16":{"kind":"call","span":{"start":[176,16],"end":[176,40],"bytes":[5014,5038]},"callee":null},"179:14":{"kind":"call","span":{"start":[179,14],"end":[179,52],"bytes":[5089,5127]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath"},"180:12":{"kind":"call","span":{"start":[180,12],"end":[180,62],"bytes":[5139,5189]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll"},"180:30":{"kind":"call","span":{"start":[180,30],"end":[180,52],"bytes":[5157,5179]},"callee":null},"186:12":{"kind":"call","span":{"start":[186,12],"end":[186,54],"bytes":[5334,5376]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.CreateFile"},"193:8":{"kind":"call","span":{"start":[193,8],"end":[198,5],"bytes":[5546,5627]},"callee":null},"200:15":{"kind":"call","span":{"start":[200,15],"end":[200,37],"bytes":[5643,5665]},"callee":null},"203:12":{"kind":"call","span":{"start":[203,12],"end":[203,20],"bytes":[5712,5720]},"callee":null},"208:13":{"kind":"call","span":{"start":[208,13],"end":[208,21],"bytes":[5789,5797]},"callee":null},"216:14":{"kind":"call","span":{"start":[216,14],"end":[216,23],"bytes":[5936,5945]},"callee":null},"220:12":{"kind":"call","span":{"start":[220,12],"end":[220,21],"bytes":[5986,5995]},"callee":null},"225:12":{"kind":"call","span":{"start":[225,12],"end":[225,44],"bytes":[6118,6150]},"callee":null},"228:12":{"kind":"call","span":{"start":[228,12],"end":[228,53],"bytes":[6197,6238]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir"},"228:30":{"kind":"call","span":{"start":[228,30],"end":[228,52],"bytes":[6215,6237]},"callee":null},"233:12":{"kind":"call","span":{"start":[233,12],"end":[233,54],"bytes":[6338,6380]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile.closure_1","kind":"function","span":{"start":[193,1],"end":[198,5],"bytes":[5539,5627]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile.closure_1","parameters":[],"body":{"194:7":{"kind":"call","span":{"start":[194,7],"end":[194,16],"bytes":[5561,5570]},"callee":null},"196:8":{"kind":"call","span":{"start":[196,8],"end":[196,30],"bytes":[5596,5618]},"callee":null}}}}},"github.com/benbjohnson/litestream/file.ReplicaClient.db":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.db","kind":"method","span":{"start":[63,1],"end":[68,2],"bytes":[1516,1625]},"signature":"github.com/benbjohnson/litestream/file.ReplicaClient.db","parameters":[],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":2},"body":{"67:9":{"kind":"call","span":{"start":[67,9],"end":[67,23],"bytes":[1609,1623]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB"}}}},"fields":{"Replica":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/Replica","kind":"field","type":"*github.com/benbjohnson/litestream.Replica","span":{"start":[36,1],"end":[36,29],"bytes":[660,688]}},"logger":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[37,1],"end":[37,22],"bytes":[689,710]}},"path":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/path","kind":"field","type":"string","span":{"start":[34,1],"end":[34,33],"bytes":[626,658]}}}}},"functions":{"github.com/benbjohnson/litestream/file.NewReplicaClient":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient","kind":"function","span":{"start":[41,1],"end":[46,2],"bytes":[775,926]},"signature":"github.com/benbjohnson/litestream/file.NewReplicaClient","parameters":[{"name":"path","type":"string","span":{"start":[41,1],"end":[41,52],"bytes":[775,826]}}],"return_type":"*github.com/benbjohnson/litestream/file.ReplicaClient","metrics":{"cyclomatic":1},"body":{"43:11":{"kind":"call","span":{"start":[43,11],"end":[43,25],"bytes":[861,875]},"callee":null}}},"github.com/benbjohnson/litestream/file.NewReplicaClientFromURL":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClientFromURL","kind":"function","span":{"start":[54,1],"end":[60,2],"bytes":[1172,1472]},"signature":"github.com/benbjohnson/litestream/file.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[54,1],"end":[54,137],"bytes":[1172,1308]}},{"name":"host","type":"string","span":{"start":[54,1],"end":[54,137],"bytes":[1172,1308]}},{"name":"urlPath","type":"string","span":{"start":[54,1],"end":[54,137],"bytes":[1172,1308]}},{"name":"query","type":"net/url.Values","span":{"start":[54,1],"end":[54,137],"bytes":[1172,1308]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[54,1],"end":[54,137],"bytes":[1172,1308]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"57:15":{"kind":"call","span":{"start":[57,15],"end":[57,55],"bytes":[1388,1428]},"callee":null},"59:9":{"kind":"call","span":{"start":[59,9],"end":[59,34],"bytes":[1440,1465]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient"}}},"github.com/benbjohnson/litestream/file.init":{"id":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.init","kind":"function","span":{"start":[22,1],"end":[24,2],"bytes":[242,331]},"signature":"github.com/benbjohnson/litestream/file.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"23:2":{"kind":"call","span":{"start":[23,2],"end":[23,74],"bytes":[257,329]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}}},"content_hash":"e962698493a721417236820987741a07fae6650a57f81bb5de664b58fc1300df"},"gs/replica_client.go":{"id":"can://go/litestream/gs/replica_client.go","kind":"module","span":{"start":[1,1],"end":[316,1],"bytes":[0,8672]},"package":"gs","source":"package gs\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\t\"time\"\n\n\t\"cloud.google.com/go/storage\"\n\t\"github.com/superfly/ltx\"\n\t\"google.golang.org/api/iterator\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"gs\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"gs\"\n\n// MetadataKeyTimestamp is the metadata key for storing LTX file timestamps in GCS.\nconst MetadataKeyTimestamp = \"litestream-timestamp\"\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files to Google Cloud Storage.\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\tclient *storage.Client // gs client\n\tbkt *storage.BucketHandle // gs bucket handle\n\tlogger *slog.Logger\n\n\t// GS bucket information\n\tBucket string\n\tPath string\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for gs replica URL\")\n\t}\n\n\tclient := NewReplicaClient()\n\tclient.Bucket = host\n\tclient.Path = urlPath\n\treturn client, nil\n}\n\n// Type returns \"gs\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to GS. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) (err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.client != nil {\n\t\treturn nil\n\t}\n\n\tif c.client, err = storage.NewClient(ctx); err != nil {\n\t\treturn fmt.Errorf(\"failed to create GCS client (bucket: %s): %w\", c.Bucket, err)\n\t}\n\tc.bkt = c.client.Bucket(c.Bucket)\n\n\treturn nil\n}\n\n// DeleteAll deletes all LTX files.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\t// Iterate over every object and delete it.\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"LIST\").Inc()\n\tfor it := c.bkt.Objects(ctx, \u0026storage.Query{Prefix: c.Path + \"/\"}); ; {\n\t\tattrs, err := it.Next()\n\t\tif errors.Is(err, iterator.Done) {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"failed to list objects in GCS bucket %s (path: %s): %w\", c.Bucket, c.Path, err)\n\t\t}\n\n\t\tif err := c.bkt.Object(attrs.Name).Delete(ctx); isNotExists(err) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"gs: cannot delete object %q: %w\", attrs.Name, err)\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\t// log.Printf(\"%s(%s): retainer: deleting\", r.db.Path(), r.Name())\n\n\treturn nil\n}\n\n// LTXFiles returns an iterator over all available LTX files for a level.\n// GCS always uses accurate timestamps from metadata since they're included in LIST operations at zero cost.\n// The useMetadata parameter is ignored.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdir := litestream.LTXLevelDir(c.Path, level)\n\tquery := \u0026storage.Query{Prefix: dir + \"/\"}\n\tif seek != 0 {\n\t\tquery.StartOffset = query.Prefix + seek.String()\n\t}\n\n\treturn newLTXFileIterator(c.bkt.Objects(ctx, query), c, level), nil\n}\n\n// WriteLTXFile writes an LTX file from rd to a remote path.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn info, err\n\t}\n\n\tkey := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(rd, \u0026buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\tfullReader := io.MultiReader(\u0026buf, rd)\n\n\twriteCtx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\tw := c.bkt.Object(key).NewWriter(writeCtx)\n\n\t// Store timestamp in GCS metadata for accurate timestamp retrieval\n\tw.Metadata = map[string]string{\n\t\tMetadataKeyTimestamp: timestamp.Format(time.RFC3339Nano),\n\t}\n\n\tn, err := io.Copy(w, fullReader)\n\tif err != nil {\n\t\tcancel()\n\t\treturn info, err\n\t} else if err := w.Close(); err != nil {\n\t\treturn info, err\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(n))\n\n\treturn \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: n,\n\t\tCreatedAt: timestamp,\n\t}, nil\n}\n\n// OpenLTXFile returns a reader for a given LTX file.\n// Returns os.ErrNotExist if no matching index/offset is found.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\t// In the GCS client, a length of -1 reads to EOF while 0 returns no data.\n\t// Callers pass size=0 to indicate \"entire object\" so translate that here.\n\t// See: https://pkg.go.dev/cloud.google.com/go/storage#ObjectHandle.NewRangeReader\n\tlength := size\n\tif length \u003c= 0 {\n\t\tlength = -1\n\t}\n\n\tr, err := c.bkt.Object(key).NewRangeReader(ctx, offset, length)\n\tif isNotExists(err) {\n\t\treturn nil, os.ErrNotExist\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Add(float64(r.Attrs.Size))\n\n\treturn r, nil\n}\n\n// DeleteLTXFiles deletes a set of LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, info := range a {\n\t\tkey := litestream.LTXFilePath(c.Path, info.Level, info.MinTXID, info.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"key\", key)\n\n\t\tif err := c.bkt.Object(key).Delete(ctx); err != nil \u0026\u0026 !isNotExists(err) {\n\t\t\treturn fmt.Errorf(\"gs: cannot delete ltx file %q: %w\", key, err)\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n\ntype ltxFileIterator struct {\n\tit *storage.ObjectIterator\n\tclient *ReplicaClient\n\tlevel int\n\tinfo *ltx.FileInfo\n\terr error\n}\n\nfunc newLTXFileIterator(it *storage.ObjectIterator, client *ReplicaClient, level int) *ltxFileIterator {\n\treturn \u0026ltxFileIterator{\n\t\tit: it,\n\t\tclient: client,\n\t\tlevel: level,\n\t}\n}\n\nfunc (itr *ltxFileIterator) Close() (err error) {\n\treturn itr.err\n}\n\nfunc (itr *ltxFileIterator) Next() bool {\n\t// Exit if an error has already occurred.\n\tif itr.err != nil {\n\t\treturn false\n\t}\n\n\tfor {\n\t\t// Fetch next object.\n\t\tattrs, err := itr.it.Next()\n\t\tif errors.Is(err, iterator.Done) {\n\t\t\treturn false\n\t\t} else if err != nil {\n\t\t\titr.err = err\n\t\t\treturn false\n\t\t}\n\n\t\t// Parse index \u0026 offset, otherwise skip to the next object.\n\t\tminTXID, maxTXID, err := ltx.ParseFilename(path.Base(attrs.Name))\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Always use accurate timestamp from metadata since it's zero-cost\n\t\t// GCS includes metadata in LIST operations, so no extra API call needed\n\t\tcreatedAt := attrs.Created.UTC()\n\t\tif attrs.Metadata != nil {\n\t\t\tif ts, ok := attrs.Metadata[MetadataKeyTimestamp]; ok {\n\t\t\t\tif parsed, err := time.Parse(time.RFC3339Nano, ts); err == nil {\n\t\t\t\t\tcreatedAt = parsed\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Store current snapshot and return.\n\t\titr.info = \u0026ltx.FileInfo{\n\t\t\tLevel: itr.level,\n\t\t\tMinTXID: minTXID,\n\t\t\tMaxTXID: maxTXID,\n\t\t\tSize: attrs.Size,\n\t\t\tCreatedAt: createdAt,\n\t\t}\n\n\t\treturn true\n\t}\n}\n\nfunc (itr *ltxFileIterator) Err() error { return itr.err }\n\nfunc (itr *ltxFileIterator) Item() *ltx.FileInfo {\n\treturn itr.info\n}\n\nfunc isNotExists(err error) bool {\n\treturn errors.Is(err, storage.ErrObjectNotExist)\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[21,29]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[30,40]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[41,50]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[51,57]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[58,63]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[64,75]}},{"name":"url","path":"net/url","span":{"start":[10,1],"end":[10,11],"bytes":[76,86]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[87,92]}},{"name":"path","path":"path","span":{"start":[12,1],"end":[12,8],"bytes":[93,100]}},{"name":"sync","path":"sync","span":{"start":[13,1],"end":[13,8],"bytes":[101,108]}},{"name":"time","path":"time","span":{"start":[14,1],"end":[14,8],"bytes":[109,116]}},{"name":"storage","path":"cloud.google.com/go/storage","span":{"start":[16,1],"end":[16,31],"bytes":[118,148]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[17,1],"end":[17,27],"bytes":[149,175]}},{"name":"iterator","path":"google.golang.org/api/iterator","span":{"start":[18,1],"end":[18,34],"bytes":[176,209]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[20,1],"end":[20,37],"bytes":[211,247]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[21,1],"end":[21,46],"bytes":[248,293]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient","kind":"struct","span":{"start":[37,1],"end":[46,2],"bytes":[745,964]},"callables":{"github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteAll":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteAll","kind":"method","span":{"start":[95,1],"end":[121,2],"bytes":[2266,3145]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[95,1],"end":[95,63],"bytes":[2266,2328]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"101:2":{"kind":"call","span":{"start":[101,2],"end":[101,78],"bytes":[2429,2505]},"callee":null},"102:12":{"kind":"call","span":{"start":[102,12],"end":[102,68],"bytes":[2523,2579]},"callee":null},"103:17":{"kind":"call","span":{"start":[103,17],"end":[103,26],"bytes":[2601,2610]},"callee":null},"104:6":{"kind":"call","span":{"start":[104,6],"end":[104,35],"bytes":[2616,2645]},"callee":null},"107:11":{"kind":"call","span":{"start":[107,11],"end":[107,102],"bytes":[2692,2783]},"callee":null},"110:13":{"kind":"call","span":{"start":[110,13],"end":[110,37],"bytes":[2801,2825]},"callee":null},"110:51":{"kind":"call","span":{"start":[110,51],"end":[110,67],"bytes":[2839,2855]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists"},"113:11":{"kind":"call","span":{"start":[113,11],"end":[113,73],"bytes":[2905,2967]},"callee":null},"115:3":{"kind":"call","span":{"start":[115,3],"end":[115,81],"bytes":[2974,3052]},"callee":null},"96:12":{"kind":"call","span":{"start":[96,12],"end":[96,23],"bytes":[2340,2351]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init"}}},"github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[222,1],"end":[239,2],"bytes":[6379,7002]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[222,1],"end":[222,87],"bytes":[6379,6465]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[222,1],"end":[222,87],"bytes":[6379,6465]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"223:12":{"kind":"call","span":{"start":[223,12],"end":[223,23],"bytes":[6477,6488]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init"},"228:10":{"kind":"call","span":{"start":[228,10],"end":[228,80],"bytes":[6555,6625]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"230:3":{"kind":"call","span":{"start":[230,3],"end":[230,121],"bytes":[6629,6747]},"callee":null},"232:13":{"kind":"call","span":{"start":[232,13],"end":[232,30],"bytes":[6761,6778]},"callee":null},"232:59":{"kind":"call","span":{"start":[232,59],"end":[232,75],"bytes":[6807,6823]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists"},"233:11":{"kind":"call","span":{"start":[233,11],"end":[233,68],"bytes":[6836,6893]},"callee":null},"235:3":{"kind":"call","span":{"start":[235,3],"end":[235,81],"bytes":[6900,6978]},"callee":null}}},"github.com/benbjohnson/litestream/gs.ReplicaClient.Init":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","kind":"method","span":{"start":[78,1],"end":[92,2],"bytes":[1898,2228]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[78,1],"end":[78,64],"bytes":[1898,1961]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"79:2":{"kind":"call","span":{"start":[79,2],"end":[79,13],"bytes":[1963,1974]},"callee":null},"80:8":{"kind":"call","span":{"start":[80,8],"end":[80,21],"bytes":[1982,1995]},"callee":null},"86:21":{"kind":"call","span":{"start":[86,21],"end":[86,43],"bytes":[2056,2078]},"callee":null},"87:10":{"kind":"call","span":{"start":[87,10],"end":[87,83],"bytes":[2102,2175]},"callee":null},"89:10":{"kind":"call","span":{"start":[89,10],"end":[89,35],"bytes":[2188,2213]},"callee":null}}},"github.com/benbjohnson/litestream/gs.ReplicaClient.LTXFiles":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.LTXFiles","kind":"method","span":{"start":[126,1],"end":[138,2],"bytes":[3371,3787]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[126,1],"end":[126,126],"bytes":[3371,3496]}},{"name":"level","type":"int","span":{"start":[126,1],"end":[126,126],"bytes":[3371,3496]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[126,1],"end":[126,126],"bytes":[3371,3496]}},{"name":"useMetadata","type":"bool","span":{"start":[126,1],"end":[126,126],"bytes":[3371,3496]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"127:12":{"kind":"call","span":{"start":[127,12],"end":[127,23],"bytes":[3508,3519]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init"},"131:9":{"kind":"call","span":{"start":[131,9],"end":[131,46],"bytes":[3564,3601]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"134:38":{"kind":"call","span":{"start":[134,38],"end":[134,51],"bytes":[3699,3712]},"callee":null},"137:28":{"kind":"call","span":{"start":[137,28],"end":[137,53],"bytes":[3744,3769]},"callee":null},"137:9":{"kind":"call","span":{"start":[137,9],"end":[137,64],"bytes":[3725,3780]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.newLTXFileIterator"}}},"github.com/benbjohnson/litestream/gs.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[193,1],"end":[219,2],"bytes":[5412,6331]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[193,1],"end":[193,140],"bytes":[5412,5551]}},{"name":"level","type":"int","span":{"start":[193,1],"end":[193,140],"bytes":[5412,5551]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[193,1],"end":[193,140],"bytes":[5412,5551]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[193,1],"end":[193,140],"bytes":[5412,5551]}},{"name":"offset","type":"int64","span":{"start":[193,1],"end":[193,140],"bytes":[5412,5551]}},{"name":"size","type":"int64","span":{"start":[193,1],"end":[193,140],"bytes":[5412,5551]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"194:12":{"kind":"call","span":{"start":[194,12],"end":[194,23],"bytes":[5563,5574]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init"},"198:9":{"kind":"call","span":{"start":[198,9],"end":[198,64],"bytes":[5619,5674]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"208:12":{"kind":"call","span":{"start":[208,12],"end":[208,29],"bytes":[5975,5992]},"callee":null},"209:5":{"kind":"call","span":{"start":[209,5],"end":[209,21],"bytes":[6033,6049]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists"},"215:2":{"kind":"call","span":{"start":[215,2],"end":[215,77],"bytes":[6128,6203]},"callee":null},"216:2":{"kind":"call","span":{"start":[216,2],"end":[216,77],"bytes":[6211,6286]},"callee":null},"216:82":{"kind":"call","span":{"start":[216,82],"end":[216,103],"bytes":[6291,6312]},"callee":null}}},"github.com/benbjohnson/litestream/gs.ReplicaClient.SetLogger":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.SetLogger","kind":"method","span":{"start":[55,1],"end":[57,2],"bytes":[1153,1259]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[55,1],"end":[55,57],"bytes":[1153,1209]}}],"metrics":{"cyclomatic":1},"body":{"56:13":{"kind":"call","span":{"start":[56,13],"end":[56,48],"bytes":[1222,1257]},"callee":null}}},"github.com/benbjohnson/litestream/gs.ReplicaClient.Type":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Type","kind":"method","span":{"start":[73,1],"end":[75,2],"bytes":[1757,1824]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/gs.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[141,1],"end":[189,2],"bytes":[3850,5292]},"signature":"github.com/benbjohnson/litestream/gs.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[141,1],"end":[141,144],"bytes":[3850,3993]}},{"name":"level","type":"int","span":{"start":[141,1],"end":[141,144],"bytes":[3850,3993]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[141,1],"end":[141,144],"bytes":[3850,3993]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[141,1],"end":[141,144],"bytes":[3850,3993]}},{"name":"rd","type":"io.Reader","span":{"start":[141,1],"end":[141,144],"bytes":[3850,3993]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"142:12":{"kind":"call","span":{"start":[142,12],"end":[142,23],"bytes":[4005,4016]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init"},"146:9":{"kind":"call","span":{"start":[146,9],"end":[146,64],"bytes":[4062,4117]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"150:15":{"kind":"call","span":{"start":[150,15],"end":[150,37],"bytes":[4228,4250]},"callee":null},"153:17":{"kind":"call","span":{"start":[153,17],"end":[153,42],"bytes":[4306,4331]},"callee":null},"155:15":{"kind":"call","span":{"start":[155,15],"end":[155,71],"bytes":[4363,4419]},"callee":null},"157:15":{"kind":"call","span":{"start":[157,15],"end":[157,44],"bytes":[4437,4466]},"callee":null},"160:16":{"kind":"call","span":{"start":[160,16],"end":[160,40],"bytes":[4535,4559]},"callee":null},"162:22":{"kind":"call","span":{"start":[162,22],"end":[162,45],"bytes":[4582,4605]},"callee":null},"163:8":{"kind":"call","span":{"start":[163,8],"end":[163,16],"bytes":[4613,4621]},"callee":null},"164:7":{"kind":"call","span":{"start":[164,7],"end":[164,24],"bytes":[4628,4645]},"callee":null},"168:25":{"kind":"call","span":{"start":[168,25],"end":[168,59],"bytes":[4793,4827]},"callee":null},"171:12":{"kind":"call","span":{"start":[171,12],"end":[171,34],"bytes":[4844,4866]},"callee":null},"173:3":{"kind":"call","span":{"start":[173,3],"end":[173,11],"bytes":[4886,4894]},"callee":null},"175:19":{"kind":"call","span":{"start":[175,19],"end":[175,28],"bytes":[4932,4941]},"callee":null},"179:2":{"kind":"call","span":{"start":[179,2],"end":[179,77],"bytes":[4980,5055]},"callee":null},"180:2":{"kind":"call","span":{"start":[180,2],"end":[180,77],"bytes":[5063,5138]},"callee":null},"180:82":{"kind":"call","span":{"start":[180,82],"end":[180,92],"bytes":[5143,5153]},"callee":null}}}},"fields":{"Bucket":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/Bucket","kind":"field","type":"string","span":{"start":[44,1],"end":[44,15],"bytes":[933,947]}},"Path":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[45,1],"end":[45,15],"bytes":[948,962]}},"bkt":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/bkt","kind":"field","type":"*cloud.google.com/go/storage.BucketHandle","span":{"start":[40,1],"end":[40,50],"bytes":[835,884]}},"client":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/client","kind":"field","type":"*cloud.google.com/go/storage.Client","span":{"start":[39,1],"end":[39,43],"bytes":[792,834]}},"logger":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[41,1],"end":[41,21],"bytes":[885,905]}},"mu":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[38,1],"end":[38,19],"bytes":[773,791]}}}},"ltxFileIterator":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator","kind":"struct","span":{"start":[241,1],"end":[247,2],"bytes":[7004,7138]},"callables":{"github.com/benbjohnson/litestream/gs.ltxFileIterator.Close":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/github.com/benbjohnson/litestream/gs.ltxFileIterator.Close","kind":"method","span":{"start":[257,1],"end":[259,2],"bytes":[7326,7393]},"signature":"github.com/benbjohnson/litestream/gs.ltxFileIterator.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/gs.ltxFileIterator.Err":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/github.com/benbjohnson/litestream/gs.ltxFileIterator.Err","kind":"method","span":{"start":[307,1],"end":[307,59],"bytes":[8454,8512]},"signature":"github.com/benbjohnson/litestream/gs.ltxFileIterator.Err","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/gs.ltxFileIterator.Item":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/github.com/benbjohnson/litestream/gs.ltxFileIterator.Item","kind":"method","span":{"start":[309,1],"end":[311,2],"bytes":[8514,8583]},"signature":"github.com/benbjohnson/litestream/gs.ltxFileIterator.Item","parameters":[],"return_type":"*github.com/superfly/ltx.FileInfo","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/gs.ltxFileIterator.Next":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/github.com/benbjohnson/litestream/gs.ltxFileIterator.Next","kind":"method","span":{"start":[261,1],"end":[305,2],"bytes":[7395,8452]},"signature":"github.com/benbjohnson/litestream/gs.ltxFileIterator.Next","parameters":[],"return_type":"bool","metrics":{"cyclomatic":9},"body":{"269:17":{"kind":"call","span":{"start":[269,17],"end":[269,30],"bytes":[7567,7580]},"callee":null},"270:6":{"kind":"call","span":{"start":[270,6],"end":[270,35],"bytes":[7586,7615]},"callee":null},"278:28":{"kind":"call","span":{"start":[278,28],"end":[278,68],"bytes":[7786,7826]},"callee":null},"278:46":{"kind":"call","span":{"start":[278,46],"end":[278,67],"bytes":[7804,7825]},"callee":null},"285:16":{"kind":"call","span":{"start":[285,16],"end":[285,35],"bytes":[8022,8041]},"callee":null},"288:23":{"kind":"call","span":{"start":[288,23],"end":[288,55],"bytes":[8152,8184]},"callee":null}}}},"fields":{"client":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/client","kind":"field","type":"*github.com/benbjohnson/litestream/gs.ReplicaClient","span":{"start":[243,1],"end":[243,23],"bytes":[7066,7088]}},"err":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/err","kind":"field","type":"error","span":{"start":[246,1],"end":[246,14],"bytes":[7123,7136]}},"info":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/info","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[245,1],"end":[245,22],"bytes":[7101,7122]}},"it":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/it","kind":"field","type":"*cloud.google.com/go/storage.ObjectIterator","span":{"start":[242,1],"end":[242,32],"bytes":[7034,7065]}},"level":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ltxFileIterator/level","kind":"field","type":"int","span":{"start":[244,1],"end":[244,12],"bytes":[7089,7100]}}}}},"functions":{"github.com/benbjohnson/litestream/gs.NewReplicaClient":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient","kind":"function","span":{"start":[49,1],"end":[53,2],"bytes":[1027,1151]},"signature":"github.com/benbjohnson/litestream/gs.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/gs.ReplicaClient","metrics":{"cyclomatic":1},"body":{"51:11":{"kind":"call","span":{"start":[51,11],"end":[51,25],"bytes":[1102,1116]},"callee":null}}},"github.com/benbjohnson/litestream/gs.NewReplicaClientFromURL":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClientFromURL","kind":"function","span":{"start":[61,1],"end":[70,2],"bytes":[1397,1714]},"signature":"github.com/benbjohnson/litestream/gs.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[61,1],"end":[61,137],"bytes":[1397,1533]}},{"name":"host","type":"string","span":{"start":[61,1],"end":[61,137],"bytes":[1397,1533]}},{"name":"urlPath","type":"string","span":{"start":[61,1],"end":[61,137],"bytes":[1397,1533]}},{"name":"query","type":"net/url.Values","span":{"start":[61,1],"end":[61,137],"bytes":[1397,1533]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[61,1],"end":[61,137],"bytes":[1397,1533]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"63:15":{"kind":"call","span":{"start":[63,15],"end":[63,63],"bytes":[1565,1613]},"callee":null},"66:12":{"kind":"call","span":{"start":[66,12],"end":[66,30],"bytes":[1629,1647]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient"}}},"github.com/benbjohnson/litestream/gs.init":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.init","kind":"function","span":{"start":[24,1],"end":[26,2],"bytes":[297,384]},"signature":"github.com/benbjohnson/litestream/gs.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"25:2":{"kind":"call","span":{"start":[25,2],"end":[25,72],"bytes":[312,382]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}},"github.com/benbjohnson/litestream/gs.isNotExists":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists","kind":"function","span":{"start":[313,1],"end":[315,2],"bytes":[8585,8671]},"signature":"github.com/benbjohnson/litestream/gs.isNotExists","parameters":[{"name":"err","type":"error","span":{"start":[313,1],"end":[313,35],"bytes":[8585,8619]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"314:9":{"kind":"call","span":{"start":[314,9],"end":[314,50],"bytes":[8628,8669]},"callee":null}}},"github.com/benbjohnson/litestream/gs.newLTXFileIterator":{"id":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.newLTXFileIterator","kind":"function","span":{"start":[249,1],"end":[255,2],"bytes":[7140,7324]},"signature":"github.com/benbjohnson/litestream/gs.newLTXFileIterator","parameters":[{"name":"it","type":"*cloud.google.com/go/storage.ObjectIterator","span":{"start":[249,1],"end":[249,105],"bytes":[7140,7244]}},{"name":"client","type":"*github.com/benbjohnson/litestream/gs.ReplicaClient","span":{"start":[249,1],"end":[249,105],"bytes":[7140,7244]}},{"name":"level","type":"int","span":{"start":[249,1],"end":[249,105],"bytes":[7140,7244]}}],"return_type":"*github.com/benbjohnson/litestream/gs.ltxFileIterator","metrics":{"cyclomatic":1},"body":{}}},"content_hash":"1bc094d2a506f33e506545d5cebb40caf43ad4fec91331d27bbb588fdf9df78b"},"heartbeat.go":{"id":"can://go/litestream/heartbeat.go","kind":"module","span":{"start":[1,1],"end":[85,1],"bytes":[0,1575]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\tDefaultHeartbeatInterval = 5 * time.Minute\n\tDefaultHeartbeatTimeout = 30 * time.Second\n\tMinHeartbeatInterval = 1 * time.Minute\n)\n\ntype HeartbeatClient struct {\n\tmu sync.Mutex\n\thttpClient *http.Client\n\n\tURL string\n\tInterval time.Duration\n\tTimeout time.Duration\n\n\tlastPingAt time.Time\n}\n\nfunc NewHeartbeatClient(url string, interval time.Duration) *HeartbeatClient {\n\tif interval \u003c MinHeartbeatInterval {\n\t\tinterval = MinHeartbeatInterval\n\t}\n\n\ttimeout := DefaultHeartbeatTimeout\n\n\treturn \u0026HeartbeatClient{\n\t\tURL: url,\n\t\tInterval: interval,\n\t\tTimeout: timeout,\n\t\thttpClient: \u0026http.Client{\n\t\t\tTimeout: timeout,\n\t\t},\n\t}\n}\n\nfunc (c *HeartbeatClient) Ping(ctx context.Context) error {\n\tif c.URL == \"\" {\n\t\treturn nil\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, c.URL, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create request: %w\", err)\n\t}\n\n\tresp, err := c.httpClient.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"http request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode \u003c 200 || resp.StatusCode \u003e= 300 {\n\t\treturn fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\treturn nil\n}\n\nfunc (c *HeartbeatClient) ShouldPing() bool {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\treturn time.Since(c.lastPingAt) \u003e= c.Interval\n}\n\nfunc (c *HeartbeatClient) LastPingAt() time.Time {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\treturn c.lastPingAt\n}\n\nfunc (c *HeartbeatClient) RecordPing() {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tc.lastPingAt = time.Now()\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"fmt","path":"fmt","span":{"start":[5,1],"end":[5,7],"bytes":[40,46]}},{"name":"http","path":"net/http","span":{"start":[6,1],"end":[6,12],"bytes":[47,58]}},{"name":"sync","path":"sync","span":{"start":[7,1],"end":[7,8],"bytes":[59,66]}},{"name":"time","path":"time","span":{"start":[8,1],"end":[8,8],"bytes":[67,74]}}],"types":{"HeartbeatClient":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient","kind":"struct","span":{"start":[17,1],"end":[26,2],"bytes":[222,390]},"callables":{"github.com/benbjohnson/litestream.HeartbeatClient.LastPingAt":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.LastPingAt","kind":"method","span":{"start":[74,1],"end":[78,2],"bytes":[1362,1469]},"signature":"github.com/benbjohnson/litestream.HeartbeatClient.LastPingAt","parameters":[],"return_type":"time.Time","metrics":{"cyclomatic":1},"body":{"75:2":{"kind":"call","span":{"start":[75,2],"end":[75,13],"bytes":[1414,1425]},"callee":null},"76:8":{"kind":"call","span":{"start":[76,8],"end":[76,21],"bytes":[1433,1446]},"callee":null}}},"github.com/benbjohnson/litestream.HeartbeatClient.Ping":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.Ping","kind":"method","span":{"start":[45,1],"end":[66,2],"bytes":[730,1230]},"signature":"github.com/benbjohnson/litestream.HeartbeatClient.Ping","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[45,1],"end":[45,60],"bytes":[730,789]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"50:14":{"kind":"call","span":{"start":[50,14],"end":[50,73],"bytes":[838,897]},"callee":null},"52:10":{"kind":"call","span":{"start":[52,10],"end":[52,47],"bytes":[924,961]},"callee":null},"55:15":{"kind":"call","span":{"start":[55,15],"end":[55,35],"bytes":[980,1000]},"callee":null},"57:10":{"kind":"call","span":{"start":[57,10],"end":[57,45],"bytes":[1027,1062]},"callee":null},"59:8":{"kind":"call","span":{"start":[59,8],"end":[59,25],"bytes":[1073,1090]},"callee":null},"62:10":{"kind":"call","span":{"start":[62,10],"end":[62,67],"bytes":[1155,1212]},"callee":null}}},"github.com/benbjohnson/litestream.HeartbeatClient.RecordPing":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.RecordPing","kind":"method","span":{"start":[80,1],"end":[84,2],"bytes":[1471,1574]},"signature":"github.com/benbjohnson/litestream.HeartbeatClient.RecordPing","parameters":[],"metrics":{"cyclomatic":1},"body":{"81:2":{"kind":"call","span":{"start":[81,2],"end":[81,13],"bytes":[1513,1524]},"callee":null},"82:8":{"kind":"call","span":{"start":[82,8],"end":[82,21],"bytes":[1532,1545]},"callee":null},"83:17":{"kind":"call","span":{"start":[83,17],"end":[83,27],"bytes":[1562,1572]},"callee":null}}},"github.com/benbjohnson/litestream.HeartbeatClient.ShouldPing":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.ShouldPing","kind":"method","span":{"start":[68,1],"end":[72,2],"bytes":[1232,1360]},"signature":"github.com/benbjohnson/litestream.HeartbeatClient.ShouldPing","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"69:2":{"kind":"call","span":{"start":[69,2],"end":[69,13],"bytes":[1279,1290]},"callee":null},"70:8":{"kind":"call","span":{"start":[70,8],"end":[70,21],"bytes":[1298,1311]},"callee":null},"71:9":{"kind":"call","span":{"start":[71,9],"end":[71,33],"bytes":[1320,1344]},"callee":null}}}},"fields":{"Interval":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/Interval","kind":"field","type":"time.Duration","span":{"start":[22,1],"end":[22,24],"bytes":[318,341]}},"Timeout":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/Timeout","kind":"field","type":"time.Duration","span":{"start":[23,1],"end":[23,24],"bytes":[342,365]}},"URL":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/URL","kind":"field","type":"string","span":{"start":[21,1],"end":[21,17],"bytes":[301,317]}},"httpClient":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/httpClient","kind":"field","type":"*net/http.Client","span":{"start":[19,1],"end":[19,25],"bytes":[275,299]}},"lastPingAt":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/lastPingAt","kind":"field","type":"time.Time","span":{"start":[25,1],"end":[25,22],"bytes":[367,388]}},"mu":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[18,1],"end":[18,23],"bytes":[252,274]}}}}},"functions":{"github.com/benbjohnson/litestream.NewHeartbeatClient":{"id":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.NewHeartbeatClient","kind":"function","span":{"start":[28,1],"end":[43,2],"bytes":[392,728]},"signature":"github.com/benbjohnson/litestream.NewHeartbeatClient","parameters":[{"name":"url","type":"string","span":{"start":[28,1],"end":[28,79],"bytes":[392,470]}},{"name":"interval","type":"time.Duration","span":{"start":[28,1],"end":[28,79],"bytes":[392,470]}}],"return_type":"*github.com/benbjohnson/litestream.HeartbeatClient","metrics":{"cyclomatic":2},"body":{}}},"content_hash":"ef90f2e79f1a8f89bd34a9cada9e3c399a410cf774b5c2b3233863dc7cbf2581"},"internal/hexdump.go":{"id":"can://go/litestream/internal/hexdump.go","kind":"module","span":{"start":[1,1],"end":[46,1],"bytes":[0,1259]},"package":"internal","source":"package internal\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\nfunc Hexdump(data []byte) string {\n\tprevRow := make([]byte, 16)\n\n\tvar buf bytes.Buffer\n\tvar dupWritten bool\n\tfor i := 0; i \u003c len(data); i += 16 {\n\t\trow := make([]byte, 16)\n\t\tcopy(row, data[i:])\n\n\t\t// Write out one line of asterisks to show that we just have duplicate rows.\n\t\tif i != 0 \u0026\u0026 i+16 \u003c len(data) \u0026\u0026 bytes.Equal(row, prevRow) {\n\t\t\tif !dupWritten {\n\t\t\t\tdupWritten = true\n\t\t\t\tfmt.Fprintln(\u0026buf, \"***\")\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Track previous row so we know when we have duplicates.\n\t\tcopy(prevRow, row)\n\t\tdupWritten = false\n\n\t\tfmt.Fprintf(\u0026buf, \"%08x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x |%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s|\\n\", i,\n\t\t\trow[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7],\n\t\t\trow[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15],\n\t\t\ttoChar(row[0]), toChar(row[1]), toChar(row[2]), toChar(row[3]), toChar(row[4]), toChar(row[5]), toChar(row[6]), toChar(row[7]),\n\t\t\ttoChar(row[8]), toChar(row[9]), toChar(row[10]), toChar(row[11]), toChar(row[12]), toChar(row[13]), toChar(row[14]), toChar(row[15]),\n\t\t)\n\t}\n\treturn buf.String()\n}\n\nfunc toChar(b byte) string {\n\tif b \u003c 32 || b \u003e 126 {\n\t\treturn \".\"\n\t}\n\treturn string(b)\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[27,35]}},{"name":"fmt","path":"fmt","span":{"start":[5,1],"end":[5,7],"bytes":[36,42]}}],"types":{},"functions":{"github.com/benbjohnson/litestream/internal.Hexdump":{"id":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.Hexdump","kind":"function","span":{"start":[8,1],"end":[38,2],"bytes":[46,1168]},"signature":"github.com/benbjohnson/litestream/internal.Hexdump","parameters":[{"name":"data","type":"[]byte","span":{"start":[8,1],"end":[8,35],"bytes":[46,80]}}],"return_type":"string","metrics":{"cyclomatic":4},"body":{"13:18":{"kind":"call","span":{"start":[13,18],"end":[13,27],"bytes":[171,180]},"callee":null},"14:10":{"kind":"call","span":{"start":[14,10],"end":[14,26],"bytes":[201,217]},"callee":null},"15:3":{"kind":"call","span":{"start":[15,3],"end":[15,22],"bytes":[220,239]},"callee":null},"18:23":{"kind":"call","span":{"start":[18,23],"end":[18,32],"bytes":[342,351]},"callee":null},"18:36":{"kind":"call","span":{"start":[18,36],"end":[18,61],"bytes":[355,380]},"callee":null},"21:5":{"kind":"call","span":{"start":[21,5],"end":[21,30],"bytes":[429,454]},"callee":null},"27:3":{"kind":"call","span":{"start":[27,3],"end":[27,21],"bytes":[539,557]},"callee":null},"30:3":{"kind":"call","span":{"start":[30,3],"end":[35,4],"bytes":[582,1142]},"callee":null},"33:100":{"kind":"call","span":{"start":[33,100],"end":[33,114],"bytes":[970,984]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:116":{"kind":"call","span":{"start":[33,116],"end":[33,130],"bytes":[986,1000]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:20":{"kind":"call","span":{"start":[33,20],"end":[33,34],"bytes":[890,904]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:36":{"kind":"call","span":{"start":[33,36],"end":[33,50],"bytes":[906,920]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:4":{"kind":"call","span":{"start":[33,4],"end":[33,18],"bytes":[874,888]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:52":{"kind":"call","span":{"start":[33,52],"end":[33,66],"bytes":[922,936]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:68":{"kind":"call","span":{"start":[33,68],"end":[33,82],"bytes":[938,952]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"33:84":{"kind":"call","span":{"start":[33,84],"end":[33,98],"bytes":[954,968]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:104":{"kind":"call","span":{"start":[34,104],"end":[34,119],"bytes":[1105,1120]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:121":{"kind":"call","span":{"start":[34,121],"end":[34,136],"bytes":[1122,1137]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:20":{"kind":"call","span":{"start":[34,20],"end":[34,34],"bytes":[1021,1035]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:36":{"kind":"call","span":{"start":[34,36],"end":[34,51],"bytes":[1037,1052]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:4":{"kind":"call","span":{"start":[34,4],"end":[34,18],"bytes":[1005,1019]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:53":{"kind":"call","span":{"start":[34,53],"end":[34,68],"bytes":[1054,1069]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:70":{"kind":"call","span":{"start":[34,70],"end":[34,85],"bytes":[1071,1086]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"34:87":{"kind":"call","span":{"start":[34,87],"end":[34,102],"bytes":[1088,1103]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"37:9":{"kind":"call","span":{"start":[37,9],"end":[37,21],"bytes":[1154,1166]},"callee":null},"9:13":{"kind":"call","span":{"start":[9,13],"end":[9,29],"bytes":[93,109]},"callee":null}}},"github.com/benbjohnson/litestream/internal.toChar":{"id":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar","kind":"function","span":{"start":[40,1],"end":[45,2],"bytes":[1170,1258]},"signature":"github.com/benbjohnson/litestream/internal.toChar","parameters":[{"name":"b","type":"byte","span":{"start":[40,1],"end":[40,29],"bytes":[1170,1198]}}],"return_type":"string","metrics":{"cyclomatic":2},"body":{"44:9":{"kind":"call","span":{"start":[44,9],"end":[44,18],"bytes":[1247,1256]},"callee":null}}}},"content_hash":"124cc6cc77df8fbcf11b7d27687a0e2fe0edb403fd28c849424522fb3f482bf6"},"internal/internal.go":{"id":"can://go/litestream/internal/internal.go","kind":"module","span":{"start":[1,1],"end":[178,1],"bytes":[0,4815]},"package":"internal","source":"package internal\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"syscall\"\n\n\t\"github.com/pierrec/lz4/v4\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promauto\"\n)\n\n// ReadCloser wraps a reader to also attach a separate closer.\ntype ReadCloser struct {\n\tr io.Reader\n\tc io.Closer\n}\n\n// NewReadCloser returns a new instance of ReadCloser.\nfunc NewReadCloser(r io.Reader, c io.Closer) *ReadCloser {\n\treturn \u0026ReadCloser{r, c}\n}\n\n// Read reads bytes into the underlying reader.\nfunc (r *ReadCloser) Read(p []byte) (n int, err error) {\n\treturn r.r.Read(p)\n}\n\n// Close closes the reader (if implementing io.ReadCloser) and the Closer.\nfunc (r *ReadCloser) Close() error {\n\tif rc, ok := r.r.(io.Closer); ok {\n\t\tif err := rc.Close(); err != nil {\n\t\t\tr.c.Close()\n\t\t\treturn err\n\t\t}\n\t}\n\treturn r.c.Close()\n}\n\n// LZ4ReadCloser wraps an LZ4 reader with the underlying source for proper closing.\ntype LZ4ReadCloser struct {\n\t*lz4.Reader\n\tunderlying io.Closer\n}\n\nfunc (r *LZ4ReadCloser) Close() error {\n\treturn r.underlying.Close()\n}\n\n// NewLZ4Reader creates an LZ4 decompressing reader that wraps the source.\n// Closing the returned reader also closes the underlying source.\nfunc NewLZ4Reader(r io.ReadCloser) io.ReadCloser {\n\treturn \u0026LZ4ReadCloser{\n\t\tReader: lz4.NewReader(r),\n\t\tunderlying: r,\n\t}\n}\n\n// ReadCounter wraps an io.Reader and counts the total number of bytes read.\ntype ReadCounter struct {\n\tr io.Reader\n\tn int64\n}\n\n// NewReadCounter returns a new instance of ReadCounter that wraps r.\nfunc NewReadCounter(r io.Reader) *ReadCounter {\n\treturn \u0026ReadCounter{r: r}\n}\n\n// Read reads from the underlying reader into p and adds the bytes read to the counter.\nfunc (r *ReadCounter) Read(p []byte) (int, error) {\n\tn, err := r.r.Read(p)\n\tr.n += int64(n)\n\treturn n, err\n}\n\n// N returns the total number of bytes read.\nfunc (r *ReadCounter) N() int64 { return r.n }\n\n// CreateFile creates the file and matches the mode \u0026 uid/gid of fi.\nfunc CreateFile(filename string, fi os.FileInfo) (*os.File, error) {\n\tmode := os.FileMode(0600)\n\tif fi != nil {\n\t\tmode = fi.Mode()\n\t}\n\n\tf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tuid, gid := Fileinfo(fi)\n\t_ = f.Chown(uid, gid)\n\treturn f, nil\n}\n\n// MkdirAll is a copy of os.MkdirAll() except that it attempts to set the\n// mode/uid/gid to match fi for each created directory.\nfunc MkdirAll(path string, fi os.FileInfo) error {\n\tuid, gid := Fileinfo(fi)\n\n\t// Fast path: if we can tell whether path is a directory or file, stop with success or error.\n\tdir, err := os.Stat(path)\n\tif err == nil {\n\t\tif dir.IsDir() {\n\t\t\treturn nil\n\t\t}\n\t\treturn \u0026os.PathError{Op: \"mkdir\", Path: path, Err: syscall.ENOTDIR}\n\t}\n\n\t// Slow path: make sure parent exists and then call Mkdir for path.\n\ti := len(path)\n\tfor i \u003e 0 \u0026\u0026 os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.\n\t\ti--\n\t}\n\n\tj := i\n\tfor j \u003e 0 \u0026\u0026 !os.IsPathSeparator(path[j-1]) { // Scan backward over element.\n\t\tj--\n\t}\n\n\tif j \u003e 1 {\n\t\t// Create parent.\n\t\terr = MkdirAll(fixRootDirectory(path[:j-1]), fi)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Parent now exists; invoke Mkdir and use its result.\n\tmode := os.FileMode(0700)\n\tif fi != nil {\n\t\tmode = fi.Mode()\n\t}\n\terr = os.Mkdir(path, mode)\n\tif err != nil {\n\t\t// Handle arguments like \"foo/.\" by\n\t\t// double-checking that directory doesn't exist.\n\t\tdir, err1 := os.Lstat(path)\n\t\tif err1 == nil \u0026\u0026 dir.IsDir() {\n\t\t\t_ = os.Chown(path, uid, gid)\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\t_ = os.Chown(path, uid, gid)\n\treturn nil\n}\n\n// Shared replica metrics.\nvar (\n\tOperationTotalCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_replica_operation_total\",\n\t\tHelp: \"The number of replica operations performed\",\n\t}, []string{\"replica_type\", \"operation\"})\n\n\tOperationBytesCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_replica_operation_bytes\",\n\t\tHelp: \"The number of bytes used by replica operations\",\n\t}, []string{\"replica_type\", \"operation\"})\n\n\tOperationDurationHistogramVec = promauto.NewHistogramVec(prometheus.HistogramOpts{\n\t\tName: \"litestream_replica_operation_duration_seconds\",\n\t\tHelp: \"Duration of replica operations by type and operation\",\n\t\tBuckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 5, 10, 30, 60},\n\t}, []string{\"replica_type\", \"operation\"})\n\n\tOperationErrorCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_replica_operation_errors_total\",\n\t\tHelp: \"Number of replica operation errors by type, operation, and error code\",\n\t}, []string{\"replica_type\", \"operation\", \"code\"})\n\n\tL0RetentionGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_l0_retention_files_total\",\n\t\tHelp: \"Number of L0 files by status during retention enforcement\",\n\t}, []string{\"db\", \"status\"})\n)\n","imports":[{"name":"io","path":"io","span":{"start":[4,1],"end":[4,6],"bytes":[27,32]}},{"name":"os","path":"os","span":{"start":[5,1],"end":[5,6],"bytes":[33,38]}},{"name":"syscall","path":"syscall","span":{"start":[6,1],"end":[6,11],"bytes":[39,49]}},{"name":"v4","path":"github.com/pierrec/lz4/v4","span":{"start":[8,1],"end":[8,29],"bytes":[51,79]}},{"name":"prometheus","path":"github.com/prometheus/client_golang/prometheus","span":{"start":[9,1],"end":[9,50],"bytes":[80,129]}},{"name":"promauto","path":"github.com/prometheus/client_golang/prometheus/promauto","span":{"start":[10,1],"end":[10,59],"bytes":[130,188]}}],"types":{"LZ4ReadCloser":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.LZ4ReadCloser","kind":"struct","span":{"start":[41,1],"end":[44,2],"bytes":[908,972]},"base_types":["*github.com/pierrec/lz4/v4.Reader"],"callables":{"github.com/benbjohnson/litestream/internal.LZ4ReadCloser.Close":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.LZ4ReadCloser/github.com/benbjohnson/litestream/internal.LZ4ReadCloser.Close","kind":"method","span":{"start":[46,1],"end":[48,2],"bytes":[974,1044]},"signature":"github.com/benbjohnson/litestream/internal.LZ4ReadCloser.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"47:9":{"kind":"call","span":{"start":[47,9],"end":[47,29],"bytes":[1022,1042]},"callee":null}}}},"fields":{"*github.com/pierrec/lz4/v4.Reader":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.LZ4ReadCloser/*github.com/pierrec/lz4/v4.Reader","kind":"field","type":"*github.com/pierrec/lz4/v4.Reader","span":{"start":[42,1],"end":[42,13],"bytes":[936,948]}},"underlying":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.LZ4ReadCloser/underlying","kind":"field","type":"io.Closer","span":{"start":[43,1],"end":[43,22],"bytes":[949,970]}}}},"ReadCloser":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCloser","kind":"struct","span":{"start":[14,1],"end":[17,2],"bytes":[255,307]},"callables":{"github.com/benbjohnson/litestream/internal.ReadCloser.Close":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCloser/github.com/benbjohnson/litestream/internal.ReadCloser.Close","kind":"method","span":{"start":[30,1],"end":[38,2],"bytes":[655,822]},"signature":"github.com/benbjohnson/litestream/internal.ReadCloser.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"32:13":{"kind":"call","span":{"start":[32,13],"end":[32,23],"bytes":[740,750]},"callee":null},"33:4":{"kind":"call","span":{"start":[33,4],"end":[33,15],"bytes":[768,779]},"callee":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar"},"37:9":{"kind":"call","span":{"start":[37,9],"end":[37,20],"bytes":[809,820]},"callee":null}}},"github.com/benbjohnson/litestream/internal.ReadCloser.Read":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCloser/github.com/benbjohnson/litestream/internal.ReadCloser.Read","kind":"method","span":{"start":[25,1],"end":[27,2],"bytes":[500,578]},"signature":"github.com/benbjohnson/litestream/internal.ReadCloser.Read","parameters":[{"name":"p","type":"[]byte","span":{"start":[25,1],"end":[25,57],"bytes":[500,556]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"26:9":{"kind":"call","span":{"start":[26,9],"end":[26,20],"bytes":[565,576]},"callee":null}}}},"fields":{"c":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCloser/c","kind":"field","type":"io.Closer","span":{"start":[16,1],"end":[16,13],"bytes":[293,305]}},"r":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCloser/r","kind":"field","type":"io.Reader","span":{"start":[15,1],"end":[15,13],"bytes":[280,292]}}}},"ReadCounter":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter","kind":"struct","span":{"start":[60,1],"end":[63,2],"bytes":[1394,1443]},"callables":{"github.com/benbjohnson/litestream/internal.ReadCounter.N":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N","kind":"method","span":{"start":[78,1],"end":[78,47],"bytes":[1836,1882]},"signature":"github.com/benbjohnson/litestream/internal.ReadCounter.N","parameters":[],"return_type":"int64","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/internal.ReadCounter.Read":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.Read","kind":"method","span":{"start":[71,1],"end":[75,2],"bytes":[1681,1789]},"signature":"github.com/benbjohnson/litestream/internal.ReadCounter.Read","parameters":[{"name":"p","type":"[]byte","span":{"start":[71,1],"end":[71,52],"bytes":[1681,1732]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"72:12":{"kind":"call","span":{"start":[72,12],"end":[72,23],"bytes":[1744,1755]},"callee":null},"73:9":{"kind":"call","span":{"start":[73,9],"end":[73,17],"bytes":[1764,1772]},"callee":null}}}},"fields":{"n":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/n","kind":"field","type":"int64","span":{"start":[62,1],"end":[62,9],"bytes":[1433,1441]}},"r":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/r","kind":"field","type":"io.Reader","span":{"start":[61,1],"end":[61,13],"bytes":[1420,1432]}}}}},"functions":{"github.com/benbjohnson/litestream/internal.CreateFile":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.CreateFile","kind":"function","span":{"start":[81,1],"end":[95,2],"bytes":[1953,2265]},"signature":"github.com/benbjohnson/litestream/internal.CreateFile","parameters":[{"name":"filename","type":"string","span":{"start":[81,1],"end":[81,69],"bytes":[1953,2021]}},{"name":"fi","type":"os.FileInfo","span":{"start":[81,1],"end":[81,69],"bytes":[1953,2021]}}],"return_type":"(*os.File, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"82:10":{"kind":"call","span":{"start":[82,10],"end":[82,27],"bytes":[2031,2048]},"callee":null},"84:10":{"kind":"call","span":{"start":[84,10],"end":[84,19],"bytes":[2074,2083]},"callee":null},"87:12":{"kind":"call","span":{"start":[87,12],"end":[87,73],"bytes":[2099,2160]},"callee":null},"92:14":{"kind":"call","span":{"start":[92,14],"end":[92,26],"bytes":[2213,2225]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo"},"93:6":{"kind":"call","span":{"start":[93,6],"end":[93,23],"bytes":[2231,2248]},"callee":null}}},"github.com/benbjohnson/litestream/internal.MkdirAll":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","kind":"function","span":{"start":[99,1],"end":[148,2],"bytes":[2397,3546]},"signature":"github.com/benbjohnson/litestream/internal.MkdirAll","parameters":[{"name":"path","type":"string","span":{"start":[99,1],"end":[99,51],"bytes":[2397,2447]}},{"name":"fi","type":"os.FileInfo","span":{"start":[99,1],"end":[99,51],"bytes":[2397,2447]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"100:14":{"kind":"call","span":{"start":[100,14],"end":[100,26],"bytes":[2461,2473]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo"},"103:14":{"kind":"call","span":{"start":[103,14],"end":[103,27],"bytes":[2583,2596]},"callee":null},"105:6":{"kind":"call","span":{"start":[105,6],"end":[105,17],"bytes":[2619,2630]},"callee":null},"112:7":{"kind":"call","span":{"start":[112,7],"end":[112,16],"bytes":[2800,2809]},"callee":null},"113:15":{"kind":"call","span":{"start":[113,15],"end":[113,44],"bytes":[2824,2853]},"callee":null},"118:16":{"kind":"call","span":{"start":[118,16],"end":[118,45],"bytes":[2922,2951]},"callee":null},"124:18":{"kind":"call","span":{"start":[124,18],"end":[124,46],"bytes":[3044,3072]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.fixRootDirectory"},"124:9":{"kind":"call","span":{"start":[124,9],"end":[124,51],"bytes":[3035,3077]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll"},"131:10":{"kind":"call","span":{"start":[131,10],"end":[131,27],"bytes":[3183,3200]},"callee":null},"133:10":{"kind":"call","span":{"start":[133,10],"end":[133,19],"bytes":[3226,3235]},"callee":null},"135:8":{"kind":"call","span":{"start":[135,8],"end":[135,28],"bytes":[3246,3266]},"callee":null},"139:16":{"kind":"call","span":{"start":[139,16],"end":[139,30],"bytes":[3388,3402]},"callee":null},"140:21":{"kind":"call","span":{"start":[140,21],"end":[140,32],"bytes":[3423,3434]},"callee":null},"141:8":{"kind":"call","span":{"start":[141,8],"end":[141,32],"bytes":[3444,3468]},"callee":null},"146:6":{"kind":"call","span":{"start":[146,6],"end":[146,30],"bytes":[3508,3532]},"callee":null}}},"github.com/benbjohnson/litestream/internal.NewLZ4Reader":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader","kind":"function","span":{"start":[52,1],"end":[57,2],"bytes":[1187,1315]},"signature":"github.com/benbjohnson/litestream/internal.NewLZ4Reader","parameters":[{"name":"r","type":"io.ReadCloser","span":{"start":[52,1],"end":[52,51],"bytes":[1187,1237]}}],"return_type":"io.ReadCloser","metrics":{"cyclomatic":1},"body":{"54:15":{"kind":"call","span":{"start":[54,15],"end":[54,31],"bytes":[1276,1292]},"callee":null}}},"github.com/benbjohnson/litestream/internal.NewReadCloser":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCloser","kind":"function","span":{"start":[20,1],"end":[22,2],"bytes":[364,450]},"signature":"github.com/benbjohnson/litestream/internal.NewReadCloser","parameters":[{"name":"r","type":"io.Reader","span":{"start":[20,1],"end":[20,59],"bytes":[364,422]}},{"name":"c","type":"io.Closer","span":{"start":[20,1],"end":[20,59],"bytes":[364,422]}}],"return_type":"*github.com/benbjohnson/litestream/internal.ReadCloser","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/internal.NewReadCounter":{"id":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter","kind":"function","span":{"start":[66,1],"end":[68,2],"bytes":[1515,1591]},"signature":"github.com/benbjohnson/litestream/internal.NewReadCounter","parameters":[{"name":"r","type":"io.Reader","span":{"start":[66,1],"end":[66,48],"bytes":[1515,1562]}}],"return_type":"*github.com/benbjohnson/litestream/internal.ReadCounter","metrics":{"cyclomatic":1},"body":{}}},"content_hash":"1dbef4f2385d3ead118aea4ee0553279841074b2fe2988a4c16fe625415de1b5"},"internal/internal_unix.go":{"id":"can://go/litestream/internal/internal_unix.go","kind":"module","span":{"start":[1,1],"end":[39,1],"bytes":[0,771]},"package":"internal","source":"//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris\n\npackage internal\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\n// Fileinfo returns syscall fields from a FileInfo object.\nfunc Fileinfo(fi os.FileInfo) (uid, gid int) {\n\tif fi == nil {\n\t\treturn -1, -1\n\t}\n\tstat, ok := fi.Sys().(*syscall.Stat_t)\n\tif !ok {\n\t\treturn -1, -1\n\t}\n\treturn int(stat.Uid), int(stat.Gid)\n}\n\nfunc fixRootDirectory(p string) string {\n\treturn p\n}\n\n// FsyncDir syncs a directory so a preceding rename within it is durable.\nfunc FsyncDir(path string) error {\n\tdir, err := os.Open(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := dir.Sync(); err != nil {\n\t\t_ = dir.Close()\n\t\treturn err\n\t}\n\treturn dir.Close()\n}\n","imports":[{"name":"os","path":"os","span":{"start":[7,1],"end":[7,6],"bytes":[186,191]}},{"name":"syscall","path":"syscall","span":{"start":[8,1],"end":[8,11],"bytes":[192,202]}}],"types":{},"functions":{"github.com/benbjohnson/litestream/internal.Fileinfo":{"id":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo","kind":"function","span":{"start":[12,1],"end":[21,2],"bytes":[265,454]},"signature":"github.com/benbjohnson/litestream/internal.Fileinfo","parameters":[{"name":"fi","type":"os.FileInfo","span":{"start":[12,1],"end":[12,47],"bytes":[265,311]}}],"return_type":"(int, int)","metrics":{"cyclomatic":3},"body":{"16:14":{"kind":"call","span":{"start":[16,14],"end":[16,22],"bytes":[360,368]},"callee":null},"20:24":{"kind":"call","span":{"start":[20,24],"end":[20,37],"bytes":[439,452]},"callee":null},"20:9":{"kind":"call","span":{"start":[20,9],"end":[20,22],"bytes":[424,437]},"callee":null}}},"github.com/benbjohnson/litestream/internal.FsyncDir":{"id":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir","kind":"function","span":{"start":[28,1],"end":[38,2],"bytes":[584,770]},"signature":"github.com/benbjohnson/litestream/internal.FsyncDir","parameters":[{"name":"path","type":"string","span":{"start":[28,1],"end":[28,35],"bytes":[584,618]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"29:14":{"kind":"call","span":{"start":[29,14],"end":[29,27],"bytes":[632,645]},"callee":null},"33:12":{"kind":"call","span":{"start":[33,12],"end":[33,22],"bytes":[690,700]},"callee":null},"34:7":{"kind":"call","span":{"start":[34,7],"end":[34,18],"bytes":[721,732]},"callee":null},"37:9":{"kind":"call","span":{"start":[37,9],"end":[37,20],"bytes":[757,768]},"callee":null}}},"github.com/benbjohnson/litestream/internal.fixRootDirectory":{"id":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.fixRootDirectory","kind":"function","span":{"start":[23,1],"end":[25,2],"bytes":[456,508]},"signature":"github.com/benbjohnson/litestream/internal.fixRootDirectory","parameters":[{"name":"p","type":"string","span":{"start":[23,1],"end":[23,41],"bytes":[456,496]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{}}},"content_hash":"cd90a0b1e0c7f49f8dd99c8818b37ffa3b4d7f8edf3215f0394eda7a0b6c8905"},"internal/limit_read_closer.go":{"id":"can://go/litestream/internal/limit_read_closer.go","kind":"module","span":{"start":[1,1],"end":[30,1],"bytes":[0,558]},"package":"internal","source":"package internal\n\nimport \"io\"\n\n// Copied from the io package to implement io.Closer.\nfunc LimitReadCloser(r io.ReadCloser, n int64) io.ReadCloser {\n\treturn \u0026LimitedReadCloser{r, n}\n}\n\ntype LimitedReadCloser struct {\n\tR io.ReadCloser // underlying reader\n\tN int64 // max bytes remaining\n}\n\nfunc (l *LimitedReadCloser) Close() error {\n\treturn l.R.Close()\n}\n\nfunc (l *LimitedReadCloser) Read(p []byte) (n int, err error) {\n\tif l.N \u003c= 0 {\n\t\treturn 0, io.EOF\n\t}\n\tif int64(len(p)) \u003e l.N {\n\t\tp = p[0:l.N]\n\t}\n\tn, err = l.R.Read(p)\n\tl.N -= int64(n)\n\treturn\n}\n","imports":[{"name":"io","path":"io","span":{"start":[3,1],"end":[3,12],"bytes":[18,29]}}],"types":{"LimitedReadCloser":{"id":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitedReadCloser","kind":"struct","span":{"start":[10,1],"end":[13,2],"bytes":[184,295]},"callables":{"github.com/benbjohnson/litestream/internal.LimitedReadCloser.Close":{"id":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitedReadCloser/github.com/benbjohnson/litestream/internal.LimitedReadCloser.Close","kind":"method","span":{"start":[15,1],"end":[17,2],"bytes":[297,362]},"signature":"github.com/benbjohnson/litestream/internal.LimitedReadCloser.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"16:9":{"kind":"call","span":{"start":[16,9],"end":[16,20],"bytes":[349,360]},"callee":null}}},"github.com/benbjohnson/litestream/internal.LimitedReadCloser.Read":{"id":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitedReadCloser/github.com/benbjohnson/litestream/internal.LimitedReadCloser.Read","kind":"method","span":{"start":[19,1],"end":[29,2],"bytes":[364,557]},"signature":"github.com/benbjohnson/litestream/internal.LimitedReadCloser.Read","parameters":[{"name":"p","type":"[]byte","span":{"start":[19,1],"end":[19,64],"bytes":[364,427]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"23:11":{"kind":"call","span":{"start":[23,11],"end":[23,17],"bytes":[475,481]},"callee":null},"23:5":{"kind":"call","span":{"start":[23,5],"end":[23,18],"bytes":[469,482]},"callee":null},"26:11":{"kind":"call","span":{"start":[26,11],"end":[26,22],"bytes":[519,530]},"callee":null},"27:9":{"kind":"call","span":{"start":[27,9],"end":[27,17],"bytes":[539,547]},"callee":null}}}},"fields":{"N":{"id":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitedReadCloser/N","kind":"field","type":"int64","span":{"start":[12,1],"end":[12,40],"bytes":[254,293]}},"R":{"id":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitedReadCloser/R","kind":"field","type":"io.ReadCloser","span":{"start":[11,1],"end":[11,38],"bytes":[216,253]}}}}},"functions":{"github.com/benbjohnson/litestream/internal.LimitReadCloser":{"id":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser","kind":"function","span":{"start":[6,1],"end":[8,2],"bytes":[85,182]},"signature":"github.com/benbjohnson/litestream/internal.LimitReadCloser","parameters":[{"name":"r","type":"io.ReadCloser","span":{"start":[6,1],"end":[6,63],"bytes":[85,147]}},{"name":"n","type":"int64","span":{"start":[6,1],"end":[6,63],"bytes":[85,147]}}],"return_type":"io.ReadCloser","metrics":{"cyclomatic":1},"body":{}}},"content_hash":"c02d22d6d9369a280aca33bc4e0a77f1ee533201289cb691fc16d304732b75d7"},"internal/lock_unix.go":{"id":"can://go/litestream/internal/lock_unix.go","kind":"module","span":{"start":[1,1],"end":[51,1],"bytes":[0,1085]},"package":"internal","source":"//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n\npackage internal\n\nimport (\n\t\"os\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tsqlitePendingByte = 0x40000000\n\tsqliteSharedFirst = sqlitePendingByte + 2\n\tsqliteSharedSize = 510\n)\n\nfunc LockFileExclusive(f *os.File) error {\n\tfd := int(f.Fd())\n\n\tif err := setFcntlLock(fd, unix.F_WRLCK, sqlitePendingByte, 1); err != nil {\n\t\treturn err\n\t}\n\n\tif err := setFcntlLock(fd, unix.F_WRLCK, sqliteSharedFirst, sqliteSharedSize); err != nil {\n\t\t_ = setFcntlLock(fd, unix.F_UNLCK, sqlitePendingByte, 1)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc UnlockFile(f *os.File) error {\n\tfd := int(f.Fd())\n\terr1 := setFcntlLock(fd, unix.F_UNLCK, sqliteSharedFirst, sqliteSharedSize)\n\terr2 := setFcntlLock(fd, unix.F_UNLCK, sqlitePendingByte, 1)\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\nfunc setFcntlLock(fd int, lockType int16, start int64, length int64) error {\n\tflock := unix.Flock_t{\n\t\tType: lockType,\n\t\tWhence: 0,\n\t\tStart: start,\n\t\tLen: length,\n\t}\n\treturn unix.FcntlFlock(uintptr(fd), unix.F_SETLKW, \u0026flock)\n}\n","imports":[{"name":"os","path":"os","span":{"start":[6,1],"end":[6,6],"bytes":[118,123]}},{"name":"unix","path":"golang.org/x/sys/unix","span":{"start":[8,1],"end":[8,25],"bytes":[125,149]}}],"types":{},"functions":{"github.com/benbjohnson/litestream/internal.LockFileExclusive":{"id":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.LockFileExclusive","kind":"function","span":{"start":[17,1],"end":[30,2],"bytes":[264,604]},"signature":"github.com/benbjohnson/litestream/internal.LockFileExclusive","parameters":[{"name":"f","type":"*os.File","span":{"start":[17,1],"end":[17,43],"bytes":[264,306]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"18:12":{"kind":"call","span":{"start":[18,12],"end":[18,18],"bytes":[318,324]},"callee":null},"18:8":{"kind":"call","span":{"start":[18,8],"end":[18,19],"bytes":[314,325]},"callee":null},"20:12":{"kind":"call","span":{"start":[20,12],"end":[20,64],"bytes":[338,390]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock"},"24:12":{"kind":"call","span":{"start":[24,12],"end":[24,79],"bytes":[433,500]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock"},"25:7":{"kind":"call","span":{"start":[25,7],"end":[25,59],"bytes":[521,573]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock"}}},"github.com/benbjohnson/litestream/internal.UnlockFile":{"id":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.UnlockFile","kind":"function","span":{"start":[32,1],"end":[40,2],"bytes":[606,849]},"signature":"github.com/benbjohnson/litestream/internal.UnlockFile","parameters":[{"name":"f","type":"*os.File","span":{"start":[32,1],"end":[32,36],"bytes":[606,641]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"33:12":{"kind":"call","span":{"start":[33,12],"end":[33,18],"bytes":[653,659]},"callee":null},"33:8":{"kind":"call","span":{"start":[33,8],"end":[33,19],"bytes":[649,660]},"callee":null},"34:10":{"kind":"call","span":{"start":[34,10],"end":[34,77],"bytes":[670,737]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock"},"35:10":{"kind":"call","span":{"start":[35,10],"end":[35,62],"bytes":[747,799]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock"}}},"github.com/benbjohnson/litestream/internal.setFcntlLock":{"id":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock","kind":"function","span":{"start":[42,1],"end":[50,2],"bytes":[851,1084]},"signature":"github.com/benbjohnson/litestream/internal.setFcntlLock","parameters":[{"name":"fd","type":"int","span":{"start":[42,1],"end":[42,77],"bytes":[851,927]}},{"name":"lockType","type":"int16","span":{"start":[42,1],"end":[42,77],"bytes":[851,927]}},{"name":"start","type":"int64","span":{"start":[42,1],"end":[42,77],"bytes":[851,927]}},{"name":"length","type":"int64","span":{"start":[42,1],"end":[42,77],"bytes":[851,927]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"49:25":{"kind":"call","span":{"start":[49,25],"end":[49,36],"bytes":[1047,1058]},"callee":null},"49:9":{"kind":"call","span":{"start":[49,9],"end":[49,60],"bytes":[1031,1082]},"callee":null}}}},"content_hash":"9d2a4e0444c4721b2c9695b88a883848ac89a7d4f28181ccc72c75ab9e76f491"},"internal/log.go":{"id":"can://go/litestream/internal/log.go","kind":"module","span":{"start":[1,1],"end":[125,1],"bytes":[0,2737]},"package":"internal","source":"package internal\n\nimport (\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/lmittmann/tint\"\n\t\"github.com/mattn/go-isatty\"\n)\n\nvar modulePrefix string\nvar moduleLastSegment string\n\nfunc init() {\n\tif info, ok := debug.ReadBuildInfo(); ok \u0026\u0026 info.Main.Path != \"\" {\n\t\tmodulePrefix = info.Main.Path + \"/\"\n\t\tmoduleLastSegment = path.Base(info.Main.Path)\n\t}\n}\n\nconst LevelTrace = slog.LevelDebug - 4\n\n// LoggingConfig configures logging.\ntype LoggingConfig struct {\n\tLevel string `yaml:\"level\"`\n\tType string `yaml:\"type\"`\n\tStderr bool `yaml:\"stderr\"`\n\tSource bool `yaml:\"source\"`\n}\n\nfunc InitLog(w io.Writer, level, typ string, addSource bool) {\n\tlogOptions := slog.HandlerOptions{\n\t\tLevel: slog.LevelInfo,\n\t\tAddSource: addSource,\n\t\tReplaceAttr: ReplaceAttr,\n\t}\n\n\t// Read log level from environment, if available.\n\tif v := os.Getenv(\"LOG_LEVEL\"); v != \"\" {\n\t\tlevel = v\n\t}\n\n\tswitch strings.ToUpper(level) {\n\tcase \"TRACE\":\n\t\tlogOptions.Level = LevelTrace\n\tcase \"DEBUG\":\n\t\tlogOptions.Level = slog.LevelDebug\n\tcase \"INFO\":\n\t\tlogOptions.Level = slog.LevelInfo\n\tcase \"WARN\", \"WARNING\":\n\t\tlogOptions.Level = slog.LevelWarn\n\tcase \"ERROR\":\n\t\tlogOptions.Level = slog.LevelError\n\t}\n\n\tvar logHandler slog.Handler\n\tswitch typ {\n\tcase \"json\":\n\t\tlogHandler = slog.NewJSONHandler(w, \u0026logOptions)\n\tcase \"pretty\":\n\t\tnoColor := true\n\t\tif f, ok := w.(*os.File); ok {\n\t\t\tnoColor = !isatty.IsTerminal(f.Fd()) \u0026\u0026 !isatty.IsCygwinTerminal(f.Fd())\n\t\t}\n\t\tlogHandler = tint.NewHandler(w, \u0026tint.Options{\n\t\t\tLevel: logOptions.Level,\n\t\t\tAddSource: addSource,\n\t\t\tTimeFormat: time.TimeOnly,\n\t\t\tNoColor: noColor,\n\t\t\tReplaceAttr: ReplaceAttr,\n\t\t})\n\tcase \"text\", \"\":\n\t\tlogHandler = slog.NewTextHandler(w, \u0026logOptions)\n\t}\n\n\t// Set global default logger.\n\tslog.SetDefault(slog.New(logHandler))\n}\n\nfunc CleanSourcePath(src *slog.Source) {\n\tif modulePrefix == \"\" {\n\t\treturn\n\t}\n\n\tif idx := strings.Index(src.File, modulePrefix); idx \u003e= 0 {\n\t\tsrc.File = src.File[idx+len(modulePrefix):]\n\t\treturn\n\t}\n\n\tmoduleName := strings.TrimSuffix(modulePrefix, \"/\")\n\tif idx := strings.Index(src.File, moduleName+\"@\"); idx \u003e= 0 {\n\t\trest := src.File[idx+len(moduleName):]\n\t\tif slashIdx := strings.Index(rest, \"/\"); slashIdx \u003e= 0 {\n\t\t\tsrc.File = rest[slashIdx+1:]\n\t\t}\n\t\treturn\n\t}\n\n\tif moduleLastSegment != \"\" {\n\t\tsep := \"/\" + moduleLastSegment + \"/\"\n\t\tif idx := strings.LastIndex(src.File, sep); idx \u003e= 0 {\n\t\t\tsrc.File = src.File[idx+len(sep):]\n\t\t}\n\t}\n}\n\nfunc ReplaceAttr(groups []string, a slog.Attr) slog.Attr {\n\tif a.Key == slog.LevelKey \u0026\u0026 a.Value.Any() == LevelTrace {\n\t\ta.Value = slog.StringValue(\"TRACE\")\n\t}\n\n\tif a.Key == slog.SourceKey {\n\t\tif src, ok := a.Value.Any().(*slog.Source); ok {\n\t\t\tCleanSourcePath(src)\n\t\t}\n\t}\n\n\treturn a\n}\n","imports":[{"name":"io","path":"io","span":{"start":[4,1],"end":[4,6],"bytes":[27,32]}},{"name":"slog","path":"log/slog","span":{"start":[5,1],"end":[5,12],"bytes":[33,44]}},{"name":"os","path":"os","span":{"start":[6,1],"end":[6,6],"bytes":[45,50]}},{"name":"path","path":"path","span":{"start":[7,1],"end":[7,8],"bytes":[51,58]}},{"name":"debug","path":"runtime/debug","span":{"start":[8,1],"end":[8,17],"bytes":[59,75]}},{"name":"strings","path":"strings","span":{"start":[9,1],"end":[9,11],"bytes":[76,86]}},{"name":"time","path":"time","span":{"start":[10,1],"end":[10,8],"bytes":[87,94]}},{"name":"tint","path":"github.com/lmittmann/tint","span":{"start":[12,1],"end":[12,29],"bytes":[96,124]}},{"name":"go-isatty","path":"github.com/mattn/go-isatty","span":{"start":[13,1],"end":[13,30],"bytes":[125,154]}}],"types":{"LoggingConfig":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.LoggingConfig","kind":"struct","span":{"start":[29,1],"end":[34,2],"bytes":[463,613]},"callables":{},"fields":{"Level":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.LoggingConfig/Level","kind":"field","type":"string","span":{"start":[30,1],"end":[30,30],"bytes":[491,520]}},"Source":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.LoggingConfig/Source","kind":"field","type":"bool","span":{"start":[33,1],"end":[33,31],"bytes":[581,611]}},"Stderr":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.LoggingConfig/Stderr","kind":"field","type":"bool","span":{"start":[32,1],"end":[32,31],"bytes":[550,580]}},"Type":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.LoggingConfig/Type","kind":"field","type":"string","span":{"start":[31,1],"end":[31,29],"bytes":[521,549]}}}}},"functions":{"github.com/benbjohnson/litestream/internal.CleanSourcePath":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.CleanSourcePath","kind":"function","span":{"start":[85,1],"end":[110,2],"bytes":[1813,2449]},"signature":"github.com/benbjohnson/litestream/internal.CleanSourcePath","parameters":[{"name":"src","type":"*log/slog.Source","span":{"start":[85,1],"end":[85,41],"bytes":[1813,1853]}}],"metrics":{"cyclomatic":7},"body":{"106:13":{"kind":"call","span":{"start":[106,13],"end":[106,45],"bytes":[2358,2390]},"callee":null},"107:28":{"kind":"call","span":{"start":[107,28],"end":[107,36],"bytes":[2430,2438]},"callee":null},"90:12":{"kind":"call","span":{"start":[90,12],"end":[90,49],"bytes":[1903,1940]},"callee":null},"91:27":{"kind":"call","span":{"start":[91,27],"end":[91,44],"bytes":[1979,1996]},"callee":null},"95:16":{"kind":"call","span":{"start":[95,16],"end":[95,53],"bytes":[2027,2064]},"callee":null},"96:12":{"kind":"call","span":{"start":[96,12],"end":[96,51],"bytes":[2076,2115]},"callee":null},"97:24":{"kind":"call","span":{"start":[97,24],"end":[97,39],"bytes":[2151,2166]},"callee":null},"98:18":{"kind":"call","span":{"start":[98,18],"end":[98,42],"bytes":[2186,2210]},"callee":null}}},"github.com/benbjohnson/litestream/internal.InitLog":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","kind":"function","span":{"start":[36,1],"end":[83,2],"bytes":[615,1811]},"signature":"github.com/benbjohnson/litestream/internal.InitLog","parameters":[{"name":"w","type":"io.Writer","span":{"start":[36,1],"end":[36,63],"bytes":[615,677]}},{"name":"level","type":"string","span":{"start":[36,1],"end":[36,63],"bytes":[615,677]}},{"name":"typ","type":"string","span":{"start":[36,1],"end":[36,63],"bytes":[615,677]}},{"name":"addSource","type":"bool","span":{"start":[36,1],"end":[36,63],"bytes":[615,677]}}],"metrics":{"cyclomatic":13},"body":{"44:10":{"kind":"call","span":{"start":[44,10],"end":[44,32],"bytes":[863,885]},"callee":null},"48:9":{"kind":"call","span":{"start":[48,9],"end":[48,31],"bytes":[921,943]},"callee":null},"64:16":{"kind":"call","span":{"start":[64,16],"end":[64,51],"bytes":[1284,1319]},"callee":null},"68:15":{"kind":"call","span":{"start":[68,15],"end":[68,40],"bytes":[1401,1426]},"callee":null},"68:33":{"kind":"call","span":{"start":[68,33],"end":[68,39],"bytes":[1419,1425]},"callee":null},"68:45":{"kind":"call","span":{"start":[68,45],"end":[68,76],"bytes":[1431,1462]},"callee":null},"68:69":{"kind":"call","span":{"start":[68,69],"end":[68,75],"bytes":[1455,1461]},"callee":null},"70:16":{"kind":"call","span":{"start":[70,16],"end":[76,5],"bytes":[1482,1666]},"callee":null},"78:16":{"kind":"call","span":{"start":[78,16],"end":[78,51],"bytes":[1700,1735]},"callee":null},"82:18":{"kind":"call","span":{"start":[82,18],"end":[82,38],"bytes":[1788,1808]},"callee":null},"82:2":{"kind":"call","span":{"start":[82,2],"end":[82,39],"bytes":[1772,1809]},"callee":null}}},"github.com/benbjohnson/litestream/internal.ReplaceAttr":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.ReplaceAttr","kind":"function","span":{"start":[112,1],"end":[124,2],"bytes":[2451,2736]},"signature":"github.com/benbjohnson/litestream/internal.ReplaceAttr","parameters":[{"name":"groups","type":"[]string","span":{"start":[112,1],"end":[112,59],"bytes":[2451,2509]}},{"name":"a","type":"log/slog.Attr","span":{"start":[112,1],"end":[112,59],"bytes":[2451,2509]}}],"return_type":"log/slog.Attr","metrics":{"cyclomatic":4},"body":{"113:31":{"kind":"call","span":{"start":[113,31],"end":[113,44],"bytes":[2540,2553]},"callee":null},"114:13":{"kind":"call","span":{"start":[114,13],"end":[114,38],"bytes":[2582,2607]},"callee":null},"118:17":{"kind":"call","span":{"start":[118,17],"end":[118,30],"bytes":[2658,2671]},"callee":null},"119:4":{"kind":"call","span":{"start":[119,4],"end":[119,24],"bytes":[2696,2716]},"callee":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.CleanSourcePath"}}},"github.com/benbjohnson/litestream/internal.init":{"id":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.init","kind":"function","span":{"start":[19,1],"end":[24,2],"bytes":[212,384]},"signature":"github.com/benbjohnson/litestream/internal.init","parameters":[],"metrics":{"cyclomatic":2},"body":{"20:17":{"kind":"call","span":{"start":[20,17],"end":[20,38],"bytes":[242,263]},"callee":null},"22:23":{"kind":"call","span":{"start":[22,23],"end":[22,48],"bytes":[354,379]},"callee":null}}}},"content_hash":"c9c5547d3de71f196df17f3249b69dfca8c99670203b2b6ef1b287535b67e95a"},"internal/resumable_reader.go":{"id":"can://go/litestream/internal/resumable_reader.go","kind":"module","span":{"start":[1,1],"end":[311,1],"bytes":[0,10457]},"package":"internal","source":"package internal\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n)\n\ntype LTXFileOpener interface {\n\tOpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error)\n}\n\n// resumableReader wraps an io.ReadCloser from a remote storage backend with\n// automatic reconnection on read errors.\n//\n// During restore, the LTX compactor opens all LTX file streams upfront, then\n// processes pages in page-number order. Incremental LTX files that only contain\n// high-numbered pages may have their S3/storage streams sit idle for minutes\n// while the compactor works through lower-numbered pages from the snapshot.\n// Storage providers (S3, Tigris, etc.) may close these idle connections,\n// causing \"unexpected EOF\" errors.\n//\n// This reader detects two failure modes:\n// 1. Non-EOF errors (connection reset, timeout) - the stream broke mid-transfer.\n// 2. Premature EOF - the server closed the connection cleanly, but we haven't\n// read all bytes yet (detected by comparing offset against known file size).\n//\n// On failure, it closes the dead stream and reopens from the current byte\n// offset using the storage backend's range request support (the offset parameter\n// of OpenLTXFile). Callers like io.ReadFull see a seamless byte stream because\n// partial reads are returned without error, prompting the caller to request\n// remaining bytes on the next Read call.\ntype ResumableReader struct {\n\tctx context.Context\n\tclient LTXFileOpener\n\tlevel int\n\tminTXID ltx.TXID\n\tmaxTXID ltx.TXID\n\tsize int64 // expected total file size from FileInfo; 0 means unknown\n\toffset int64\n\tretryN int\n\terr error\n\tlogger *slog.Logger\n\n\t// mu guards rc and closed. Close may be called from a different goroutine\n\t// than Read: Compactor.Compact closes its source readers when it returns\n\t// while its compaction goroutine may still be reading them. Once closed is\n\t// set, Read must not reopen the stream or it would leak the new connection.\n\tmu sync.Mutex\n\trc io.ReadCloser\n\tcancel context.CancelFunc\n\tclosed bool\n}\n\n// NewResumableReader creates a ResumableReader. Primarily exposed for testing.\nfunc NewResumableReader(ctx context.Context, client LTXFileOpener, level int, minTXID, maxTXID ltx.TXID, size int64, rc io.ReadCloser, logger *slog.Logger) *ResumableReader {\n\treturn \u0026ResumableReader{\n\t\tctx: ctx,\n\t\tclient: client,\n\t\tlevel: level,\n\t\tminTXID: minTXID,\n\t\tmaxTXID: maxTXID,\n\t\tsize: size,\n\t\trc: rc,\n\t\tlogger: logger,\n\t}\n}\n\nconst resumableReaderMaxRetries = 3\n\n// resumableReaderBackoff is the base delay between retry attempts, doubling\n// per attempt. Zero-delay retries land every attempt inside the same provider\n// throttle window (e.g. Tigris 408 load shedding), guaranteeing exhaustion.\nconst resumableReaderBackoff = 250 * time.Millisecond\n\n// resumableReaderStallTimeout bounds a single Read of an open stream. Nothing\n// else does: the S3 client sets no ResponseHeaderTimeout and no read deadline on\n// the response body, and TCP keepalive does not fire against a peer that is\n// alive but simply sending nothing. It is a var rather than a const only so\n// tests can shorten it.\nvar resumableReaderStallTimeout = 30 * time.Second\n\n// ErrReadStalled reports a read that produced nothing for\n// resumableReaderStallTimeout and had its stream cancelled to force a reconnect.\nvar ErrReadStalled = errors.New(\"ltx stream read stalled\")\n\nfunc (r *ResumableReader) Read(p []byte) (int, error) {\n\tfor {\n\t\trc, cancel, err := r.stream()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif r.err != nil {\n\t\t\treturn 0, r.err\n\t\t}\n\n\t\t// Reopen the stream from the current offset if the previous\n\t\t// connection was closed (the stream is nil after a retry).\n\t\tif rc == nil {\n\t\t\tstreamCtx, streamCancel := context.WithCancel(r.ctx)\n\t\t\tnewRC, err := r.client.OpenLTXFile(streamCtx, r.level, r.minTXID, r.maxTXID, r.offset, 0)\n\t\t\tif err != nil {\n\t\t\t\tstreamCancel()\n\t\t\t\tif errors.Is(err, os.ErrNotExist) || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n\t\t\t\t\treturn 0, fmt.Errorf(\"reopen ltx file at offset %d: %w\", r.offset, err)\n\t\t\t\t}\n\t\t\t\tif ctxErr := r.ctx.Err(); ctxErr != nil {\n\t\t\t\t\tr.err = fmt.Errorf(\"reopen ltx file at offset %d: %v: %w\", r.offset, err, ctxErr)\n\t\t\t\t\treturn 0, r.err\n\t\t\t\t}\n\t\t\t\tif retryErr := r.retry(fmt.Errorf(\"reopen ltx file at offset %d: %w\", r.offset, err)); retryErr != nil {\n\t\t\t\t\treturn 0, retryErr\n\t\t\t\t}\n\t\t\t\tr.logger.Debug(\"reopen ltx file failed, retrying\",\n\t\t\t\t\t\"level\", r.level, \"min\", r.minTXID, \"max\", r.maxTXID,\n\t\t\t\t\t\"offset\", r.offset, \"error\", err, \"attempt\", r.retryN)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !r.setStream(newRC, streamCancel) {\n\t\t\t\t// Closed while the stream was being reopened.\n\t\t\t\tstreamCancel()\n\t\t\t\t_ = newRC.Close()\n\t\t\t\treturn 0, os.ErrClosed\n\t\t\t}\n\t\t\trc, cancel = newRC, streamCancel\n\t\t}\n\n\t\tn, err := r.readStream(rc, cancel, p)\n\t\tr.offset += int64(n)\n\n\t\tif err == nil {\n\t\t\tif n \u003e 0 \u0026\u0026 n == len(p) {\n\t\t\t\tr.retryN = 0\n\t\t\t}\n\t\t\treturn n, nil\n\t\t}\n\n\t\tif err == io.EOF {\n\t\t\t// Distinguish legitimate EOF (fully read) from premature EOF\n\t\t\t// (server closed idle connection). When the file size is known\n\t\t\t// and we haven't read it all, treat as a connection drop.\n\t\t\tif r.size \u003e 0 \u0026\u0026 r.offset \u003c r.size {\n\t\t\t\tr.logger.Debug(\"premature EOF on ltx file, reconnecting\",\n\t\t\t\t\t\"level\", r.level, \"min\", r.minTXID, \"max\", r.maxTXID,\n\t\t\t\t\t\"offset\", r.offset, \"size\", r.size, \"attempt\", r.retryN+1)\n\t\t\t\tr.dropStream()\n\t\t\t\tif retryErr := r.retry(io.ErrUnexpectedEOF); retryErr != nil {\n\t\t\t\t\treturn n, retryErr\n\t\t\t\t}\n\t\t\t\tif n \u003e 0 {\n\t\t\t\t\t// Return the bytes we did get. The caller (e.g. io.ReadFull)\n\t\t\t\t\t// will call Read again, which will trigger the reopen above.\n\t\t\t\t\treturn n, nil\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn n, io.EOF\n\t\t}\n\n\t\t// Non-EOF error (connection reset, timeout, etc.). Close the dead\n\t\t// stream so the next iteration reopens from the current offset.\n\t\tr.logger.Debug(\"read error on ltx file, reconnecting\",\n\t\t\t\"level\", r.level, \"min\", r.minTXID, \"max\", r.maxTXID,\n\t\t\t\"error\", err, \"offset\", r.offset, \"attempt\", r.retryN+1)\n\t\tr.dropStream()\n\t\tif retryErr := r.retry(err); retryErr != nil {\n\t\t\treturn n, retryErr\n\t\t}\n\t\tif n \u003e 0 {\n\t\t\treturn n, nil\n\t\t}\n\t}\n}\n\n// readStream reads from the current stream under a stall timeout. A read that\n// produces nothing before the timeout expires has its stream's request context\n// cancelled, which unblocks the read with an error and sends the caller down the\n// reconnect path that already handles a dropped connection.\n//\n// Cancellation rather than Close: net/http's (*body).Read holds the body mutex\n// for the duration of the network read and (*body).Close wants that same mutex,\n// so a watchdog calling Close would queue behind the very read it is trying to\n// interrupt. Only the request context reaches a parked read.\nfunc (r *ResumableReader) readStream(rc io.ReadCloser, cancel context.CancelFunc, p []byte) (int, error) {\n\t// Captured per stream: by the time the timer fires, r.cancel may already\n\t// belong to a healthy replacement, and cancelling that one would be a\n\t// self-inflicted drop.\n\ttimeout, offset := resumableReaderStallTimeout, r.offset\n\tif cancel == nil || timeout \u003c= 0 {\n\t\treturn rc.Read(p)\n\t}\n\n\tvar stalled atomic.Bool\n\ttimer := time.AfterFunc(timeout, func() {\n\t\tstalled.Store(true)\n\t\tr.logger.Debug(\"ltx file read stalled, cancelling stream\",\n\t\t\t\"level\", r.level, \"min\", r.minTXID, \"max\", r.maxTXID,\n\t\t\t\"offset\", offset, \"timeout\", timeout)\n\t\tcancel()\n\t})\n\tdefer timer.Stop()\n\n\tn, err := rc.Read(p)\n\tif err != nil \u0026\u0026 stalled.Load() {\n\t\t// %v, not %w: the underlying error is context.Canceled, and a stall is\n\t\t// retryable where a cancelled parent context is terminal. Letting\n\t\t// context.Canceled into the chain invites an errors.Is check above to\n\t\t// abandon a restore that only needed to reconnect.\n\t\terr = fmt.Errorf(\"%w after %s: %v\", ErrReadStalled, timeout, err)\n\t}\n\treturn n, err\n}\n\n// Close closes the current underlying stream, if any, and marks the reader as\n// closed. Closing is permanent: subsequent reads fail with os.ErrClosed rather\n// than reopening the remote stream, which would leak the new connection.\nfunc (r *ResumableReader) Close() error {\n\tr.mu.Lock()\n\tif r.closed {\n\t\tr.mu.Unlock()\n\t\treturn nil\n\t}\n\tr.closed = true\n\trc, cancel := r.rc, r.cancel\n\tr.rc, r.cancel = nil, nil\n\tr.mu.Unlock()\n\n\tif cancel != nil {\n\t\tcancel()\n\t}\n\tif rc != nil {\n\t\treturn rc.Close()\n\t}\n\treturn nil\n}\n\n// stream returns the current underlying stream, or nil if the next read should\n// reopen it. Returns os.ErrClosed if the reader has been closed.\nfunc (r *ResumableReader) stream() (io.ReadCloser, context.CancelFunc, error) {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\tif r.closed {\n\t\treturn nil, nil, os.ErrClosed\n\t}\n\treturn r.rc, r.cancel, nil\n}\n\n// setStream installs a newly reopened stream. It reports false if the reader\n// was closed while the stream was being opened, in which case the caller must\n// close the new stream itself.\nfunc (r *ResumableReader) setStream(rc io.ReadCloser, cancel context.CancelFunc) bool {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\tif r.closed {\n\t\treturn false\n\t}\n\tr.rc, r.cancel = rc, cancel\n\treturn true\n}\n\n// dropStream closes and clears the current stream after a read failure so the\n// next read reopens from the current offset. The stream is already being\n// discarded, so a close error should not stop recovery. Log it only to aid\n// debugging.\nfunc (r *ResumableReader) dropStream() {\n\tr.mu.Lock()\n\trc, cancel := r.rc, r.cancel\n\tr.rc, r.cancel = nil, nil\n\tr.mu.Unlock()\n\n\tif cancel != nil {\n\t\tcancel()\n\t}\n\tif rc == nil {\n\t\treturn\n\t}\n\tif err := rc.Close(); err != nil {\n\t\tr.logger.Debug(\"close ltx file\",\n\t\t\t\"level\", r.level, \"min\", r.minTXID, \"max\", r.maxTXID,\n\t\t\t\"offset\", r.offset, \"error\", err)\n\t}\n}\n\nfunc (r *ResumableReader) retry(err error) error {\n\tr.retryN++\n\tif r.retryN \u003e resumableReaderMaxRetries {\n\t\tr.err = fmt.Errorf(\"max retries exceeded reading ltx file (level=%d, min=%s, max=%s, offset=%d): %w\",\n\t\t\tr.level, r.minTXID, r.maxTXID, r.offset, err)\n\t\treturn r.err\n\t}\n\n\t// Wait before the caller reopens. Retrying with no delay lands every\n\t// attempt inside the same provider throttle window, so the attempts are\n\t// spent without the provider ever getting a chance to recover.\n\tselect {\n\tcase \u003c-r.ctx.Done():\n\t\tr.err = r.ctx.Err()\n\t\treturn r.err\n\tcase \u003c-time.After(resumableReaderBackoff \u003c\u003c (r.retryN - 1)):\n\t}\n\treturn nil\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[27,37]}},{"name":"errors","path":"errors","span":{"start":[5,1],"end":[5,10],"bytes":[38,47]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[48,54]}},{"name":"io","path":"io","span":{"start":[7,1],"end":[7,6],"bytes":[55,60]}},{"name":"slog","path":"log/slog","span":{"start":[8,1],"end":[8,12],"bytes":[61,72]}},{"name":"os","path":"os","span":{"start":[9,1],"end":[9,6],"bytes":[73,78]}},{"name":"sync","path":"sync","span":{"start":[10,1],"end":[10,8],"bytes":[79,86]}},{"name":"atomic","path":"sync/atomic","span":{"start":[11,1],"end":[11,15],"bytes":[87,101]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[102,109]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[14,1],"end":[14,27],"bytes":[111,137]}}],"types":{"LTXFileOpener":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.LTXFileOpener","kind":"interface","span":{"start":[17,1],"end":[19,2],"bytes":[141,288]},"callables":{"github.com/benbjohnson/litestream/internal.LTXFileOpener.OpenLTXFile":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.LTXFileOpener/github.com/benbjohnson/litestream/internal.LTXFileOpener.OpenLTXFile","kind":"method","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]},"signature":"github.com/benbjohnson/litestream/internal.LTXFileOpener.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]}},{"name":"level","type":"int","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]}},{"name":"offset","type":"int64","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]}},{"name":"size","type":"int64","span":{"start":[18,1],"end":[18,115],"bytes":[172,286]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"body":{}}}},"ResumableReader":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader","kind":"struct","span":{"start":[41,1],"end":[61,2],"bytes":[1485,2140]},"callables":{"github.com/benbjohnson/litestream/internal.ResumableReader.Close":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Close","kind":"method","span":{"start":[225,1],"end":[243,2],"bytes":[8208,8486]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"226:2":{"kind":"call","span":{"start":[226,2],"end":[226,13],"bytes":[8251,8262]},"callee":null},"228:3":{"kind":"call","span":{"start":[228,3],"end":[228,16],"bytes":[8280,8293]},"callee":null},"234:2":{"kind":"call","span":{"start":[234,2],"end":[234,15],"bytes":[8385,8398]},"callee":null},"237:3":{"kind":"call","span":{"start":[237,3],"end":[237,11],"bytes":[8422,8430]},"callee":null},"240:10":{"kind":"call","span":{"start":[240,10],"end":[240,20],"bytes":[8459,8469]},"callee":null}}},"github.com/benbjohnson/litestream/internal.ResumableReader.Read":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","kind":"method","span":{"start":[95,1],"end":[181,2],"bytes":[3491,6267]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.Read","parameters":[{"name":"p","type":"[]byte","span":{"start":[95,1],"end":[95,56],"bytes":[3491,3546]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":18},"body":{"108:31":{"kind":"call","span":{"start":[108,31],"end":[108,56],"bytes":[3841,3866]},"callee":null},"109:18":{"kind":"call","span":{"start":[109,18],"end":[109,93],"bytes":[3884,3959]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.LTXFileOpener/github.com/benbjohnson/litestream/internal.LTXFileOpener.OpenLTXFile"},"111:5":{"kind":"call","span":{"start":[111,5],"end":[111,19],"bytes":[3983,3997]},"callee":null},"112:42":{"kind":"call","span":{"start":[112,42],"end":[112,74],"bytes":[4039,4071]},"callee":null},"112:78":{"kind":"call","span":{"start":[112,78],"end":[112,118],"bytes":[4075,4115]},"callee":null},"112:8":{"kind":"call","span":{"start":[112,8],"end":[112,38],"bytes":[4005,4035]},"callee":null},"113:16":{"kind":"call","span":{"start":[113,16],"end":[113,77],"bytes":[4133,4194]},"callee":null},"115:18":{"kind":"call","span":{"start":[115,18],"end":[115,29],"bytes":[4218,4229]},"callee":null},"116:14":{"kind":"call","span":{"start":[116,14],"end":[116,87],"bytes":[4260,4333]},"callee":null},"119:20":{"kind":"call","span":{"start":[119,20],"end":[119,90],"bytes":[4380,4450]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.retry"},"119:28":{"kind":"call","span":{"start":[119,28],"end":[119,89],"bytes":[4388,4449]},"callee":null},"122:5":{"kind":"call","span":{"start":[122,5],"end":[124,60],"bytes":[4504,4673]},"callee":null},"127:8":{"kind":"call","span":{"start":[127,8],"end":[127,40],"bytes":[4699,4731]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.setStream"},"129:5":{"kind":"call","span":{"start":[129,5],"end":[129,19],"bytes":[4789,4803]},"callee":null},"130:9":{"kind":"call","span":{"start":[130,9],"end":[130,22],"bytes":[4812,4825]},"callee":null},"136:13":{"kind":"call","span":{"start":[136,13],"end":[136,40],"bytes":[4911,4938]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.readStream"},"137:15":{"kind":"call","span":{"start":[137,15],"end":[137,23],"bytes":[4953,4961]},"callee":null},"140:21":{"kind":"call","span":{"start":[140,21],"end":[140,27],"bytes":[5001,5007]},"callee":null},"151:5":{"kind":"call","span":{"start":[151,5],"end":[153,64],"bytes":[5313,5493]},"callee":null},"154:5":{"kind":"call","span":{"start":[154,5],"end":[154,19],"bytes":[5498,5512]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.dropStream"},"155:20":{"kind":"call","span":{"start":[155,20],"end":[155,48],"bytes":[5532,5560]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.retry"},"170:3":{"kind":"call","span":{"start":[170,3],"end":[172,60],"bytes":[5965,6136]},"callee":null},"173:3":{"kind":"call","span":{"start":[173,3],"end":[173,17],"bytes":[6139,6153]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.dropStream"},"174:18":{"kind":"call","span":{"start":[174,18],"end":[174,30],"bytes":[6171,6183]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.retry"},"97:22":{"kind":"call","span":{"start":[97,22],"end":[97,32],"bytes":[3575,3585]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.stream"}}},"github.com/benbjohnson/litestream/internal.ResumableReader.dropStream":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.dropStream","kind":"method","span":{"start":[273,1],"end":[290,2],"bytes":[9461,9819]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.dropStream","parameters":[],"metrics":{"cyclomatic":4},"body":{"274:2":{"kind":"call","span":{"start":[274,2],"end":[274,13],"bytes":[9503,9514]},"callee":null},"277:2":{"kind":"call","span":{"start":[277,2],"end":[277,15],"bytes":[9573,9586]},"callee":null},"280:3":{"kind":"call","span":{"start":[280,3],"end":[280,11],"bytes":[9610,9618]},"callee":null},"285:12":{"kind":"call","span":{"start":[285,12],"end":[285,22],"bytes":[9661,9671]},"callee":null},"286:3":{"kind":"call","span":{"start":[286,3],"end":[288,37],"bytes":[9688,9814]},"callee":null}}},"github.com/benbjohnson/litestream/internal.ResumableReader.readStream":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.readStream","kind":"method","span":{"start":[192,1],"end":[220,2],"bytes":[6877,7973]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.readStream","parameters":[{"name":"rc","type":"io.ReadCloser","span":{"start":[192,1],"end":[192,107],"bytes":[6877,6983]}},{"name":"cancel","type":"context.CancelFunc","span":{"start":[192,1],"end":[192,107],"bytes":[6877,6983]}},{"name":"p","type":"[]byte","span":{"start":[192,1],"end":[192,107],"bytes":[6877,6983]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"198:10":{"kind":"call","span":{"start":[198,10],"end":[198,20],"bytes":[7259,7269]},"callee":null},"202:11":{"kind":"call","span":{"start":[202,11],"end":[208,4],"bytes":[7309,7537]},"callee":null},"209:8":{"kind":"call","span":{"start":[209,8],"end":[209,20],"bytes":[7545,7557]},"callee":null},"211:12":{"kind":"call","span":{"start":[211,12],"end":[211,22],"bytes":[7570,7580]},"callee":null},"212:19":{"kind":"call","span":{"start":[212,19],"end":[212,33],"bytes":[7599,7613]},"callee":null},"217:9":{"kind":"call","span":{"start":[217,9],"end":[217,68],"bytes":[7894,7953]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.readStream/github.com/benbjohnson/litestream/internal.ResumableReader.readStream.closure_1","kind":"function","span":{"start":[202,1],"end":[208,4],"bytes":[7299,7537]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.readStream.closure_1","parameters":[],"body":{"203:3":{"kind":"call","span":{"start":[203,3],"end":[203,22],"bytes":[7344,7363]},"callee":null},"204:3":{"kind":"call","span":{"start":[204,3],"end":[206,41],"bytes":[7366,7522]},"callee":null},"207:3":{"kind":"call","span":{"start":[207,3],"end":[207,11],"bytes":[7525,7533]},"callee":null}}}}},"github.com/benbjohnson/litestream/internal.ResumableReader.retry":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.retry","kind":"method","span":{"start":[292,1],"end":[310,2],"bytes":[9821,10456]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.retry","parameters":[{"name":"err","type":"error","span":{"start":[292,1],"end":[292,51],"bytes":[9821,9871]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"295:11":{"kind":"call","span":{"start":[295,11],"end":[296,49],"bytes":[9937,10079]},"callee":null},"304:9":{"kind":"call","span":{"start":[304,9],"end":[304,21],"bytes":[10327,10339]},"callee":null},"305:11":{"kind":"call","span":{"start":[305,11],"end":[305,22],"bytes":[10351,10362]},"callee":null},"307:9":{"kind":"call","span":{"start":[307,9],"end":[307,61],"bytes":[10386,10438]},"callee":null}}},"github.com/benbjohnson/litestream/internal.ResumableReader.setStream":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.setStream","kind":"method","span":{"start":[259,1],"end":[267,2],"bytes":[9018,9216]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.setStream","parameters":[{"name":"rc","type":"io.ReadCloser","span":{"start":[259,1],"end":[259,88],"bytes":[9018,9105]}},{"name":"cancel","type":"context.CancelFunc","span":{"start":[259,1],"end":[259,88],"bytes":[9018,9105]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"260:2":{"kind":"call","span":{"start":[260,2],"end":[260,13],"bytes":[9107,9118]},"callee":null},"261:8":{"kind":"call","span":{"start":[261,8],"end":[261,21],"bytes":[9126,9139]},"callee":null}}},"github.com/benbjohnson/litestream/internal.ResumableReader.stream":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.stream","kind":"method","span":{"start":[247,1],"end":[254,2],"bytes":[8634,8827]},"signature":"github.com/benbjohnson/litestream/internal.ResumableReader.stream","parameters":[],"return_type":"(io.ReadCloser, context.CancelFunc, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"248:2":{"kind":"call","span":{"start":[248,2],"end":[248,13],"bytes":[8715,8726]},"callee":null},"249:8":{"kind":"call","span":{"start":[249,8],"end":[249,21],"bytes":[8734,8747]},"callee":null}}}},"fields":{"cancel":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/cancel","kind":"field","type":"context.CancelFunc","span":{"start":[59,1],"end":[59,27],"bytes":[2099,2125]}},"client":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/client","kind":"field","type":"github.com/benbjohnson/litestream/internal.LTXFileOpener","span":{"start":[43,1],"end":[43,23],"bytes":[1540,1562]}},"closed":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/closed","kind":"field","type":"bool","span":{"start":[60,1],"end":[60,13],"bytes":[2126,2138]}},"ctx":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/ctx","kind":"field","type":"context.Context","span":{"start":[42,1],"end":[42,25],"bytes":[1515,1539]}},"err":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/err","kind":"field","type":"error","span":{"start":[50,1],"end":[50,15],"bytes":[1714,1728]}},"level":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/level","kind":"field","type":"int","span":{"start":[44,1],"end":[44,13],"bytes":[1563,1575]}},"logger":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[51,1],"end":[51,22],"bytes":[1729,1750]}},"maxTXID":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/maxTXID","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[46,1],"end":[46,18],"bytes":[1594,1611]}},"minTXID":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/minTXID","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[45,1],"end":[45,18],"bytes":[1576,1593]}},"mu":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/mu","kind":"field","type":"sync.Mutex","span":{"start":[57,1],"end":[57,19],"bytes":[2058,2076]}},"offset":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/offset","kind":"field","type":"int64","span":{"start":[48,1],"end":[48,15],"bytes":[1686,1700]}},"rc":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/rc","kind":"field","type":"io.ReadCloser","span":{"start":[58,1],"end":[58,22],"bytes":[2077,2098]}},"retryN":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/retryN","kind":"field","type":"int","span":{"start":[49,1],"end":[49,13],"bytes":[1701,1713]}},"size":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/size","kind":"field","type":"int64","span":{"start":[47,1],"end":[47,74],"bytes":[1612,1685]}}}}},"functions":{"github.com/benbjohnson/litestream/internal.NewResumableReader":{"id":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.NewResumableReader","kind":"function","span":{"start":[64,1],"end":[75,2],"bytes":[2222,2571]},"signature":"github.com/benbjohnson/litestream/internal.NewResumableReader","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"client","type":"github.com/benbjohnson/litestream/internal.LTXFileOpener","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"level","type":"int","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"size","type":"int64","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"rc","type":"io.ReadCloser","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}},{"name":"logger","type":"*log/slog.Logger","span":{"start":[64,1],"end":[64,175],"bytes":[2222,2396]}}],"return_type":"*github.com/benbjohnson/litestream/internal.ResumableReader","metrics":{"cyclomatic":1},"body":{}}},"content_hash":"fa33ef78e5f66fb580013297d9d6f83b89578cdc31f5c6f77ab5322525ad28aa"},"internal/testingutil/testingutil.go":{"id":"can://go/litestream/internal/testingutil/testingutil.go","kind":"module","span":{"start":[1,1],"end":[525,1],"bytes":[0,16630]},"package":"testingutil","source":"package testingutil\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"math/rand/v2\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\tsftpserver \"github.com/pkg/sftp\"\n\t\"golang.org/x/crypto/ssh\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/abs\"\n\t\"github.com/benbjohnson/litestream/file\"\n\t\"github.com/benbjohnson/litestream/gs\"\n\t\"github.com/benbjohnson/litestream/internal\"\n\t\"github.com/benbjohnson/litestream/nats\"\n\t\"github.com/benbjohnson/litestream/oss\"\n\t\"github.com/benbjohnson/litestream/s3\"\n\t\"github.com/benbjohnson/litestream/sftp\"\n\t\"github.com/benbjohnson/litestream/webdav\"\n)\n\nconst (\n\tdefaultTigrisEndpoint = \"https://fly.storage.tigris.dev\"\n\tdefaultTigrisRegion = \"auto\"\n\tdefaultTigrisBucket = \"litestream-dev\"\n\tdefaultTigrisPathRoot = \"integration-tests\"\n)\n\nvar (\n\t// Enables integration tests.\n\tintegration = flag.Bool(\"integration\", false, \"\")\n\t// Enables specific types of replicas to be tested.\n\treplicaClientTypes = flag.String(\"replica-clients\", \"file\", \"\")\n\t// Sets the log level for the tests.\n\tlogLevel = flag.String(\"log.level\", \"debug\", \"\")\n)\n\n// S3 settings\nvar (\n\t// Replica client settings\n\ts3AccessKeyID = flag.String(\"s3-access-key-id\", os.Getenv(\"LITESTREAM_S3_ACCESS_KEY_ID\"), \"\")\n\ts3SecretAccessKey = flag.String(\"s3-secret-access-key\", os.Getenv(\"LITESTREAM_S3_SECRET_ACCESS_KEY\"), \"\")\n\ts3Region = flag.String(\"s3-region\", os.Getenv(\"LITESTREAM_S3_REGION\"), \"\")\n\ts3Bucket = flag.String(\"s3-bucket\", os.Getenv(\"LITESTREAM_S3_BUCKET\"), \"\")\n\ts3Path = flag.String(\"s3-path\", os.Getenv(\"LITESTREAM_S3_PATH\"), \"\")\n\ts3Endpoint = flag.String(\"s3-endpoint\", os.Getenv(\"LITESTREAM_S3_ENDPOINT\"), \"\")\n\ts3ForcePathStyle = flag.Bool(\"s3-force-path-style\", os.Getenv(\"LITESTREAM_S3_FORCE_PATH_STYLE\") == \"true\", \"\")\n\ts3SkipVerify = flag.Bool(\"s3-skip-verify\", os.Getenv(\"LITESTREAM_S3_SKIP_VERIFY\") == \"true\", \"\")\n)\n\n// Tigris settings (S3-compatible)\nvar (\n\ttigrisAccessKeyID = flag.String(\"tigris-access-key-id\", os.Getenv(\"LITESTREAM_TIGRIS_ACCESS_KEY_ID\"), \"\")\n\ttigrisSecretAccessKey = flag.String(\"tigris-secret-access-key\", os.Getenv(\"LITESTREAM_TIGRIS_SECRET_ACCESS_KEY\"), \"\")\n)\n\n// Cloudflare R2 settings (S3-compatible)\nvar (\n\tr2AccessKeyID = flag.String(\"r2-access-key-id\", os.Getenv(\"LITESTREAM_R2_ACCESS_KEY_ID\"), \"\")\n\tr2SecretAccessKey = flag.String(\"r2-secret-access-key\", os.Getenv(\"LITESTREAM_R2_SECRET_ACCESS_KEY\"), \"\")\n\tr2Endpoint = flag.String(\"r2-endpoint\", os.Getenv(\"LITESTREAM_R2_ENDPOINT\"), \"\")\n\tr2Bucket = flag.String(\"r2-bucket\", os.Getenv(\"LITESTREAM_R2_BUCKET\"), \"\")\n)\n\n// Backblaze B2 settings (S3-compatible)\nvar (\n\tb2KeyID = flag.String(\"b2-key-id\", os.Getenv(\"LITESTREAM_B2_KEY_ID\"), \"\")\n\tb2ApplicationKey = flag.String(\"b2-application-key\", os.Getenv(\"LITESTREAM_B2_APPLICATION_KEY\"), \"\")\n\tb2Endpoint = flag.String(\"b2-endpoint\", os.Getenv(\"LITESTREAM_B2_ENDPOINT\"), \"\")\n\tb2Bucket = flag.String(\"b2-bucket\", os.Getenv(\"LITESTREAM_B2_BUCKET\"), \"\")\n)\n\n// Google cloud storage settings\nvar (\n\tgsBucket = flag.String(\"gs-bucket\", os.Getenv(\"LITESTREAM_GS_BUCKET\"), \"\")\n\tgsPath = flag.String(\"gs-path\", os.Getenv(\"LITESTREAM_GS_PATH\"), \"\")\n)\n\n// Azure blob storage settings\nvar (\n\tabsAccountName = flag.String(\"abs-account-name\", os.Getenv(\"LITESTREAM_ABS_ACCOUNT_NAME\"), \"\")\n\tabsAccountKey = flag.String(\"abs-account-key\", os.Getenv(\"LITESTREAM_ABS_ACCOUNT_KEY\"), \"\")\n\tabsSASToken = flag.String(\"abs-sas-token\", os.Getenv(\"LITESTREAM_ABS_SAS_TOKEN\"), \"\")\n\tabsBucket = flag.String(\"abs-bucket\", os.Getenv(\"LITESTREAM_ABS_BUCKET\"), \"\")\n\tabsPath = flag.String(\"abs-path\", os.Getenv(\"LITESTREAM_ABS_PATH\"), \"\")\n)\n\n// SFTP settings\nvar (\n\tsftpHost = flag.String(\"sftp-host\", os.Getenv(\"LITESTREAM_SFTP_HOST\"), \"\")\n\tsftpUser = flag.String(\"sftp-user\", os.Getenv(\"LITESTREAM_SFTP_USER\"), \"\")\n\tsftpPassword = flag.String(\"sftp-password\", os.Getenv(\"LITESTREAM_SFTP_PASSWORD\"), \"\")\n\tsftpKeyPath = flag.String(\"sftp-key-path\", os.Getenv(\"LITESTREAM_SFTP_KEY_PATH\"), \"\")\n\tsftpPath = flag.String(\"sftp-path\", os.Getenv(\"LITESTREAM_SFTP_PATH\"), \"\")\n)\n\n// WebDAV settings\nvar (\n\twebdavURL = flag.String(\"webdav-url\", os.Getenv(\"LITESTREAM_WEBDAV_URL\"), \"\")\n\twebdavUsername = flag.String(\"webdav-username\", os.Getenv(\"LITESTREAM_WEBDAV_USERNAME\"), \"\")\n\twebdavPassword = flag.String(\"webdav-password\", os.Getenv(\"LITESTREAM_WEBDAV_PASSWORD\"), \"\")\n\twebdavPath = flag.String(\"webdav-path\", os.Getenv(\"LITESTREAM_WEBDAV_PATH\"), \"\")\n)\n\n// NATS settings\nvar (\n\tnatsURL = flag.String(\"nats-url\", os.Getenv(\"LITESTREAM_NATS_URL\"), \"\")\n\tnatsBucket = flag.String(\"nats-bucket\", os.Getenv(\"LITESTREAM_NATS_BUCKET\"), \"\")\n\tnatsCreds = flag.String(\"nats-creds\", os.Getenv(\"LITESTREAM_NATS_CREDS\"), \"\")\n\tnatsUsername = flag.String(\"nats-username\", os.Getenv(\"LITESTREAM_NATS_USERNAME\"), \"\")\n\tnatsPassword = flag.String(\"nats-password\", os.Getenv(\"LITESTREAM_NATS_PASSWORD\"), \"\")\n)\n\n// Alibaba Cloud OSS settings\nvar (\n\tossAccessKeyID = flag.String(\"oss-access-key-id\", os.Getenv(\"LITESTREAM_OSS_ACCESS_KEY_ID\"), \"\")\n\tossAccessKeySecret = flag.String(\"oss-access-key-secret\", os.Getenv(\"LITESTREAM_OSS_ACCESS_KEY_SECRET\"), \"\")\n\tossRegion = flag.String(\"oss-region\", os.Getenv(\"LITESTREAM_OSS_REGION\"), \"\")\n\tossBucket = flag.String(\"oss-bucket\", os.Getenv(\"LITESTREAM_OSS_BUCKET\"), \"\")\n\tossPath = flag.String(\"oss-path\", os.Getenv(\"LITESTREAM_OSS_PATH\"), \"\")\n\tossEndpoint = flag.String(\"oss-endpoint\", os.Getenv(\"LITESTREAM_OSS_ENDPOINT\"), \"\")\n)\n\nfunc Integration() bool {\n\treturn *integration\n}\n\nfunc ReplicaClientTypes() []string {\n\treturn strings.Split(*replicaClientTypes, \",\")\n}\n\nfunc NewDB(tb testing.TB, path string) *litestream.DB {\n\ttb.Helper()\n\ttb.Logf(\"db=%s\", path)\n\n\tlevel := slog.LevelDebug\n\tif strings.EqualFold(*logLevel, \"trace\") {\n\t\tlevel = internal.LevelTrace\n\t}\n\n\tdb := litestream.NewDB(path)\n\tdb.Logger = slog.New(slog.NewTextHandler(os.Stdout, \u0026slog.HandlerOptions{\n\t\tLevel: level,\n\t\tReplaceAttr: internal.ReplaceAttr,\n\t}))\n\treturn db\n}\n\n// MustOpenDBs returns a new instance of a DB \u0026 associated SQL DB.\nfunc MustOpenDBs(tb testing.TB) (*litestream.DB, *sql.DB) {\n\ttb.Helper()\n\tdb := MustOpenDB(tb)\n\treturn db, MustOpenSQLDB(tb, db.Path())\n}\n\n// MustCloseDBs closes db \u0026 sqldb and removes the parent directory.\nfunc MustCloseDBs(tb testing.TB, db *litestream.DB, sqldb *sql.DB) {\n\ttb.Helper()\n\tMustCloseDB(tb, db)\n\tMustCloseSQLDB(tb, sqldb)\n}\n\n// MustOpenDB returns a new instance of a DB.\nfunc MustOpenDB(tb testing.TB) *litestream.DB {\n\ttb.Helper()\n\tdir := tb.TempDir()\n\treturn MustOpenDBAt(tb, filepath.Join(dir, \"db\"))\n}\n\n// MustOpenDBAt returns a new instance of a DB for a given path.\nfunc MustOpenDBAt(tb testing.TB, path string) *litestream.DB {\n\ttb.Helper()\n\tdb := NewDB(tb, path)\n\tdb.MonitorInterval = 0 // disable background goroutine\n\tdb.ShutdownSyncTimeout = 0 // disable shutdown sync retry for faster tests\n\tdb.Replica = litestream.NewReplica(db)\n\tdb.Replica.Client = NewFileReplicaClient(tb)\n\tdb.Replica.MonitorEnabled = false // disable background goroutine\n\tif err := db.Open(); err != nil {\n\t\ttb.Fatal(err)\n\t}\n\treturn db\n}\n\n// MustCloseDB closes db and removes its parent directory.\nfunc MustCloseDB(tb testing.TB, db *litestream.DB) {\n\ttb.Helper()\n\tif err := db.Close(context.Background()); err != nil \u0026\u0026 !strings.Contains(err.Error(), `database is closed`) \u0026\u0026 !strings.Contains(err.Error(), `file already closed`) {\n\t\ttb.Fatal(err)\n\t} else if err := os.RemoveAll(filepath.Dir(db.Path())); err != nil {\n\t\ttb.Fatal(err)\n\t}\n}\n\n// MustOpenSQLDB returns a database/sql DB.\nfunc MustOpenSQLDB(tb testing.TB, path string) *sql.DB {\n\ttb.Helper()\n\td, err := sql.Open(\"sqlite\", path)\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t} else if _, err := d.ExecContext(context.Background(), `PRAGMA journal_mode = wal;`); err != nil {\n\t\ttb.Fatal(err)\n\t} else if _, err := d.ExecContext(context.Background(), `PRAGMA busy_timeout = 5000;`); err != nil {\n\t\ttb.Fatal(err)\n\t}\n\treturn d\n}\n\n// MustCloseSQLDB closes a database/sql DB.\nfunc MustCloseSQLDB(tb testing.TB, d *sql.DB) {\n\ttb.Helper()\n\tif err := d.Close(); err != nil {\n\t\ttb.Fatal(err)\n\t}\n}\n\n// NewReplicaClient returns a new client for integration testing by type name.\nfunc NewReplicaClient(tb testing.TB, typ string) litestream.ReplicaClient {\n\ttb.Helper()\n\n\tswitch typ {\n\tcase file.ReplicaClientType:\n\t\treturn NewFileReplicaClient(tb)\n\tcase s3.ReplicaClientType:\n\t\treturn NewS3ReplicaClient(tb)\n\tcase gs.ReplicaClientType:\n\t\treturn NewGSReplicaClient(tb)\n\tcase abs.ReplicaClientType:\n\t\treturn NewABSReplicaClient(tb)\n\tcase sftp.ReplicaClientType:\n\t\treturn NewSFTPReplicaClient(tb)\n\tcase webdav.ReplicaClientType:\n\t\treturn NewWebDAVReplicaClient(tb)\n\tcase nats.ReplicaClientType:\n\t\treturn NewNATSReplicaClient(tb)\n\tcase oss.ReplicaClientType:\n\t\treturn NewOSSReplicaClient(tb)\n\tcase \"tigris\":\n\t\treturn NewTigrisReplicaClient(tb)\n\tcase \"r2\":\n\t\treturn NewR2ReplicaClient(tb)\n\tcase \"b2\":\n\t\treturn NewB2ReplicaClient(tb)\n\tdefault:\n\t\ttb.Fatalf(\"invalid replica client type: %q\", typ)\n\t\treturn nil\n\t}\n}\n\n// NewFileReplicaClient returns a new client for integration testing.\nfunc NewFileReplicaClient(tb testing.TB) *file.ReplicaClient {\n\ttb.Helper()\n\treturn file.NewReplicaClient(tb.TempDir())\n}\n\n// NewS3ReplicaClient returns a new client for integration testing.\nfunc NewS3ReplicaClient(tb testing.TB) *s3.ReplicaClient {\n\ttb.Helper()\n\n\tc := s3.NewReplicaClient()\n\tc.AccessKeyID = *s3AccessKeyID\n\tc.SecretAccessKey = *s3SecretAccessKey\n\tc.Region = *s3Region\n\tc.Bucket = *s3Bucket\n\tc.Path = path.Join(*s3Path, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\tc.Endpoint = *s3Endpoint\n\tc.ForcePathStyle = *s3ForcePathStyle\n\tc.SkipVerify = *s3SkipVerify\n\treturn c\n}\n\n// NewTigrisReplicaClient returns an S3 client configured for Fly.io Tigris.\nfunc NewTigrisReplicaClient(tb testing.TB) *s3.ReplicaClient {\n\ttb.Helper()\n\n\tif *tigrisAccessKeyID == \"\" || *tigrisSecretAccessKey == \"\" {\n\t\ttb.Skip(\"tigris credentials not configured (set LITESTREAM_TIGRIS_ACCESS_KEY_ID/SECRET_ACCESS_KEY)\")\n\t}\n\n\tc := s3.NewReplicaClient()\n\tc.AccessKeyID = *tigrisAccessKeyID\n\tc.SecretAccessKey = *tigrisSecretAccessKey\n\tc.Region = defaultTigrisRegion\n\tc.Bucket = defaultTigrisBucket\n\tc.Path = path.Join(defaultTigrisPathRoot, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\tc.Endpoint = defaultTigrisEndpoint\n\tc.ForcePathStyle = true\n\tc.RequireContentMD5 = false\n\treturn c\n}\n\n// NewR2ReplicaClient returns an S3 client configured for Cloudflare R2.\n// Skips the test if R2 credentials are not configured.\nfunc NewR2ReplicaClient(tb testing.TB) *s3.ReplicaClient {\n\ttb.Helper()\n\n\tif *r2AccessKeyID == \"\" || *r2SecretAccessKey == \"\" {\n\t\ttb.Skip(\"r2 credentials not configured (set LITESTREAM_R2_ACCESS_KEY_ID/SECRET_ACCESS_KEY)\")\n\t}\n\tif *r2Endpoint == \"\" {\n\t\ttb.Skip(\"r2 endpoint not configured (set LITESTREAM_R2_ENDPOINT)\")\n\t}\n\tif *r2Bucket == \"\" {\n\t\ttb.Skip(\"r2 bucket not configured (set LITESTREAM_R2_BUCKET)\")\n\t}\n\n\tc := s3.NewReplicaClient()\n\tc.AccessKeyID = *r2AccessKeyID\n\tc.SecretAccessKey = *r2SecretAccessKey\n\tc.Region = \"auto\"\n\tc.Bucket = *r2Bucket\n\tc.Path = path.Join(\"integration-tests\", fmt.Sprintf(\"%016x\", rand.Uint64()))\n\tc.Endpoint = *r2Endpoint\n\tc.ForcePathStyle = true\n\tc.SignPayload = true\n\treturn c\n}\n\n// NewB2ReplicaClient returns a new Backblaze B2 client for integration testing.\n// B2 uses S3-compatible API with path-style URLs and signed payloads.\nfunc NewB2ReplicaClient(tb testing.TB) *s3.ReplicaClient {\n\ttb.Helper()\n\n\tif *b2KeyID == \"\" || *b2ApplicationKey == \"\" {\n\t\ttb.Skip(\"b2 credentials not configured (set LITESTREAM_B2_KEY_ID/APPLICATION_KEY)\")\n\t}\n\tif *b2Endpoint == \"\" {\n\t\ttb.Skip(\"b2 endpoint not configured (set LITESTREAM_B2_ENDPOINT)\")\n\t}\n\tif *b2Bucket == \"\" {\n\t\ttb.Skip(\"b2 bucket not configured (set LITESTREAM_B2_BUCKET)\")\n\t}\n\n\tc := s3.NewReplicaClient()\n\tc.AccessKeyID = *b2KeyID\n\tc.SecretAccessKey = *b2ApplicationKey\n\tc.Region = \"us-west-002\" // B2 uses region in endpoint format\n\tc.Bucket = *b2Bucket\n\tc.Path = path.Join(\"integration-tests\", fmt.Sprintf(\"%016x\", rand.Uint64()))\n\tc.Endpoint = *b2Endpoint\n\tc.ForcePathStyle = true\n\tc.SignPayload = true\n\treturn c\n}\n\n// NewGSReplicaClient returns a new client for integration testing.\nfunc NewGSReplicaClient(tb testing.TB) *gs.ReplicaClient {\n\ttb.Helper()\n\n\t// Log basic diagnostic information for integration test troubleshooting\n\ttb.Logf(\"GCS Integration Test Setup:\")\n\tcredsSet := \"not set\"\n\tif os.Getenv(\"GOOGLE_APPLICATION_CREDENTIALS\") != \"\" {\n\t\tcredsSet = \"set\"\n\t}\n\ttb.Logf(\" GOOGLE_APPLICATION_CREDENTIALS: %s\", credsSet)\n\ttb.Logf(\" LITESTREAM_GS_BUCKET: %s\", *gsBucket)\n\ttb.Logf(\" LITESTREAM_GS_PATH: %s\", *gsPath)\n\n\tc := gs.NewReplicaClient()\n\tc.Bucket = *gsBucket\n\tc.Path = path.Join(*gsPath, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\n\t// Test basic connectivity\n\tctx := context.Background()\n\tif err := c.Init(ctx); err != nil {\n\t\ttb.Logf(\"GCS client initialization failed: %v\", err)\n\t\ttb.Logf(\"This may indicate credential or project issues\")\n\t\treturn c // Return anyway to let the actual test show the detailed error\n\t}\n\ttb.Logf(\"GCS client initialized successfully\")\n\n\treturn c\n}\n\n// NewABSReplicaClient returns a new client for integration testing.\nfunc NewABSReplicaClient(tb testing.TB) *abs.ReplicaClient {\n\ttb.Helper()\n\n\tc := abs.NewReplicaClient()\n\tc.AccountName = *absAccountName\n\tc.AccountKey = *absAccountKey\n\tc.SASToken = *absSASToken\n\tc.Bucket = *absBucket\n\tc.Path = path.Join(*absPath, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\treturn c\n}\n\n// NewSFTPReplicaClient returns a new client for integration testing.\nfunc NewSFTPReplicaClient(tb testing.TB) *sftp.ReplicaClient {\n\ttb.Helper()\n\n\tc := sftp.NewReplicaClient()\n\tc.Host = *sftpHost\n\tc.User = *sftpUser\n\tc.Password = *sftpPassword\n\tc.KeyPath = *sftpKeyPath\n\tc.Path = path.Join(*sftpPath, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\treturn c\n}\n\n// NewWebDAVReplicaClient returns a new client for integration testing.\nfunc NewWebDAVReplicaClient(tb testing.TB) *webdav.ReplicaClient {\n\ttb.Helper()\n\n\tc := webdav.NewReplicaClient()\n\tc.URL = *webdavURL\n\tc.Username = *webdavUsername\n\tc.Password = *webdavPassword\n\tc.Path = path.Join(*webdavPath, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\treturn c\n}\n\n// NewNATSReplicaClient returns a new client for integration testing.\nfunc NewNATSReplicaClient(tb testing.TB) *nats.ReplicaClient {\n\ttb.Helper()\n\n\tc := nats.NewReplicaClient()\n\tc.URL = *natsURL\n\tc.BucketName = *natsBucket\n\tc.Creds = *natsCreds\n\tc.Username = *natsUsername\n\tc.Password = *natsPassword\n\treturn c\n}\n\n// NewOSSReplicaClient returns a new client for integration testing.\nfunc NewOSSReplicaClient(tb testing.TB) *oss.ReplicaClient {\n\ttb.Helper()\n\n\tc := oss.NewReplicaClient()\n\tc.AccessKeyID = *ossAccessKeyID\n\tc.AccessKeySecret = *ossAccessKeySecret\n\tc.Region = *ossRegion\n\tc.Bucket = *ossBucket\n\tc.Path = path.Join(*ossPath, fmt.Sprintf(\"%016x\", rand.Uint64()))\n\tc.Endpoint = *ossEndpoint\n\treturn c\n}\n\n// MustDeleteAll deletes all objects under the client's path.\nfunc MustDeleteAll(tb testing.TB, c litestream.ReplicaClient) {\n\ttb.Helper()\n\n\tif err := c.DeleteAll(context.Background()); err != nil {\n\t\ttb.Fatalf(\"cannot delete all: %s\", err)\n\t}\n\n\tswitch c := c.(type) {\n\tcase *sftp.ReplicaClient:\n\t\tif err := c.Cleanup(context.Background()); err != nil {\n\t\t\ttb.Fatalf(\"cannot cleanup sftp: %s\", err)\n\t\t}\n\t}\n}\n\nfunc MockSFTPServer(t *testing.T, hostKey ssh.Signer) string {\n\tconfig := \u0026ssh.ServerConfig{NoClientAuth: true}\n\tconfig.AddHostKey(hostKey)\n\n\tlistener, err := net.Listen(\"tcp\", \"127.0.0.1:0\") // random available port\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tconn, err := listener.Accept()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgo func() {\n\t\t\t\t_, chans, reqs, err := ssh.NewServerConn(conn, config)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tgo ssh.DiscardRequests(reqs)\n\n\t\t\t\tfor ch := range chans {\n\t\t\t\t\tif ch.ChannelType() != \"session\" {\n\t\t\t\t\t\tch.Reject(ssh.UnknownChannelType, \"unsupported\")\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tchannel, requests, err := ch.Accept()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tgo func(in \u003c-chan *ssh.Request) {\n\t\t\t\t\t\tfor req := range in {\n\t\t\t\t\t\t\tif req.Type == \"subsystem\" \u0026\u0026 string(req.Payload[4:]) == \"sftp\" {\n\t\t\t\t\t\t\t\treq.Reply(true, nil)\n\n\t\t\t\t\t\t\t\tserver, err := sftpserver.NewServer(channel)\n\t\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif err := server.Serve(); err != nil \u0026\u0026 err != io.EOF {\n\t\t\t\t\t\t\t\t\tt.Logf(\"SFTP server error: %v\", err)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treq.Reply(false, nil)\n\t\t\t\t\t\t}\n\t\t\t\t\t}(requests)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}()\n\n\treturn listener.Addr().String()\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[30,40]}},{"name":"sql","path":"database/sql","span":{"start":[5,1],"end":[5,16],"bytes":[41,56]}},{"name":"flag","path":"flag","span":{"start":[6,1],"end":[6,8],"bytes":[57,64]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[65,71]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[72,77]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[78,89]}},{"name":"v2","path":"math/rand/v2","span":{"start":[10,1],"end":[10,16],"bytes":[90,105]}},{"name":"net","path":"net","span":{"start":[11,1],"end":[11,7],"bytes":[106,112]}},{"name":"os","path":"os","span":{"start":[12,1],"end":[12,6],"bytes":[113,118]}},{"name":"path","path":"path","span":{"start":[13,1],"end":[13,8],"bytes":[119,126]}},{"name":"filepath","path":"path/filepath","span":{"start":[14,1],"end":[14,17],"bytes":[127,143]}},{"name":"strings","path":"strings","span":{"start":[15,1],"end":[15,11],"bytes":[144,154]}},{"name":"testing","path":"testing","span":{"start":[16,1],"end":[16,11],"bytes":[155,165]}},{"name":"sftp","path":"github.com/pkg/sftp","alias":"sftpserver","span":{"start":[18,1],"end":[18,34],"bytes":[167,200]}},{"name":"ssh","path":"golang.org/x/crypto/ssh","span":{"start":[19,1],"end":[19,27],"bytes":[201,227]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[21,1],"end":[21,37],"bytes":[229,265]}},{"name":"abs","path":"github.com/benbjohnson/litestream/abs","span":{"start":[22,1],"end":[22,41],"bytes":[266,306]}},{"name":"file","path":"github.com/benbjohnson/litestream/file","span":{"start":[23,1],"end":[23,42],"bytes":[307,348]}},{"name":"gs","path":"github.com/benbjohnson/litestream/gs","span":{"start":[24,1],"end":[24,40],"bytes":[349,388]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[25,1],"end":[25,46],"bytes":[389,434]}},{"name":"nats","path":"github.com/benbjohnson/litestream/nats","span":{"start":[26,1],"end":[26,42],"bytes":[435,476]}},{"name":"oss","path":"github.com/benbjohnson/litestream/oss","span":{"start":[27,1],"end":[27,41],"bytes":[477,517]}},{"name":"s3","path":"github.com/benbjohnson/litestream/s3","span":{"start":[28,1],"end":[28,40],"bytes":[518,557]}},{"name":"sftp","path":"github.com/benbjohnson/litestream/sftp","span":{"start":[29,1],"end":[29,42],"bytes":[558,599]}},{"name":"webdav","path":"github.com/benbjohnson/litestream/webdav","span":{"start":[30,1],"end":[30,44],"bytes":[600,643]}}],"types":{},"functions":{"github.com/benbjohnson/litestream/internal/testingutil.Integration":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.Integration","kind":"function","span":{"start":[135,1],"end":[137,2],"bytes":[5610,5658]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.Integration","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer","kind":"function","span":{"start":[468,1],"end":[524,2],"bytes":[15381,16629]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer","parameters":[{"name":"t","type":"*testing.T","span":{"start":[468,1],"end":[468,63],"bytes":[15381,15443]}},{"name":"hostKey","type":"golang.org/x/crypto/ssh.Signer","span":{"start":[468,1],"end":[468,63],"bytes":[15381,15443]}}],"return_type":"string","metrics":{"cyclomatic":12},"body":{"470:2":{"kind":"call","span":{"start":[470,2],"end":[470,28],"bytes":[15494,15520]},"callee":null},"472:19":{"kind":"call","span":{"start":[472,19],"end":[472,51],"bytes":[15540,15572]},"callee":null},"474:3":{"kind":"call","span":{"start":[474,3],"end":[474,15],"bytes":[15617,15629]},"callee":null},"477:5":{"kind":"call","span":{"start":[477,5],"end":[521,5],"bytes":[15638,16593]},"callee":null,"is_goroutine":true},"523:9":{"kind":"call","span":{"start":[523,9],"end":[523,24],"bytes":[16603,16618]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1","kind":"function","span":{"start":[477,1],"end":[521,5],"bytes":[15634,16593]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1","parameters":[],"body":{"479:17":{"kind":"call","span":{"start":[479,17],"end":[479,34],"bytes":[15671,15688]},"callee":null},"484:7":{"kind":"call","span":{"start":[484,7],"end":[519,7],"bytes":[15731,16584]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1.closure_1","kind":"function","span":{"start":[484,1],"end":[519,7],"bytes":[15725,16584]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1.closure_1","parameters":[],"body":{"485:28":{"kind":"call","span":{"start":[485,28],"end":[485,59],"bytes":[15767,15798]},"callee":null},"489:8":{"kind":"call","span":{"start":[489,8],"end":[489,33],"bytes":[15844,15869]},"callee":null,"is_goroutine":true},"492:9":{"kind":"call","span":{"start":[492,9],"end":[492,25],"bytes":[15907,15923]},"callee":null},"493:7":{"kind":"call","span":{"start":[493,7],"end":[493,55],"bytes":[15945,15993]},"callee":null},"496:32":{"kind":"call","span":{"start":[496,32],"end":[496,43],"bytes":[16047,16058]},"callee":null},"501:9":{"kind":"call","span":{"start":[501,9],"end":[517,17],"bytes":[16109,16571]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1.closure_1/github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1.closure_1.closure_1","kind":"function","span":{"start":[501,1],"end":[517,17],"bytes":[16101,16571]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MockSFTPServer.closure_1.closure_1.closure_1","parameters":[{"name":"in","type":"\u003c-chan *golang.org/x/crypto/ssh.Request","span":{"start":[501,1],"end":[501,39],"bytes":[16101,16139]}}],"body":{"503:38":{"kind":"call","span":{"start":[503,38],"end":[503,61],"bytes":[16205,16228]},"callee":null},"504:9":{"kind":"call","span":{"start":[504,9],"end":[504,29],"bytes":[16249,16269]},"callee":null},"506:24":{"kind":"call","span":{"start":[506,24],"end":[506,53],"bytes":[16294,16323]},"callee":null},"510:19":{"kind":"call","span":{"start":[510,19],"end":[510,33],"bytes":[16392,16406]},"callee":null},"511:10":{"kind":"call","span":{"start":[511,10],"end":[511,46],"bytes":[16447,16483]},"callee":null},"515:8":{"kind":"call","span":{"start":[515,8],"end":[515,29],"bytes":[16525,16546]},"callee":null}}}}}}}}},"github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB","kind":"function","span":{"start":[197,1],"end":[204,2],"bytes":[7298,7639]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[197,1],"end":[197,53],"bytes":[7298,7350]}},{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[197,1],"end":[197,53],"bytes":[7298,7350]}}],"metrics":{"cyclomatic":3},"body":{"198:2":{"kind":"call","span":{"start":[198,2],"end":[198,13],"bytes":[7352,7363]},"callee":null},"199:115":{"kind":"call","span":{"start":[199,115],"end":[199,167],"bytes":[7478,7530]},"callee":null},"199:12":{"kind":"call","span":{"start":[199,12],"end":[199,42],"bytes":[7375,7405]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close"},"199:132":{"kind":"call","span":{"start":[199,132],"end":[199,143],"bytes":[7495,7506]},"callee":null},"199:21":{"kind":"call","span":{"start":[199,21],"end":[199,41],"bytes":[7384,7404]},"callee":null},"199:59":{"kind":"call","span":{"start":[199,59],"end":[199,110],"bytes":[7422,7473]},"callee":null},"199:76":{"kind":"call","span":{"start":[199,76],"end":[199,87],"bytes":[7439,7450]},"callee":null},"200:3":{"kind":"call","span":{"start":[200,3],"end":[200,16],"bytes":[7535,7548]},"callee":null},"201:19":{"kind":"call","span":{"start":[201,19],"end":[201,56],"bytes":[7567,7604]},"callee":null},"201:32":{"kind":"call","span":{"start":[201,32],"end":[201,55],"bytes":[7580,7603]},"callee":null},"201:45":{"kind":"call","span":{"start":[201,45],"end":[201,54],"bytes":[7593,7602]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"202:3":{"kind":"call","span":{"start":[202,3],"end":[202,16],"bytes":[7621,7634]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.MustCloseDBs":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDBs","kind":"function","span":{"start":[168,1],"end":[172,2],"bytes":[6403,6534]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustCloseDBs","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[168,1],"end":[168,69],"bytes":[6403,6471]}},{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[168,1],"end":[168,69],"bytes":[6403,6471]}},{"name":"sqldb","type":"*database/sql.DB","span":{"start":[168,1],"end":[168,69],"bytes":[6403,6471]}}],"metrics":{"cyclomatic":1},"body":{"169:2":{"kind":"call","span":{"start":[169,2],"end":[169,13],"bytes":[6473,6484]},"callee":null},"170:2":{"kind":"call","span":{"start":[170,2],"end":[170,21],"bytes":[6486,6505]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB"},"171:2":{"kind":"call","span":{"start":[171,2],"end":[171,27],"bytes":[6507,6532]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseSQLDB"}}},"github.com/benbjohnson/litestream/internal/testingutil.MustCloseSQLDB":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseSQLDB","kind":"function","span":{"start":[221,1],"end":[226,2],"bytes":[8119,8235]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustCloseSQLDB","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[221,1],"end":[221,48],"bytes":[8119,8166]}},{"name":"d","type":"*database/sql.DB","span":{"start":[221,1],"end":[221,48],"bytes":[8119,8166]}}],"metrics":{"cyclomatic":2},"body":{"222:2":{"kind":"call","span":{"start":[222,2],"end":[222,13],"bytes":[8168,8179]},"callee":null},"223:12":{"kind":"call","span":{"start":[223,12],"end":[223,21],"bytes":[8191,8200]},"callee":null},"224:3":{"kind":"call","span":{"start":[224,3],"end":[224,16],"bytes":[8217,8230]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.MustDeleteAll":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustDeleteAll","kind":"function","span":{"start":[453,1],"end":[466,2],"bytes":[15034,15379]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustDeleteAll","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[453,1],"end":[453,64],"bytes":[15034,15097]}},{"name":"c","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[453,1],"end":[453,64],"bytes":[15034,15097]}}],"metrics":{"cyclomatic":5},"body":{"454:2":{"kind":"call","span":{"start":[454,2],"end":[454,13],"bytes":[15099,15110]},"callee":null},"456:12":{"kind":"call","span":{"start":[456,12],"end":[456,45],"bytes":[15123,15156]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteAll"},"456:24":{"kind":"call","span":{"start":[456,24],"end":[456,44],"bytes":[15135,15155]},"callee":null},"457:3":{"kind":"call","span":{"start":[457,3],"end":[457,42],"bytes":[15173,15212]},"callee":null},"462:13":{"kind":"call","span":{"start":[462,13],"end":[462,44],"bytes":[15280,15311]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup"},"462:23":{"kind":"call","span":{"start":[462,23],"end":[462,43],"bytes":[15290,15310]},"callee":null},"463:4":{"kind":"call","span":{"start":[463,4],"end":[463,45],"bytes":[15329,15370]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.MustOpenDB":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDB","kind":"function","span":{"start":[175,1],"end":[179,2],"bytes":[6582,6716]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustOpenDB","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[175,1],"end":[175,48],"bytes":[6582,6629]}}],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":1},"body":{"176:2":{"kind":"call","span":{"start":[176,2],"end":[176,13],"bytes":[6631,6642]},"callee":null},"177:9":{"kind":"call","span":{"start":[177,9],"end":[177,21],"bytes":[6651,6663]},"callee":null},"178:26":{"kind":"call","span":{"start":[178,26],"end":[178,50],"bytes":[6689,6713]},"callee":null},"178:9":{"kind":"call","span":{"start":[178,9],"end":[178,51],"bytes":[6672,6714]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt"}}},"github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","kind":"function","span":{"start":[182,1],"end":[194,2],"bytes":[6783,7237]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[182,1],"end":[182,63],"bytes":[6783,6845]}},{"name":"path","type":"string","span":{"start":[182,1],"end":[182,63],"bytes":[6783,6845]}}],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":2},"body":{"183:2":{"kind":"call","span":{"start":[183,2],"end":[183,13],"bytes":[6847,6858]},"callee":null},"184:8":{"kind":"call","span":{"start":[184,8],"end":[184,23],"bytes":[6866,6881]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewDB"},"187:15":{"kind":"call","span":{"start":[187,15],"end":[187,40],"bytes":[7032,7057]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica"},"188:22":{"kind":"call","span":{"start":[188,22],"end":[188,46],"bytes":[7079,7103]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient"},"190:12":{"kind":"call","span":{"start":[190,12],"end":[190,21],"bytes":[7182,7191]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open"},"191:3":{"kind":"call","span":{"start":[191,3],"end":[191,16],"bytes":[7208,7221]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBs":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBs","kind":"function","span":{"start":[161,1],"end":[165,2],"bytes":[6196,6333]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBs","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[161,1],"end":[161,60],"bytes":[6196,6255]}}],"return_type":"(*github.com/benbjohnson/litestream.DB, *database/sql.DB)","metrics":{"cyclomatic":1},"body":{"162:2":{"kind":"call","span":{"start":[162,2],"end":[162,13],"bytes":[6257,6268]},"callee":null},"163:8":{"kind":"call","span":{"start":[163,8],"end":[163,22],"bytes":[6276,6290]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDB"},"164:13":{"kind":"call","span":{"start":[164,13],"end":[164,41],"bytes":[6303,6331]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenSQLDB"},"164:31":{"kind":"call","span":{"start":[164,31],"end":[164,40],"bytes":[6321,6330]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"}}},"github.com/benbjohnson/litestream/internal/testingutil.MustOpenSQLDB":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenSQLDB","kind":"function","span":{"start":[207,1],"end":[218,2],"bytes":[7685,8073]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.MustOpenSQLDB","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[207,1],"end":[207,57],"bytes":[7685,7741]}},{"name":"path","type":"string","span":{"start":[207,1],"end":[207,57],"bytes":[7685,7741]}}],"return_type":"*database/sql.DB","metrics":{"cyclomatic":4},"body":{"208:2":{"kind":"call","span":{"start":[208,2],"end":[208,13],"bytes":[7743,7754]},"callee":null},"209:12":{"kind":"call","span":{"start":[209,12],"end":[209,36],"bytes":[7766,7790]},"callee":null},"211:3":{"kind":"call","span":{"start":[211,3],"end":[211,16],"bytes":[7810,7823]},"callee":null},"212:22":{"kind":"call","span":{"start":[212,22],"end":[212,87],"bytes":[7845,7910]},"callee":null},"212:36":{"kind":"call","span":{"start":[212,36],"end":[212,56],"bytes":[7859,7879]},"callee":null},"213:3":{"kind":"call","span":{"start":[213,3],"end":[213,16],"bytes":[7927,7940]},"callee":null},"214:22":{"kind":"call","span":{"start":[214,22],"end":[214,88],"bytes":[7962,8028]},"callee":null},"214:36":{"kind":"call","span":{"start":[214,36],"end":[214,56],"bytes":[7976,7996]},"callee":null},"215:3":{"kind":"call","span":{"start":[215,3],"end":[215,16],"bytes":[8045,8058]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewABSReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewABSReplicaClient","kind":"function","span":{"start":[388,1],"end":[398,2],"bytes":[13260,13556]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewABSReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[388,1],"end":[388,61],"bytes":[13260,13320]}}],"return_type":"*github.com/benbjohnson/litestream/abs.ReplicaClient","metrics":{"cyclomatic":1},"body":{"389:2":{"kind":"call","span":{"start":[389,2],"end":[389,13],"bytes":[13322,13333]},"callee":null},"391:7":{"kind":"call","span":{"start":[391,7],"end":[391,29],"bytes":[13341,13363]},"callee":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient"},"396:11":{"kind":"call","span":{"start":[396,11],"end":[396,67],"bytes":[13488,13544]},"callee":null},"396:31":{"kind":"call","span":{"start":[396,31],"end":[396,66],"bytes":[13508,13543]},"callee":null},"396:52":{"kind":"call","span":{"start":[396,52],"end":[396,65],"bytes":[13529,13542]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewB2ReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewB2ReplicaClient","kind":"function","span":{"start":[332,1],"end":[355,2],"bytes":[11474,12211]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewB2ReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[332,1],"end":[332,59],"bytes":[11474,11532]}}],"return_type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","metrics":{"cyclomatic":4},"body":{"333:2":{"kind":"call","span":{"start":[333,2],"end":[333,13],"bytes":[11534,11545]},"callee":null},"336:3":{"kind":"call","span":{"start":[336,3],"end":[336,86],"bytes":[11597,11680]},"callee":null},"339:3":{"kind":"call","span":{"start":[339,3],"end":[339,69],"bytes":[11710,11776]},"callee":null},"342:3":{"kind":"call","span":{"start":[342,3],"end":[342,65],"bytes":[11804,11866]},"callee":null},"345:7":{"kind":"call","span":{"start":[345,7],"end":[345,28],"bytes":[11877,11898]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient"},"350:11":{"kind":"call","span":{"start":[350,11],"end":[350,78],"bytes":[12059,12126]},"callee":null},"350:42":{"kind":"call","span":{"start":[350,42],"end":[350,77],"bytes":[12090,12125]},"callee":null},"350:63":{"kind":"call","span":{"start":[350,63],"end":[350,76],"bytes":[12111,12124]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewDB":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewDB","kind":"function","span":{"start":[143,1],"end":[158,2],"bytes":[5748,6127]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewDB","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[143,1],"end":[143,56],"bytes":[5748,5803]}},{"name":"path","type":"string","span":{"start":[143,1],"end":[143,56],"bytes":[5748,5803]}}],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":2},"body":{"144:2":{"kind":"call","span":{"start":[144,2],"end":[144,13],"bytes":[5805,5816]},"callee":null},"145:2":{"kind":"call","span":{"start":[145,2],"end":[145,24],"bytes":[5818,5840]},"callee":null},"148:5":{"kind":"call","span":{"start":[148,5],"end":[148,42],"bytes":[5872,5909]},"callee":null},"152:8":{"kind":"call","span":{"start":[152,8],"end":[152,30],"bytes":[5953,5975]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB"},"153:14":{"kind":"call","span":{"start":[153,14],"end":[156,5],"bytes":[5989,6114]},"callee":null},"153:23":{"kind":"call","span":{"start":[153,23],"end":[156,4],"bytes":[5998,6113]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient","kind":"function","span":{"start":[262,1],"end":[265,2],"bytes":[9215,9336]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[262,1],"end":[262,63],"bytes":[9215,9277]}}],"return_type":"*github.com/benbjohnson/litestream/file.ReplicaClient","metrics":{"cyclomatic":1},"body":{"263:2":{"kind":"call","span":{"start":[263,2],"end":[263,13],"bytes":[9279,9290]},"callee":null},"264:31":{"kind":"call","span":{"start":[264,31],"end":[264,43],"bytes":[9321,9333]},"callee":null},"264:9":{"kind":"call","span":{"start":[264,9],"end":[264,44],"bytes":[9299,9334]},"callee":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient"}}},"github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient","kind":"function","span":{"start":[358,1],"end":[385,2],"bytes":[12281,13189]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[358,1],"end":[358,59],"bytes":[12281,12339]}}],"return_type":"*github.com/benbjohnson/litestream/gs.ReplicaClient","metrics":{"cyclomatic":3},"body":{"359:2":{"kind":"call","span":{"start":[359,2],"end":[359,13],"bytes":[12341,12352]},"callee":null},"362:2":{"kind":"call","span":{"start":[362,2],"end":[362,40],"bytes":[12429,12467]},"callee":null},"364:5":{"kind":"call","span":{"start":[364,5],"end":[364,48],"bytes":[12495,12538]},"callee":null},"367:2":{"kind":"call","span":{"start":[367,2],"end":[367,59],"bytes":[12570,12627]},"callee":null},"368:2":{"kind":"call","span":{"start":[368,2],"end":[368,50],"bytes":[12629,12677]},"callee":null},"369:2":{"kind":"call","span":{"start":[369,2],"end":[369,46],"bytes":[12679,12723]},"callee":null},"371:7":{"kind":"call","span":{"start":[371,7],"end":[371,28],"bytes":[12731,12752]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient"},"373:11":{"kind":"call","span":{"start":[373,11],"end":[373,66],"bytes":[12785,12840]},"callee":null},"373:30":{"kind":"call","span":{"start":[373,30],"end":[373,65],"bytes":[12804,12839]},"callee":null},"373:51":{"kind":"call","span":{"start":[373,51],"end":[373,64],"bytes":[12825,12838]},"callee":null},"376:9":{"kind":"call","span":{"start":[376,9],"end":[376,29],"bytes":[12878,12898]},"callee":null},"377:12":{"kind":"call","span":{"start":[377,12],"end":[377,23],"bytes":[12910,12921]},"callee":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init"},"378:3":{"kind":"call","span":{"start":[378,3],"end":[378,55],"bytes":[12938,12990]},"callee":null},"379:3":{"kind":"call","span":{"start":[379,3],"end":[379,60],"bytes":[12993,13050]},"callee":null},"382:2":{"kind":"call","span":{"start":[382,2],"end":[382,48],"bytes":[13130,13176]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewNATSReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewNATSReplicaClient","kind":"function","span":{"start":[426,1],"end":[436,2],"bytes":[14328,14570]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewNATSReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[426,1],"end":[426,63],"bytes":[14328,14390]}}],"return_type":"*github.com/benbjohnson/litestream/nats.ReplicaClient","metrics":{"cyclomatic":1},"body":{"427:2":{"kind":"call","span":{"start":[427,2],"end":[427,13],"bytes":[14392,14403]},"callee":null},"429:7":{"kind":"call","span":{"start":[429,7],"end":[429,30],"bytes":[14411,14434]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient"}}},"github.com/benbjohnson/litestream/internal/testingutil.NewOSSReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewOSSReplicaClient","kind":"function","span":{"start":[439,1],"end":[450,2],"bytes":[14641,14970]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewOSSReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[439,1],"end":[439,61],"bytes":[14641,14701]}}],"return_type":"*github.com/benbjohnson/litestream/oss.ReplicaClient","metrics":{"cyclomatic":1},"body":{"440:2":{"kind":"call","span":{"start":[440,2],"end":[440,13],"bytes":[14703,14714]},"callee":null},"442:7":{"kind":"call","span":{"start":[442,7],"end":[442,29],"bytes":[14722,14744]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient"},"447:11":{"kind":"call","span":{"start":[447,11],"end":[447,67],"bytes":[14875,14931]},"callee":null},"447:31":{"kind":"call","span":{"start":[447,31],"end":[447,66],"bytes":[14895,14930]},"callee":null},"447:52":{"kind":"call","span":{"start":[447,52],"end":[447,65],"bytes":[14916,14929]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewR2ReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewR2ReplicaClient","kind":"function","span":{"start":[305,1],"end":[328,2],"bytes":[10604,11320]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewR2ReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[305,1],"end":[305,59],"bytes":[10604,10662]}}],"return_type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","metrics":{"cyclomatic":4},"body":{"306:2":{"kind":"call","span":{"start":[306,2],"end":[306,13],"bytes":[10664,10675]},"callee":null},"309:3":{"kind":"call","span":{"start":[309,3],"end":[309,95],"bytes":[10734,10826]},"callee":null},"312:3":{"kind":"call","span":{"start":[312,3],"end":[312,69],"bytes":[10856,10922]},"callee":null},"315:3":{"kind":"call","span":{"start":[315,3],"end":[315,65],"bytes":[10950,11012]},"callee":null},"318:7":{"kind":"call","span":{"start":[318,7],"end":[318,28],"bytes":[11023,11044]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient"},"323:11":{"kind":"call","span":{"start":[323,11],"end":[323,78],"bytes":[11168,11235]},"callee":null},"323:42":{"kind":"call","span":{"start":[323,42],"end":[323,77],"bytes":[11199,11234]},"callee":null},"323:63":{"kind":"call","span":{"start":[323,63],"end":[323,76],"bytes":[11220,11233]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","kind":"function","span":{"start":[229,1],"end":[259,2],"bytes":[8316,9143]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[229,1],"end":[229,76],"bytes":[8316,8391]}},{"name":"typ","type":"string","span":{"start":[229,1],"end":[229,76],"bytes":[8316,8391]}}],"return_type":"github.com/benbjohnson/litestream.ReplicaClient","metrics":{"cyclomatic":14},"body":{"230:2":{"kind":"call","span":{"start":[230,2],"end":[230,13],"bytes":[8393,8404]},"callee":null},"234:10":{"kind":"call","span":{"start":[234,10],"end":[234,34],"bytes":[8459,8483]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient"},"236:10":{"kind":"call","span":{"start":[236,10],"end":[236,32],"bytes":[8521,8543]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewS3ReplicaClient"},"238:10":{"kind":"call","span":{"start":[238,10],"end":[238,32],"bytes":[8581,8603]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient"},"240:10":{"kind":"call","span":{"start":[240,10],"end":[240,33],"bytes":[8642,8665]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewABSReplicaClient"},"242:10":{"kind":"call","span":{"start":[242,10],"end":[242,34],"bytes":[8705,8729]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewSFTPReplicaClient"},"244:10":{"kind":"call","span":{"start":[244,10],"end":[244,36],"bytes":[8771,8797]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewWebDAVReplicaClient"},"246:10":{"kind":"call","span":{"start":[246,10],"end":[246,34],"bytes":[8837,8861]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewNATSReplicaClient"},"248:10":{"kind":"call","span":{"start":[248,10],"end":[248,33],"bytes":[8900,8923]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewOSSReplicaClient"},"250:10":{"kind":"call","span":{"start":[250,10],"end":[250,36],"bytes":[8949,8975]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewTigrisReplicaClient"},"252:10":{"kind":"call","span":{"start":[252,10],"end":[252,32],"bytes":[8997,9019]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewR2ReplicaClient"},"254:10":{"kind":"call","span":{"start":[254,10],"end":[254,32],"bytes":[9041,9063]},"callee":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewB2ReplicaClient"},"256:3":{"kind":"call","span":{"start":[256,3],"end":[256,52],"bytes":[9076,9125]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewS3ReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewS3ReplicaClient","kind":"function","span":{"start":[268,1],"end":[281,2],"bytes":[9406,9794]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewS3ReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[268,1],"end":[268,59],"bytes":[9406,9464]}}],"return_type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","metrics":{"cyclomatic":1},"body":{"269:2":{"kind":"call","span":{"start":[269,2],"end":[269,13],"bytes":[9466,9477]},"callee":null},"271:7":{"kind":"call","span":{"start":[271,7],"end":[271,28],"bytes":[9485,9506]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient"},"276:11":{"kind":"call","span":{"start":[276,11],"end":[276,66],"bytes":[9633,9688]},"callee":null},"276:30":{"kind":"call","span":{"start":[276,30],"end":[276,65],"bytes":[9652,9687]},"callee":null},"276:51":{"kind":"call","span":{"start":[276,51],"end":[276,64],"bytes":[9673,9686]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewSFTPReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewSFTPReplicaClient","kind":"function","span":{"start":[401,1],"end":[411,2],"bytes":[13628,13908]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewSFTPReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[401,1],"end":[401,63],"bytes":[13628,13690]}}],"return_type":"*github.com/benbjohnson/litestream/sftp.ReplicaClient","metrics":{"cyclomatic":1},"body":{"402:2":{"kind":"call","span":{"start":[402,2],"end":[402,13],"bytes":[13692,13703]},"callee":null},"404:7":{"kind":"call","span":{"start":[404,7],"end":[404,30],"bytes":[13711,13734]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient"},"409:11":{"kind":"call","span":{"start":[409,11],"end":[409,68],"bytes":[13839,13896]},"callee":null},"409:32":{"kind":"call","span":{"start":[409,32],"end":[409,67],"bytes":[13860,13895]},"callee":null},"409:53":{"kind":"call","span":{"start":[409,53],"end":[409,66],"bytes":[13881,13894]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewTigrisReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewTigrisReplicaClient","kind":"function","span":{"start":[284,1],"end":[301,2],"bytes":[9873,10473]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewTigrisReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[284,1],"end":[284,63],"bytes":[9873,9935]}}],"return_type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","metrics":{"cyclomatic":2},"body":{"285:2":{"kind":"call","span":{"start":[285,2],"end":[285,13],"bytes":[9937,9948]},"callee":null},"288:3":{"kind":"call","span":{"start":[288,3],"end":[288,103],"bytes":[10015,10115]},"callee":null},"291:7":{"kind":"call","span":{"start":[291,7],"end":[291,28],"bytes":[10126,10147]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient"},"296:11":{"kind":"call","span":{"start":[296,11],"end":[296,80],"bytes":[10302,10371]},"callee":null},"296:44":{"kind":"call","span":{"start":[296,44],"end":[296,79],"bytes":[10335,10370]},"callee":null},"296:65":{"kind":"call","span":{"start":[296,65],"end":[296,78],"bytes":[10356,10369]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.NewWebDAVReplicaClient":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewWebDAVReplicaClient","kind":"function","span":{"start":[414,1],"end":[423,2],"bytes":[13982,14256]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.NewWebDAVReplicaClient","parameters":[{"name":"tb","type":"testing.TB","span":{"start":[414,1],"end":[414,67],"bytes":[13982,14048]}}],"return_type":"*github.com/benbjohnson/litestream/webdav.ReplicaClient","metrics":{"cyclomatic":1},"body":{"415:2":{"kind":"call","span":{"start":[415,2],"end":[415,13],"bytes":[14050,14061]},"callee":null},"417:7":{"kind":"call","span":{"start":[417,7],"end":[417,32],"bytes":[14069,14094]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient"},"421:11":{"kind":"call","span":{"start":[421,11],"end":[421,70],"bytes":[14185,14244]},"callee":null},"421:34":{"kind":"call","span":{"start":[421,34],"end":[421,69],"bytes":[14208,14243]},"callee":null},"421:55":{"kind":"call","span":{"start":[421,55],"end":[421,68],"bytes":[14229,14242]},"callee":null}}},"github.com/benbjohnson/litestream/internal/testingutil.ReplicaClientTypes":{"id":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.ReplicaClientTypes","kind":"function","span":{"start":[139,1],"end":[141,2],"bytes":[5660,5746]},"signature":"github.com/benbjohnson/litestream/internal/testingutil.ReplicaClientTypes","parameters":[],"return_type":"[]string","metrics":{"cyclomatic":1},"body":{"140:9":{"kind":"call","span":{"start":[140,9],"end":[140,48],"bytes":[5705,5744]},"callee":null}}}},"content_hash":"05ab44549f9219b4b32ffca96ff4acb54d6340b69512a42f2006aae45e5e2397"},"leaser.go":{"id":"can://go/litestream/leaser.go","kind":"module","span":{"start":[1,1],"end":[45,1],"bytes":[0,995]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n)\n\nvar ErrLeaseNotHeld = errors.New(\"lease not held\")\n\ntype LeaseExistsError struct {\n\tOwner string\n\tExpiresAt time.Time\n}\n\nfunc (e *LeaseExistsError) Error() string {\n\tif e.Owner != \"\" {\n\t\treturn fmt.Sprintf(\"lease already held by %s until %s\", e.Owner, e.ExpiresAt.Format(time.RFC3339))\n\t}\n\treturn fmt.Sprintf(\"lease already held until %s\", e.ExpiresAt.Format(time.RFC3339))\n}\n\ntype Leaser interface {\n\tType() string\n\tAcquireLease(ctx context.Context) (*Lease, error)\n\tRenewLease(ctx context.Context, lease *Lease) (*Lease, error)\n\tReleaseLease(ctx context.Context, lease *Lease) error\n}\n\ntype Lease struct {\n\tGeneration int64 `json:\"generation\"`\n\tExpiresAt time.Time `json:\"expires_at\"`\n\tOwner string `json:\"owner,omitempty\"`\n\tETag string `json:\"-\"`\n}\n\nfunc (l *Lease) IsExpired() bool {\n\treturn time.Now().After(l.ExpiresAt)\n}\n\nfunc (l *Lease) TTL() time.Duration {\n\treturn time.Until(l.ExpiresAt)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"errors","path":"errors","span":{"start":[5,1],"end":[5,10],"bytes":[40,49]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[50,56]}},{"name":"time","path":"time","span":{"start":[7,1],"end":[7,8],"bytes":[57,64]}}],"types":{"Lease":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease","kind":"struct","span":{"start":[31,1],"end":[36,2],"bytes":[660,845]},"callables":{"github.com/benbjohnson/litestream.Lease.IsExpired":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/github.com/benbjohnson/litestream.Lease.IsExpired","kind":"method","span":{"start":[38,1],"end":[40,2],"bytes":[847,921]},"signature":"github.com/benbjohnson/litestream.Lease.IsExpired","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"39:9":{"kind":"call","span":{"start":[39,9],"end":[39,19],"bytes":[890,900]},"callee":null}}},"github.com/benbjohnson/litestream.Lease.TTL":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/github.com/benbjohnson/litestream.Lease.TTL","kind":"method","span":{"start":[42,1],"end":[44,2],"bytes":[923,994]},"signature":"github.com/benbjohnson/litestream.Lease.TTL","parameters":[],"return_type":"time.Duration","metrics":{"cyclomatic":1},"body":{"43:9":{"kind":"call","span":{"start":[43,9],"end":[43,32],"bytes":[969,992]},"callee":null}}}},"fields":{"ETag":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/ETag","kind":"field","type":"string","span":{"start":[35,1],"end":[35,33],"bytes":[811,843]}},"ExpiresAt":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/ExpiresAt","kind":"field","type":"time.Time","span":{"start":[33,1],"end":[33,42],"bytes":[722,763]}},"Generation":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/Generation","kind":"field","type":"int64","span":{"start":[32,1],"end":[32,42],"bytes":[680,721]}},"Owner":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/Owner","kind":"field","type":"string","span":{"start":[34,1],"end":[34,47],"bytes":[764,810]}}}},"LeaseExistsError":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.LeaseExistsError","kind":"struct","span":{"start":[12,1],"end":[15,2],"bytes":[120,191]},"callables":{"github.com/benbjohnson/litestream.LeaseExistsError.Error":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.LeaseExistsError/github.com/benbjohnson/litestream.LeaseExistsError.Error","kind":"method","span":{"start":[17,1],"end":[22,2],"bytes":[193,447]},"signature":"github.com/benbjohnson/litestream.LeaseExistsError.Error","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{"19:10":{"kind":"call","span":{"start":[19,10],"end":[19,101],"bytes":[266,357]},"callee":null},"19:68":{"kind":"call","span":{"start":[19,68],"end":[19,100],"bytes":[324,356]},"callee":null},"21:52":{"kind":"call","span":{"start":[21,52],"end":[21,84],"bytes":[412,444]},"callee":null},"21:9":{"kind":"call","span":{"start":[21,9],"end":[21,85],"bytes":[369,445]},"callee":null}}}},"fields":{"ExpiresAt":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.LeaseExistsError/ExpiresAt","kind":"field","type":"time.Time","span":{"start":[14,1],"end":[14,21],"bytes":[169,189]}},"Owner":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.LeaseExistsError/Owner","kind":"field","type":"string","span":{"start":[13,1],"end":[13,18],"bytes":[151,168]}}}},"Leaser":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Leaser","kind":"interface","span":{"start":[24,1],"end":[29,2],"bytes":[449,658]},"callables":{"github.com/benbjohnson/litestream.Leaser.AcquireLease":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Leaser/github.com/benbjohnson/litestream.Leaser.AcquireLease","kind":"method","span":{"start":[26,1],"end":[26,51],"bytes":[488,538]},"signature":"github.com/benbjohnson/litestream.Leaser.AcquireLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[26,1],"end":[26,51],"bytes":[488,538]}}],"return_type":"(*github.com/benbjohnson/litestream.Lease, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.Leaser.ReleaseLease":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Leaser/github.com/benbjohnson/litestream.Leaser.ReleaseLease","kind":"method","span":{"start":[28,1],"end":[28,55],"bytes":[602,656]},"signature":"github.com/benbjohnson/litestream.Leaser.ReleaseLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[28,1],"end":[28,55],"bytes":[602,656]}},{"name":"lease","type":"*github.com/benbjohnson/litestream.Lease","span":{"start":[28,1],"end":[28,55],"bytes":[602,656]}}],"return_type":"error","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.Leaser.RenewLease":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Leaser/github.com/benbjohnson/litestream.Leaser.RenewLease","kind":"method","span":{"start":[27,1],"end":[27,63],"bytes":[539,601]},"signature":"github.com/benbjohnson/litestream.Leaser.RenewLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[27,1],"end":[27,63],"bytes":[539,601]}},{"name":"lease","type":"*github.com/benbjohnson/litestream.Lease","span":{"start":[27,1],"end":[27,63],"bytes":[539,601]}}],"return_type":"(*github.com/benbjohnson/litestream.Lease, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.Leaser.Type":{"id":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Leaser/github.com/benbjohnson/litestream.Leaser.Type","kind":"method","span":{"start":[25,1],"end":[25,15],"bytes":[473,487]},"signature":"github.com/benbjohnson/litestream.Leaser.Type","parameters":[],"return_type":"string","body":{}}}}},"functions":{},"content_hash":"86026b64494febb4e5dd447ddebcb76377306ae30fe8d8dcb68428d9833e9b1a"},"litestream.go":{"id":"can://go/litestream/litestream.go","kind":"module","span":{"start":[1,1],"end":[214,1],"bytes":[0,6059]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/superfly/ltx\"\n\t_ \"modernc.org/sqlite\"\n)\n\n// Naming constants.\nconst (\n\tMetaDirSuffix = \"-litestream\"\n)\n\n// SQLite checkpoint modes.\nconst (\n\tCheckpointModePassive = \"PASSIVE\"\n\tCheckpointModeFull = \"FULL\"\n\tCheckpointModeRestart = \"RESTART\"\n\tCheckpointModeTruncate = \"TRUNCATE\"\n)\n\n// Litestream errors.\nvar (\n\tErrNoSnapshots = errors.New(\"no snapshots available\")\n\tErrChecksumMismatch = errors.New(\"invalid replica, checksum mismatch\")\n\tErrLTXCorrupted = errors.New(\"ltx file corrupted\")\n\tErrLTXMissing = errors.New(\"ltx file missing\")\n\tErrDiskFull = errors.New(\"disk full\")\n)\n\n// LTXError provides detailed context for LTX file errors with recovery hints.\ntype LTXError struct {\n\tOp string // Operation that failed (e.g., \"open\", \"read\", \"validate\")\n\tPath string // File path\n\tLevel int // LTX level (0 = L0, etc.)\n\tMinTXID uint64 // Minimum transaction ID\n\tMaxTXID uint64 // Maximum transaction ID\n\tErr error // Underlying error\n\tHint string // Recovery hint for users\n}\n\nfunc (e *LTXError) Error() string {\n\tif e.Path != \"\" {\n\t\treturn e.Op + \" ltx file \" + e.Path + \": \" + e.Err.Error()\n\t}\n\treturn e.Op + \" ltx file: \" + e.Err.Error()\n}\n\nfunc (e *LTXError) Unwrap() error { return e.Err }\n\n// IsAutoRecoverable reports whether the underlying error indicates local state\n// corruption that can be fixed by resetting and re-downloading from remote.\n// Returns false for transient OS errors (EMFILE, EIO, EACCES) that should be\n// retried with backoff instead.\nfunc (e *LTXError) IsAutoRecoverable() bool {\n\tif os.IsNotExist(e.Err) || errors.Is(e.Err, ErrLTXMissing) {\n\t\treturn true\n\t}\n\tif errors.Is(e.Err, ErrLTXCorrupted) || errors.Is(e.Err, ErrChecksumMismatch) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// NewLTXError creates a new LTX error with appropriate hints based on the error type.\nfunc NewLTXError(op, path string, level int, minTXID, maxTXID uint64, err error) *LTXError {\n\tltxErr := \u0026LTXError{\n\t\tOp: op,\n\t\tPath: path,\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tErr: err,\n\t}\n\n\t// Set appropriate hint based on error type\n\tif os.IsNotExist(err) || errors.Is(err, ErrLTXMissing) {\n\t\tltxErr.Hint = \"LTX file is missing. This can happen after VACUUM, manual checkpoint, or state corruption. \" +\n\t\t\t\"Run 'litestream reset \u003cdb\u003e' or delete the .sqlite-litestream directory and restart.\"\n\t} else if errors.Is(err, ErrLTXCorrupted) || errors.Is(err, ErrChecksumMismatch) {\n\t\tltxErr.Hint = \"LTX file is corrupted. Delete the .sqlite-litestream directory and restart to recover from replica.\"\n\t}\n\n\treturn ltxErr\n}\n\n// isShutdownError reports whether err is a context error caused by ctx itself\n// being done. Context errors that occur while ctx is still live, such as a\n// replica client whose credentials are bound to a cancelled context, are real\n// failures and must not be silently ignored.\nfunc isShutdownError(ctx context.Context, err error) bool {\n\treturn ctx.Err() != nil \u0026\u0026 (errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded))\n}\n\n// SQLite WAL constants.\nconst (\n\tWALHeaderChecksumOffset = 24\n\tWALFrameHeaderChecksumOffset = 16\n)\n\nvar (\n\t// LogWriter is the destination writer for all logging.\n\tLogWriter = os.Stdout\n\n\t// LogFlags are the flags passed to log.New().\n\tLogFlags = 0\n)\n\n// Checksum computes a running SQLite checksum over a byte slice.\nfunc Checksum(bo binary.ByteOrder, s0, s1 uint32, b []byte) (uint32, uint32) {\n\tassert(len(b)%8 == 0, \"misaligned checksum byte slice\")\n\n\t// Iterate over 8-byte units and compute checksum.\n\tfor i := 0; i \u003c len(b); i += 8 {\n\t\ts0 += bo.Uint32(b[i:]) + s1\n\t\ts1 += bo.Uint32(b[i+4:]) + s0\n\t}\n\treturn s0, s1\n}\n\nconst (\n\t// WALHeaderSize is the size of the WAL header, in bytes.\n\tWALHeaderSize = 32\n\n\t// WALFrameHeaderSize is the size of the WAL frame header, in bytes.\n\tWALFrameHeaderSize = 24\n)\n\n// rollback rolls back tx. Ignores already-rolled-back errors.\nfunc rollback(tx *sql.Tx) error {\n\tif err := tx.Rollback(); err != nil \u0026\u0026 !strings.Contains(err.Error(), `transaction has already been committed or rolled back`) {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// readWALHeader returns the header read from a WAL file.\nfunc readWALHeader(filename string) ([]byte, error) {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tbuf := make([]byte, WALHeaderSize)\n\tn, err := io.ReadFull(f, buf)\n\treturn buf[:n], err\n}\n\n// readWALFileAt reads a slice from a file. Do not use this with database files\n// as it causes problems with non-OFD locks.\nfunc readWALFileAt(filename string, offset, n int64) ([]byte, error) {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tbuf := make([]byte, n)\n\tif n, err := f.ReadAt(buf, offset); err != nil {\n\t\treturn buf[:n], err\n\t} else if n \u003c len(buf) {\n\t\treturn buf[:n], io.ErrUnexpectedEOF\n\t}\n\treturn buf, nil\n}\n\n// removeTmpFiles recursively finds and removes .tmp files.\nfunc removeTmpFiles(root string) error {\n\treturn filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn nil // skip errored files\n\t\tcase info.IsDir():\n\t\t\treturn nil // skip directories\n\t\tcase !strings.HasSuffix(path, \".tmp\"):\n\t\t\treturn nil // skip non-temp files\n\t\tdefault:\n\t\t\treturn os.Remove(path)\n\t\t}\n\t})\n}\n\n// LTXDir returns the path to an LTX directory.\nfunc LTXDir(root string) string {\n\treturn path.Join(root, \"ltx\")\n}\n\n// LTXLevelDir returns the path to an LTX level directory.\nfunc LTXLevelDir(root string, level int) string {\n\treturn path.Join(LTXDir(root), strconv.Itoa(level))\n}\n\n// LTXFilePath returns the path to a single LTX file.\nfunc LTXFilePath(root string, level int, minTXID, maxTXID ltx.TXID) string {\n\treturn path.Join(LTXLevelDir(root, level), ltx.FormatFilename(minTXID, maxTXID))\n}\n\nfunc assert(condition bool, message string) {\n\tif !condition {\n\t\tpanic(\"assertion failed: \" + message)\n\t}\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"sql","path":"database/sql","span":{"start":[5,1],"end":[5,16],"bytes":[40,55]}},{"name":"binary","path":"encoding/binary","span":{"start":[6,1],"end":[6,19],"bytes":[56,74]}},{"name":"errors","path":"errors","span":{"start":[7,1],"end":[7,10],"bytes":[75,84]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[85,90]}},{"name":"os","path":"os","span":{"start":[9,1],"end":[9,6],"bytes":[91,96]}},{"name":"path","path":"path","span":{"start":[10,1],"end":[10,8],"bytes":[97,104]}},{"name":"filepath","path":"path/filepath","span":{"start":[11,1],"end":[11,17],"bytes":[105,121]}},{"name":"strconv","path":"strconv","span":{"start":[12,1],"end":[12,11],"bytes":[122,132]}},{"name":"strings","path":"strings","span":{"start":[13,1],"end":[13,11],"bytes":[133,143]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[15,1],"end":[15,27],"bytes":[145,171]}},{"name":"sqlite","path":"modernc.org/sqlite","alias":"_","span":{"start":[16,1],"end":[16,24],"bytes":[172,195]}}],"types":{"LTXError":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError","kind":"struct","span":{"start":[42,1],"end":[50,2],"bytes":[842,1178]},"callables":{"github.com/benbjohnson/litestream.LTXError.Error":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/github.com/benbjohnson/litestream.LTXError.Error","kind":"method","span":{"start":[52,1],"end":[57,2],"bytes":[1180,1345]},"signature":"github.com/benbjohnson/litestream.LTXError.Error","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{"54:48":{"kind":"call","span":{"start":[54,48],"end":[54,61],"bytes":[1282,1295]},"callee":null},"56:32":{"kind":"call","span":{"start":[56,32],"end":[56,45],"bytes":[1330,1343]},"callee":null}}},"github.com/benbjohnson/litestream.LTXError.IsAutoRecoverable":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/github.com/benbjohnson/litestream.LTXError.IsAutoRecoverable","kind":"method","span":{"start":[65,1],"end":[73,2],"bytes":[1667,1905]},"signature":"github.com/benbjohnson/litestream.LTXError.IsAutoRecoverable","parameters":[],"return_type":"bool","metrics":{"cyclomatic":3},"body":{"66:29":{"kind":"call","span":{"start":[66,29],"end":[66,60],"bytes":[1741,1772]},"callee":null},"66:5":{"kind":"call","span":{"start":[66,5],"end":[66,25],"bytes":[1717,1737]},"callee":null},"69:42":{"kind":"call","span":{"start":[69,42],"end":[69,79],"bytes":[1833,1870]},"callee":null},"69:5":{"kind":"call","span":{"start":[69,5],"end":[69,38],"bytes":[1796,1829]},"callee":null}}},"github.com/benbjohnson/litestream.LTXError.Unwrap":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/github.com/benbjohnson/litestream.LTXError.Unwrap","kind":"method","span":{"start":[59,1],"end":[59,51],"bytes":[1347,1397]},"signature":"github.com/benbjohnson/litestream.LTXError.Unwrap","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}}},"fields":{"Err":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/Err","kind":"field","type":"error","span":{"start":[48,1],"end":[48,36],"bytes":[1098,1133]}},"Hint":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/Hint","kind":"field","type":"string","span":{"start":[49,1],"end":[49,43],"bytes":[1134,1176]}},"Level":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/Level","kind":"field","type":"int","span":{"start":[45,1],"end":[45,44],"bytes":[970,1013]}},"MaxTXID":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/MaxTXID","kind":"field","type":"uint64","span":{"start":[47,1],"end":[47,42],"bytes":[1056,1097]}},"MinTXID":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/MinTXID","kind":"field","type":"uint64","span":{"start":[46,1],"end":[46,42],"bytes":[1014,1055]}},"Op":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/Op","kind":"field","type":"string","span":{"start":[43,1],"end":[43,76],"bytes":[865,940]}},"Path":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/Path","kind":"field","type":"string","span":{"start":[44,1],"end":[44,29],"bytes":[941,969]}}}}},"functions":{"github.com/benbjohnson/litestream.Checksum":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.Checksum","kind":"function","span":{"start":[120,1],"end":[129,2],"bytes":[3521,3825]},"signature":"github.com/benbjohnson/litestream.Checksum","parameters":[{"name":"bo","type":"encoding/binary.ByteOrder","span":{"start":[120,1],"end":[120,79],"bytes":[3521,3599]}},{"name":"s0","type":"uint32","span":{"start":[120,1],"end":[120,79],"bytes":[3521,3599]}},{"name":"s1","type":"uint32","span":{"start":[120,1],"end":[120,79],"bytes":[3521,3599]}},{"name":"b","type":"[]byte","span":{"start":[120,1],"end":[120,79],"bytes":[3521,3599]}}],"return_type":"(uint32, uint32)","metrics":{"cyclomatic":2},"body":{"121:2":{"kind":"call","span":{"start":[121,2],"end":[121,57],"bytes":[3601,3656]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert"},"121:9":{"kind":"call","span":{"start":[121,9],"end":[121,15],"bytes":[3608,3614]},"callee":null},"124:18":{"kind":"call","span":{"start":[124,18],"end":[124,24],"bytes":[3727,3733]},"callee":null},"125:9":{"kind":"call","span":{"start":[125,9],"end":[125,25],"bytes":[3752,3768]},"callee":null},"126:9":{"kind":"call","span":{"start":[126,9],"end":[126,27],"bytes":[3782,3800]},"callee":null}}},"github.com/benbjohnson/litestream.LTXDir":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXDir","kind":"function","span":{"start":[195,1],"end":[197,2],"bytes":[5502,5568]},"signature":"github.com/benbjohnson/litestream.LTXDir","parameters":[{"name":"root","type":"string","span":{"start":[195,1],"end":[195,34],"bytes":[5502,5535]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"196:9":{"kind":"call","span":{"start":[196,9],"end":[196,31],"bytes":[5544,5566]},"callee":null}}},"github.com/benbjohnson/litestream.LTXFilePath":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","kind":"function","span":{"start":[205,1],"end":[207,2],"bytes":[5789,5949]},"signature":"github.com/benbjohnson/litestream.LTXFilePath","parameters":[{"name":"root","type":"string","span":{"start":[205,1],"end":[205,77],"bytes":[5789,5865]}},{"name":"level","type":"int","span":{"start":[205,1],"end":[205,77],"bytes":[5789,5865]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[205,1],"end":[205,77],"bytes":[5789,5865]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[205,1],"end":[205,77],"bytes":[5789,5865]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"206:19":{"kind":"call","span":{"start":[206,19],"end":[206,43],"bytes":[5884,5908]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"206:45":{"kind":"call","span":{"start":[206,45],"end":[206,81],"bytes":[5910,5946]},"callee":null},"206:9":{"kind":"call","span":{"start":[206,9],"end":[206,82],"bytes":[5874,5947]},"callee":null}}},"github.com/benbjohnson/litestream.LTXLevelDir":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","kind":"function","span":{"start":[200,1],"end":[202,2],"bytes":[5629,5733]},"signature":"github.com/benbjohnson/litestream.LTXLevelDir","parameters":[{"name":"root","type":"string","span":{"start":[200,1],"end":[200,50],"bytes":[5629,5678]}},{"name":"level","type":"int","span":{"start":[200,1],"end":[200,50],"bytes":[5629,5678]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"201:19":{"kind":"call","span":{"start":[201,19],"end":[201,31],"bytes":[5697,5709]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXDir"},"201:33":{"kind":"call","span":{"start":[201,33],"end":[201,52],"bytes":[5711,5730]},"callee":null},"201:9":{"kind":"call","span":{"start":[201,9],"end":[201,53],"bytes":[5687,5731]},"callee":null}}},"github.com/benbjohnson/litestream.NewLTXError":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","kind":"function","span":{"start":[76,1],"end":[95,2],"bytes":[1994,2745]},"signature":"github.com/benbjohnson/litestream.NewLTXError","parameters":[{"name":"op","type":"string","span":{"start":[76,1],"end":[76,93],"bytes":[1994,2086]}},{"name":"path","type":"string","span":{"start":[76,1],"end":[76,93],"bytes":[1994,2086]}},{"name":"level","type":"int","span":{"start":[76,1],"end":[76,93],"bytes":[1994,2086]}},{"name":"minTXID","type":"uint64","span":{"start":[76,1],"end":[76,93],"bytes":[1994,2086]}},{"name":"maxTXID","type":"uint64","span":{"start":[76,1],"end":[76,93],"bytes":[1994,2086]}},{"name":"err","type":"error","span":{"start":[76,1],"end":[76,93],"bytes":[1994,2086]}}],"return_type":"*github.com/benbjohnson/litestream.LTXError","metrics":{"cyclomatic":3},"body":{"87:27":{"kind":"call","span":{"start":[87,27],"end":[87,56],"bytes":[2290,2319]},"callee":null},"87:5":{"kind":"call","span":{"start":[87,5],"end":[87,23],"bytes":[2268,2286]},"callee":null},"90:12":{"kind":"call","span":{"start":[90,12],"end":[90,43],"bytes":[2534,2565]},"callee":null},"90:47":{"kind":"call","span":{"start":[90,47],"end":[90,82],"bytes":[2569,2604]},"callee":null}}},"github.com/benbjohnson/litestream.assert":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert","kind":"function","span":{"start":[209,1],"end":[213,2],"bytes":[5951,6058]},"signature":"github.com/benbjohnson/litestream.assert","parameters":[{"name":"condition","type":"bool","span":{"start":[209,1],"end":[209,46],"bytes":[5951,5996]}},{"name":"message","type":"string","span":{"start":[209,1],"end":[209,46],"bytes":[5951,5996]}}],"metrics":{"cyclomatic":2},"body":{"211:3":{"kind":"call","span":{"start":[211,3],"end":[211,40],"bytes":[6016,6053]},"callee":null}}},"github.com/benbjohnson/litestream.isShutdownError":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","kind":"function","span":{"start":[101,1],"end":[103,2],"bytes":[3027,3195]},"signature":"github.com/benbjohnson/litestream.isShutdownError","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[101,1],"end":[101,60],"bytes":[3027,3086]}},{"name":"err","type":"error","span":{"start":[101,1],"end":[101,60],"bytes":[3027,3086]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"102:30":{"kind":"call","span":{"start":[102,30],"end":[102,62],"bytes":[3116,3148]},"callee":null},"102:66":{"kind":"call","span":{"start":[102,66],"end":[102,106],"bytes":[3152,3192]},"callee":null},"102:9":{"kind":"call","span":{"start":[102,9],"end":[102,18],"bytes":[3095,3104]},"callee":null}}},"github.com/benbjohnson/litestream.readWALFileAt":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALFileAt","kind":"function","span":{"start":[162,1],"end":[176,2],"bytes":[4684,5021]},"signature":"github.com/benbjohnson/litestream.readWALFileAt","parameters":[{"name":"filename","type":"string","span":{"start":[162,1],"end":[162,71],"bytes":[4684,4754]}},{"name":"offset","type":"int64","span":{"start":[162,1],"end":[162,71],"bytes":[4684,4754]}},{"name":"n","type":"int64","span":{"start":[162,1],"end":[162,71],"bytes":[4684,4754]}}],"return_type":"([]byte, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"163:12":{"kind":"call","span":{"start":[163,12],"end":[163,29],"bytes":[4766,4783]},"callee":null},"167:8":{"kind":"call","span":{"start":[167,8],"end":[167,17],"bytes":[4829,4838]},"callee":null},"169:9":{"kind":"call","span":{"start":[169,9],"end":[169,24],"bytes":[4848,4863]},"callee":null},"170:15":{"kind":"call","span":{"start":[170,15],"end":[170,36],"bytes":[4878,4899]},"callee":null},"172:16":{"kind":"call","span":{"start":[172,16],"end":[172,24],"bytes":[4951,4959]},"callee":null}}},"github.com/benbjohnson/litestream.readWALHeader":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader","kind":"function","span":{"start":[148,1],"end":[158,2],"bytes":[4329,4557]},"signature":"github.com/benbjohnson/litestream.readWALHeader","parameters":[{"name":"filename","type":"string","span":{"start":[148,1],"end":[148,54],"bytes":[4329,4382]}}],"return_type":"([]byte, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"149:12":{"kind":"call","span":{"start":[149,12],"end":[149,29],"bytes":[4394,4411]},"callee":null},"153:8":{"kind":"call","span":{"start":[153,8],"end":[153,17],"bytes":[4457,4466]},"callee":null},"155:9":{"kind":"call","span":{"start":[155,9],"end":[155,36],"bytes":[4476,4503]},"callee":null},"156:12":{"kind":"call","span":{"start":[156,12],"end":[156,31],"bytes":[4515,4534]},"callee":null}}},"github.com/benbjohnson/litestream.removeTmpFiles":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.removeTmpFiles","kind":"function","span":{"start":[179,1],"end":[192,2],"bytes":[5083,5452]},"signature":"github.com/benbjohnson/litestream.removeTmpFiles","parameters":[{"name":"root","type":"string","span":{"start":[179,1],"end":[179,41],"bytes":[5083,5123]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"180:9":{"kind":"call","span":{"start":[180,9],"end":[191,4],"bytes":[5132,5450]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.removeTmpFiles/github.com/benbjohnson/litestream.removeTmpFiles.closure_1","kind":"function","span":{"start":[180,1],"end":[191,4],"bytes":[5124,5450]},"signature":"github.com/benbjohnson/litestream.removeTmpFiles.closure_1","parameters":[{"name":"path","type":"string","span":{"start":[180,1],"end":[180,83],"bytes":[5124,5206]}},{"name":"info","type":"os.FileInfo","span":{"start":[180,1],"end":[180,83],"bytes":[5124,5206]}},{"name":"err","type":"error","span":{"start":[180,1],"end":[180,83],"bytes":[5124,5206]}}],"return_type":"error","error_channel":["error"],"body":{"184:8":{"kind":"call","span":{"start":[184,8],"end":[184,20],"bytes":[5280,5292]},"callee":null},"186:9":{"kind":"call","span":{"start":[186,9],"end":[186,40],"bytes":[5336,5367]},"callee":null},"189:11":{"kind":"call","span":{"start":[189,11],"end":[189,26],"bytes":[5427,5442]},"callee":null}}}}},"github.com/benbjohnson/litestream.rollback":{"id":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.rollback","kind":"function","span":{"start":[140,1],"end":[145,2],"bytes":[4076,4269]},"signature":"github.com/benbjohnson/litestream.rollback","parameters":[{"name":"tx","type":"*database/sql.Tx","span":{"start":[140,1],"end":[140,34],"bytes":[4076,4109]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"141:12":{"kind":"call","span":{"start":[141,12],"end":[141,25],"bytes":[4121,4134]},"callee":null},"141:42":{"kind":"call","span":{"start":[141,42],"end":[141,128],"bytes":[4151,4237]},"callee":null},"141:59":{"kind":"call","span":{"start":[141,59],"end":[141,70],"bytes":[4168,4179]},"callee":null}}}},"content_hash":"e4b652a8e8b83c18ff7d9f824a98e686528587ce7eba00d1a216bc5122e655b9"},"log.go":{"id":"can://go/litestream/log.go","kind":"module","span":{"start":[1,1],"end":[18,1],"bytes":[0,265]},"package":"litestream","source":"package litestream\n\nconst (\n\tLogKeySystem = \"system\"\n\tLogKeySubsystem = \"subsystem\"\n\tLogKeyDB = \"db\"\n)\n\nconst (\n\tLogSystemStore = \"store\"\n\tLogSystemServer = \"server\"\n)\n\nconst (\n\tLogSubsystemCompactor = \"compactor\"\n\tLogSubsystemWALReader = \"wal-reader\"\n)\n","imports":[],"types":{},"functions":{},"content_hash":"65bf3b1e8a77873e7202c299afb7bb22108561e035ee8dcd82da238b625816d4"},"mock/replica_client.go":{"id":"can://go/litestream/mock/replica_client.go","kind":"module","span":{"start":[1,1],"end":[54,1],"bytes":[0,1796]},"package":"mock","source":"package mock\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log/slog\"\n\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\ntype ReplicaClient struct {\n\tInitFunc func(ctx context.Context) error\n\tDeleteAllFunc func(ctx context.Context) error\n\tLTXFilesFunc func(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error)\n\tOpenLTXFileFunc func(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error)\n\tWriteLTXFileFunc func(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error)\n\tDeleteLTXFilesFunc func(ctx context.Context, a []*ltx.FileInfo) error\n}\n\nfunc (c *ReplicaClient) Type() string { return \"mock\" }\n\nfunc (c *ReplicaClient) Init(ctx context.Context) error {\n\tif c.InitFunc != nil {\n\t\treturn c.InitFunc(ctx)\n\t}\n\treturn nil\n}\n\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\treturn c.DeleteAllFunc(ctx)\n}\n\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\treturn c.LTXFilesFunc(ctx, level, seek, useMetadata)\n}\n\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\treturn c.OpenLTXFileFunc(ctx, level, minTXID, maxTXID, offset, size)\n}\n\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {\n\treturn c.WriteLTXFileFunc(ctx, level, minTXID, maxTXID, r)\n}\n\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\treturn c.DeleteLTXFilesFunc(ctx, a)\n}\n\nfunc (c *ReplicaClient) SetLogger(_ *slog.Logger) {}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[23,33]}},{"name":"io","path":"io","span":{"start":[5,1],"end":[5,6],"bytes":[34,39]}},{"name":"slog","path":"log/slog","span":{"start":[6,1],"end":[6,12],"bytes":[40,51]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[8,1],"end":[8,27],"bytes":[53,79]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[10,1],"end":[10,37],"bytes":[81,117]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient","kind":"struct","span":{"start":[15,1],"end":[22,2],"bytes":[177,744]},"callables":{"github.com/benbjohnson/litestream/mock.ReplicaClient.DeleteAll":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.DeleteAll","kind":"method","span":{"start":[33,1],"end":[35,2],"bytes":[928,1021]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[33,1],"end":[33,63],"bytes":[928,990]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"34:9":{"kind":"call","span":{"start":[34,9],"end":[34,29],"bytes":[999,1019]},"callee":null}}},"github.com/benbjohnson/litestream/mock.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[49,1],"end":[51,2],"bytes":[1616,1741]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[49,1],"end":[49,87],"bytes":[1616,1702]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[49,1],"end":[49,87],"bytes":[1616,1702]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"50:9":{"kind":"call","span":{"start":[50,9],"end":[50,37],"bytes":[1711,1739]},"callee":null}}},"github.com/benbjohnson/litestream/mock.ReplicaClient.Init":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.Init","kind":"method","span":{"start":[26,1],"end":[31,2],"bytes":[803,926]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[26,1],"end":[26,58],"bytes":[803,860]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"28:10":{"kind":"call","span":{"start":[28,10],"end":[28,25],"bytes":[894,909]},"callee":null}}},"github.com/benbjohnson/litestream/mock.ReplicaClient.LTXFiles":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.LTXFiles","kind":"method","span":{"start":[37,1],"end":[39,2],"bytes":[1023,1204]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[37,1],"end":[37,126],"bytes":[1023,1148]}},{"name":"level","type":"int","span":{"start":[37,1],"end":[37,126],"bytes":[1023,1148]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[37,1],"end":[37,126],"bytes":[1023,1148]}},{"name":"useMetadata","type":"bool","span":{"start":[37,1],"end":[37,126],"bytes":[1023,1148]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"38:9":{"kind":"call","span":{"start":[38,9],"end":[38,54],"bytes":[1157,1202]},"callee":null}}},"github.com/benbjohnson/litestream/mock.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[41,1],"end":[43,2],"bytes":[1206,1417]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[41,1],"end":[41,140],"bytes":[1206,1345]}},{"name":"level","type":"int","span":{"start":[41,1],"end":[41,140],"bytes":[1206,1345]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[41,1],"end":[41,140],"bytes":[1206,1345]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[41,1],"end":[41,140],"bytes":[1206,1345]}},{"name":"offset","type":"int64","span":{"start":[41,1],"end":[41,140],"bytes":[1206,1345]}},{"name":"size","type":"int64","span":{"start":[41,1],"end":[41,140],"bytes":[1206,1345]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"42:9":{"kind":"call","span":{"start":[42,9],"end":[42,70],"bytes":[1354,1415]},"callee":null}}},"github.com/benbjohnson/litestream/mock.ReplicaClient.SetLogger":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.SetLogger","kind":"method","span":{"start":[53,1],"end":[53,53],"bytes":[1743,1795]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.SetLogger","parameters":[{"name":"_","type":"*log/slog.Logger","span":{"start":[53,1],"end":[53,53],"bytes":[1743,1795]}}],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/mock.ReplicaClient.Type":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.Type","kind":"method","span":{"start":[24,1],"end":[24,56],"bytes":[746,801]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/mock.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/github.com/benbjohnson/litestream/mock.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[45,1],"end":[47,2],"bytes":[1419,1614]},"signature":"github.com/benbjohnson/litestream/mock.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[45,1],"end":[45,134],"bytes":[1419,1552]}},{"name":"level","type":"int","span":{"start":[45,1],"end":[45,134],"bytes":[1419,1552]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[45,1],"end":[45,134],"bytes":[1419,1552]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[45,1],"end":[45,134],"bytes":[1419,1552]}},{"name":"r","type":"io.Reader","span":{"start":[45,1],"end":[45,134],"bytes":[1419,1552]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"46:9":{"kind":"call","span":{"start":[46,9],"end":[46,60],"bytes":[1561,1612]},"callee":null}}}},"fields":{"DeleteAllFunc":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/DeleteAllFunc","kind":"field","type":"func(ctx context.Context) error","span":{"start":[17,1],"end":[17,52],"bytes":[257,308]}},"DeleteLTXFilesFunc":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/DeleteLTXFilesFunc","kind":"field","type":"func(ctx context.Context, a []*github.com/superfly/ltx.FileInfo) error","span":{"start":[21,1],"end":[21,71],"bytes":[672,742]}},"InitFunc":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/InitFunc","kind":"field","type":"func(ctx context.Context) error","span":{"start":[16,1],"end":[16,52],"bytes":[205,256]}},"LTXFilesFunc":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/LTXFilesFunc","kind":"field","type":"func(ctx context.Context, level int, seek github.com/superfly/ltx.TXID, useMetadata bool) (github.com/superfly/ltx.FileIterator, error)","span":{"start":[18,1],"end":[18,116],"bytes":[309,424]}},"OpenLTXFileFunc":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/OpenLTXFileFunc","kind":"field","type":"func(ctx context.Context, level int, minTXID github.com/superfly/ltx.TXID, maxTXID github.com/superfly/ltx.TXID, offset int64, size int64) (io.ReadCloser, error)","span":{"start":[19,1],"end":[19,127],"bytes":[425,551]}},"WriteLTXFileFunc":{"id":"can://go/litestream/mock/replica_client.go/github.com/benbjohnson/litestream/mock.ReplicaClient/WriteLTXFileFunc","kind":"field","type":"func(ctx context.Context, level int, minTXID github.com/superfly/ltx.TXID, maxTXID github.com/superfly/ltx.TXID, r io.Reader) (*github.com/superfly/ltx.FileInfo, error)","span":{"start":[20,1],"end":[20,120],"bytes":[552,671]}}}}},"functions":{},"content_hash":"e4ae0946c515afc92b2b5c565860eb69a9495833baf931bf4947550cadb1926b"},"nats/replica_client.go":{"id":"can://go/litestream/nats/replica_client.go","kind":"module","span":{"start":[1,1],"end":[521,1],"bytes":[0,15428]},"package":"nats","source":"package nats\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/nats-io/nats.go\"\n\t\"github.com/nats-io/nats.go/jetstream\"\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"nats\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"nats\"\n\n// HeaderKeyTimestamp is the header key for storing LTX file timestamps in NATS object headers.\nconst HeaderKeyTimestamp = \"Litestream-Timestamp\"\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files to NATS JetStream Object Store.\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\tlogger *slog.Logger\n\n\t// NATS connection and JetStream context\n\tnc *nats.Conn\n\tjs jetstream.JetStream\n\tobjectStore jetstream.ObjectStore\n\n\t// Configuration\n\tURL string // NATS server URL\n\tBucketName string // Object store bucket name\n\tPath string // Base path for LTX files within the bucket\n\tJWT string // JWT token for authentication\n\tSeed string // Seed for JWT authentication\n\tCreds string // Credentials file path\n\tNKey string // NKey for authentication\n\tUsername string // Username for authentication\n\tPassword string // Password for authentication\n\tToken string // Token for authentication\n\tTLS bool // Enable TLS\n\tRootCAs []string // Root CA certificates\n\tClientCert string // Client certificate file path\n\tClientKey string // Client key file path\n\n\t// Note: Bucket configuration (replicas, storage, TTL, etc.) should be\n\t// managed externally via NATS CLI or API, not by Litestream\n\n\t// Connection options\n\tMaxReconnects int // Maximum reconnection attempts (-1 for unlimited)\n\tReconnectWait time.Duration // Wait time between reconnection attempts\n\tReconnectJitter time.Duration // Random jitter for reconnection\n\tTimeout time.Duration // Connection timeout\n\tPingInterval time.Duration // Ping interval\n\tMaxPingsOut int // Maximum number of pings without response\n\tReconnectBufSize int // Reconnection buffer size\n\tUserJWT func() (string, error) // JWT callback\n\tSigCB func([]byte) ([]byte, error) // Signature callback\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t\tMaxReconnects: -1, // Unlimited\n\t\tReconnectWait: 2 * time.Second,\n\t\tTimeout: 10 * time.Second,\n\t\tPingInterval: 2 * time.Minute,\n\t\tMaxPingsOut: 2,\n\t\tReconnectBufSize: 8 * 1024 * 1024, // 8MB\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\n// URL format: nats://[user:pass@]host[:port]/bucket\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tclient := NewReplicaClient()\n\n\t// Reconstruct URL without bucket path\n\tif host != \"\" {\n\t\tclient.URL = fmt.Sprintf(\"nats://%s\", host)\n\t}\n\n\t// Extract credentials from userinfo if present\n\tif userinfo != nil {\n\t\tclient.Username = userinfo.Username()\n\t\tclient.Password, _ = userinfo.Password()\n\t}\n\n\t// Extract bucket name from path\n\tbucket := strings.Trim(urlPath, \"/\")\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for nats replica URL\")\n\t}\n\tclient.BucketName = bucket\n\n\treturn client, nil\n}\n\n// Type returns \"nats\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to NATS JetStream. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.objectStore != nil {\n\t\treturn nil\n\t}\n\n\tif c.nc == nil {\n\t\tif err := c.connect(ctx); err != nil {\n\t\t\treturn fmt.Errorf(\"nats: failed to connect: %w\", err)\n\t\t}\n\t}\n\n\tif err := c.initObjectStore(ctx); err != nil {\n\t\treturn fmt.Errorf(\"nats: failed to initialize object store: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// connect establishes a connection to NATS server with proper configuration.\nfunc (c *ReplicaClient) connect(_ context.Context) error {\n\turl := c.URL\n\tif url == \"\" {\n\t\turl = nats.DefaultURL\n\t}\n\n\tnc, err := nats.Connect(url, c.options()...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to NATS server: %w\", err)\n\t}\n\n\tjs, err := jetstream.New(nc)\n\tif err != nil {\n\t\tnc.Close()\n\t\treturn fmt.Errorf(\"failed to create JetStream context: %w\", err)\n\t}\n\n\tc.nc = nc\n\tc.js = js\n\treturn nil\n}\n\nfunc (c *ReplicaClient) options() []nats.Option {\n\topts := []nats.Option{\n\t\tnats.MaxReconnects(c.MaxReconnects),\n\t\tnats.ReconnectWait(c.ReconnectWait),\n\t\tnats.ReconnectJitter(c.ReconnectJitter, c.ReconnectJitter*2),\n\t\tnats.Timeout(c.Timeout),\n\t\tnats.PingInterval(c.PingInterval),\n\t\tnats.MaxPingsOutstanding(c.MaxPingsOut),\n\t\tnats.ReconnectBufSize(c.ReconnectBufSize),\n\t}\n\n\t// Authentication options\n\tswitch {\n\tcase c.JWT != \"\" \u0026\u0026 c.Seed != \"\":\n\t\topts = append(opts, nats.UserJWTAndSeed(c.JWT, c.Seed))\n\tcase c.Creds != \"\":\n\t\topts = append(opts, nats.UserCredentials(c.Creds))\n\tcase c.NKey != \"\":\n\t\topts = append(opts, nats.Nkey(c.NKey, c.SigCB))\n\tcase c.Username != \"\" \u0026\u0026 c.Password != \"\":\n\t\topts = append(opts, nats.UserInfo(c.Username, c.Password))\n\tcase c.Token != \"\":\n\t\topts = append(opts, nats.Token(c.Token))\n\t}\n\t// JWT callback\n\tif c.UserJWT != nil {\n\t\topts = append(opts, nats.UserJWT(c.UserJWT, c.SigCB))\n\t}\n\n\t// TLS configuration\n\tif c.TLS {\n\t\topts = append(opts, nats.Secure())\n\t}\n\n\tif c.ClientCert != \"\" \u0026\u0026 c.ClientKey != \"\" {\n\t\topts = append(opts, nats.ClientCert(c.ClientCert, c.ClientKey))\n\t}\n\n\tif len(c.RootCAs) \u003e 0 {\n\t\topts = append(opts, nats.RootCAs(c.RootCAs...))\n\t}\n\n\treturn opts\n}\n\n// initObjectStore retrieves the existing object store bucket.\n// The bucket must be pre-created using the NATS CLI or API.\nfunc (c *ReplicaClient) initObjectStore(ctx context.Context) error {\n\tif c.BucketName == \"\" {\n\t\treturn fmt.Errorf(\"bucket name is required\")\n\t}\n\n\t// Get existing object store - do not auto-create\n\tobjectStore, err := c.js.ObjectStore(ctx, c.BucketName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to access object store bucket %q (bucket must be created beforehand): %w\", c.BucketName, err)\n\t}\n\n\tc.objectStore = objectStore\n\treturn nil\n}\n\n// Close closes the NATS connection.\nfunc (c *ReplicaClient) Close() error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.nc != nil {\n\t\tc.nc.Close()\n\t\tc.nc = nil\n\t\tc.js = nil\n\t\tc.objectStore = nil\n\t}\n\treturn nil\n}\n\n// ltxPath returns the object path for an LTX file.\nfunc (c *ReplicaClient) ltxPath(level int, minTXID, maxTXID ltx.TXID) string {\n\treturn litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n}\n\n// parseLTXPath parses an LTX object path and returns level, minTXID, and maxTXID.\nfunc (c *ReplicaClient) parseLTXPath(objPath string) (level int, minTXID, maxTXID ltx.TXID, err error) {\n\t// Remove the base path prefix if present\n\tif c.Path != \"\" \u0026\u0026 strings.HasPrefix(objPath, c.Path+\"/\") {\n\t\tobjPath = strings.TrimPrefix(objPath, c.Path+\"/\")\n\t}\n\n\t// Expected format: \"ltx/\u003clevel\u003e/\u003cminTXID\u003e-\u003cmaxTXID\u003e.ltx\"\n\tparts := strings.Split(objPath, \"/\")\n\tif len(parts) \u003c 3 || parts[0] != \"ltx\" {\n\t\treturn 0, 0, 0, fmt.Errorf(\"invalid ltx path: %s\", objPath)\n\t}\n\n\t// Parse level\n\tif level, err = strconv.Atoi(parts[1]); err != nil {\n\t\treturn 0, 0, 0, fmt.Errorf(\"invalid level in path %s: %w\", objPath, err)\n\t}\n\n\t// Parse filename (minTXID-maxTXID.ltx)\n\tfilename := parts[2]\n\tminTXIDVal, maxTXIDVal, err := ltx.ParseFilename(filename)\n\tif err != nil {\n\t\treturn 0, 0, 0, fmt.Errorf(\"invalid filename in path %s: %w\", objPath, err)\n\t}\n\n\treturn level, minTXIDVal, maxTXIDVal, nil\n}\n\n// LTXFiles returns an iterator of all LTX files on the replica for a given level.\n// NATS always uses accurate timestamps from headers since they're included in LIST operations at zero cost.\n// The useMetadata parameter is ignored.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// List all objects in the store\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"LIST\").Inc()\n\tobjectList, err := c.objectStore.List(ctx)\n\tif err != nil {\n\t\t// NATS returns \"no objects found\" when bucket is empty, treat as empty list\n\t\tif strings.Contains(err.Error(), \"no objects found\") {\n\t\t\tobjectList = nil // Empty list\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"failed to list objects: %w\", err)\n\t\t}\n\t}\n\n\tprefix := litestream.LTXLevelDir(c.Path, level) + \"/\"\n\tfileInfos := make([]*ltx.FileInfo, 0, len(objectList))\n\n\tfor _, objInfo := range objectList {\n\t\t// Filter by level prefix\n\t\tif !strings.HasPrefix(objInfo.Name, prefix) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfileLevel, minTXID, maxTXID, err := c.parseLTXPath(objInfo.Name)\n\t\tif err != nil {\n\t\t\tcontinue // Skip invalid paths\n\t\t}\n\n\t\tif fileLevel != level {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Apply seek filter\n\t\tif minTXID \u003c seek {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Always use accurate timestamp from headers since it's zero-cost\n\t\t// NATS includes headers in LIST operations, so no extra API call needed\n\t\tcreatedAt := objInfo.ModTime\n\t\tif objInfo.Headers != nil {\n\t\t\tif values, ok := objInfo.Headers[HeaderKeyTimestamp]; ok \u0026\u0026 len(values) \u003e 0 {\n\t\t\t\tif parsed, err := time.Parse(time.RFC3339Nano, values[0]); err == nil {\n\t\t\t\t\tcreatedAt = parsed\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfileInfos = append(fileInfos, \u0026ltx.FileInfo{\n\t\t\tLevel: fileLevel,\n\t\t\tMinTXID: minTXID,\n\t\t\tMaxTXID: maxTXID,\n\t\t\tSize: int64(objInfo.Size),\n\t\t\tCreatedAt: createdAt,\n\t\t})\n\t}\n\n\t// Sort by minTXID\n\tsort.Slice(fileInfos, func(i, j int) bool {\n\t\treturn fileInfos[i].MinTXID \u003c fileInfos[j].MinTXID\n\t})\n\n\treturn \u0026ltxFileIterator{files: fileInfos, index: -1}, nil\n}\n\n// OpenLTXFile returns a reader that contains an LTX file at a given TXID range.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tobjectPath := c.ltxPath(level, minTXID, maxTXID)\n\n\tobjectResult, err := c.objectStore.Get(ctx, objectPath)\n\tif err != nil {\n\t\tif isNotFoundError(err) {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to get object %s: %w\", objectPath, err)\n\t}\n\n\t// Record metrics\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\t// Note: We can't get the size from NATS object reader directly, so we skip bytes counter\n\n\t// If offset is non-zero then discard the beginning bytes.\n\tif offset \u003e 0 {\n\t\tif _, err := io.CopyN(io.Discard, objectResult, offset); err != nil {\n\t\t\tobjectResult.Close()\n\t\t\treturn nil, fmt.Errorf(\"failed to discard offset bytes: %w\", err)\n\t\t}\n\t}\n\n\t// If size is non-zero then limit the reader to the size.\n\tif size \u003e 0 {\n\t\treturn internal.LimitReadCloser(objectResult, size), nil\n\t}\n\n\treturn objectResult, nil\n}\n\n// WriteLTXFile writes an LTX file to the replica.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tobjectPath := c.ltxPath(level, minTXID, maxTXID)\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(r, \u0026buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\trc := internal.NewReadCounter(io.MultiReader(\u0026buf, r))\n\n\t// Store timestamp in NATS object headers for accurate timestamp retrieval\n\tobjectInfo, err := c.objectStore.Put(ctx, jetstream.ObjectMeta{\n\t\tName: objectPath,\n\t\tHeaders: map[string][]string{\n\t\t\tHeaderKeyTimestamp: {timestamp.Format(time.RFC3339Nano)},\n\t\t},\n\t}, rc)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to put object %s: %w\", objectPath, err)\n\t}\n\n\t// Record metrics\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(objectInfo.Size))\n\n\treturn \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: int64(objectInfo.Size),\n\t\tCreatedAt: timestamp,\n\t}, nil\n}\n\n// DeleteLTXFiles deletes one or more LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, fileInfo := range a {\n\t\tobjectPath := c.ltxPath(fileInfo.Level, fileInfo.MinTXID, fileInfo.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", fileInfo.Level, \"minTXID\", fileInfo.MinTXID, \"maxTXID\", fileInfo.MaxTXID, \"path\", objectPath)\n\n\t\tif err := c.objectStore.Delete(ctx, objectPath); err != nil {\n\t\t\tif !isNotFoundError(err) {\n\t\t\t\treturn fmt.Errorf(\"failed to delete object %s: %w\", objectPath, err)\n\t\t\t}\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n\n// DeleteAll deletes all files in the object store.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\t// List all objects in the bucket\n\tobjectList, err := c.objectStore.List(ctx)\n\tif err != nil {\n\t\t// NATS returns \"no objects found\" when bucket is empty, treat as empty list\n\t\tif strings.Contains(err.Error(), \"no objects found\") {\n\t\t\tobjectList = nil // Empty list, nothing to delete\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"failed to list all objects: %w\", err)\n\t\t}\n\t}\n\n\tfor _, objInfo := range objectList {\n\t\tif err := c.objectStore.Delete(ctx, objInfo.Name); err != nil {\n\t\t\tif !isNotFoundError(err) {\n\t\t\t\treturn fmt.Errorf(\"failed to delete object %s: %w\", objInfo.Name, err)\n\t\t\t}\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n\n// isNotFoundError checks if the error is a \"not found\" error.\nfunc isNotFoundError(err error) bool {\n\treturn err != nil \u0026\u0026 (errors.Is(err, jetstream.ErrObjectNotFound) || strings.Contains(err.Error(), \"not found\"))\n}\n\n// ltxFileIterator implements ltx.FileIterator for NATS object store.\ntype ltxFileIterator struct {\n\tfiles []*ltx.FileInfo\n\tindex int\n\terr error\n}\n\n// Next advances the iterator to the next file.\nfunc (itr *ltxFileIterator) Next() bool {\n\titr.index++\n\treturn itr.index \u003c len(itr.files)\n}\n\n// Item returns the current file info.\nfunc (itr *ltxFileIterator) Item() *ltx.FileInfo {\n\tif itr.index \u003c 0 || itr.index \u003e= len(itr.files) {\n\t\treturn nil\n\t}\n\treturn itr.files[itr.index]\n}\n\n// Err returns any error that occurred during iteration.\nfunc (itr *ltxFileIterator) Err() error {\n\treturn itr.err\n}\n\n// Close closes the iterator and returns any error that occurred during iteration.\nfunc (itr *ltxFileIterator) Close() error {\n\treturn itr.err\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[43,52]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[53,59]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[60,65]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[66,77]}},{"name":"url","path":"net/url","span":{"start":[10,1],"end":[10,11],"bytes":[78,88]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[89,94]}},{"name":"sort","path":"sort","span":{"start":[12,1],"end":[12,8],"bytes":[95,102]}},{"name":"strconv","path":"strconv","span":{"start":[13,1],"end":[13,11],"bytes":[103,113]}},{"name":"strings","path":"strings","span":{"start":[14,1],"end":[14,11],"bytes":[114,124]}},{"name":"sync","path":"sync","span":{"start":[15,1],"end":[15,8],"bytes":[125,132]}},{"name":"time","path":"time","span":{"start":[16,1],"end":[16,8],"bytes":[133,140]}},{"name":"nats.go","path":"github.com/nats-io/nats.go","span":{"start":[18,1],"end":[18,30],"bytes":[142,171]}},{"name":"jetstream","path":"github.com/nats-io/nats.go/jetstream","span":{"start":[19,1],"end":[19,40],"bytes":[172,211]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[20,1],"end":[20,27],"bytes":[212,238]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[22,1],"end":[22,37],"bytes":[240,276]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[23,1],"end":[23,46],"bytes":[277,322]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient","kind":"struct","span":{"start":[39,1],"end":[77,2],"bytes":[795,2563]},"callables":{"github.com/benbjohnson/litestream/nats.ReplicaClient.Close":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Close","kind":"method","span":{"start":[236,1],"end":[247,2],"bytes":[6815,6987]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"237:2":{"kind":"call","span":{"start":[237,2],"end":[237,13],"bytes":[6856,6867]},"callee":null},"238:8":{"kind":"call","span":{"start":[238,8],"end":[238,21],"bytes":[6875,6888]},"callee":null},"241:3":{"kind":"call","span":{"start":[241,3],"end":[241,15],"bytes":[6910,6922]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteAll":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteAll","kind":"method","span":{"start":[458,1],"end":[484,2],"bytes":[13661,14464]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[458,1],"end":[458,63],"bytes":[13661,13723]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"459:12":{"kind":"call","span":{"start":[459,12],"end":[459,23],"bytes":[13735,13746]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init"},"464:21":{"kind":"call","span":{"start":[464,21],"end":[464,44],"bytes":[13833,13856]},"callee":null},"467:23":{"kind":"call","span":{"start":[467,23],"end":[467,34],"bytes":[13975,13986]},"callee":null},"467:6":{"kind":"call","span":{"start":[467,6],"end":[467,55],"bytes":[13958,14007]},"callee":null},"470:11":{"kind":"call","span":{"start":[470,11],"end":[470,60],"bytes":[14084,14133]},"callee":null},"475:13":{"kind":"call","span":{"start":[475,13],"end":[475,52],"bytes":[14192,14231]},"callee":null},"476:8":{"kind":"call","span":{"start":[476,8],"end":[476,28],"bytes":[14253,14273]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError"},"477:12":{"kind":"call","span":{"start":[477,12],"end":[477,75],"bytes":[14287,14350]},"callee":null},"480:3":{"kind":"call","span":{"start":[480,3],"end":[480,81],"bytes":[14362,14440]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[436,1],"end":[455,2],"bytes":[12935,13607]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[436,1],"end":[436,87],"bytes":[12935,13021]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[436,1],"end":[436,87],"bytes":[12935,13021]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"437:12":{"kind":"call","span":{"start":[437,12],"end":[437,23],"bytes":[13033,13044]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init"},"442:17":{"kind":"call","span":{"start":[442,17],"end":[442,78],"bytes":[13122,13183]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath"},"444:3":{"kind":"call","span":{"start":[444,3],"end":[444,141],"bytes":[13187,13325]},"callee":null},"446:13":{"kind":"call","span":{"start":[446,13],"end":[446,50],"bytes":[13339,13376]},"callee":null},"447:8":{"kind":"call","span":{"start":[447,8],"end":[447,28],"bytes":[13398,13418]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError"},"448:12":{"kind":"call","span":{"start":[448,12],"end":[448,73],"bytes":[13432,13493]},"callee":null},"451:3":{"kind":"call","span":{"start":[451,3],"end":[451,81],"bytes":[13505,13583]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.Init":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","kind":"method","span":{"start":[129,1],"end":[148,2],"bytes":[4115,4513]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[129,1],"end":[129,58],"bytes":[4115,4172]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"130:2":{"kind":"call","span":{"start":[130,2],"end":[130,13],"bytes":[4174,4185]},"callee":null},"131:8":{"kind":"call","span":{"start":[131,8],"end":[131,21],"bytes":[4193,4206]},"callee":null},"138:13":{"kind":"call","span":{"start":[138,13],"end":[138,27],"bytes":[4282,4296]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.connect"},"139:11":{"kind":"call","span":{"start":[139,11],"end":[139,57],"bytes":[4321,4367]},"callee":null},"143:12":{"kind":"call","span":{"start":[143,12],"end":[143,34],"bytes":[4387,4409]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.initObjectStore"},"144:10":{"kind":"call","span":{"start":[144,10],"end":[144,72],"bytes":[4433,4495]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles","kind":"method","span":{"start":[285,1],"end":[351,2],"bytes":[8390,10248]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[285,1],"end":[285,126],"bytes":[8390,8515]}},{"name":"level","type":"int","span":{"start":[285,1],"end":[285,126],"bytes":[8390,8515]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[285,1],"end":[285,126],"bytes":[8390,8515]}},{"name":"useMetadata","type":"bool","span":{"start":[285,1],"end":[285,126],"bytes":[8390,8515]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"286:12":{"kind":"call","span":{"start":[286,12],"end":[286,23],"bytes":[8527,8538]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init"},"291:2":{"kind":"call","span":{"start":[291,2],"end":[291,78],"bytes":[8610,8686]},"callee":null},"292:21":{"kind":"call","span":{"start":[292,21],"end":[292,44],"bytes":[8713,8736]},"callee":null},"295:23":{"kind":"call","span":{"start":[295,23],"end":[295,34],"bytes":[8855,8866]},"callee":null},"295:6":{"kind":"call","span":{"start":[295,6],"end":[295,55],"bytes":[8838,8887]},"callee":null},"298:16":{"kind":"call","span":{"start":[298,16],"end":[298,61],"bytes":[8950,8995]},"callee":null},"302:12":{"kind":"call","span":{"start":[302,12],"end":[302,49],"bytes":[9015,9052]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"303:15":{"kind":"call","span":{"start":[303,15],"end":[303,56],"bytes":[9073,9114]},"callee":null},"303:40":{"kind":"call","span":{"start":[303,40],"end":[303,55],"bytes":[9098,9113]},"callee":null},"307:7":{"kind":"call","span":{"start":[307,7],"end":[307,46],"bytes":[9188,9227]},"callee":null},"311:39":{"kind":"call","span":{"start":[311,39],"end":[311,67],"bytes":[9285,9313]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.parseLTXPath"},"329:64":{"kind":"call","span":{"start":[329,64],"end":[329,75],"bytes":[9744,9755]},"callee":null},"330:23":{"kind":"call","span":{"start":[330,23],"end":[330,62],"bytes":[9784,9823]},"callee":null},"336:15":{"kind":"call","span":{"start":[336,15],"end":[342,5],"bytes":[9892,10060]},"callee":null},"340:15":{"kind":"call","span":{"start":[340,15],"end":[340,34],"bytes":[10010,10029]},"callee":null},"346:2":{"kind":"call","span":{"start":[346,2],"end":[348,4],"bytes":[10086,10186]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles/github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles.closure_1","kind":"function","span":{"start":[346,1],"end":[348,4],"bytes":[10085,10186]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles.closure_1","parameters":[{"name":"i","type":"int","span":{"start":[346,1],"end":[346,45],"bytes":[10085,10129]}},{"name":"j","type":"int","span":{"start":[346,1],"end":[346,45],"bytes":[10085,10129]}}],"return_type":"bool","body":{}}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[354,1],"end":[387,2],"bytes":[10331,11402]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[354,1],"end":[354,140],"bytes":[10331,10470]}},{"name":"level","type":"int","span":{"start":[354,1],"end":[354,140],"bytes":[10331,10470]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[354,1],"end":[354,140],"bytes":[10331,10470]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[354,1],"end":[354,140],"bytes":[10331,10470]}},{"name":"offset","type":"int64","span":{"start":[354,1],"end":[354,140],"bytes":[10331,10470]}},{"name":"size","type":"int64","span":{"start":[354,1],"end":[354,140],"bytes":[10331,10470]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"355:12":{"kind":"call","span":{"start":[355,12],"end":[355,23],"bytes":[10482,10493]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init"},"359:16":{"kind":"call","span":{"start":[359,16],"end":[359,50],"bytes":[10545,10579]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath"},"361:23":{"kind":"call","span":{"start":[361,23],"end":[361,57],"bytes":[10603,10637]},"callee":null},"363:6":{"kind":"call","span":{"start":[363,6],"end":[363,26],"bytes":[10660,10680]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError"},"366:15":{"kind":"call","span":{"start":[366,15],"end":[366,73],"bytes":[10731,10789]},"callee":null},"370:2":{"kind":"call","span":{"start":[370,2],"end":[370,77],"bytes":[10814,10889]},"callee":null},"375:16":{"kind":"call","span":{"start":[375,16],"end":[375,58],"bytes":[11080,11122]},"callee":null},"376:4":{"kind":"call","span":{"start":[376,4],"end":[376,24],"bytes":[11140,11160]},"callee":null},"377:16":{"kind":"call","span":{"start":[377,16],"end":[377,69],"bytes":[11176,11229]},"callee":null},"383:10":{"kind":"call","span":{"start":[383,10],"end":[383,54],"bytes":[11321,11365]},"callee":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser"}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.SetLogger":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.SetLogger","kind":"method","span":{"start":[92,1],"end":[94,2],"bytes":[2978,3084]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[92,1],"end":[92,57],"bytes":[2978,3034]}}],"metrics":{"cyclomatic":1},"body":{"93:13":{"kind":"call","span":{"start":[93,13],"end":[93,48],"bytes":[3047,3082]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.Type":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Type","kind":"method","span":{"start":[124,1],"end":[126,2],"bytes":[3962,4029]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/nats.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[390,1],"end":[433,2],"bytes":[11455,12884]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[390,1],"end":[390,134],"bytes":[11455,11588]}},{"name":"level","type":"int","span":{"start":[390,1],"end":[390,134],"bytes":[11455,11588]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[390,1],"end":[390,134],"bytes":[11455,11588]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[390,1],"end":[390,134],"bytes":[11455,11588]}},{"name":"r","type":"io.Reader","span":{"start":[390,1],"end":[390,134],"bytes":[11455,11588]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"391:12":{"kind":"call","span":{"start":[391,12],"end":[391,23],"bytes":[11600,11611]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init"},"395:16":{"kind":"call","span":{"start":[395,16],"end":[395,50],"bytes":[11663,11697]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath"},"399:15":{"kind":"call","span":{"start":[399,15],"end":[399,36],"bytes":[11808,11829]},"callee":null},"402:17":{"kind":"call","span":{"start":[402,17],"end":[402,42],"bytes":[11885,11910]},"callee":null},"404:15":{"kind":"call","span":{"start":[404,15],"end":[404,71],"bytes":[11942,11998]},"callee":null},"406:15":{"kind":"call","span":{"start":[406,15],"end":[406,44],"bytes":[12016,12045]},"callee":null},"409:32":{"kind":"call","span":{"start":[409,32],"end":[409,55],"bytes":[12130,12153]},"callee":null},"409:8":{"kind":"call","span":{"start":[409,8],"end":[409,56],"bytes":[12106,12154]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter"},"412:21":{"kind":"call","span":{"start":[412,21],"end":[417,8],"bytes":[12252,12422]},"callee":null},"415:25":{"kind":"call","span":{"start":[415,25],"end":[415,59],"bytes":[12373,12407]},"callee":null},"419:15":{"kind":"call","span":{"start":[419,15],"end":[419,73],"bytes":[12454,12512]},"callee":null},"423:2":{"kind":"call","span":{"start":[423,2],"end":[423,77],"bytes":[12537,12612]},"callee":null},"424:2":{"kind":"call","span":{"start":[424,2],"end":[424,77],"bytes":[12620,12695]},"callee":null},"424:82":{"kind":"call","span":{"start":[424,82],"end":[424,106],"bytes":[12700,12724]},"callee":null},"430:14":{"kind":"call","span":{"start":[430,14],"end":[430,36],"bytes":[12827,12849]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.connect":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.connect","kind":"method","span":{"start":[151,1],"end":[171,2],"bytes":[4593,5008]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.connect","parameters":[{"name":"_","type":"context.Context","span":{"start":[151,1],"end":[151,59],"bytes":[4593,4651]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"157:13":{"kind":"call","span":{"start":[157,13],"end":[157,46],"bytes":[4722,4755]},"callee":null},"157:31":{"kind":"call","span":{"start":[157,31],"end":[157,42],"bytes":[4740,4751]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.options"},"159:10":{"kind":"call","span":{"start":[159,10],"end":[159,65],"bytes":[4782,4837]},"callee":null},"162:13":{"kind":"call","span":{"start":[162,13],"end":[162,30],"bytes":[4854,4871]},"callee":null},"164:3":{"kind":"call","span":{"start":[164,3],"end":[164,13],"bytes":[4891,4901]},"callee":null},"165:10":{"kind":"call","span":{"start":[165,10],"end":[165,67],"bytes":[4911,4968]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.initObjectStore":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.initObjectStore","kind":"method","span":{"start":[220,1],"end":[233,2],"bytes":[6338,6776]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.initObjectStore","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[220,1],"end":[220,69],"bytes":[6338,6406]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"222:10":{"kind":"call","span":{"start":[222,10],"end":[222,47],"bytes":[6441,6478]},"callee":null},"226:22":{"kind":"call","span":{"start":[226,22],"end":[226,57],"bytes":[6555,6590]},"callee":null},"228:10":{"kind":"call","span":{"start":[228,10],"end":[228,122],"bytes":[6617,6729]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath","kind":"method","span":{"start":[250,1],"end":[252,2],"bytes":[7041,7185]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath","parameters":[{"name":"level","type":"int","span":{"start":[250,1],"end":[250,79],"bytes":[7041,7119]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[250,1],"end":[250,79],"bytes":[7041,7119]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[250,1],"end":[250,79],"bytes":[7041,7119]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"251:9":{"kind":"call","span":{"start":[251,9],"end":[251,64],"bytes":[7128,7183]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.options":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.options","kind":"method","span":{"start":[173,1],"end":[216,2],"bytes":[5010,6212]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.options","parameters":[],"return_type":"[]github.com/nats-io/nats.go.Option","metrics":{"cyclomatic":11},"body":{"175:3":{"kind":"call","span":{"start":[175,3],"end":[175,38],"bytes":[5086,5121]},"callee":null},"176:3":{"kind":"call","span":{"start":[176,3],"end":[176,38],"bytes":[5125,5160]},"callee":null},"177:3":{"kind":"call","span":{"start":[177,3],"end":[177,63],"bytes":[5164,5224]},"callee":null},"178:3":{"kind":"call","span":{"start":[178,3],"end":[178,26],"bytes":[5228,5251]},"callee":null},"179:3":{"kind":"call","span":{"start":[179,3],"end":[179,36],"bytes":[5255,5288]},"callee":null},"180:3":{"kind":"call","span":{"start":[180,3],"end":[180,42],"bytes":[5292,5331]},"callee":null},"181:3":{"kind":"call","span":{"start":[181,3],"end":[181,44],"bytes":[5335,5376]},"callee":null},"187:10":{"kind":"call","span":{"start":[187,10],"end":[187,58],"bytes":[5463,5511]},"callee":null},"187:23":{"kind":"call","span":{"start":[187,23],"end":[187,57],"bytes":[5476,5510]},"callee":null},"189:10":{"kind":"call","span":{"start":[189,10],"end":[189,53],"bytes":[5542,5585]},"callee":null},"189:23":{"kind":"call","span":{"start":[189,23],"end":[189,52],"bytes":[5555,5584]},"callee":null},"191:10":{"kind":"call","span":{"start":[191,10],"end":[191,50],"bytes":[5615,5655]},"callee":null},"191:23":{"kind":"call","span":{"start":[191,23],"end":[191,49],"bytes":[5628,5654]},"callee":null},"193:10":{"kind":"call","span":{"start":[193,10],"end":[193,61],"bytes":[5709,5760]},"callee":null},"193:23":{"kind":"call","span":{"start":[193,23],"end":[193,60],"bytes":[5722,5759]},"callee":null},"195:10":{"kind":"call","span":{"start":[195,10],"end":[195,43],"bytes":[5791,5824]},"callee":null},"195:23":{"kind":"call","span":{"start":[195,23],"end":[195,42],"bytes":[5804,5823]},"callee":null},"199:10":{"kind":"call","span":{"start":[199,10],"end":[199,56],"bytes":[5877,5923]},"callee":null},"199:23":{"kind":"call","span":{"start":[199,23],"end":[199,55],"bytes":[5890,5922]},"callee":null},"204:10":{"kind":"call","span":{"start":[204,10],"end":[204,37],"bytes":[5971,5998]},"callee":null},"204:23":{"kind":"call","span":{"start":[204,23],"end":[204,36],"bytes":[5984,5997]},"callee":null},"208:10":{"kind":"call","span":{"start":[208,10],"end":[208,66],"bytes":[6058,6114]},"callee":null},"208:23":{"kind":"call","span":{"start":[208,23],"end":[208,65],"bytes":[6071,6113]},"callee":null},"211:5":{"kind":"call","span":{"start":[211,5],"end":[211,19],"bytes":[6123,6137]},"callee":null},"212:10":{"kind":"call","span":{"start":[212,10],"end":[212,50],"bytes":[6153,6193]},"callee":null},"212:23":{"kind":"call","span":{"start":[212,23],"end":[212,49],"bytes":[6166,6192]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ReplicaClient.parseLTXPath":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.parseLTXPath","kind":"method","span":{"start":[255,1],"end":[280,2],"bytes":[7270,8155]},"signature":"github.com/benbjohnson/litestream/nats.ReplicaClient.parseLTXPath","parameters":[{"name":"objPath","type":"string","span":{"start":[255,1],"end":[255,105],"bytes":[7270,7374]}}],"return_type":"(int, github.com/superfly/ltx.TXID, github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"257:21":{"kind":"call","span":{"start":[257,21],"end":[257,59],"bytes":[7438,7476]},"callee":null},"258:13":{"kind":"call","span":{"start":[258,13],"end":[258,52],"bytes":[7491,7530]},"callee":null},"262:11":{"kind":"call","span":{"start":[262,11],"end":[262,38],"bytes":[7604,7631]},"callee":null},"263:5":{"kind":"call","span":{"start":[263,5],"end":[263,15],"bytes":[7636,7646]},"callee":null},"264:19":{"kind":"call","span":{"start":[264,19],"end":[264,62],"bytes":[7692,7735]},"callee":null},"268:18":{"kind":"call","span":{"start":[268,18],"end":[268,40],"bytes":[7773,7795]},"callee":null},"269:19":{"kind":"call","span":{"start":[269,19],"end":[269,75],"bytes":[7828,7884]},"callee":null},"274:33":{"kind":"call","span":{"start":[274,33],"end":[274,60],"bytes":[7984,8011]},"callee":null},"276:19":{"kind":"call","span":{"start":[276,19],"end":[276,78],"bytes":[8047,8106]},"callee":null}}}},"fields":{"BucketName":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/BucketName","kind":"field","type":"string","span":{"start":[50,1],"end":[50,49],"bytes":[1057,1105]}},"ClientCert":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/ClientCert","kind":"field","type":"string","span":{"start":[61,1],"end":[61,53],"bytes":[1604,1656]}},"ClientKey":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/ClientKey","kind":"field","type":"string","span":{"start":[62,1],"end":[62,45],"bytes":[1657,1701]}},"Creds":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Creds","kind":"field","type":"string","span":{"start":[54,1],"end":[54,46],"bytes":[1277,1322]}},"JWT":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/JWT","kind":"field","type":"string","span":{"start":[52,1],"end":[52,53],"bytes":[1172,1224]}},"MaxPingsOut":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/MaxPingsOut","kind":"field","type":"int","span":{"start":[73,1],"end":[73,91],"bytes":[2264,2354]}},"MaxReconnects":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/MaxReconnects","kind":"field","type":"int","span":{"start":[68,1],"end":[68,99],"bytes":[1861,1959]}},"NKey":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/NKey","kind":"field","type":"string","span":{"start":[55,1],"end":[55,48],"bytes":[1323,1370]}},"Password":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Password","kind":"field","type":"string","span":{"start":[57,1],"end":[57,52],"bytes":[1423,1474]}},"Path":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[51,1],"end":[51,66],"bytes":[1106,1171]}},"PingInterval":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/PingInterval","kind":"field","type":"time.Duration","span":{"start":[72,1],"end":[72,64],"bytes":[2200,2263]}},"ReconnectBufSize":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/ReconnectBufSize","kind":"field","type":"int","span":{"start":[74,1],"end":[74,75],"bytes":[2355,2429]}},"ReconnectJitter":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/ReconnectJitter","kind":"field","type":"time.Duration","span":{"start":[70,1],"end":[70,81],"bytes":[2050,2130]}},"ReconnectWait":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/ReconnectWait","kind":"field","type":"time.Duration","span":{"start":[69,1],"end":[69,90],"bytes":[1960,2049]}},"RootCAs":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/RootCAs","kind":"field","type":"[]string","span":{"start":[60,1],"end":[60,45],"bytes":[1559,1603]}},"Seed":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Seed","kind":"field","type":"string","span":{"start":[53,1],"end":[53,52],"bytes":[1225,1276]}},"SigCB":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/SigCB","kind":"field","type":"func([]byte) ([]byte, error)","span":{"start":[76,1],"end":[76,69],"bytes":[2493,2561]}},"TLS":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/TLS","kind":"field","type":"bool","span":{"start":[59,1],"end":[59,35],"bytes":[1524,1558]}},"Timeout":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Timeout","kind":"field","type":"time.Duration","span":{"start":[71,1],"end":[71,69],"bytes":[2131,2199]}},"Token":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Token","kind":"field","type":"string","span":{"start":[58,1],"end":[58,49],"bytes":[1475,1523]}},"URL":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/URL","kind":"field","type":"string","span":{"start":[49,1],"end":[49,40],"bytes":[1017,1056]}},"UserJWT":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/UserJWT","kind":"field","type":"func() (string, error)","span":{"start":[75,1],"end":[75,63],"bytes":[2430,2492]}},"Username":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/Username","kind":"field","type":"string","span":{"start":[56,1],"end":[56,52],"bytes":[1371,1422]}},"js":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/js","kind":"field","type":"github.com/nats-io/nats.go/jetstream.JetStream","span":{"start":[45,1],"end":[45,33],"bytes":[930,962]}},"logger":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[41,1],"end":[41,21],"bytes":[842,862]}},"mu":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[40,1],"end":[40,19],"bytes":[823,841]}},"nc":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/nc","kind":"field","type":"*github.com/nats-io/nats.go.Conn","span":{"start":[44,1],"end":[44,24],"bytes":[906,929]}},"objectStore":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/objectStore","kind":"field","type":"github.com/nats-io/nats.go/jetstream.ObjectStore","span":{"start":[46,1],"end":[46,35],"bytes":[963,997]}}}},"ltxFileIterator":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator","kind":"struct","span":{"start":[492,1],"end":[496,2],"bytes":[14755,14833]},"callables":{"github.com/benbjohnson/litestream/nats.ltxFileIterator.Close":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/github.com/benbjohnson/litestream/nats.ltxFileIterator.Close","kind":"method","span":{"start":[518,1],"end":[520,2],"bytes":[15366,15427]},"signature":"github.com/benbjohnson/litestream/nats.ltxFileIterator.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/nats.ltxFileIterator.Err":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/github.com/benbjohnson/litestream/nats.ltxFileIterator.Err","kind":"method","span":{"start":[513,1],"end":[515,2],"bytes":[15222,15281]},"signature":"github.com/benbjohnson/litestream/nats.ltxFileIterator.Err","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/nats.ltxFileIterator.Item":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/github.com/benbjohnson/litestream/nats.ltxFileIterator.Item","kind":"method","span":{"start":[505,1],"end":[510,2],"bytes":[15015,15163]},"signature":"github.com/benbjohnson/litestream/nats.ltxFileIterator.Item","parameters":[],"return_type":"*github.com/superfly/ltx.FileInfo","metrics":{"cyclomatic":2},"body":{"506:35":{"kind":"call","span":{"start":[506,35],"end":[506,49],"bytes":[15100,15114]},"callee":null}}},"github.com/benbjohnson/litestream/nats.ltxFileIterator.Next":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/github.com/benbjohnson/litestream/nats.ltxFileIterator.Next","kind":"method","span":{"start":[499,1],"end":[502,2],"bytes":[14883,14974]},"signature":"github.com/benbjohnson/litestream/nats.ltxFileIterator.Next","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"501:21":{"kind":"call","span":{"start":[501,21],"end":[501,35],"bytes":[14958,14972]},"callee":null}}}},"fields":{"err":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/err","kind":"field","type":"error","span":{"start":[495,1],"end":[495,13],"bytes":[14819,14831]}},"files":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/files","kind":"field","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[493,1],"end":[493,23],"bytes":[14785,14807]}},"index":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ltxFileIterator/index","kind":"field","type":"int","span":{"start":[494,1],"end":[494,11],"bytes":[14808,14818]}}}}},"functions":{"github.com/benbjohnson/litestream/nats.NewReplicaClient":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient","kind":"function","span":{"start":[80,1],"end":[90,2],"bytes":[2626,2976]},"signature":"github.com/benbjohnson/litestream/nats.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/nats.ReplicaClient","metrics":{"cyclomatic":1},"body":{"82:21":{"kind":"call","span":{"start":[82,21],"end":[82,35],"bytes":[2711,2725]},"callee":null}}},"github.com/benbjohnson/litestream/nats.NewReplicaClientFromURL":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClientFromURL","kind":"function","span":{"start":[99,1],"end":[121,2],"bytes":[3275,3917]},"signature":"github.com/benbjohnson/litestream/nats.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[99,1],"end":[99,137],"bytes":[3275,3411]}},{"name":"host","type":"string","span":{"start":[99,1],"end":[99,137],"bytes":[3275,3411]}},{"name":"urlPath","type":"string","span":{"start":[99,1],"end":[99,137],"bytes":[3275,3411]}},{"name":"query","type":"net/url.Values","span":{"start":[99,1],"end":[99,137],"bytes":[3275,3411]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[99,1],"end":[99,137],"bytes":[3275,3411]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"100:12":{"kind":"call","span":{"start":[100,12],"end":[100,30],"bytes":[3423,3441]},"callee":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient"},"104:16":{"kind":"call","span":{"start":[104,16],"end":[104,46],"bytes":[3515,3545]},"callee":null},"109:21":{"kind":"call","span":{"start":[109,21],"end":[109,40],"bytes":[3641,3660]},"callee":null},"110:24":{"kind":"call","span":{"start":[110,24],"end":[110,43],"bytes":[3684,3703]},"callee":null},"114:12":{"kind":"call","span":{"start":[114,12],"end":[114,38],"bytes":[3753,3779]},"callee":null},"116:15":{"kind":"call","span":{"start":[116,15],"end":[116,65],"bytes":[3813,3863]},"callee":null}}},"github.com/benbjohnson/litestream/nats.init":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.init","kind":"function","span":{"start":[26,1],"end":[28,2],"bytes":[326,415]},"signature":"github.com/benbjohnson/litestream/nats.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"27:2":{"kind":"call","span":{"start":[27,2],"end":[27,74],"bytes":[341,413]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}},"github.com/benbjohnson/litestream/nats.isNotFoundError":{"id":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError","kind":"function","span":{"start":[487,1],"end":[489,2],"bytes":[14529,14683]},"signature":"github.com/benbjohnson/litestream/nats.isNotFoundError","parameters":[{"name":"err","type":"error","span":{"start":[487,1],"end":[487,39],"bytes":[14529,14567]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"488:24":{"kind":"call","span":{"start":[488,24],"end":[488,67],"bytes":[14591,14634]},"callee":null},"488:71":{"kind":"call","span":{"start":[488,71],"end":[488,113],"bytes":[14638,14680]},"callee":null},"488:88":{"kind":"call","span":{"start":[488,88],"end":[488,99],"bytes":[14655,14666]},"callee":null}}}},"content_hash":"f98e6196bb9a1f560d754c595e7899192c063306a4efe6165f2801aa222ba8ec"},"oss/replica_client.go":{"id":"can://go/litestream/oss/replica_client.go","kind":"module","span":{"start":[1,1],"end":[727,1],"bytes":[0,19595]},"package":"oss","source":"package oss\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss\"\n\t\"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials\"\n\t\"github.com/superfly/ltx\"\n\t\"golang.org/x/sync/errgroup\"\n\t\"golang.org/x/sync/semaphore\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"oss\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"oss\"\n\n// MetadataKeyTimestamp is the metadata key for storing LTX file timestamps in OSS.\n// Note: OSS SDK automatically adds \"x-oss-meta-\" prefix when setting metadata.\nconst MetadataKeyTimestamp = \"litestream-timestamp\"\n\n// MaxKeys is the number of keys OSS can operate on per batch.\nconst MaxKeys = 1000\n\n// DefaultRegion is the region used if one is not specified.\nconst DefaultRegion = \"cn-hangzhou\"\n\n// DefaultMetadataConcurrency is the default number of concurrent HeadObject calls\n// for fetching accurate timestamps during timestamp-based restore.\nconst DefaultMetadataConcurrency = 50\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files to Alibaba Cloud OSS.\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\tclient *oss.Client\n\tuploader *oss.Uploader\n\tlogger *slog.Logger\n\n\t// Alibaba Cloud authentication keys.\n\tAccessKeyID string\n\tAccessKeySecret string\n\n\t// OSS bucket information\n\tRegion string\n\tBucket string\n\tPath string\n\tEndpoint string\n\n\t// Upload configuration\n\tPartSize int64 // Part size for multipart uploads (default: 5MB)\n\tConcurrency int // Number of concurrent parts to upload (default: 3)\n\n\t// MetadataConcurrency controls parallel HeadObject calls for timestamp-based restore.\n\t// Higher values improve restore speed for large backup histories.\n\t// Default: 50\n\tMetadataConcurrency int\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\n// URL format: oss://bucket[.oss-region.aliyuncs.com]/path\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tclient := NewReplicaClient()\n\n\tbucket, region, _ := ParseHost(host)\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for oss replica URL\")\n\t}\n\n\tclient.Bucket = bucket\n\tclient.Region = region\n\tclient.Path = urlPath\n\n\treturn client, nil\n}\n\n// Type returns \"oss\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to OSS. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) (err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.client != nil {\n\t\treturn nil\n\t}\n\n\t// Validate required configuration\n\tif c.Bucket == \"\" {\n\t\treturn fmt.Errorf(\"oss: bucket name is required\")\n\t}\n\n\t// Use default region if not specified\n\tregion := c.Region\n\tif region == \"\" {\n\t\tregion = DefaultRegion\n\t}\n\n\t// Build configuration\n\tcfg := oss.LoadDefaultConfig()\n\n\t// Configure credentials\n\tif c.AccessKeyID != \"\" \u0026\u0026 c.AccessKeySecret != \"\" {\n\t\tcfg = cfg.WithCredentialsProvider(\n\t\t\tcredentials.NewStaticCredentialsProvider(c.AccessKeyID, c.AccessKeySecret),\n\t\t)\n\t} else {\n\t\t// Use environment variable credentials provider\n\t\tcfg = cfg.WithCredentialsProvider(\n\t\t\tcredentials.NewEnvironmentVariableCredentialsProvider(),\n\t\t)\n\t}\n\n\t// Configure region\n\tcfg = cfg.WithRegion(region)\n\n\t// Configure custom endpoint if specified\n\tif c.Endpoint != \"\" {\n\t\tendpoint := c.Endpoint\n\t\t// Add scheme if not present\n\t\tif !strings.HasPrefix(endpoint, \"http://\") \u0026\u0026 !strings.HasPrefix(endpoint, \"https://\") {\n\t\t\tendpoint = \"https://\" + endpoint\n\t\t}\n\t\tcfg = cfg.WithEndpoint(endpoint)\n\t}\n\n\t// Create OSS client\n\tc.client = oss.NewClient(cfg)\n\n\t// Create uploader with configurable part size and concurrency\n\tuploaderOpts := []func(*oss.UploaderOptions){}\n\tif c.PartSize \u003e 0 {\n\t\tuploaderOpts = append(uploaderOpts, func(o *oss.UploaderOptions) {\n\t\t\to.PartSize = c.PartSize\n\t\t})\n\t}\n\tif c.Concurrency \u003e 0 {\n\t\tuploaderOpts = append(uploaderOpts, func(o *oss.UploaderOptions) {\n\t\t\to.ParallelNum = c.Concurrency\n\t\t})\n\t}\n\tc.uploader = c.client.NewUploader(uploaderOpts...)\n\n\treturn nil\n}\n\n// LTXFiles returns an iterator over all LTX files on the replica for the given level.\n// When useMetadata is true, fetches accurate timestamps from OSS metadata via HeadObject.\n// This uses parallel batched requests (controlled by MetadataConcurrency) to avoid hangs\n// with large backup histories (see issue #930).\n// When false, uses fast LastModified timestamps from LIST operation.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\treturn newFileIterator(ctx, c, level, seek, useMetadata), nil\n}\n\n// OpenLTXFile returns a reader for an LTX file.\n// Returns os.ErrNotExist if no matching file is found.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Build the key from the file info\n\tfilename := ltx.FormatFilename(minTXID, maxTXID)\n\tkey := c.ltxPath(level, filename)\n\n\trequest := \u0026oss.GetObjectRequest{\n\t\tBucket: oss.Ptr(c.Bucket),\n\t\tKey: oss.Ptr(key),\n\t}\n\n\t// Set range header if offset is specified\n\tif size \u003e 0 {\n\t\trequest.RangeBehavior = oss.Ptr(\"standard\")\n\t\trequest.Range = oss.Ptr(fmt.Sprintf(\"bytes=%d-%d\", offset, offset+size-1))\n\t} else if offset \u003e 0 {\n\t\trequest.RangeBehavior = oss.Ptr(\"standard\")\n\t\trequest.Range = oss.Ptr(fmt.Sprintf(\"bytes=%d-\", offset))\n\t}\n\n\tresult, err := c.client.GetObject(ctx, request)\n\tif err != nil {\n\t\tif isNotExists(err) {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, fmt.Errorf(\"oss: get object %s: %w\", key, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\n\treturn result.Body, nil\n}\n\n// WriteLTXFile writes an LTX file to the replica.\n// Extracts timestamp from LTX header and stores it in OSS metadata to preserve original creation time.\n// Uses multipart upload for large files via the uploader.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(r, \u0026buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\trc := internal.NewReadCounter(io.MultiReader(\u0026buf, r))\n\n\tfilename := ltx.FormatFilename(minTXID, maxTXID)\n\tkey := c.ltxPath(level, filename)\n\n\t// Store timestamp in OSS metadata for accurate timestamp retrieval\n\tmetadata := map[string]string{\n\t\tMetadataKeyTimestamp: timestamp.Format(time.RFC3339Nano),\n\t}\n\n\t// Use uploader for automatic multipart handling (files \u003e5GB)\n\tresult, err := c.uploader.UploadFrom(ctx, \u0026oss.PutObjectRequest{\n\t\tBucket: oss.Ptr(c.Bucket),\n\t\tKey: oss.Ptr(key),\n\t\tMetadata: metadata,\n\t}, rc)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"oss: upload to %s: %w\", key, err)\n\t}\n\n\t// Build file info from the uploaded file\n\tinfo := \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: rc.N(),\n\t\tCreatedAt: timestamp,\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(rc.N()))\n\n\t// ETag indicates successful upload\n\tif result.ETag == nil || *result.ETag == \"\" {\n\t\treturn nil, fmt.Errorf(\"oss: upload failed: no ETag returned\")\n\t}\n\n\treturn info, nil\n}\n\n// DeleteLTXFiles deletes one or more LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tif len(a) == 0 {\n\t\treturn nil\n\t}\n\n\t// Convert file infos to object identifiers\n\tobjects := make([]oss.DeleteObject, 0, len(a))\n\tfor _, info := range a {\n\t\tfilename := ltx.FormatFilename(info.MinTXID, info.MaxTXID)\n\t\tkey := c.ltxPath(info.Level, filename)\n\t\tobjects = append(objects, oss.DeleteObject{Key: oss.Ptr(key)})\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"key\", key)\n\t}\n\n\t// Delete in batches\n\tfor len(objects) \u003e 0 {\n\t\tn := min(len(objects), MaxKeys)\n\t\tbatch := objects[:n]\n\n\t\trequest := \u0026oss.DeleteMultipleObjectsRequest{\n\t\t\tBucket: oss.Ptr(c.Bucket),\n\t\t\tObjects: batch,\n\t\t}\n\n\t\tout, err := c.client.DeleteMultipleObjects(ctx, request)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"oss: delete batch of %d objects: %w\", n, err)\n\t\t} else if err := deleteResultError(batch, out); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\n\t\tobjects = objects[n:]\n\t}\n\n\treturn nil\n}\n\n// DeleteAll deletes all files.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tvar objects []oss.DeleteObject\n\n\t// Create paginator for listing objects\n\tprefix := c.Path + \"/\"\n\tpaginator := c.client.NewListObjectsV2Paginator(\u0026oss.ListObjectsV2Request{\n\t\tBucket: oss.Ptr(c.Bucket),\n\t\tPrefix: oss.Ptr(prefix),\n\t})\n\n\t// Iterate through all pages\n\tfor paginator.HasNext() {\n\t\tpage, err := paginator.NextPage(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"oss: list objects page: %w\", err)\n\t\t}\n\n\t\t// Collect object identifiers\n\t\tfor _, obj := range page.Contents {\n\t\t\tif obj.Key != nil {\n\t\t\t\tobjects = append(objects, oss.DeleteObject{Key: obj.Key})\n\t\t\t}\n\t\t}\n\t}\n\n\t// Delete all collected objects in batches\n\tfor len(objects) \u003e 0 {\n\t\tn := min(len(objects), MaxKeys)\n\t\tbatch := objects[:n]\n\n\t\trequest := \u0026oss.DeleteMultipleObjectsRequest{\n\t\t\tBucket: oss.Ptr(c.Bucket),\n\t\t\tObjects: batch,\n\t\t}\n\n\t\tout, err := c.client.DeleteMultipleObjects(ctx, request)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"oss: delete all batch of %d objects: %w\", n, err)\n\t\t} else if err := deleteResultError(batch, out); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tobjects = objects[n:]\n\t}\n\n\treturn nil\n}\n\n// ltxPath returns the full path to an LTX file.\nfunc (c *ReplicaClient) ltxPath(level int, filename string) string {\n\treturn c.Path + \"/\" + fmt.Sprintf(\"%04x/%s\", level, filename)\n}\n\n// fileIterator represents an iterator over LTX files in OSS.\ntype fileIterator struct {\n\tctx context.Context\n\tcancel context.CancelFunc\n\tclient *ReplicaClient\n\tlevel int\n\tseek ltx.TXID\n\n\tuseMetadata bool // When true, fetch accurate timestamps from metadata\n\tmetadataCache map[string]time.Time // key -\u003e timestamp cache for batch fetches\n\n\tpaginator *oss.ListObjectsV2Paginator\n\tpage *oss.ListObjectsV2Result\n\tpageIndex int\n\tinitialized bool\n\n\tclosed bool\n\terr error\n\tinfo *ltx.FileInfo\n}\n\nfunc newFileIterator(ctx context.Context, client *ReplicaClient, level int, seek ltx.TXID, useMetadata bool) *fileIterator {\n\tctx, cancel := context.WithCancel(ctx)\n\n\titr := \u0026fileIterator{\n\t\tctx: ctx,\n\t\tcancel: cancel,\n\t\tclient: client,\n\t\tlevel: level,\n\t\tseek: seek,\n\t\tuseMetadata: useMetadata,\n\t\tmetadataCache: make(map[string]time.Time),\n\t}\n\n\treturn itr\n}\n\n// fetchMetadataBatch fetches timestamps from OSS metadata for a batch of keys in parallel.\nfunc (itr *fileIterator) fetchMetadataBatch(keys []string) error {\n\tif len(keys) == 0 {\n\t\treturn nil\n\t}\n\n\t// Determine concurrency limit\n\tconcurrency := itr.client.MetadataConcurrency\n\tif concurrency \u003c= 0 {\n\t\tconcurrency = DefaultMetadataConcurrency\n\t}\n\n\t// Pre-allocate results map to avoid lock contention during writes\n\tresults := make(map[string]time.Time, len(keys))\n\tvar mu sync.Mutex\n\n\t// Use x/sync/semaphore for precise concurrency control with context support\n\tsem := semaphore.NewWeighted(int64(concurrency))\n\tg, ctx := errgroup.WithContext(itr.ctx)\n\n\tfor _, key := range keys {\n\t\tkey := key // capture for goroutine\n\n\t\tg.Go(func() error {\n\t\t\t// Acquire semaphore slot (blocking with context cancellation)\n\t\t\tif err := sem.Acquire(ctx, 1); err != nil {\n\t\t\t\treturn err // context cancelled\n\t\t\t}\n\t\t\tdefer sem.Release(1)\n\n\t\t\thead, err := itr.client.client.HeadObject(ctx, \u0026oss.HeadObjectRequest{\n\t\t\t\tBucket: oss.Ptr(itr.client.Bucket),\n\t\t\t\tKey: oss.Ptr(key),\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\t// Non-fatal: file might not have metadata, use LastModified\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif head.Metadata != nil {\n\t\t\t\tif ts, ok := head.Metadata[MetadataKeyTimestamp]; ok {\n\t\t\t\t\tif parsed, err := time.Parse(time.RFC3339Nano, ts); err == nil {\n\t\t\t\t\t\tmu.Lock()\n\t\t\t\t\t\tresults[key] = parsed\n\t\t\t\t\t\tmu.Unlock()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\n\tif err := g.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\t// Merge results into cache\n\tfor k, v := range results {\n\t\titr.metadataCache[k] = v\n\t}\n\treturn nil\n}\n\n// initPaginator initializes the paginator lazily.\nfunc (itr *fileIterator) initPaginator() {\n\tif itr.initialized {\n\t\treturn\n\t}\n\titr.initialized = true\n\n\t// Create paginator for listing objects with level prefix\n\tprefix := itr.client.ltxPath(itr.level, \"\")\n\titr.paginator = itr.client.client.NewListObjectsV2Paginator(\u0026oss.ListObjectsV2Request{\n\t\tBucket: oss.Ptr(itr.client.Bucket),\n\t\tPrefix: oss.Ptr(prefix),\n\t})\n}\n\n// Close stops iteration and returns any error that occurred during iteration.\nfunc (itr *fileIterator) Close() (err error) {\n\titr.closed = true\n\titr.cancel()\n\treturn itr.err\n}\n\n// Next returns the next file. Returns false when no more files are available.\nfunc (itr *fileIterator) Next() bool {\n\tif itr.closed || itr.err != nil {\n\t\treturn false\n\t}\n\n\t// Initialize paginator on first call\n\titr.initPaginator()\n\n\t// Process objects until we find a valid LTX file\n\tfor {\n\t\t// Load next page if needed\n\t\tif itr.page == nil || itr.pageIndex \u003e= len(itr.page.Contents) {\n\t\t\tif !itr.paginator.HasNext() {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tvar err error\n\t\t\titr.page, err = itr.paginator.NextPage(itr.ctx)\n\t\t\tif err != nil {\n\t\t\t\titr.err = err\n\t\t\t\treturn false\n\t\t\t}\n\t\t\titr.pageIndex = 0\n\n\t\t\t// Batch fetch metadata for the entire page when useMetadata is true.\n\t\t\t// This uses parallel HeadObject calls controlled by MetadataConcurrency\n\t\t\t// to avoid the O(N) sequential calls that caused restore hangs (issue #930).\n\t\t\tif itr.useMetadata \u0026\u0026 len(itr.page.Contents) \u003e 0 {\n\t\t\t\tkeys := make([]string, 0, len(itr.page.Contents))\n\t\t\t\tfor _, obj := range itr.page.Contents {\n\t\t\t\t\tif obj.Key != nil {\n\t\t\t\t\t\tkeys = append(keys, *obj.Key)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err := itr.fetchMetadataBatch(keys); err != nil {\n\t\t\t\t\titr.err = err\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Process current object\n\t\tif itr.pageIndex \u003c len(itr.page.Contents) {\n\t\t\tobj := itr.page.Contents[itr.pageIndex]\n\t\t\titr.pageIndex++\n\n\t\t\tif obj.Key == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Extract file info from key\n\t\t\tfullKey := *obj.Key\n\t\t\tkey := path.Base(fullKey)\n\t\t\tminTXID, maxTXID, err := ltx.ParseFilename(key)\n\t\t\tif err != nil {\n\t\t\t\tcontinue // Skip non-LTX files\n\t\t\t}\n\n\t\t\t// Build file info\n\t\t\tinfo := \u0026ltx.FileInfo{\n\t\t\t\tLevel: itr.level,\n\t\t\t\tMinTXID: minTXID,\n\t\t\t\tMaxTXID: maxTXID,\n\t\t\t}\n\n\t\t\t// Skip if below seek TXID\n\t\t\tif info.MinTXID \u003c itr.seek {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Set file info\n\t\t\tinfo.Size = obj.Size\n\n\t\t\t// Use cached metadata timestamp if available (from batch fetch),\n\t\t\t// otherwise fallback to LastModified from LIST operation.\n\t\t\tif itr.useMetadata {\n\t\t\t\tif ts, ok := itr.metadataCache[fullKey]; ok {\n\t\t\t\t\tinfo.CreatedAt = ts\n\t\t\t\t} else if obj.LastModified != nil {\n\t\t\t\t\tinfo.CreatedAt = obj.LastModified.UTC()\n\t\t\t\t} else {\n\t\t\t\t\tinfo.CreatedAt = time.Now().UTC()\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif obj.LastModified != nil {\n\t\t\t\t\tinfo.CreatedAt = obj.LastModified.UTC()\n\t\t\t\t} else {\n\t\t\t\t\tinfo.CreatedAt = time.Now().UTC()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\titr.info = info\n\t\t\treturn true\n\t\t}\n\t}\n}\n\n// Item returns the metadata for the current file.\nfunc (itr *fileIterator) Item() *ltx.FileInfo {\n\treturn itr.info\n}\n\n// Err returns any error that occurred during iteration.\nfunc (itr *fileIterator) Err() error {\n\treturn itr.err\n}\n\n// ParseURL parses an OSS URL into its host and path parts.\nfunc ParseURL(s string) (bucket, region, key string, err error) {\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", err\n\t}\n\n\tif u.Scheme != \"oss\" {\n\t\treturn \"\", \"\", \"\", fmt.Errorf(\"oss: invalid url scheme\")\n\t}\n\n\t// Parse host to extract bucket and region\n\tbucket, region, _ = ParseHost(u.Host)\n\tif bucket == \"\" {\n\t\tbucket = u.Host\n\t}\n\n\tkey = strings.TrimPrefix(u.Path, \"/\")\n\treturn bucket, region, key, nil\n}\n\n// ParseHost parses the host/endpoint for an OSS storage system.\n// Supports formats like:\n// - bucket.oss-cn-hangzhou.aliyuncs.com\n// - bucket.oss-cn-hangzhou-internal.aliyuncs.com\n// - bucket (just bucket name)\nfunc ParseHost(host string) (bucket, region, endpoint string) {\n\t// Check for internal OSS URL format first (more specific pattern)\n\tif a := ossInternalRegex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tbucket = a[1]\n\t\tif len(a) \u003e 2 \u0026\u0026 a[2] != \"\" {\n\t\t\tregion = a[2]\n\t\t}\n\t\treturn bucket, region, \"\"\n\t}\n\n\t// Check for standard OSS URL format\n\tif a := ossRegex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tbucket = a[1]\n\t\tif len(a) \u003e 2 \u0026\u0026 a[2] != \"\" {\n\t\t\tregion = a[2]\n\t\t}\n\t\treturn bucket, region, \"\"\n\t}\n\n\t// For other hosts, assume it's just the bucket name\n\treturn host, \"\", \"\"\n}\n\nvar (\n\t// oss-cn-hangzhou.aliyuncs.com or bucket.oss-cn-hangzhou.aliyuncs.com\n\tossRegex = regexp.MustCompile(`^(?:([^.]+)\\.)?oss-([^.]+)\\.aliyuncs\\.com$`)\n\t// oss-cn-hangzhou-internal.aliyuncs.com or bucket.oss-cn-hangzhou-internal.aliyuncs.com\n\t// Uses non-greedy .+? to correctly extract region without -internal suffix\n\tossInternalRegex = regexp.MustCompile(`^(?:([^.]+)\\.)?oss-(.+?)-internal\\.aliyuncs\\.com$`)\n)\n\nfunc isNotExists(err error) bool {\n\tvar serviceErr *oss.ServiceError\n\tif errors.As(err, \u0026serviceErr) {\n\t\treturn serviceErr.Code == \"NoSuchKey\"\n\t}\n\treturn false\n}\n\n// deleteResultError checks if all requested objects were deleted.\n// OSS SDK doesn't have explicit per-object error reporting like S3, so we verify\n// all requested keys appear in the deleted list.\nfunc deleteResultError(requested []oss.DeleteObject, out *oss.DeleteMultipleObjectsResult) error {\n\tif out == nil {\n\t\treturn nil\n\t}\n\n\t// Build set of deleted keys for quick lookup\n\tdeleted := make(map[string]struct{}, len(out.DeletedObjects))\n\tfor _, obj := range out.DeletedObjects {\n\t\tif obj.Key != nil {\n\t\t\tdeleted[*obj.Key] = struct{}{}\n\t\t}\n\t}\n\n\t// Check that all requested keys were deleted\n\tvar failed []string\n\tfor _, obj := range requested {\n\t\tif obj.Key == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := deleted[*obj.Key]; !ok {\n\t\t\tfailed = append(failed, *obj.Key)\n\t\t}\n\t}\n\n\tif len(failed) == 0 {\n\t\treturn nil\n\t}\n\n\t// Build error message listing failed keys\n\tvar b strings.Builder\n\tb.WriteString(\"oss: failed to delete files:\")\n\tfor _, key := range failed {\n\t\tfmt.Fprintf(\u0026b, \"\\n%s\", key)\n\t}\n\treturn errors.New(b.String())\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[22,30]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[31,41]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[42,51]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[52,58]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[59,64]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[65,76]}},{"name":"url","path":"net/url","span":{"start":[10,1],"end":[10,11],"bytes":[77,87]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[88,93]}},{"name":"path","path":"path","span":{"start":[12,1],"end":[12,8],"bytes":[94,101]}},{"name":"regexp","path":"regexp","span":{"start":[13,1],"end":[13,10],"bytes":[102,111]}},{"name":"strings","path":"strings","span":{"start":[14,1],"end":[14,11],"bytes":[112,122]}},{"name":"sync","path":"sync","span":{"start":[15,1],"end":[15,8],"bytes":[123,130]}},{"name":"time","path":"time","span":{"start":[16,1],"end":[16,8],"bytes":[131,138]}},{"name":"oss","path":"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss","span":{"start":[18,1],"end":[18,52],"bytes":[140,191]}},{"name":"credentials","path":"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials","span":{"start":[19,1],"end":[19,64],"bytes":[192,255]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[20,1],"end":[20,27],"bytes":[256,282]}},{"name":"errgroup","path":"golang.org/x/sync/errgroup","span":{"start":[21,1],"end":[21,30],"bytes":[283,312]}},{"name":"semaphore","path":"golang.org/x/sync/semaphore","span":{"start":[22,1],"end":[22,31],"bytes":[313,343]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[24,1],"end":[24,37],"bytes":[345,381]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[25,1],"end":[25,46],"bytes":[382,427]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient","kind":"struct","span":{"start":[52,1],"end":[76,2],"bytes":[1331,1999]},"callables":{"github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteAll":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteAll","kind":"method","span":{"start":[336,1],"end":[386,2],"bytes":[9636,10832]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[336,1],"end":[336,63],"bytes":[9636,9698]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"337:12":{"kind":"call","span":{"start":[337,12],"end":[337,23],"bytes":[9710,9721]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init"},"345:15":{"kind":"call","span":{"start":[345,15],"end":[348,4],"bytes":[9865,9986]},"callee":null},"346:11":{"kind":"call","span":{"start":[346,11],"end":[346,28],"bytes":[9937,9954]},"callee":null},"347:11":{"kind":"call","span":{"start":[347,11],"end":[347,26],"bytes":[9966,9981]},"callee":null},"351:6":{"kind":"call","span":{"start":[351,6],"end":[351,25],"bytes":[10023,10042]},"callee":null},"352:16":{"kind":"call","span":{"start":[352,16],"end":[352,39],"bytes":[10060,10083]},"callee":null},"354:11":{"kind":"call","span":{"start":[354,11],"end":[354,56],"bytes":[10112,10157]},"callee":null},"360:15":{"kind":"call","span":{"start":[360,15],"end":[360,62],"bytes":[10270,10317]},"callee":null},"366:6":{"kind":"call","span":{"start":[366,6],"end":[366,18],"bytes":[10380,10392]},"callee":null},"367:12":{"kind":"call","span":{"start":[367,12],"end":[367,24],"bytes":[10410,10422]},"callee":null},"367:8":{"kind":"call","span":{"start":[367,8],"end":[367,34],"bytes":[10406,10432]},"callee":null},"371:13":{"kind":"call","span":{"start":[371,13],"end":[371,30],"bytes":[10517,10534]},"callee":null},"375:15":{"kind":"call","span":{"start":[375,15],"end":[375,59],"bytes":[10574,10618]},"callee":null},"377:11":{"kind":"call","span":{"start":[377,11],"end":[377,72],"bytes":[10647,10708]},"callee":null},"378:20":{"kind":"call","span":{"start":[378,20],"end":[378,49],"bytes":[10728,10757]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.deleteResultError"}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[291,1],"end":[333,2],"bytes":[8451,9602]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[291,1],"end":[291,87],"bytes":[8451,8537]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[291,1],"end":[291,87],"bytes":[8451,8537]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"292:12":{"kind":"call","span":{"start":[292,12],"end":[292,23],"bytes":[8549,8560]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init"},"296:5":{"kind":"call","span":{"start":[296,5],"end":[296,11],"bytes":[8596,8602]},"callee":null},"301:13":{"kind":"call","span":{"start":[301,13],"end":[301,48],"bytes":[8684,8719]},"callee":null},"301:41":{"kind":"call","span":{"start":[301,41],"end":[301,47],"bytes":[8712,8718]},"callee":null},"303:15":{"kind":"call","span":{"start":[303,15],"end":[303,61],"bytes":[8760,8806]},"callee":null},"304:10":{"kind":"call","span":{"start":[304,10],"end":[304,41],"bytes":[8816,8847]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath"},"305:13":{"kind":"call","span":{"start":[305,13],"end":[305,65],"bytes":[8860,8912]},"callee":null},"305:51":{"kind":"call","span":{"start":[305,51],"end":[305,63],"bytes":[8898,8910]},"callee":null},"307:3":{"kind":"call","span":{"start":[307,3],"end":[307,121],"bytes":[8916,9034]},"callee":null},"311:6":{"kind":"call","span":{"start":[311,6],"end":[311,18],"bytes":[9066,9078]},"callee":null},"312:12":{"kind":"call","span":{"start":[312,12],"end":[312,24],"bytes":[9096,9108]},"callee":null},"312:8":{"kind":"call","span":{"start":[312,8],"end":[312,34],"bytes":[9092,9118]},"callee":null},"316:13":{"kind":"call","span":{"start":[316,13],"end":[316,30],"bytes":[9203,9220]},"callee":null},"320:15":{"kind":"call","span":{"start":[320,15],"end":[320,59],"bytes":[9260,9304]},"callee":null},"322:11":{"kind":"call","span":{"start":[322,11],"end":[322,68],"bytes":[9333,9390]},"callee":null},"323:20":{"kind":"call","span":{"start":[323,20],"end":[323,49],"bytes":[9410,9439]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.deleteResultError"},"327:3":{"kind":"call","span":{"start":[327,3],"end":[327,81],"bytes":[9475,9553]},"callee":null}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.Init":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init","kind":"method","span":{"start":[113,1],"end":[178,2],"bytes":[3063,4679]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[113,1],"end":[113,64],"bytes":[3063,3126]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"114:2":{"kind":"call","span":{"start":[114,2],"end":[114,13],"bytes":[3128,3139]},"callee":null},"115:8":{"kind":"call","span":{"start":[115,8],"end":[115,21],"bytes":[3147,3160]},"callee":null},"123:10":{"kind":"call","span":{"start":[123,10],"end":[123,52],"bytes":[3267,3309]},"callee":null},"133:9":{"kind":"call","span":{"start":[133,9],"end":[133,32],"bytes":[3454,3477]},"callee":null},"137:9":{"kind":"call","span":{"start":[137,9],"end":[139,4],"bytes":[3566,3677]},"callee":null},"138:4":{"kind":"call","span":{"start":[138,4],"end":[138,78],"bytes":[3598,3672]},"callee":null},"142:9":{"kind":"call","span":{"start":[142,9],"end":[144,4],"bytes":[3747,3839]},"callee":null},"143:4":{"kind":"call","span":{"start":[143,4],"end":[143,59],"bytes":[3779,3834]},"callee":null},"148:8":{"kind":"call","span":{"start":[148,8],"end":[148,30],"bytes":[3872,3894]},"callee":null},"154:50":{"kind":"call","span":{"start":[154,50],"end":[154,89],"bytes":[4067,4106]},"callee":null},"154:7":{"kind":"call","span":{"start":[154,7],"end":[154,45],"bytes":[4024,4062]},"callee":null},"157:9":{"kind":"call","span":{"start":[157,9],"end":[157,35],"bytes":[4157,4183]},"callee":null},"161:13":{"kind":"call","span":{"start":[161,13],"end":[161,31],"bytes":[4222,4240]},"callee":null},"166:18":{"kind":"call","span":{"start":[166,18],"end":[168,5],"bytes":[4392,4475]},"callee":null},"171:18":{"kind":"call","span":{"start":[171,18],"end":[173,5],"bytes":[4520,4609]},"callee":null},"175:15":{"kind":"call","span":{"start":[175,15],"end":[175,52],"bytes":[4627,4664]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init/github.com/benbjohnson/litestream/oss.ReplicaClient.Init.closure_1","kind":"function","span":{"start":[166,1],"end":[168,5],"bytes":[4375,4475]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.Init.closure_1","parameters":[{"name":"o","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.UploaderOptions","span":{"start":[166,1],"end":[166,69],"bytes":[4375,4443]}}],"body":{}},"closure_2":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init/github.com/benbjohnson/litestream/oss.ReplicaClient.Init.closure_2","kind":"function","span":{"start":[171,1],"end":[173,5],"bytes":[4503,4609]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.Init.closure_2","parameters":[{"name":"o","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.UploaderOptions","span":{"start":[171,1],"end":[171,69],"bytes":[4503,4571]}}],"body":{}}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.LTXFiles":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.LTXFiles","kind":"method","span":{"start":[185,1],"end":[190,2],"bytes":[5068,5316]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[185,1],"end":[185,126],"bytes":[5068,5193]}},{"name":"level","type":"int","span":{"start":[185,1],"end":[185,126],"bytes":[5068,5193]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[185,1],"end":[185,126],"bytes":[5068,5193]}},{"name":"useMetadata","type":"bool","span":{"start":[185,1],"end":[185,126],"bytes":[5068,5193]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"186:12":{"kind":"call","span":{"start":[186,12],"end":[186,23],"bytes":[5205,5216]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init"},"189:9":{"kind":"call","span":{"start":[189,9],"end":[189,58],"bytes":[5260,5309]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.newFileIterator"}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[194,1],"end":[228,2],"bytes":[5423,6452]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[194,1],"end":[194,140],"bytes":[5423,5562]}},{"name":"level","type":"int","span":{"start":[194,1],"end":[194,140],"bytes":[5423,5562]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[194,1],"end":[194,140],"bytes":[5423,5562]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[194,1],"end":[194,140],"bytes":[5423,5562]}},{"name":"offset","type":"int64","span":{"start":[194,1],"end":[194,140],"bytes":[5423,5562]}},{"name":"size","type":"int64","span":{"start":[194,1],"end":[194,140],"bytes":[5423,5562]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"195:12":{"kind":"call","span":{"start":[195,12],"end":[195,23],"bytes":[5574,5585]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init"},"200:14":{"kind":"call","span":{"start":[200,14],"end":[200,50],"bytes":[5672,5708]},"callee":null},"201:9":{"kind":"call","span":{"start":[201,9],"end":[201,35],"bytes":[5717,5743]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath"},"204:11":{"kind":"call","span":{"start":[204,11],"end":[204,28],"bytes":[5790,5807]},"callee":null},"205:11":{"kind":"call","span":{"start":[205,11],"end":[205,23],"bytes":[5819,5831]},"callee":null},"210:27":{"kind":"call","span":{"start":[210,27],"end":[210,46],"bytes":[5922,5941]},"callee":null},"211:19":{"kind":"call","span":{"start":[211,19],"end":[211,77],"bytes":[5960,6018]},"callee":null},"211:27":{"kind":"call","span":{"start":[211,27],"end":[211,76],"bytes":[5968,6017]},"callee":null},"213:27":{"kind":"call","span":{"start":[213,27],"end":[213,46],"bytes":[6069,6088]},"callee":null},"214:19":{"kind":"call","span":{"start":[214,19],"end":[214,60],"bytes":[6107,6148]},"callee":null},"214:27":{"kind":"call","span":{"start":[214,27],"end":[214,59],"bytes":[6115,6147]},"callee":null},"217:17":{"kind":"call","span":{"start":[217,17],"end":[217,49],"bytes":[6169,6201]},"callee":null},"219:6":{"kind":"call","span":{"start":[219,6],"end":[219,22],"bytes":[6224,6240]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.isNotExists"},"222:15":{"kind":"call","span":{"start":[222,15],"end":[222,61],"bytes":[6291,6337]},"callee":null},"225:2":{"kind":"call","span":{"start":[225,2],"end":[225,77],"bytes":[6343,6418]},"callee":null}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.SetLogger":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.SetLogger","kind":"method","span":{"start":[85,1],"end":[87,2],"bytes":[2188,2294]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[85,1],"end":[85,57],"bytes":[2188,2244]}}],"metrics":{"cyclomatic":1},"body":{"86:13":{"kind":"call","span":{"start":[86,13],"end":[86,48],"bytes":[2257,2292]},"callee":null}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.Type":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Type","kind":"method","span":{"start":[108,1],"end":[110,2],"bytes":[2921,2988]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[233,1],"end":[288,2],"bytes":[6668,8400]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[233,1],"end":[233,134],"bytes":[6668,6801]}},{"name":"level","type":"int","span":{"start":[233,1],"end":[233,134],"bytes":[6668,6801]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[233,1],"end":[233,134],"bytes":[6668,6801]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[233,1],"end":[233,134],"bytes":[6668,6801]}},{"name":"r","type":"io.Reader","span":{"start":[233,1],"end":[233,134],"bytes":[6668,6801]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"234:12":{"kind":"call","span":{"start":[234,12],"end":[234,23],"bytes":[6813,6824]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init"},"240:15":{"kind":"call","span":{"start":[240,15],"end":[240,36],"bytes":[6970,6991]},"callee":null},"243:17":{"kind":"call","span":{"start":[243,17],"end":[243,42],"bytes":[7047,7072]},"callee":null},"245:15":{"kind":"call","span":{"start":[245,15],"end":[245,71],"bytes":[7104,7160]},"callee":null},"247:15":{"kind":"call","span":{"start":[247,15],"end":[247,44],"bytes":[7178,7207]},"callee":null},"250:32":{"kind":"call","span":{"start":[250,32],"end":[250,55],"bytes":[7292,7315]},"callee":null},"250:8":{"kind":"call","span":{"start":[250,8],"end":[250,56],"bytes":[7268,7316]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter"},"252:14":{"kind":"call","span":{"start":[252,14],"end":[252,50],"bytes":[7331,7367]},"callee":null},"253:9":{"kind":"call","span":{"start":[253,9],"end":[253,35],"bytes":[7376,7402]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath"},"257:25":{"kind":"call","span":{"start":[257,25],"end":[257,59],"bytes":[7529,7563]},"callee":null},"261:17":{"kind":"call","span":{"start":[261,17],"end":[265,8],"bytes":[7648,7784]},"callee":null},"262:13":{"kind":"call","span":{"start":[262,13],"end":[262,30],"bytes":[7710,7727]},"callee":null},"263:13":{"kind":"call","span":{"start":[263,13],"end":[263,25],"bytes":[7741,7753]},"callee":null},"267:15":{"kind":"call","span":{"start":[267,15],"end":[267,60],"bytes":[7816,7861]},"callee":null},"275:14":{"kind":"call","span":{"start":[275,14],"end":[275,20],"bytes":[8010,8016]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N"},"279:2":{"kind":"call","span":{"start":[279,2],"end":[279,77],"bytes":[8047,8122]},"callee":null},"280:2":{"kind":"call","span":{"start":[280,2],"end":[280,77],"bytes":[8130,8205]},"callee":null},"280:82":{"kind":"call","span":{"start":[280,82],"end":[280,97],"bytes":[8210,8225]},"callee":null},"280:90":{"kind":"call","span":{"start":[280,90],"end":[280,96],"bytes":[8218,8224]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N"},"284:15":{"kind":"call","span":{"start":[284,15],"end":[284,65],"bytes":[8326,8376]},"callee":null}}},"github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath","kind":"method","span":{"start":[389,1],"end":[391,2],"bytes":[10883,11016]},"signature":"github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath","parameters":[{"name":"level","type":"int","span":{"start":[389,1],"end":[389,69],"bytes":[10883,10951]}},{"name":"filename","type":"string","span":{"start":[389,1],"end":[389,69],"bytes":[10883,10951]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"390:24":{"kind":"call","span":{"start":[390,24],"end":[390,63],"bytes":[10975,11014]},"callee":null}}}},"fields":{"AccessKeyID":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/AccessKeyID","kind":"field","type":"string","span":{"start":[59,1],"end":[59,24],"bytes":[1489,1512]}},"AccessKeySecret":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/AccessKeySecret","kind":"field","type":"string","span":{"start":[60,1],"end":[60,24],"bytes":[1513,1536]}},"Bucket":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/Bucket","kind":"field","type":"string","span":{"start":[64,1],"end":[64,17],"bytes":[1582,1598]}},"Concurrency":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/Concurrency","kind":"field","type":"int","span":{"start":[70,1],"end":[70,72],"bytes":[1728,1799]}},"Endpoint":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/Endpoint","kind":"field","type":"string","span":{"start":[66,1],"end":[66,17],"bytes":[1616,1632]}},"MetadataConcurrency":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/MetadataConcurrency","kind":"field","type":"int","span":{"start":[75,1],"end":[75,25],"bytes":[1973,1997]}},"PartSize":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/PartSize","kind":"field","type":"int64","span":{"start":[69,1],"end":[69,69],"bytes":[1659,1727]}},"Path":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[65,1],"end":[65,17],"bytes":[1599,1615]}},"Region":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/Region","kind":"field","type":"string","span":{"start":[63,1],"end":[63,17],"bytes":[1565,1581]}},"client":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/client","kind":"field","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.Client","span":{"start":[54,1],"end":[54,22],"bytes":[1380,1401]}},"logger":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[56,1],"end":[56,23],"bytes":[1426,1448]}},"mu":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[53,1],"end":[53,21],"bytes":[1359,1379]}},"uploader":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/uploader","kind":"field","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.Uploader","span":{"start":[55,1],"end":[55,24],"bytes":[1402,1425]}}}},"fileIterator":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator","kind":"struct","span":{"start":[394,1],"end":[412,2],"bytes":[11080,11547]},"callables":{"github.com/benbjohnson/litestream/oss.fileIterator.Close":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.Close","kind":"method","span":{"start":[509,1],"end":[513,2],"bytes":[14035,14132]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"511:2":{"kind":"call","span":{"start":[511,2],"end":[511,14],"bytes":[14102,14114]},"callee":null}}},"github.com/benbjohnson/litestream/oss.fileIterator.Err":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.Err","kind":"method","span":{"start":[619,1],"end":[621,2],"bytes":[16662,16718]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.Err","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/oss.fileIterator.Item":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.Item","kind":"method","span":{"start":[614,1],"end":[616,2],"bytes":[16537,16603]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.Item","parameters":[],"return_type":"*github.com/superfly/ltx.FileInfo","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/oss.fileIterator.Next":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.Next","kind":"method","span":{"start":[516,1],"end":[611,2],"bytes":[14213,16484]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.Next","parameters":[],"return_type":"bool","metrics":{"cyclomatic":18},"body":{"522:2":{"kind":"call","span":{"start":[522,2],"end":[522,21],"bytes":[14346,14365]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.initPaginator"},"527:42":{"kind":"call","span":{"start":[527,42],"end":[527,64],"bytes":[14496,14518]},"callee":null},"528:8":{"kind":"call","span":{"start":[528,8],"end":[528,31],"bytes":[14528,14551]},"callee":null},"533:20":{"kind":"call","span":{"start":[533,20],"end":[533,51],"bytes":[14613,14644]},"callee":null},"543:26":{"kind":"call","span":{"start":[543,26],"end":[543,48],"bytes":[14981,15003]},"callee":null},"544:13":{"kind":"call","span":{"start":[544,13],"end":[544,54],"bytes":[15022,15063]},"callee":null},"544:31":{"kind":"call","span":{"start":[544,31],"end":[544,53],"bytes":[15040,15062]},"callee":null},"547:14":{"kind":"call","span":{"start":[547,14],"end":[547,36],"bytes":[15146,15168]},"callee":null},"550:15":{"kind":"call","span":{"start":[550,15],"end":[550,43],"bytes":[15196,15224]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch"},"558:22":{"kind":"call","span":{"start":[558,22],"end":[558,44],"bytes":[15341,15363]},"callee":null},"568:11":{"kind":"call","span":{"start":[568,11],"end":[568,29],"bytes":[15537,15555]},"callee":null},"569:29":{"kind":"call","span":{"start":[569,29],"end":[569,51],"bytes":[15584,15606]},"callee":null},"595:23":{"kind":"call","span":{"start":[595,23],"end":[595,45],"bytes":[16207,16229]},"callee":null},"597:23":{"kind":"call","span":{"start":[597,23],"end":[597,33],"bytes":[16265,16275]},"callee":null},"601:23":{"kind":"call","span":{"start":[601,23],"end":[601,45],"bytes":[16355,16377]},"callee":null},"603:23":{"kind":"call","span":{"start":[603,23],"end":[603,33],"bytes":[16413,16423]},"callee":null}}},"github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch","kind":"method","span":{"start":[431,1],"end":[491,2],"bytes":[12043,13537]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch","parameters":[{"name":"keys","type":"[]string","span":{"start":[431,1],"end":[431,67],"bytes":[12043,12109]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"432:5":{"kind":"call","span":{"start":[432,5],"end":[432,14],"bytes":[12114,12123]},"callee":null},"443:13":{"kind":"call","span":{"start":[443,13],"end":[443,50],"bytes":[12377,12414]},"callee":null},"443:40":{"kind":"call","span":{"start":[443,40],"end":[443,49],"bytes":[12404,12413]},"callee":null},"447:31":{"kind":"call","span":{"start":[447,31],"end":[447,49],"bytes":[12543,12561]},"callee":null},"447:9":{"kind":"call","span":{"start":[447,9],"end":[447,50],"bytes":[12521,12562]},"callee":null},"448:12":{"kind":"call","span":{"start":[448,12],"end":[448,41],"bytes":[12574,12603]},"callee":null},"453:3":{"kind":"call","span":{"start":[453,3],"end":[479,5],"bytes":[12674,13380]},"callee":null},"482:12":{"kind":"call","span":{"start":[482,12],"end":[482,20],"bytes":[13396,13404]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch/github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch.closure_1","kind":"function","span":{"start":[453,1],"end":[479,5],"bytes":[12672,13380]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch.closure_1","parameters":[],"return_type":"error","error_channel":["error"],"body":{"455:14":{"kind":"call","span":{"start":[455,14],"end":[455,33],"bytes":[12773,12792]},"callee":null},"458:10":{"kind":"call","span":{"start":[458,10],"end":[458,24],"bytes":[12857,12871]},"callee":null},"460:17":{"kind":"call","span":{"start":[460,17],"end":[463,6],"bytes":[12889,13018]},"callee":null},"461:13":{"kind":"call","span":{"start":[461,13],"end":[461,39],"bytes":[12959,12985]},"callee":null},"462:13":{"kind":"call","span":{"start":[462,13],"end":[462,25],"bytes":[12999,13011]},"callee":null},"471:24":{"kind":"call","span":{"start":[471,24],"end":[471,56],"bytes":[13235,13267]},"callee":null},"472:7":{"kind":"call","span":{"start":[472,7],"end":[472,16],"bytes":[13288,13297]},"callee":null},"474:7":{"kind":"call","span":{"start":[474,7],"end":[474,18],"bytes":[13332,13343]},"callee":null}}}}},"github.com/benbjohnson/litestream/oss.fileIterator.initPaginator":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.initPaginator","kind":"method","span":{"start":[494,1],"end":[506,2],"bytes":[13590,13954]},"signature":"github.com/benbjohnson/litestream/oss.fileIterator.initPaginator","parameters":[],"metrics":{"cyclomatic":2},"body":{"501:12":{"kind":"call","span":{"start":[501,12],"end":[501,45],"bytes":[13762,13795]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath"},"502:18":{"kind":"call","span":{"start":[502,18],"end":[505,4],"bytes":[13813,13952]},"callee":null},"503:11":{"kind":"call","span":{"start":[503,11],"end":[503,37],"bytes":[13894,13920]},"callee":null},"504:11":{"kind":"call","span":{"start":[504,11],"end":[504,26],"bytes":[13932,13947]},"callee":null}}}},"fields":{"cancel":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/cancel","kind":"field","type":"context.CancelFunc","span":{"start":[396,1],"end":[396,27],"bytes":[11131,11157]}},"client":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/client","kind":"field","type":"*github.com/benbjohnson/litestream/oss.ReplicaClient","span":{"start":[397,1],"end":[397,23],"bytes":[11158,11180]}},"closed":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/closed","kind":"field","type":"bool","span":{"start":[409,1],"end":[409,13],"bytes":[11497,11509]}},"ctx":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/ctx","kind":"field","type":"context.Context","span":{"start":[395,1],"end":[395,24],"bytes":[11107,11130]}},"err":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/err","kind":"field","type":"error","span":{"start":[410,1],"end":[410,14],"bytes":[11510,11523]}},"info":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/info","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[411,1],"end":[411,22],"bytes":[11524,11545]}},"initialized":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/initialized","kind":"field","type":"bool","span":{"start":[407,1],"end":[407,18],"bytes":[11478,11495]}},"level":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/level","kind":"field","type":"int","span":{"start":[398,1],"end":[398,12],"bytes":[11181,11192]}},"metadataCache":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/metadataCache","kind":"field","type":"map[string]time.Time","span":{"start":[402,1],"end":[402,80],"bytes":[11301,11380]}},"page":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/page","kind":"field","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.ListObjectsV2Result","span":{"start":[405,1],"end":[405,38],"bytes":[11423,11460]}},"pageIndex":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/pageIndex","kind":"field","type":"int","span":{"start":[406,1],"end":[406,17],"bytes":[11461,11477]}},"paginator":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/paginator","kind":"field","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.ListObjectsV2Paginator","span":{"start":[404,1],"end":[404,41],"bytes":[11382,11422]}},"seek":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/seek","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[399,1],"end":[399,17],"bytes":[11193,11209]}},"useMetadata":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/useMetadata","kind":"field","type":"bool","span":{"start":[401,1],"end":[401,90],"bytes":[11211,11300]}}}}},"functions":{"github.com/benbjohnson/litestream/oss.NewReplicaClient":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient","kind":"function","span":{"start":[79,1],"end":[83,2],"bytes":[2062,2186]},"signature":"github.com/benbjohnson/litestream/oss.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/oss.ReplicaClient","metrics":{"cyclomatic":1},"body":{"81:11":{"kind":"call","span":{"start":[81,11],"end":[81,25],"bytes":[2137,2151]},"callee":null}}},"github.com/benbjohnson/litestream/oss.NewReplicaClientFromURL":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClientFromURL","kind":"function","span":{"start":[92,1],"end":[105,2],"bytes":[2491,2877]},"signature":"github.com/benbjohnson/litestream/oss.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[92,1],"end":[92,137],"bytes":[2491,2627]}},{"name":"host","type":"string","span":{"start":[92,1],"end":[92,137],"bytes":[2491,2627]}},{"name":"urlPath","type":"string","span":{"start":[92,1],"end":[92,137],"bytes":[2491,2627]}},{"name":"query","type":"net/url.Values","span":{"start":[92,1],"end":[92,137],"bytes":[2491,2627]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[92,1],"end":[92,137],"bytes":[2491,2627]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"93:12":{"kind":"call","span":{"start":[93,12],"end":[93,30],"bytes":[2639,2657]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient"},"95:23":{"kind":"call","span":{"start":[95,23],"end":[95,38],"bytes":[2681,2696]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost"},"97:15":{"kind":"call","span":{"start":[97,15],"end":[97,64],"bytes":[2730,2779]},"callee":null}}},"github.com/benbjohnson/litestream/oss.ParseHost":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost","kind":"function","span":{"start":[649,1],"end":[670,2],"bytes":[17421,17991]},"signature":"github.com/benbjohnson/litestream/oss.ParseHost","parameters":[{"name":"host","type":"string","span":{"start":[649,1],"end":[649,64],"bytes":[17421,17484]}}],"return_type":"(string, string, string)","metrics":{"cyclomatic":5},"body":{"651:10":{"kind":"call","span":{"start":[651,10],"end":[651,51],"bytes":[17562,17603]},"callee":null},"651:53":{"kind":"call","span":{"start":[651,53],"end":[651,59],"bytes":[17605,17611]},"callee":null},"653:6":{"kind":"call","span":{"start":[653,6],"end":[653,12],"bytes":[17639,17645]},"callee":null},"660:10":{"kind":"call","span":{"start":[660,10],"end":[660,43],"bytes":[17766,17799]},"callee":null},"660:45":{"kind":"call","span":{"start":[660,45],"end":[660,51],"bytes":[17801,17807]},"callee":null},"662:6":{"kind":"call","span":{"start":[662,6],"end":[662,12],"bytes":[17835,17841]},"callee":null}}},"github.com/benbjohnson/litestream/oss.ParseURL":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseURL","kind":"function","span":{"start":[624,1],"end":[642,2],"bytes":[16780,17200]},"signature":"github.com/benbjohnson/litestream/oss.ParseURL","parameters":[{"name":"s","type":"string","span":{"start":[624,1],"end":[624,66],"bytes":[16780,16845]}}],"return_type":"(string, string, string, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"625:12":{"kind":"call","span":{"start":[625,12],"end":[625,24],"bytes":[16857,16869]},"callee":null},"631:22":{"kind":"call","span":{"start":[631,22],"end":[631,59],"bytes":[16961,16998]},"callee":null},"635:22":{"kind":"call","span":{"start":[635,22],"end":[635,39],"bytes":[17068,17085]},"callee":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost"},"640:8":{"kind":"call","span":{"start":[640,8],"end":[640,39],"bytes":[17134,17165]},"callee":null}}},"github.com/benbjohnson/litestream/oss.deleteResultError":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.deleteResultError","kind":"function","span":{"start":[691,1],"end":[726,2],"bytes":[18772,19594]},"signature":"github.com/benbjohnson/litestream/oss.deleteResultError","parameters":[{"name":"requested","type":"[]github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.DeleteObject","span":{"start":[691,1],"end":[691,99],"bytes":[18772,18870]}},{"name":"out","type":"*github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss.DeleteMultipleObjectsResult","span":{"start":[691,1],"end":[691,99],"bytes":[18772,18870]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"697:13":{"kind":"call","span":{"start":[697,13],"end":[697,63],"bytes":[18964,19014]},"callee":null},"697:39":{"kind":"call","span":{"start":[697,39],"end":[697,62],"bytes":[18990,19013]},"callee":null},"711:13":{"kind":"call","span":{"start":[711,13],"end":[711,37],"bytes":[19311,19335]},"callee":null},"715:5":{"kind":"call","span":{"start":[715,5],"end":[715,16],"bytes":[19348,19359]},"callee":null},"721:2":{"kind":"call","span":{"start":[721,2],"end":[721,47],"bytes":[19452,19497]},"callee":null},"723:3":{"kind":"call","span":{"start":[723,3],"end":[723,31],"bytes":[19530,19558]},"callee":null},"725:20":{"kind":"call","span":{"start":[725,20],"end":[725,30],"bytes":[19581,19591]},"callee":null},"725:9":{"kind":"call","span":{"start":[725,9],"end":[725,31],"bytes":[19570,19592]},"callee":null}}},"github.com/benbjohnson/litestream/oss.init":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.init","kind":"function","span":{"start":[28,1],"end":[30,2],"bytes":[431,519]},"signature":"github.com/benbjohnson/litestream/oss.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"29:2":{"kind":"call","span":{"start":[29,2],"end":[29,73],"bytes":[446,517]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}},"github.com/benbjohnson/litestream/oss.isNotExists":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.isNotExists","kind":"function","span":{"start":[680,1],"end":[686,2],"bytes":[18410,18571]},"signature":"github.com/benbjohnson/litestream/oss.isNotExists","parameters":[{"name":"err","type":"error","span":{"start":[680,1],"end":[680,35],"bytes":[18410,18444]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"682:5":{"kind":"call","span":{"start":[682,5],"end":[682,32],"bytes":[18483,18510]},"callee":null}}},"github.com/benbjohnson/litestream/oss.newFileIterator":{"id":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.newFileIterator","kind":"function","span":{"start":[414,1],"end":[428,2],"bytes":[11549,11949]},"signature":"github.com/benbjohnson/litestream/oss.newFileIterator","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[414,1],"end":[414,125],"bytes":[11549,11673]}},{"name":"client","type":"*github.com/benbjohnson/litestream/oss.ReplicaClient","span":{"start":[414,1],"end":[414,125],"bytes":[11549,11673]}},{"name":"level","type":"int","span":{"start":[414,1],"end":[414,125],"bytes":[11549,11673]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[414,1],"end":[414,125],"bytes":[11549,11673]}},{"name":"useMetadata","type":"bool","span":{"start":[414,1],"end":[414,125],"bytes":[11549,11673]}}],"return_type":"*github.com/benbjohnson/litestream/oss.fileIterator","metrics":{"cyclomatic":1},"body":{"415:17":{"kind":"call","span":{"start":[415,17],"end":[415,40],"bytes":[11690,11713]},"callee":null},"424:18":{"kind":"call","span":{"start":[424,18],"end":[424,44],"bytes":[11904,11930]},"callee":null}}}},"content_hash":"c4aa5689e4bfc9330f1ff5f76ce0208a37895f43f5de7ca9427f243d3f037e17"},"replica.go":{"id":"can://go/litestream/replica.go","kind":"module","span":{"start":[1,1],"end":[1847,1],"bytes":[0,54263]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n\t\"golang.org/x/sync/semaphore\"\n\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\n// Default replica settings.\nconst (\n\tDefaultSyncInterval = 1 * time.Second\n\tDefaultMaxSyncLTXFiles = 256\n)\n\nvar errReplicaWaitForData = errors.New(\"no position, waiting for data\")\n\n// Replica connects a database to a replication destination via a ReplicaClient.\n// The replica manages periodic synchronization and maintaining the current\n// replica position.\ntype Replica struct {\n\tdb *DB\n\n\tmu sync.RWMutex\n\tpos ltx.Pos // current replicated position\n\n\tsyncSem *semaphore.Weighted\n\tsyncWaiters atomic.Int64 // diagnostic instrumentation: goroutines queued on syncSem\n\n\tmuf sync.Mutex\n\tf *os.File // long-running file descriptor to avoid non-OFD lock issues\n\n\twg sync.WaitGroup\n\tcancel func()\n\n\t// Client used to connect to the remote replica.\n\tClient ReplicaClient\n\n\t// Time between syncs with the shadow WAL.\n\tSyncInterval time.Duration\n\n\t// Maximum L0 files to upload in a single monitor sync batch.\n\t// Set to zero to process all pending L0 files in one batch.\n\tMaxSyncLTXFiles int\n\n\t// If true, replica monitors database for changes automatically.\n\t// Set to false if replica is being used synchronously (such as in tests).\n\tMonitorEnabled bool\n\n\t// If true, automatically reset local state when LTX errors are detected.\n\t// This allows recovery from corrupted/missing LTX files by resetting\n\t// the position file and removing local LTX files, forcing a fresh sync.\n\t// Disabled by default to prevent silent data loss scenarios.\n\tAutoRecoverEnabled bool\n}\n\nfunc NewReplica(db *DB) *Replica {\n\tr := \u0026Replica{\n\t\tdb: db,\n\t\tsyncSem: semaphore.NewWeighted(1),\n\t\tcancel: func() {},\n\n\t\tSyncInterval: DefaultSyncInterval,\n\t\tMaxSyncLTXFiles: DefaultMaxSyncLTXFiles,\n\t\tMonitorEnabled: true,\n\t}\n\n\treturn r\n}\n\nfunc NewReplicaWithClient(db *DB, client ReplicaClient) *Replica {\n\tr := NewReplica(db)\n\tr.Client = client\n\treturn r\n}\n\n// Logger returns the DB sub-logger for this replica.\nfunc (r *Replica) Logger() *slog.Logger {\n\tlogger := slog.Default()\n\tif r.db != nil {\n\t\tlogger = r.db.Logger\n\t}\n\treturn logger.With(\"replica\", r.Client.Type())\n}\n\n// DB returns a reference to the database the replica is attached to, if any.\nfunc (r *Replica) DB() *DB { return r.db }\n\n// Starts replicating in a background goroutine.\nfunc (r *Replica) Start(ctx context.Context) error {\n\t// Ignore if replica is being used sychronously.\n\tif !r.MonitorEnabled {\n\t\treturn nil\n\t}\n\n\t// Stop previous replication.\n\tr.Stop(false)\n\n\t// Wrap context with cancelation.\n\tctx, r.cancel = context.WithCancel(ctx)\n\n\t// Start goroutine to replicate data.\n\tr.wg.Add(1)\n\tgo func() { defer r.wg.Done(); r.monitor(ctx) }()\n\n\treturn nil\n}\n\n// Stop cancels any outstanding replication and blocks until finished.\n//\n// Performing a hard stop will close the DB file descriptor which could release\n// locks on per-process locks. Hard stops should only be performed when\n// stopping the entire process.\nfunc (r *Replica) Stop(hard bool) (err error) {\n\tr.cancel()\n\tr.wg.Wait()\n\n\tr.muf.Lock()\n\tdefer r.muf.Unlock()\n\tif hard \u0026\u0026 r.f != nil {\n\t\tif e := r.f.Close(); e != nil \u0026\u0026 err == nil {\n\t\t\terr = e\n\t\t}\n\t}\n\treturn err\n}\n\n// Sync copies new WAL frames from the shadow WAL to the replica client.\n// Only one Sync can run at a time to prevent concurrent uploads of the same file.\nfunc (r *Replica) Sync(ctx context.Context) (err error) {\n\treturn r.sync(ctx, 0)\n}\n\nfunc (r *Replica) sync(ctx context.Context, maxSyncLTXFiles int) error {\n\tfor {\n\t\tresult, err := r.syncOnce(ctx, maxSyncLTXFiles)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t} else if !result.limited {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\ntype replicaSyncResult struct {\n\tsynced bool\n\tlimited bool\n}\n\nfunc (r *Replica) syncOnce(ctx context.Context, maxSyncLTXFiles int) (result replicaSyncResult, err error) {\n\tif err := r.lockSync(ctx); err != nil {\n\t\treturn result, err\n\t}\n\tdefer r.syncSem.Release(1)\n\n\t// Clear last position if if an error occurs during sync.\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tr.mu.Lock()\n\t\t\tr.pos = ltx.Pos{}\n\t\t\tr.mu.Unlock()\n\t\t}\n\t}()\n\n\tif err := ctx.Err(); err != nil {\n\t\treturn result, context.Cause(ctx)\n\t}\n\n\t// Calculate current replica position, if unknown.\n\tif r.Pos().IsZero() {\n\t\tpos, err := r.calcPos(ctx)\n\t\tif err != nil {\n\t\t\treturn result, fmt.Errorf(\"calc pos: %w\", err)\n\t\t}\n\t\tr.SetPos(pos)\n\t}\n\n\t// Find current position of database.\n\tdpos, err := r.db.Pos()\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"cannot determine current position: %w\", err)\n\t} else if dpos.IsZero() {\n\t\treturn result, errReplicaWaitForData\n\t}\n\n\tr.Logger().Debug(\"replica sync\",\n\t\tslog.Group(\"txid\",\n\t\t\tslog.String(\"replica\", r.Pos().TXID.String()),\n\t\t\tslog.String(\"db\", dpos.TXID.String()),\n\t\t))\n\n\t// Replicate all L0 LTX files since last replica position.\n\tfor txID, syncedFileN := r.Pos().TXID+1, 0; txID \u003c= dpos.TXID; txID = r.Pos().TXID + 1 {\n\t\tif maxSyncLTXFiles \u003e 0 \u0026\u0026 syncedFileN \u003e= maxSyncLTXFiles {\n\t\t\tresult.limited = true\n\t\t\t// Uploads succeeded, so record sync health; otherwise a\n\t\t\t// sustained backlog reads as unhealthy while progressing.\n\t\t\tr.db.RecordSuccessfulSync()\n\t\t\tr.Logger().Debug(\"replica sync limited\",\n\t\t\t\t\"synced_files\", syncedFileN,\n\t\t\t\t\"db_txid\", dpos.TXID.String(),\n\t\t\t\t\"replica_txid\", r.Pos().TXID.String())\n\t\t\treturn result, nil\n\t\t}\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn result, context.Cause(ctx)\n\t\t}\n\t\tif err := r.uploadLTXFile(ctx, 0, txID, txID); err != nil {\n\t\t\treturn result, err\n\t\t}\n\t\tr.SetPos(ltx.Pos{TXID: txID})\n\t\tresult.synced = true\n\t\tsyncedFileN++\n\t}\n\n\t// Record successful sync for heartbeat monitoring.\n\tr.db.RecordSuccessfulSync()\n\n\treturn result, nil\n}\n\nfunc (r *Replica) lockSync(ctx context.Context) error {\n\tif r.syncSem.TryAcquire(1) {\n\t\treturn nil\n\t}\n\tr.syncWaiters.Add(1)\n\tdefer r.syncWaiters.Add(-1)\n\tif err := r.syncSem.Acquire(ctx, 1); err != nil {\n\t\treturn fmt.Errorf(\"wait for replica sync: %w\", context.Cause(ctx))\n\t}\n\treturn nil\n}\n\nfunc (r *Replica) uploadLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID) (err error) {\n\tfilename := r.db.LTXPath(level, minTXID, maxTXID)\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn NewLTXError(\"open\", filename, level, uint64(minTXID), uint64(maxTXID), err)\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tinfo, err := r.Client.WriteLTXFile(ctx, level, minTXID, maxTXID, f)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"write ltx file: %w\", err)\n\t}\n\tr.Logger().Debug(\"ltx file uploaded\",\n\t\t\"level\", info.Level,\n\t\t\"minTXID\", info.MinTXID,\n\t\t\"maxTXID\", info.MaxTXID,\n\t\t\"size\", info.Size)\n\n\t// Track current position\n\t//replicaWALIndexGaugeVec.WithLabelValues(r.db.Path(), r.Name()).Set(float64(rd.Pos().Index))\n\t//replicaWALOffsetGaugeVec.WithLabelValues(r.db.Path(), r.Name()).Set(float64(rd.Pos().Offset))\n\n\treturn nil\n}\n\n// calcPos returns the last position saved to the replica for level 0.\nfunc (r *Replica) calcPos(ctx context.Context) (pos ltx.Pos, err error) {\n\tinfo, err := r.MaxLTXFileInfo(ctx, 0)\n\tif err != nil {\n\t\treturn pos, fmt.Errorf(\"max ltx file: %w\", err)\n\t}\n\treturn ltx.Pos{TXID: info.MaxTXID}, nil\n}\n\n// MaxLTXFileInfo returns metadata about the last LTX file for a given level.\n// Returns nil if no files exist for the level.\nfunc (r *Replica) MaxLTXFileInfo(ctx context.Context, level int) (info ltx.FileInfo, err error) {\n\t// Normal operation - use fast timestamps\n\titr, err := r.Client.LTXFiles(ctx, level, 0, false)\n\tif err != nil {\n\t\treturn info, err\n\t}\n\tdefer itr.Close()\n\n\tfor itr.Next() {\n\t\titem := itr.Item()\n\t\tif item.MaxTXID \u003e info.MaxTXID {\n\t\t\tinfo = *item\n\t\t}\n\t}\n\treturn info, itr.Close()\n}\n\n// Pos returns the current replicated position.\n// Returns a zero value if the current position cannot be determined.\nfunc (r *Replica) Pos() ltx.Pos {\n\tr.mu.RLock()\n\tdefer r.mu.RUnlock()\n\treturn r.pos\n}\n\n// SetPos sets the current replicated position.\nfunc (r *Replica) SetPos(pos ltx.Pos) {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\tr.pos = pos\n}\n\n// EnforceRetention forces a new snapshot once the retention interval has passed.\n// Older snapshots and WAL files are then removed.\nfunc (r *Replica) EnforceRetention(ctx context.Context) (err error) {\n\tpanic(\"TODO(ltx): Re-implement after multi-level compaction\")\n\n\t/*\n\t\t// Obtain list of snapshots that are within the retention period.\n\t\tsnapshots, err := r.Snapshots(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"snapshots: %w\", err)\n\t\t}\n\t\tretained := FilterSnapshotsAfter(snapshots, time.Now().Add(-r.Retention))\n\n\t\t// If no retained snapshots exist, create a new snapshot.\n\t\tif len(retained) == 0 {\n\t\t\tsnapshot, err := r.Snapshot(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"snapshot: %w\", err)\n\t\t\t}\n\t\t\tretained = append(retained, snapshot)\n\t\t}\n\n\t\t// Delete unretained snapshots \u0026 WAL files.\n\t\tsnapshot := FindMinSnapshot(retained)\n\n\t\t// Otherwise remove all earlier snapshots \u0026 WAL segments.\n\t\tif err := r.deleteSnapshotsBeforeIndex(ctx, snapshot.Index); err != nil {\n\t\t\treturn fmt.Errorf(\"delete snapshots before index: %w\", err)\n\t\t} else if err := r.deleteWALSegmentsBeforeIndex(ctx, snapshot.Index); err != nil {\n\t\t\treturn fmt.Errorf(\"delete wal segments before index: %w\", err)\n\t\t}\n\n\t\treturn nil\n\t*/\n}\n\n/*\nfunc (r *Replica) deleteBeforeTXID(ctx context.Context, level int, txID ltx.TXID) error {\n\titr, err := r.Client.LTXFiles(ctx, level)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar a []*ltx.FileInfo\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\t\tif info.MinTXID \u003e= txID {\n\t\t\tcontinue\n\t\t}\n\t\ta = append(a, info)\n\t}\n\tif err := itr.Close(); err != nil {\n\t\treturn err\n\t}\n\n\tif len(a) == 0 {\n\t\treturn nil\n\t}\n\n\tif err := r.Client.DeleteLTXFiles(ctx, a); err != nil {\n\t\treturn fmt.Errorf(\"delete wal segments: %w\", err)\n\t}\n\n\tr.Logger().Info(\"ltx files deleted before\",\n\t\tslog.Int(\"level\", level),\n\t\tslog.String(\"txID\", txID.String()),\n\t\tslog.Int(\"n\", len(a)))\n\n\treturn nil\n}\n*/\n\n// monitor runs in a separate goroutine and continuously replicates the DB.\n// Implements exponential backoff on repeated sync errors to prevent log spam\n// and reduce load when persistent errors occur. See issue #927.\nfunc (r *Replica) monitor(ctx context.Context) {\n\tticker := time.NewTicker(r.SyncInterval)\n\tdefer ticker.Stop()\n\n\tnotify := r.db.Notify()\n\n\tvar backoff time.Duration\n\tvar lastLogTime time.Time\n\tvar consecutiveErrs int\n\n\tfor initial := true; ; initial = false {\n\t\t// Enforce a minimum time between synchronization.\n\t\tif !initial {\n\t\t\tselect {\n\t\t\tcase \u003c-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase \u003c-ticker.C:\n\t\t\t}\n\t\t}\n\n\t\t// If in backoff mode, wait additional time before retrying.\n\t\tif backoff \u003e 0 {\n\t\t\tselect {\n\t\t\tcase \u003c-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase \u003c-time.After(backoff):\n\t\t\t}\n\t\t}\n\n\t\t// If the position is unavailable, skip the wait so Sync() surfaces\n\t\t// the error to the backoff \u0026 auto-recovery handling below.\n\t\tif pos, err := r.db.Pos(); err == nil \u0026\u0026 pos.TXID \u003c= r.Pos().TXID {\n\t\t\t// Wait for new data, but still sync on an interval so idle\n\t\t\t// databases continue recording sync health for heartbeats.\n\t\t\tselect {\n\t\t\tcase \u003c-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase \u003c-ticker.C:\n\t\t\tcase \u003c-notify:\n\t\t\t}\n\t\t} else if ctx.Err() != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Fetch new notify channel before replicating data.\n\t\tnotify = r.db.Notify()\n\n\t\t// Synchronize the shadow wal into the replication directory.\n\t\tif err := r.sync(ctx, r.MaxSyncLTXFiles); err != nil {\n\t\t\tif errors.Is(err, errReplicaWaitForData) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Don't log context cancellation errors during shutdown\n\t\t\tif !isShutdownError(ctx, err) {\n\t\t\t\tconsecutiveErrs++\n\n\t\t\t\t// Exponential backoff: SyncInterval -\u003e 2x -\u003e 4x -\u003e ... -\u003e max\n\t\t\t\tif backoff == 0 {\n\t\t\t\t\tbackoff = r.SyncInterval\n\t\t\t\t} else {\n\t\t\t\t\tbackoff *= 2\n\t\t\t\t\tif backoff \u003e DefaultSyncBackoffMax {\n\t\t\t\t\t\tbackoff = DefaultSyncBackoffMax\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for LTX errors and include recovery hints\n\t\t\t\tvar ltxErr *LTXError\n\t\t\t\tif errors.As(err, \u0026ltxErr) {\n\t\t\t\t\t// Log with rate limiting to avoid log spam during persistent errors.\n\t\t\t\t\tif time.Since(lastLogTime) \u003e= SyncErrorLogInterval {\n\t\t\t\t\t\tif ltxErr.Hint != \"\" {\n\t\t\t\t\t\t\tr.Logger().Error(\"monitor error\",\n\t\t\t\t\t\t\t\t\"error\", err,\n\t\t\t\t\t\t\t\t\"path\", ltxErr.Path,\n\t\t\t\t\t\t\t\t\"hint\", ltxErr.Hint,\n\t\t\t\t\t\t\t\t\"consecutive_errors\", consecutiveErrs,\n\t\t\t\t\t\t\t\t\"backoff\", backoff)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tr.Logger().Error(\"monitor error\",\n\t\t\t\t\t\t\t\t\"error\", err,\n\t\t\t\t\t\t\t\t\"path\", ltxErr.Path,\n\t\t\t\t\t\t\t\t\"consecutive_errors\", consecutiveErrs,\n\t\t\t\t\t\t\t\t\"backoff\", backoff)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlastLogTime = time.Now()\n\t\t\t\t\t}\n\n\t\t\t\t\t// Attempt auto-recovery only for missing/corrupt LTX files.\n\t\t\t\t\t// Transient OS errors (EMFILE, EIO, EACCES) should retry\n\t\t\t\t\t// with backoff rather than destructively resetting state.\n\t\t\t\t\tif r.AutoRecoverEnabled \u0026\u0026 ltxErr.IsAutoRecoverable() {\n\t\t\t\t\t\tr.Logger().Warn(\"auto-recovery enabled, resetting local state\")\n\t\t\t\t\t\tif resetErr := r.db.ResetLocalState(ctx); resetErr != nil {\n\t\t\t\t\t\t\tr.Logger().Error(\"auto-recovery failed\", \"error\", resetErr)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tr.Logger().Info(\"auto-recovery complete, resuming replication\")\n\t\t\t\t\t\t\t// Reset backoff after successful recovery\n\t\t\t\t\t\t\tbackoff = 0\n\t\t\t\t\t\t\tconsecutiveErrs = 0\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Log with rate limiting to avoid log spam during persistent errors.\n\t\t\t\t\tif time.Since(lastLogTime) \u003e= SyncErrorLogInterval {\n\t\t\t\t\t\tr.Logger().Error(\"monitor error\",\n\t\t\t\t\t\t\t\"error\", err,\n\t\t\t\t\t\t\t\"consecutive_errors\", consecutiveErrs,\n\t\t\t\t\t\t\t\"backoff\", backoff)\n\t\t\t\t\t\tlastLogTime = time.Now()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Success - reset backoff and error counter.\n\t\tif consecutiveErrs \u003e 0 {\n\t\t\tr.Logger().Info(\"replica sync recovered\", \"previous_errors\", consecutiveErrs)\n\t\t}\n\t\tbackoff = 0\n\t\tconsecutiveErrs = 0\n\t}\n}\n\n// CreatedAt returns the earliest creation time of any LTX file.\n// Returns zero time if no LTX files exist.\nfunc (r *Replica) CreatedAt(ctx context.Context) (time.Time, error) {\n\tvar min time.Time\n\n\t// Normal operation - use fast timestamps\n\titr, err := r.Client.LTXFiles(ctx, 0, 0, false)\n\tif err != nil {\n\t\treturn min, err\n\t}\n\tdefer itr.Close()\n\n\tif itr.Next() {\n\t\tmin = itr.Item().CreatedAt\n\t}\n\treturn min, itr.Close()\n}\n\n// TimeBounds returns the creation time \u0026 last updated time.\n// Returns zero time if no LTX files exist.\nfunc (r *Replica) TimeBounds(ctx context.Context) (createdAt, updatedAt time.Time, err error) {\n\tfor level := SnapshotLevel; level \u003e= 0; level-- {\n\t\titr, err := r.Client.LTXFiles(ctx, level, 0, false)\n\t\tif err != nil {\n\t\t\treturn createdAt, updatedAt, err\n\t\t}\n\n\t\tfor itr.Next() {\n\t\t\tinfo := itr.Item()\n\t\t\tif createdAt.IsZero() || info.CreatedAt.Before(createdAt) {\n\t\t\t\tcreatedAt = info.CreatedAt\n\t\t\t}\n\t\t\tif updatedAt.IsZero() || info.CreatedAt.After(updatedAt) {\n\t\t\t\tupdatedAt = info.CreatedAt\n\t\t\t}\n\t\t}\n\t\tif err := itr.Close(); err != nil {\n\t\t\treturn createdAt, updatedAt, err\n\t\t}\n\t}\n\treturn createdAt, updatedAt, nil\n}\n\n// CalcRestoreTarget returns a target time restore from.\nfunc (r *Replica) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (updatedAt time.Time, err error) {\n\t// Determine the replicated time bounds from LTX files.\n\tcreatedAt, updatedAt, err := r.TimeBounds(ctx)\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"created at: %w\", err)\n\t}\n\n\t// Also check v0.3.x time bounds if client supports it.\n\tif client, ok := r.Client.(ReplicaClientV3); ok {\n\t\tv3CreatedAt, v3UpdatedAt, err := r.TimeBoundsV3(ctx, client)\n\t\tif err != nil {\n\t\t\treturn time.Time{}, fmt.Errorf(\"v0.3.x time bounds: %w\", err)\n\t\t}\n\t\t// Extend time bounds to include v0.3.x backups.\n\t\tif !v3CreatedAt.IsZero() \u0026\u0026 (createdAt.IsZero() || v3CreatedAt.Before(createdAt)) {\n\t\t\tcreatedAt = v3CreatedAt\n\t\t}\n\t\tif !v3UpdatedAt.IsZero() \u0026\u0026 (updatedAt.IsZero() || v3UpdatedAt.After(updatedAt)) {\n\t\t\tupdatedAt = v3UpdatedAt\n\t\t}\n\t}\n\n\t// Skip if it does not contain timestamp.\n\tif !opt.Timestamp.IsZero() {\n\t\tif createdAt.IsZero() \u0026\u0026 updatedAt.IsZero() {\n\t\t\treturn time.Time{}, fmt.Errorf(\"no backups found\")\n\t\t}\n\t\tif opt.Timestamp.Before(createdAt) || opt.Timestamp.After(updatedAt) {\n\t\t\treturn time.Time{}, fmt.Errorf(\"timestamp does not exist\")\n\t\t}\n\t}\n\n\treturn updatedAt, nil\n}\n\n// Replica restores the database from a replica based on the options given.\n// This method will restore into opt.OutputPath, if specified, or into the\n// DB's original database path. It can optionally restore from a specific\n// replica or it will automatically choose the best one. Finally,\n// a timestamp can be specified to restore the database to a specific\n// point-in-time.\n//\n// When the replica contains both v0.3.x and LTX format backups, this method\n// compares snapshots from both formats and uses whichever has the better backup:\n// - With timestamp: uses the format with the most recent snapshot before timestamp\n// - Without timestamp: uses the format with the most recent backup overall\nfunc (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {\n\t// Validate options.\n\tif opt.OutputPath == \"\" {\n\t\treturn fmt.Errorf(\"output path required\")\n\t} else if opt.TXID != 0 \u0026\u0026 !opt.Timestamp.IsZero() {\n\t\treturn fmt.Errorf(\"cannot specify index \u0026 timestamp to restore\")\n\t} else if opt.Follow \u0026\u0026 opt.TXID != 0 {\n\t\treturn fmt.Errorf(\"cannot use follow mode with -txid\")\n\t} else if opt.Follow \u0026\u0026 !opt.Timestamp.IsZero() {\n\t\treturn fmt.Errorf(\"cannot use follow mode with -timestamp\")\n\t} else if opt.IntegrityCheck != IntegrityCheckNone \u0026\u0026 opt.IntegrityCheck != IntegrityCheckQuick \u0026\u0026 opt.IntegrityCheck != IntegrityCheckFull {\n\t\treturn fmt.Errorf(\"unsupported integrity check mode: %d\", opt.IntegrityCheck)\n\t}\n\n\t// In follow mode, if the database already exists, attempt crash recovery\n\t// by reading the last applied TXID from the sidecar file.\n\tif opt.Follow {\n\t\tif _, statErr := os.Stat(opt.OutputPath); statErr == nil {\n\t\t\ttxid, readErr := ReadTXIDFile(opt.OutputPath)\n\t\t\tif readErr != nil {\n\t\t\t\treturn fmt.Errorf(\"read txid file for crash recovery: %w\", readErr)\n\t\t\t}\n\t\t\tif txid == 0 {\n\t\t\t\treturn fmt.Errorf(\"cannot resume follow mode: database exists but no -txid file found; delete the database to re-restore: %s\", opt.OutputPath)\n\t\t\t}\n\t\t\t// Validate saved TXID is still reachable. If the earliest snapshot\n\t\t\t// starts after our saved TXID, retention has pruned the history\n\t\t\t// and we can't catch up incrementally.\n\t\t\tsnapshotItr, itrErr := r.Client.LTXFiles(ctx, SnapshotLevel, 0, false)\n\t\t\tif itrErr != nil {\n\t\t\t\treturn fmt.Errorf(\"cannot validate saved TXID for crash recovery: %w\", itrErr)\n\t\t\t}\n\n\t\t\tvar latestSnapshot *ltx.FileInfo\n\t\t\tfor snapshotItr.Next() {\n\t\t\t\tlatestSnapshot = snapshotItr.Item()\n\t\t\t}\n\t\t\tif err := snapshotItr.Err(); err != nil {\n\t\t\t\t_ = snapshotItr.Close()\n\t\t\t\treturn fmt.Errorf(\"iterate snapshots for crash recovery validation: %w\", err)\n\t\t\t}\n\t\t\t_ = snapshotItr.Close()\n\n\t\t\tif latestSnapshot != nil {\n\t\t\t\tif latestSnapshot.MinTXID \u003e txid {\n\t\t\t\t\treturn fmt.Errorf(\"cannot resume follow mode: saved TXID %s is behind the earliest snapshot (min TXID %s); replica history has been pruned -- delete %s and %s-txid to re-restore\", txid, latestSnapshot.MinTXID, opt.OutputPath, opt.OutputPath)\n\t\t\t\t}\n\t\t\t\tif txid \u003e latestSnapshot.MaxTXID {\n\t\t\t\t\treturn fmt.Errorf(\"cannot resume follow mode: saved TXID %s is ahead of latest snapshot (max TXID %s); delete %s and %s-txid to re-restore\", txid, latestSnapshot.MaxTXID, opt.OutputPath, opt.OutputPath)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tr.Logger().Info(\"resuming follow mode from crash recovery\", \"txid\", txid, \"output\", opt.OutputPath)\n\t\t\treturn r.follow(ctx, opt.OutputPath, txid, opt.FollowInterval)\n\t\t}\n\t}\n\n\t// Ensure output path does not already exist.\n\tif _, err := os.Stat(opt.OutputPath); err == nil {\n\t\treturn fmt.Errorf(\"cannot restore, output path already exists: %s\", opt.OutputPath)\n\t} else if !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\n\t// Compare v0.3.x and LTX formats to find the best backup (unless TXID is specified).\n\t// Skip V3 format when follow mode is enabled (V3 doesn't support incremental following).\n\tif opt.TXID == 0 \u0026\u0026 !opt.Follow {\n\t\tif client, ok := r.Client.(ReplicaClientV3); ok {\n\t\t\tuseV3, err := r.shouldUseV3Restore(ctx, client, opt.Timestamp)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif useV3 {\n\t\t\t\treturn r.RestoreV3(ctx, opt)\n\t\t\t}\n\t\t}\n\t}\n\n\tinfos, err := CalcRestorePlan(ctx, r.Client, opt.TXID, opt.Timestamp, r.Logger())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot calc restore plan: %w\", err)\n\t}\n\n\tr.Logger().Debug(\"restore plan\", \"n\", len(infos), \"txid\", infos[len(infos)-1].MaxTXID, \"timestamp\", infos[len(infos)-1].CreatedAt)\n\n\trdrs := make([]io.Reader, 0, len(infos))\n\tdefer func() {\n\t\tfor _, rd := range rdrs {\n\t\t\tif closer, ok := rd.(io.Closer); ok {\n\t\t\t\t_ = closer.Close()\n\t\t\t}\n\t\t}\n\t}()\n\n\tfor _, info := range infos {\n\t\t// Validate file size - must be at least header size to be readable\n\t\tif info.Size \u003c ltx.HeaderSize {\n\t\t\treturn fmt.Errorf(\"invalid ltx file: level=%d min=%s max=%s has size %d bytes (minimum %d)\",\n\t\t\t\tinfo.Level, info.MinTXID, info.MaxTXID, info.Size, ltx.HeaderSize)\n\t\t}\n\n\t\tr.Logger().Debug(\"opening ltx file for restore\", \"level\", info.Level, \"min\", info.MinTXID, \"max\", info.MaxTXID)\n\n\t\trdrs = append(rdrs, internal.NewResumableReader(ctx, r.Client, info.Level, info.MinTXID, info.MaxTXID, info.Size, nil, r.Logger()))\n\t}\n\n\tif len(rdrs) == 0 {\n\t\treturn fmt.Errorf(\"no matching backup files available\")\n\t}\n\n\t// Create parent directory if it doesn't exist.\n\tvar dirInfo os.FileInfo\n\tif db := r.DB(); db != nil {\n\t\tdirInfo = db.dirInfo\n\t}\n\tif err := internal.MkdirAll(filepath.Dir(opt.OutputPath), dirInfo); err != nil {\n\t\treturn fmt.Errorf(\"create parent directory: %w\", err)\n\t}\n\n\t// Output to temp file \u0026 atomically rename.\n\ttmpOutputPath := opt.OutputPath + \".tmp\"\n\tr.Logger().Debug(\"compacting into database\", \"path\", tmpOutputPath, \"n\", len(rdrs))\n\tdefer func() { _ = os.Remove(tmpOutputPath) }()\n\n\tf, err := os.Create(tmpOutputPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create temp database path: %w\", err)\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tpr, pw := io.Pipe()\n\n\tgo func() {\n\t\tc, err := ltx.NewCompactor(pw, rdrs)\n\t\tif err != nil {\n\t\t\tpw.CloseWithError(fmt.Errorf(\"new ltx compactor: %w\", err))\n\t\t\treturn\n\t\t}\n\t\tc.HeaderFlags = ltx.HeaderFlagNoChecksum\n\t\t_ = pw.CloseWithError(c.Compact(ctx))\n\t}()\n\n\tdec := ltx.NewDecoder(pr)\n\tif err := dec.DecodeDatabaseTo(f); err != nil {\n\t\treturn fmt.Errorf(\"decode database: %w\", err)\n\t}\n\n\tif err := f.Sync(); err != nil {\n\t\treturn err\n\t} else if err := f.Close(); err != nil {\n\t\treturn err\n\t}\n\n\t// Copy file to final location.\n\tr.Logger().Debug(\"renaming database from temporary location\")\n\tif err := os.Rename(tmpOutputPath, opt.OutputPath); err != nil {\n\t\treturn err\n\t}\n\tif err := internal.FsyncDir(filepath.Dir(opt.OutputPath)); err != nil {\n\t\treturn fmt.Errorf(\"sync restore output dir: %w\", err)\n\t}\n\n\tif opt.IntegrityCheck != IntegrityCheckNone {\n\t\tif err := checkIntegrity(ctx, opt.OutputPath, opt.IntegrityCheck); err != nil {\n\t\t\tif ctx.Err() == nil {\n\t\t\t\t_ = os.Remove(opt.OutputPath)\n\t\t\t\t_ = os.Remove(opt.OutputPath + \"-shm\")\n\t\t\t\t_ = os.Remove(opt.OutputPath + \"-wal\")\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"post-restore integrity check: %w\", err)\n\t\t}\n\t\tr.Logger().Info(\"post-restore integrity check passed\")\n\t}\n\n\t// Enter follow mode if enabled, continuously applying new LTX files.\n\tif opt.Follow {\n\t\tfor _, rd := range rdrs {\n\t\t\tif closer, ok := rd.(io.Closer); ok {\n\t\t\t\t_ = closer.Close()\n\t\t\t}\n\t\t}\n\t\trdrs = nil\n\n\t\tmaxTXID := infos[len(infos)-1].MaxTXID\n\t\tif err := WriteTXIDFile(opt.OutputPath, maxTXID); err != nil {\n\t\t\treturn fmt.Errorf(\"write initial txid file: %w\", err)\n\t\t}\n\t\treturn r.follow(ctx, opt.OutputPath, maxTXID, opt.FollowInterval)\n\t}\n\n\treturn nil\n}\n\n// follow enters a continuous restore loop, polling for new LTX files and\n// applying them to the restored database. It blocks until the context is\n// cancelled (e.g. Ctrl+C). Returns nil on clean shutdown.\nfunc (r *Replica) follow(ctx context.Context, outputPath string, lastTXID ltx.TXID, interval time.Duration) error {\n\tf, err := os.OpenFile(outputPath, os.O_RDWR, 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open database for follow: %w\", err)\n\t}\n\tdefer func() {\n\t\t_ = f.Sync()\n\t\t_ = f.Close()\n\t}()\n\n\t// Read page size from SQLite header (offset 16, 2 bytes, big-endian).\n\tvar buf [2]byte\n\tif _, err := f.ReadAt(buf[:], 16); err != nil {\n\t\treturn fmt.Errorf(\"read page size from database header: %w\", err)\n\t}\n\tpageSize := uint32(buf[0])\u003c\u003c8 | uint32(buf[1])\n\tif pageSize == 1 {\n\t\tpageSize = 65536\n\t}\n\n\tif interval \u003c= 0 {\n\t\tinterval = DefaultFollowInterval\n\t}\n\n\tr.Logger().Info(\"entering follow mode\", \"output\", outputPath, \"txid\", lastTXID, \"interval\", interval)\n\n\tticker := time.NewTicker(interval)\n\tdefer ticker.Stop()\n\n\tvar consecutiveErrors int\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\tr.Logger().Info(\"follow mode stopped\")\n\t\t\treturn nil\n\t\tcase \u003c-ticker.C:\n\t\t\tnewTXID, err := r.applyNewLTXFiles(ctx, f, lastTXID, pageSize)\n\t\t\tif err != nil {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\tr.Logger().Info(\"follow mode stopped\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tconsecutiveErrors++\n\t\t\t\tr.Logger().Error(\"follow: error applying updates\", \"err\", err, \"consecutive_errors\", consecutiveErrors)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif newTXID \u003e lastTXID {\n\t\t\t\tif err := WriteTXIDFile(outputPath, newTXID); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"write txid file: %w\", err)\n\t\t\t\t}\n\t\t\t\tr.Logger().Info(\"follow: applied updates\", \"from_txid\", lastTXID, \"to_txid\", newTXID)\n\t\t\t\tlastTXID = newTXID\n\t\t\t\tconsecutiveErrors = 0\n\t\t\t}\n\t\t}\n\t}\n}\n\n// applyNewLTXFiles polls for new LTX files and applies them to the database.\n// It starts from level 0 and falls back to higher levels if there are gaps\n// (e.g., level 0 files were compacted away).\nfunc (r *Replica) applyNewLTXFiles(ctx context.Context, f *os.File, afterTXID ltx.TXID, pageSize uint32) (ltx.TXID, error) {\n\tcurrentTXID := afterTXID\n\n\t// Poll level 0 for the most recent incremental files.\n\titr, err := r.Client.LTXFiles(ctx, 0, currentTXID+1, false)\n\tif err != nil {\n\t\treturn currentTXID, fmt.Errorf(\"list level 0 ltx files: %w\", err)\n\t}\n\tcloseLevel0 := func(retErr error) (ltx.TXID, error) {\n\t\tif closeErr := itr.Close(); closeErr != nil {\n\t\t\tcloseErr = fmt.Errorf(\"close level 0 ltx iterator: %w\", closeErr)\n\t\t\tif retErr != nil {\n\t\t\t\treturn currentTXID, errors.Join(retErr, closeErr)\n\t\t\t}\n\t\t\treturn currentTXID, closeErr\n\t\t}\n\t\treturn currentTXID, retErr\n\t}\n\n\tvar sawLevel0 bool\n\tfor itr.Next() {\n\t\tsawLevel0 = true\n\t\tinfo := itr.Item()\n\n\t\t// If there's a gap, try to fill it from higher compaction levels.\n\t\tif info.MinTXID \u003e currentTXID+1 {\n\t\t\tbridgedTXID, err := r.fillFollowGap(ctx, f, currentTXID, info.MinTXID, pageSize)\n\t\t\tif err != nil {\n\t\t\t\treturn closeLevel0(err)\n\t\t\t}\n\t\t\tcurrentTXID = bridgedTXID\n\n\t\t\t// Re-check if this file is still needed after bridging.\n\t\t\tif info.MaxTXID \u003c= currentTXID {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif info.MinTXID \u003e currentTXID+1 {\n\t\t\t\treturn closeLevel0(nil)\n\t\t\t}\n\t\t}\n\n\t\t// Skip if already covered by a higher-level file.\n\t\tif info.MaxTXID \u003c= currentTXID {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := r.applyLTXFile(ctx, f, info, pageSize); err != nil {\n\t\t\treturn closeLevel0(fmt.Errorf(\n\t\t\t\t\"apply ltx file (level=%d, min=%s, max=%s): %w\",\n\t\t\t\tinfo.Level, info.MinTXID, info.MaxTXID, err,\n\t\t\t))\n\t\t}\n\t\tcurrentTXID = info.MaxTXID\n\t}\n\n\tif iterErr := itr.Err(); iterErr != nil {\n\t\treturn closeLevel0(fmt.Errorf(\"iterate level 0 ltx files: %w\", iterErr))\n\t}\n\tif _, err := closeLevel0(nil); err != nil {\n\t\treturn currentTXID, err\n\t}\n\n\tif !sawLevel0 {\n\t\tbridgedTXID, err := r.fillFollowGap(ctx, f, currentTXID, currentTXID+1, pageSize)\n\t\tif err != nil {\n\t\t\treturn currentTXID, err\n\t\t}\n\t\tcurrentTXID = bridgedTXID\n\t}\n\n\treturn currentTXID, nil\n}\n\n// applyLTXFile applies a single LTX file's pages to the database file.\n// This follows the same pattern as Hydrator.ApplyLTX (vfs.go:712-747).\n//\n// To prevent concurrent SQLite readers from seeing partial updates, we acquire\n// an exclusive file lock before writing. We also rewrite the SQLite header\n// (bytes 18-19) to indicate DELETE journal mode instead of WAL mode, and\n// randomize the schema change counter (bytes 24-27) to invalidate cached\n// schemas in other connections.\nfunc (r *Replica) applyLTXFile(ctx context.Context, f *os.File, info *ltx.FileInfo, pageSize uint32) error {\n\trc, err := r.Client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\tdefer rc.Close()\n\n\tdec := ltx.NewDecoder(rc)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\treturn fmt.Errorf(\"decode header: %w\", err)\n\t}\n\n\thdr := dec.Header()\n\n\tif err := internal.LockFileExclusive(f); err != nil {\n\t\treturn fmt.Errorf(\"acquire exclusive lock: %w\", err)\n\t}\n\tdefer internal.UnlockFile(f)\n\n\tfor {\n\t\tvar phdr ltx.PageHeader\n\t\tdata := make([]byte, pageSize)\n\t\tif err := dec.DecodePage(\u0026phdr, data); err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"decode page: %w\", err)\n\t\t}\n\n\t\tif phdr.Pgno == 1 \u0026\u0026 len(data) \u003e= 28 {\n\t\t\tdata[18], data[19] = 0x01, 0x01\n\t\t\t_, _ = rand.Read(data[24:28])\n\t\t}\n\n\t\toff := int64(phdr.Pgno-1) * int64(pageSize)\n\t\tif _, err := f.WriteAt(data, off); err != nil {\n\t\t\treturn fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n\t\t}\n\t}\n\n\tif hdr.Commit \u003e 0 {\n\t\tif err := f.Sync(); err != nil {\n\t\t\treturn fmt.Errorf(\"sync before truncate: %w\", err)\n\t\t}\n\t\tnewSize := int64(hdr.Commit) * int64(pageSize)\n\t\tif err := f.Truncate(newSize); err != nil {\n\t\t\treturn fmt.Errorf(\"truncate: %w\", err)\n\t\t}\n\t}\n\n\tif err := dec.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close decoder: %w\", err)\n\t}\n\n\treturn f.Sync()\n}\n\n// fillFollowGap attempts to bridge a gap in level 0 files by searching\n// higher compaction levels for a file that covers the missing TXID range.\nfunc (r *Replica) fillFollowGap(ctx context.Context, f *os.File, afterTXID ltx.TXID, gapMinTXID ltx.TXID, pageSize uint32) (ltx.TXID, error) {\n\tcurrentTXID := afterTXID\n\n\tfor level := 1; level \u003c SnapshotLevel; level++ {\n\t\titr, err := r.Client.LTXFiles(ctx, level, 0, false)\n\t\tif err != nil {\n\t\t\treturn currentTXID, fmt.Errorf(\"list level %d ltx files: %w\", level, err)\n\t\t}\n\t\tcloseLevel := func(retErr error) (ltx.TXID, error) {\n\t\t\tif closeErr := itr.Close(); closeErr != nil {\n\t\t\t\tcloseErr = fmt.Errorf(\"close level %d ltx iterator: %w\", level, closeErr)\n\t\t\t\tif retErr != nil {\n\t\t\t\t\treturn currentTXID, errors.Join(retErr, closeErr)\n\t\t\t\t}\n\t\t\t\treturn currentTXID, closeErr\n\t\t\t}\n\t\t\treturn currentTXID, retErr\n\t\t}\n\n\t\tfor itr.Next() {\n\t\t\tinfo := itr.Item()\n\n\t\t\t// Skip if there's a gap at this level too.\n\t\t\tif info.MinTXID \u003e currentTXID+1 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Skip if already covered.\n\t\t\tif info.MaxTXID \u003c= currentTXID {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := r.applyLTXFile(ctx, f, info, pageSize); err != nil {\n\t\t\t\treturn closeLevel(fmt.Errorf(\n\t\t\t\t\t\"apply gap-fill ltx file (level=%d, min=%s, max=%s): %w\",\n\t\t\t\t\tinfo.Level, info.MinTXID, info.MaxTXID, err,\n\t\t\t\t))\n\t\t\t}\n\t\t\tcurrentTXID = info.MaxTXID\n\n\t\t\t// If we've bridged past the gap, we're done.\n\t\t\tif currentTXID+1 \u003e= gapMinTXID {\n\t\t\t\treturn closeLevel(nil)\n\t\t\t}\n\t\t}\n\n\t\tif iterErr := itr.Err(); iterErr != nil {\n\t\t\treturn closeLevel(fmt.Errorf(\"iterate level %d ltx files: %w\", level, iterErr))\n\t\t}\n\t\tif _, err := closeLevel(nil); err != nil {\n\t\t\treturn currentTXID, err\n\t\t}\n\n\t\t// If we made progress at this level, restart from level 1.\n\t\tif currentTXID \u003e afterTXID {\n\t\t\treturn currentTXID, nil\n\t\t}\n\t}\n\n\treturn currentTXID, nil\n}\n\n// RestoreV3 restores from a v0.3.x format backup.\nfunc (r *Replica) RestoreV3(ctx context.Context, opt RestoreOptions) error {\n\tclient, ok := r.Client.(ReplicaClientV3)\n\tif !ok {\n\t\treturn fmt.Errorf(\"replica client does not support v0.3.x restore\")\n\t}\n\n\t// Validate options.\n\tif opt.OutputPath == \"\" {\n\t\treturn fmt.Errorf(\"output path required\")\n\t} else if opt.IntegrityCheck != IntegrityCheckNone \u0026\u0026 opt.IntegrityCheck != IntegrityCheckQuick \u0026\u0026 opt.IntegrityCheck != IntegrityCheckFull {\n\t\treturn fmt.Errorf(\"unsupported integrity check mode: %d\", opt.IntegrityCheck)\n\t}\n\n\t// Ensure output path does not already exist.\n\tif _, err := os.Stat(opt.OutputPath); err == nil {\n\t\treturn fmt.Errorf(\"cannot restore, output path already exists: %s\", opt.OutputPath)\n\t} else if !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\n\t// Find all generations.\n\tgenerations, err := client.GenerationsV3(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"list generations: %w\", err)\n\t}\n\tif len(generations) == 0 {\n\t\treturn ErrNoSnapshots\n\t}\n\n\t// Collect all snapshots across all generations.\n\tvar allSnapshots []SnapshotInfoV3\n\tfor _, gen := range generations {\n\t\tsnapshots, err := client.SnapshotsV3(ctx, gen)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"list snapshots for generation %s: %w\", gen, err)\n\t\t}\n\t\tallSnapshots = append(allSnapshots, snapshots...)\n\t}\n\tif len(allSnapshots) == 0 {\n\t\treturn ErrNoSnapshots\n\t}\n\n\t// Sort all snapshots by CreatedAt for timestamp-based selection.\n\tsortSnapshotsV3ByCreatedAt(allSnapshots)\n\n\t// Find best snapshot across all generations (latest, or before timestamp if specified).\n\tsnapshot := findBestSnapshotV3(allSnapshots, opt.Timestamp)\n\tif snapshot == nil {\n\t\treturn ErrNoSnapshots\n\t}\n\n\tr.Logger().Debug(\"selected v0.3.x snapshot\",\n\t\t\"generation\", snapshot.Generation,\n\t\t\"index\", snapshot.Index,\n\t\t\"created_at\", snapshot.CreatedAt)\n\n\t// Get WAL segments for the snapshot's generation.\n\tsegments, err := client.WALSegmentsV3(ctx, snapshot.Generation)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"list WAL segments: %w\", err)\n\t}\n\tsegments = filterWALSegmentsV3(segments, snapshot.Index, opt.Timestamp)\n\n\tr.Logger().Debug(\"found v0.3.x WAL segments\", \"n\", len(segments))\n\n\t// Create parent directory if it doesn't exist.\n\tvar dirInfo os.FileInfo\n\tif db := r.DB(); db != nil {\n\t\tdirInfo = db.DirInfo()\n\t}\n\tif err := internal.MkdirAll(filepath.Dir(opt.OutputPath), dirInfo); err != nil {\n\t\treturn fmt.Errorf(\"create parent directory: %w\", err)\n\t}\n\n\t// Create temp file for restore.\n\ttmpPath := opt.OutputPath + \".tmp\"\n\tdefer func() { _ = os.Remove(tmpPath) }()\n\n\t// Download and decompress snapshot.\n\tif err := r.downloadSnapshotV3(ctx, client, snapshot.Generation, snapshot.Index, tmpPath); err != nil {\n\t\treturn fmt.Errorf(\"download snapshot: %w\", err)\n\t}\n\n\t// Apply WAL segments.\n\tif err := r.applyWALSegmentsV3(ctx, client, snapshot.Generation, snapshot.Index, segments, tmpPath); err != nil {\n\t\treturn fmt.Errorf(\"apply WAL segments: %w\", err)\n\t}\n\n\t// Rename to final path.\n\tif err := os.Rename(tmpPath, opt.OutputPath); err != nil {\n\t\treturn fmt.Errorf(\"rename to output path: %w\", err)\n\t}\n\tif err := internal.FsyncDir(filepath.Dir(opt.OutputPath)); err != nil {\n\t\treturn fmt.Errorf(\"sync restore output dir: %w\", err)\n\t}\n\n\tif opt.IntegrityCheck != IntegrityCheckNone {\n\t\tif err := checkIntegrity(ctx, opt.OutputPath, opt.IntegrityCheck); err != nil {\n\t\t\tif ctx.Err() == nil {\n\t\t\t\t_ = os.Remove(opt.OutputPath)\n\t\t\t\t_ = os.Remove(opt.OutputPath + \"-shm\")\n\t\t\t\t_ = os.Remove(opt.OutputPath + \"-wal\")\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"post-restore integrity check: %w\", err)\n\t\t}\n\t\tr.Logger().Info(\"post-restore integrity check passed\")\n\t}\n\n\treturn nil\n}\n\n// sortSnapshotsV3ByCreatedAt sorts snapshots by creation time in ascending order.\nfunc sortSnapshotsV3ByCreatedAt(snapshots []SnapshotInfoV3) {\n\tfor i := 0; i \u003c len(snapshots)-1; i++ {\n\t\tfor j := i + 1; j \u003c len(snapshots); j++ {\n\t\t\tif snapshots[i].CreatedAt.After(snapshots[j].CreatedAt) {\n\t\t\t\tsnapshots[i], snapshots[j] = snapshots[j], snapshots[i]\n\t\t\t}\n\t\t}\n\t}\n}\n\n// findBestSnapshotV3 finds the best snapshot for restore.\n// If timestamp is zero, returns the latest snapshot.\n// Otherwise, returns the latest snapshot created before or at the timestamp.\nfunc findBestSnapshotV3(snapshots []SnapshotInfoV3, timestamp time.Time) *SnapshotInfoV3 {\n\tif len(snapshots) == 0 {\n\t\treturn nil\n\t}\n\tif timestamp.IsZero() {\n\t\treturn \u0026snapshots[len(snapshots)-1]\n\t}\n\tfor i := len(snapshots) - 1; i \u003e= 0; i-- {\n\t\tif !snapshots[i].CreatedAt.After(timestamp) {\n\t\t\treturn \u0026snapshots[i]\n\t\t}\n\t}\n\treturn nil\n}\n\n// filterWALSegmentsV3 filters WAL segments to those at or after the snapshot index\n// and optionally before the timestamp.\nfunc filterWALSegmentsV3(segments []WALSegmentInfoV3, snapshotIndex int, timestamp time.Time) []WALSegmentInfoV3 {\n\tvar result []WALSegmentInfoV3\n\tfor _, seg := range segments {\n\t\tif seg.Index \u003c snapshotIndex {\n\t\t\tcontinue\n\t\t}\n\t\tif !timestamp.IsZero() \u0026\u0026 seg.CreatedAt.After(timestamp) {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, seg)\n\t}\n\treturn result\n}\n\n// downloadSnapshotV3 downloads and decompresses a v0.3.x snapshot to the destination path.\nfunc (r *Replica) downloadSnapshotV3(ctx context.Context, client ReplicaClientV3, generation string, index int, destPath string) error {\n\trc, err := client.OpenSnapshotV3(ctx, generation, index)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() { _ = rc.Close() }()\n\n\tf, err := os.Create(destPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tif _, err := io.Copy(f, rc); err != nil {\n\t\treturn err\n\t}\n\treturn f.Sync()\n}\n\n// applyWALSegmentsV3 applies WAL segments to the database file.\nfunc (r *Replica) applyWALSegmentsV3(ctx context.Context, client ReplicaClientV3, generation string, snapshotIndex int, segments []WALSegmentInfoV3, dbPath string) error {\n\tif len(segments) == 0 {\n\t\treturn nil\n\t}\n\n\t// Write all WAL segments to the WAL file.\n\twalPath := dbPath + \"-wal\"\n\tvar f *os.File\n\tvar err error\n\texpectedIndex := snapshotIndex\n\toffset := int64(0)\n\n\tdefer func() {\n\t\tif f != nil {\n\t\t\t_ = f.Close()\n\t\t}\n\t}()\n\n\tapplyLastWalFile := func() error {\n\t\tif f == nil {\n\t\t\treturn nil\n\t\t} else if err = f.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tf = nil\n\t\tif err = checkpointV3(dbPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr.Logger().Debug(\"applied WAL index\", \"generation\", generation, \"index\", expectedIndex-1, \"bytes\", offset)\n\t\treturn nil\n\t}\n\n\t// Reconstruct each wal file, appending non-zero offsets, and apply them\n\t// to the db.\n\tfor _, seg := range segments {\n\t\tif seg.Offset == 0 {\n\t\t\t// Apply the last WAL file, if any\n\t\t\tif err = applyLastWalFile(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif seg.Index != expectedIndex {\n\t\t\t\treturn fmt.Errorf(\"missing WAL index: expected %d/0, got %d/%d\", expectedIndex, seg.Index, seg.Offset)\n\t\t\t}\n\t\t\toffset = 0\n\t\t\t// Open a new WAL file\n\t\t\tif f, err = os.OpenFile(walPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\texpectedIndex++\n\t\t} else if seg.Offset != offset {\n\t\t\treturn fmt.Errorf(\"missing WAL segment: expected %d/%d, got %d/%d\", seg.Index, offset, seg.Index, seg.Offset)\n\t\t}\n\t\tif n, err := r.appendWALSegmentV3(ctx, client, generation, seg, f); err != nil {\n\t\t\treturn fmt.Errorf(\"write WAL segment %d/%d: %w\", seg.Index, seg.Offset, err)\n\t\t} else {\n\t\t\toffset += n\n\t\t\tr.Logger().Debug(\"wrote WAL segment\", \"generation\", generation, \"index\", seg.Index, \"offset\", seg.Offset, \"bytes\", n)\n\t\t}\n\t}\n\n\treturn applyLastWalFile()\n}\n\n// appendWALSegmentV3 appends the specified segment to an open WAL file f.\nfunc (r *Replica) appendWALSegmentV3(ctx context.Context, client ReplicaClientV3, generation string, seg WALSegmentInfoV3, f *os.File) (int64, error) {\n\t// Download WAL segment.\n\trc, err := client.OpenWALSegmentV3(ctx, generation, seg.Index, seg.Offset)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer func() { _ = rc.Close() }()\n\n\treturn io.Copy(f, rc)\n}\n\n// checkpointV3 checkpoints the WAL file into the database.\nfunc checkpointV3(dbPath string) error {\n\tdb, err := sql.Open(\"sqlite\", dbPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() { _ = db.Close() }()\n\n\t_, err = db.Exec(\"PRAGMA wal_checkpoint(TRUNCATE)\")\n\treturn err\n}\n\n// checkIntegrity runs a SQLite integrity check on the database at dbPath.\nfunc checkIntegrity(ctx context.Context, dbPath string, mode IntegrityCheckMode) error {\n\tif mode == IntegrityCheckNone {\n\t\treturn nil\n\t}\n\n\tdb, err := sql.Open(\"sqlite\", dbPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open database for integrity check: %w\", err)\n\t}\n\tdefer func() { _ = db.Close() }()\n\n\tvar pragma string\n\tswitch mode {\n\tcase IntegrityCheckQuick:\n\t\tpragma = \"quick_check\"\n\tcase IntegrityCheckFull:\n\t\tpragma = \"integrity_check\"\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported integrity check mode: %d\", mode)\n\t}\n\n\tvar result string\n\tif err := db.QueryRowContext(ctx, \"PRAGMA \"+pragma).Scan(\u0026result); err != nil {\n\t\treturn fmt.Errorf(\"integrity check: %w\", err)\n\t}\n\tif result != \"ok\" {\n\t\treturn fmt.Errorf(\"integrity check failed: %s\", result)\n\t}\n\n\t// Clean up -shm and -wal files that SQLite may create during the PRAGMA.\n\t_ = os.Remove(dbPath + \"-shm\")\n\t_ = os.Remove(dbPath + \"-wal\")\n\n\treturn nil\n}\n\n// findBestV3SnapshotForTimestamp returns the best v0.3.x snapshot for the given timestamp.\n// Returns nil if no suitable snapshot exists.\nfunc (r *Replica) findBestV3SnapshotForTimestamp(ctx context.Context, client ReplicaClientV3, timestamp time.Time) (*SnapshotInfoV3, error) {\n\tgenerations, err := client.GenerationsV3(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"list v0.3.x generations: %w\", err)\n\t}\n\tif len(generations) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvar allSnapshots []SnapshotInfoV3\n\tfor _, gen := range generations {\n\t\tsnapshots, err := client.SnapshotsV3(ctx, gen)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"list v0.3.x snapshots for generation %s: %w\", gen, err)\n\t\t}\n\t\tallSnapshots = append(allSnapshots, snapshots...)\n\t}\n\n\tif len(allSnapshots) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Sort by CreatedAt for timestamp-based selection.\n\tsortSnapshotsV3ByCreatedAt(allSnapshots)\n\n\treturn findBestSnapshotV3(allSnapshots, timestamp), nil\n}\n\n// shouldUseV3Restore determines whether to use v0.3.x restore instead of LTX.\n// Returns true if v0.3.x has a better backup for the given options.\nfunc (r *Replica) shouldUseV3Restore(ctx context.Context, client ReplicaClientV3, timestamp time.Time) (bool, error) {\n\t// Get v0.3.x time bounds.\n\t_, v3UpdatedAt, err := r.TimeBoundsV3(ctx, client)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"get v0.3.x time bounds: %w\", err)\n\t}\n\n\t// Get LTX time bounds.\n\t_, ltxUpdatedAt, err := r.TimeBounds(ctx)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"get LTX time bounds: %w\", err)\n\t}\n\n\t// If no v0.3.x backups exist, use LTX.\n\tif v3UpdatedAt.IsZero() {\n\t\treturn false, nil\n\t}\n\n\t// If no LTX backups exist, use v0.3.x.\n\tif ltxUpdatedAt.IsZero() {\n\t\tr.Logger().Debug(\"using v0.3.x restore (no LTX backups)\")\n\t\treturn true, nil\n\t}\n\n\t// Both formats have backups - compare based on timestamp or latest.\n\tif !timestamp.IsZero() {\n\t\t// With timestamp: use format with best snapshot before timestamp.\n\t\tv3Snapshot, err := r.findBestV3SnapshotForTimestamp(ctx, client, timestamp)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"find v0.3.x snapshot: %w\", err)\n\t\t}\n\n\t\tltxSnapshot, err := r.findBestLTXSnapshotForTimestamp(ctx, timestamp)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"find LTX snapshot: %w\", err)\n\t\t}\n\n\t\tif v3Snapshot != nil \u0026\u0026 (ltxSnapshot == nil || v3Snapshot.CreatedAt.After(ltxSnapshot.CreatedAt)) {\n\t\t\tr.Logger().Debug(\"using v0.3.x restore (better snapshot for timestamp)\",\n\t\t\t\t\"v3_snapshot\", v3Snapshot.CreatedAt,\n\t\t\t\t\"ltx_snapshot\", ltxSnapshot)\n\t\t\treturn true, nil\n\t\t}\n\t} else {\n\t\t// Without timestamp: use format with most recent backup.\n\t\tif v3UpdatedAt.After(ltxUpdatedAt) {\n\t\t\tr.Logger().Debug(\"using v0.3.x restore (more recent backup)\",\n\t\t\t\t\"v3_updated_at\", v3UpdatedAt,\n\t\t\t\t\"ltx_updated_at\", ltxUpdatedAt)\n\t\t\treturn true, nil\n\t\t}\n\t}\n\n\treturn false, nil\n}\n\n// TimeBoundsV3 returns the time bounds of v0.3.x backups.\n// Returns zero times if no v0.3.x backups exist.\nfunc (r *Replica) TimeBoundsV3(ctx context.Context, client ReplicaClientV3) (createdAt, updatedAt time.Time, err error) {\n\tgenerations, err := client.GenerationsV3(ctx)\n\tif err != nil {\n\t\treturn time.Time{}, time.Time{}, err\n\t}\n\n\tfor _, gen := range generations {\n\t\tsnapshots, err := client.SnapshotsV3(ctx, gen)\n\t\tif err != nil {\n\t\t\treturn time.Time{}, time.Time{}, err\n\t\t}\n\t\tfor _, snap := range snapshots {\n\t\t\tif createdAt.IsZero() || snap.CreatedAt.Before(createdAt) {\n\t\t\t\tcreatedAt = snap.CreatedAt\n\t\t\t}\n\t\t\tif updatedAt.IsZero() || snap.CreatedAt.After(updatedAt) {\n\t\t\t\tupdatedAt = snap.CreatedAt\n\t\t\t}\n\t\t}\n\n\t\tsegments, err := client.WALSegmentsV3(ctx, gen)\n\t\tif err != nil {\n\t\t\treturn time.Time{}, time.Time{}, err\n\t\t}\n\t\tfor _, seg := range segments {\n\t\t\tif createdAt.IsZero() || seg.CreatedAt.Before(createdAt) {\n\t\t\t\tcreatedAt = seg.CreatedAt\n\t\t\t}\n\t\t\tif updatedAt.IsZero() || seg.CreatedAt.After(updatedAt) {\n\t\t\t\tupdatedAt = seg.CreatedAt\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createdAt, updatedAt, nil\n}\n\n// findBestLTXSnapshotForTimestamp returns the best LTX snapshot for the given timestamp.\n// Returns nil if no suitable snapshot exists.\nfunc (r *Replica) findBestLTXSnapshotForTimestamp(ctx context.Context, timestamp time.Time) (*ltx.FileInfo, error) {\n\t// Find snapshots at the snapshot level that are before the timestamp.\n\tsnapshots, err := FindLTXFiles(ctx, r.Client, SnapshotLevel, true, func(info *ltx.FileInfo) (bool, error) {\n\t\treturn info.CreatedAt.Before(timestamp), nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"find LTX snapshots: %w\", err)\n\t}\n\tif len(snapshots) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Return the latest snapshot before the timestamp (last in the sorted list).\n\treturn snapshots[len(snapshots)-1], nil\n}\n\n// CalcRestorePlan returns a list of storage paths to restore a snapshot at the given TXID.\nfunc CalcRestorePlan(ctx context.Context, client ReplicaClient, txID ltx.TXID, timestamp time.Time, logger *slog.Logger) ([]*ltx.FileInfo, error) {\n\tif txID != 0 \u0026\u0026 !timestamp.IsZero() {\n\t\treturn nil, fmt.Errorf(\"cannot specify both TXID \u0026 timestamp to restore\")\n\t}\n\n\tvar infos ltx.FileInfoSlice\n\tlogger = logger.With(\"target\", txID)\n\n\t// Start with latest snapshot before target TXID or timestamp.\n\t// Pass useMetadata flag to enable accurate timestamp fetching for timestamp-based restore.\n\tvar snapshot *ltx.FileInfo\n\tsnapshotItr, err := client.LTXFiles(ctx, SnapshotLevel, 0, !timestamp.IsZero())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor snapshotItr.Next() {\n\t\tinfo := snapshotItr.Item()\n\t\tlogger.Debug(\"finding snapshot before target TXID or timestamp\", \"snapshot\", info.MaxTXID)\n\t\tif txID != 0 \u0026\u0026 info.MaxTXID \u003e txID {\n\t\t\tcontinue\n\t\t}\n\t\tif !timestamp.IsZero() \u0026\u0026 !info.CreatedAt.Before(timestamp) {\n\t\t\tcontinue\n\t\t}\n\t\tsnapshot = info\n\t}\n\tif err := snapshotItr.Close(); err != nil {\n\t\treturn nil, err\n\t}\n\tif snapshot != nil {\n\t\tlogger.Debug(\"found snapshot before target TXID or timestamp\", \"snapshot\", snapshot.MaxTXID)\n\t\tinfos = append(infos, snapshot)\n\t}\n\n\t// Collect candidates across all compaction levels and pick the next file\n\t// from any level that extends the longest contiguous TXID range.\n\tconst maxLevel = SnapshotLevel - 1\n\tstartTXID := infos.MaxTXID()\n\tcurrentMax := startTXID\n\tif txID != 0 \u0026\u0026 currentMax \u003e= txID {\n\t\treturn infos, nil\n\t}\n\n\tcursors := make([]*restoreLevelCursor, 0, maxLevel+1)\n\tfor level := maxLevel; level \u003e= 0; level-- {\n\t\tlogger.Debug(\"finding ltx files for level\", \"level\", level)\n\t\titr, err := client.LTXFiles(ctx, level, 0, !timestamp.IsZero())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursors = append(cursors, \u0026restoreLevelCursor{\n\t\t\titr: itr,\n\t\t})\n\t}\n\tdefer func() {\n\t\tfor _, cursor := range cursors {\n\t\t\tif cursor != nil {\n\t\t\t\t_ = cursor.itr.Close()\n\t\t\t}\n\t\t}\n\t}()\n\n\tfor {\n\t\tvar next *restoreLevelCursor\n\t\tfor _, cursor := range cursors {\n\t\t\tif err := cursor.refresh(currentMax, txID, timestamp); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif cursor.candidate == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif next == nil || restoreCandidateBetter(next.candidate, cursor.candidate) {\n\t\t\t\tnext = cursor\n\t\t\t}\n\t\t}\n\n\t\tif next == nil || next.candidate == nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif next.candidate.MaxTXID \u003c= currentMax {\n\t\t\tnext.candidate = nil\n\t\t\tcontinue\n\t\t}\n\n\t\tlogger.Debug(\"matching LTX file for restore\",\n\t\t\t\"filename\", ltx.FormatFilename(next.candidate.MinTXID, next.candidate.MaxTXID),\n\t\t\t\"level\", next.candidate.Level)\n\t\tinfos = append(infos, next.candidate)\n\t\tcurrentMax = next.candidate.MaxTXID\n\t\tnext.candidate = nil\n\n\t\tif txID != 0 \u0026\u0026 currentMax \u003e= txID {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif len(infos) \u003e 0 \u0026\u0026 txID == 0 \u0026\u0026 timestamp.IsZero() {\n\t\tfor _, cursor := range cursors {\n\t\t\tif err := cursor.ensureCurrent(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif cursor.current != nil \u0026\u0026 cursor.current.MinTXID \u003e currentMax+1 {\n\t\t\t\treturn nil, fmt.Errorf(\"non-contiguous ltx files: have up to %s but next file starts at %s\", currentMax, cursor.current.MinTXID)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(infos) == 0 {\n\t\treturn nil, ErrTxNotAvailable\n\t}\n\tif txID != 0 \u0026\u0026 infos.MaxTXID() \u003c txID {\n\t\treturn nil, ErrTxNotAvailable\n\t}\n\n\treturn infos, nil\n}\n\ntype restoreLevelCursor struct {\n\t// itr streams LTX file infos for a single level in filename order.\n\titr ltx.FileIterator\n\t// current holds the last item read from itr but not yet evaluated.\n\tcurrent *ltx.FileInfo\n\t// candidate is the best eligible file at this level for the currentMax.\n\tcandidate *ltx.FileInfo\n\t// done indicates the iterator has been exhausted or errored.\n\tdone bool\n}\n\nfunc (c *restoreLevelCursor) refresh(currentMax, txID ltx.TXID, timestamp time.Time) error {\n\t// Advance the iterator until we've evaluated all files that could be\n\t// contiguous with currentMax. Keep the best eligible candidate.\n\tif c.done {\n\t\treturn nil\n\t}\n\tif c.candidate != nil \u0026\u0026 c.candidate.MaxTXID \u003c= currentMax {\n\t\tc.candidate = nil\n\t}\n\n\tfor {\n\t\tif err := c.ensureCurrent(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif c.done {\n\t\t\treturn nil\n\t\t}\n\n\t\tinfo := c.current\n\t\tif info.MinTXID \u003e currentMax+1 {\n\t\t\treturn nil\n\t\t}\n\t\tc.current = nil\n\n\t\tif info.MaxTXID \u003c= currentMax {\n\t\t\tcontinue\n\t\t}\n\t\tif txID != 0 \u0026\u0026 info.MaxTXID \u003e txID {\n\t\t\tcontinue\n\t\t}\n\t\tif !timestamp.IsZero() \u0026\u0026 !info.CreatedAt.Before(timestamp) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif c.candidate == nil || restoreCandidateBetter(c.candidate, info) {\n\t\t\tc.candidate = info\n\t\t}\n\t}\n}\n\nfunc (c *restoreLevelCursor) ensureCurrent() error {\n\t// Ensure current is populated with the next iterator item, or mark done.\n\tif c.done || c.current != nil {\n\t\treturn nil\n\t}\n\tif !c.itr.Next() {\n\t\tif err := c.itr.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc.done = true\n\t\treturn nil\n\t}\n\tc.current = c.itr.Item()\n\treturn nil\n}\n\nfunc restoreCandidateBetter(curr, next *ltx.FileInfo) bool {\n\tif next.MaxTXID != curr.MaxTXID {\n\t\treturn next.MaxTXID \u003e curr.MaxTXID\n\t}\n\tif next.MinTXID != curr.MinTXID {\n\t\treturn next.MinTXID \u003c curr.MinTXID\n\t}\n\tif next.Level != curr.Level {\n\t\treturn next.Level \u003e curr.Level\n\t}\n\treturn next.CreatedAt.Before(curr.CreatedAt)\n}\n\n// TXIDPath returns the path to the TXID sidecar file for the given database path.\n// Uses -txid suffix to match SQLite's naming convention for associated files (-wal, -shm).\nfunc TXIDPath(outputPath string) string {\n\treturn outputPath + \"-txid\"\n}\n\n// WriteTXIDFile atomically writes a TXID to a sidecar file at \u003coutputPath\u003e-txid.\n// Uses temp-file + fsync + rename for crash safety.\nfunc WriteTXIDFile(outputPath string, txid ltx.TXID) error {\n\ttxidPath := TXIDPath(outputPath)\n\ttmpPath := txidPath + \".tmp\"\n\n\tf, err := os.Create(tmpPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create txid temp file: %w\", err)\n\t}\n\tdefer f.Close()\n\tdefer os.Remove(tmpPath)\n\n\tif _, err := fmt.Fprintln(f, txid); err != nil {\n\t\treturn fmt.Errorf(\"write txid: %w\", err)\n\t}\n\n\tif err := f.Sync(); err != nil {\n\t\treturn fmt.Errorf(\"sync txid file: %w\", err)\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close txid file: %w\", err)\n\t}\n\n\tif err := os.Rename(tmpPath, txidPath); err != nil {\n\t\treturn fmt.Errorf(\"rename txid file: %w\", err)\n\t}\n\n\tif err := internal.FsyncDir(filepath.Dir(txidPath)); err != nil {\n\t\treturn fmt.Errorf(\"sync txid dir: %w\", err)\n\t}\n\treturn nil\n}\n\n// ReadTXIDFile reads the TXID from a sidecar file at \u003coutputPath\u003e-txid.\n// Returns 0, nil if the file does not exist (first run).\nfunc ReadTXIDFile(outputPath string) (ltx.TXID, error) {\n\ttxidPath := TXIDPath(outputPath)\n\n\tdata, err := os.ReadFile(txidPath)\n\tif os.IsNotExist(err) {\n\t\treturn 0, nil\n\t} else if err != nil {\n\t\treturn 0, fmt.Errorf(\"read txid file: %w\", err)\n\t}\n\n\ttxid, err := ltx.ParseTXID(strings.TrimSpace(string(data)))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"parse txid file: %w\", err)\n\t}\n\n\treturn txid, nil\n}\n\n// ValidationError represents a single validation issue.\ntype ValidationError struct {\n\tLevel int // compaction level\n\tType string // \"gap\", \"overlap\", or \"unsorted\"\n\tMessage string // human-readable description\n\tPrevFile *ltx.FileInfo // previous file\n\tCurrFile *ltx.FileInfo // current file that caused error\n}\n\n// ValidateLevel checks LTX files at the given level are sorted and contiguous.\n// Returns a slice of validation errors (empty if valid).\nfunc (r *Replica) ValidateLevel(ctx context.Context, level int) ([]ValidationError, error) {\n\titr, err := r.Client.LTXFiles(ctx, level, 0, false)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n\n\tvar errors []ValidationError\n\tvar prevInfo *ltx.FileInfo\n\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\n\t\t// Skip first file - nothing to compare against\n\t\tif prevInfo == nil {\n\t\t\tprevInfo = info\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for sort order: curr.MinTXID should be \u003e= prev.MinTXID\n\t\tif info.MinTXID \u003c prevInfo.MinTXID {\n\t\t\terrors = append(errors, ValidationError{\n\t\t\t\tLevel: level,\n\t\t\t\tType: \"unsorted\",\n\t\t\t\tMessage: fmt.Sprintf(\"files out of order: curr.MinTXID=%s \u003c prev.MinTXID=%s\", info.MinTXID, prevInfo.MinTXID),\n\t\t\t\tPrevFile: prevInfo,\n\t\t\t\tCurrFile: info,\n\t\t\t})\n\t\t\tprevInfo = info\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for TXID contiguity: prev.MaxTXID + 1 should equal curr.MinTXID\n\t\texpectedMinTXID := prevInfo.MaxTXID + 1\n\t\tif info.MinTXID != expectedMinTXID {\n\t\t\tif info.MinTXID \u003e expectedMinTXID {\n\t\t\t\terrors = append(errors, ValidationError{\n\t\t\t\t\tLevel: level,\n\t\t\t\t\tType: \"gap\",\n\t\t\t\t\tMessage: fmt.Sprintf(\"TXID gap: prev.MaxTXID=%s, curr.MinTXID=%s (expected %s)\", prevInfo.MaxTXID, info.MinTXID, expectedMinTXID),\n\t\t\t\t\tPrevFile: prevInfo,\n\t\t\t\t\tCurrFile: info,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\terrors = append(errors, ValidationError{\n\t\t\t\t\tLevel: level,\n\t\t\t\t\tType: \"overlap\",\n\t\t\t\t\tMessage: fmt.Sprintf(\"TXID overlap: prev.MaxTXID=%s, curr.MinTXID=%s\", prevInfo.MaxTXID, info.MinTXID),\n\t\t\t\t\tPrevFile: prevInfo,\n\t\t\t\t\tCurrFile: info,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tprevInfo = info\n\t}\n\n\tif err := itr.Close(); err != nil {\n\t\treturn nil, fmt.Errorf(\"close iterator: %w\", err)\n\t}\n\n\treturn errors, nil\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"rand","path":"crypto/rand","span":{"start":[5,1],"end":[5,15],"bytes":[40,54]}},{"name":"sql","path":"database/sql","span":{"start":[6,1],"end":[6,16],"bytes":[55,70]}},{"name":"errors","path":"errors","span":{"start":[7,1],"end":[7,10],"bytes":[71,80]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[81,87]}},{"name":"io","path":"io","span":{"start":[9,1],"end":[9,6],"bytes":[88,93]}},{"name":"slog","path":"log/slog","span":{"start":[10,1],"end":[10,12],"bytes":[94,105]}},{"name":"os","path":"os","span":{"start":[11,1],"end":[11,6],"bytes":[106,111]}},{"name":"filepath","path":"path/filepath","span":{"start":[12,1],"end":[12,17],"bytes":[112,128]}},{"name":"strings","path":"strings","span":{"start":[13,1],"end":[13,11],"bytes":[129,139]}},{"name":"sync","path":"sync","span":{"start":[14,1],"end":[14,8],"bytes":[140,147]}},{"name":"atomic","path":"sync/atomic","span":{"start":[15,1],"end":[15,15],"bytes":[148,162]}},{"name":"time","path":"time","span":{"start":[16,1],"end":[16,8],"bytes":[163,170]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[18,1],"end":[18,27],"bytes":[172,198]}},{"name":"semaphore","path":"golang.org/x/sync/semaphore","span":{"start":[19,1],"end":[19,31],"bytes":[199,229]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[21,1],"end":[21,46],"bytes":[231,276]}}],"types":{"Replica":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica","kind":"struct","span":{"start":[35,1],"end":[69,2],"bytes":[643,1754]},"callables":{"github.com/benbjohnson/litestream.Replica.CalcRestoreTarget":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CalcRestoreTarget","kind":"method","span":{"start":[562,1],"end":[595,2],"bytes":[15010,16196]},"signature":"github.com/benbjohnson/litestream.Replica.CalcRestoreTarget","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[562,1],"end":[562,112],"bytes":[15010,15121]}},{"name":"opt","type":"github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[562,1],"end":[562,112],"bytes":[15010,15121]}}],"return_type":"(time.Time, error)","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"564:31":{"kind":"call","span":{"start":[564,31],"end":[564,48],"bytes":[15209,15226]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBounds"},"566:23":{"kind":"call","span":{"start":[566,23],"end":[566,56],"bytes":[15266,15299]},"callee":null},"571:36":{"kind":"call","span":{"start":[571,36],"end":[571,63],"bytes":[15447,15474]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3"},"573:24":{"kind":"call","span":{"start":[573,24],"end":[573,65],"bytes":[15516,15557]},"callee":null},"576:32":{"kind":"call","span":{"start":[576,32],"end":[576,50],"bytes":[15644,15662]},"callee":null},"576:54":{"kind":"call","span":{"start":[576,54],"end":[576,83],"bytes":[15666,15695]},"callee":null},"576:7":{"kind":"call","span":{"start":[576,7],"end":[576,27],"bytes":[15619,15639]},"callee":null},"579:32":{"kind":"call","span":{"start":[579,32],"end":[579,50],"bytes":[15761,15779]},"callee":null},"579:54":{"kind":"call","span":{"start":[579,54],"end":[579,82],"bytes":[15783,15811]},"callee":null},"579:7":{"kind":"call","span":{"start":[579,7],"end":[579,27],"bytes":[15736,15756]},"callee":null},"585:6":{"kind":"call","span":{"start":[585,6],"end":[585,28],"bytes":[15898,15920]},"callee":null},"586:28":{"kind":"call","span":{"start":[586,28],"end":[586,46],"bytes":[15950,15968]},"callee":null},"586:6":{"kind":"call","span":{"start":[586,6],"end":[586,24],"bytes":[15928,15946]},"callee":null},"587:24":{"kind":"call","span":{"start":[587,24],"end":[587,54],"bytes":[15994,16024]},"callee":null},"589:41":{"kind":"call","span":{"start":[589,41],"end":[589,71],"bytes":[16069,16099]},"callee":null},"589:6":{"kind":"call","span":{"start":[589,6],"end":[589,37],"bytes":[16034,16065]},"callee":null},"590:24":{"kind":"call","span":{"start":[590,24],"end":[590,62],"bytes":[16125,16163]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.CreatedAt":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CreatedAt","kind":"method","span":{"start":[520,1],"end":[534,2],"bytes":[13911,14226]},"signature":"github.com/benbjohnson/litestream.Replica.CreatedAt","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[520,1],"end":[520,70],"bytes":[13911,13980]}}],"return_type":"(time.Time, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"524:14":{"kind":"call","span":{"start":[524,14],"end":[524,49],"bytes":[14057,14092]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"528:8":{"kind":"call","span":{"start":[528,8],"end":[528,19],"bytes":[14138,14149]},"callee":null},"530:5":{"kind":"call","span":{"start":[530,5],"end":[530,15],"bytes":[14155,14165]},"callee":null},"531:9":{"kind":"call","span":{"start":[531,9],"end":[531,19],"bytes":[14176,14186]},"callee":null},"533:14":{"kind":"call","span":{"start":[533,14],"end":[533,25],"bytes":[14213,14224]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.DB":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB","kind":"method","span":{"start":[101,1],"end":[101,43],"bytes":[2422,2464]},"signature":"github.com/benbjohnson/litestream.Replica.DB","parameters":[],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.Replica.EnforceRetention":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.EnforceRetention","kind":"method","span":{"start":[318,1],"end":[350,2],"bytes":[8223,9303]},"signature":"github.com/benbjohnson/litestream.Replica.EnforceRetention","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[318,1],"end":[318,70],"bytes":[8223,8292]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"319:2":{"kind":"call","span":{"start":[319,2],"end":[319,63],"bytes":[8294,8355]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.Logger":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","kind":"method","span":{"start":[92,1],"end":[98,2],"bytes":[2181,2342]},"signature":"github.com/benbjohnson/litestream.Replica.Logger","parameters":[],"return_type":"*log/slog.Logger","metrics":{"cyclomatic":2},"body":{"93:12":{"kind":"call","span":{"start":[93,12],"end":[93,26],"bytes":[2234,2248]},"callee":null},"97:32":{"kind":"call","span":{"start":[97,32],"end":[97,47],"bytes":[2324,2339]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type"},"97:9":{"kind":"call","span":{"start":[97,9],"end":[97,48],"bytes":[2301,2340]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo","kind":"method","span":{"start":[284,1],"end":[299,2],"bytes":[7368,7745]},"signature":"github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[284,1],"end":[284,98],"bytes":[7368,7465]}},{"name":"level","type":"int","span":{"start":[284,1],"end":[284,98],"bytes":[7368,7465]}}],"return_type":"(github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"286:14":{"kind":"call","span":{"start":[286,14],"end":[286,53],"bytes":[7522,7561]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"290:8":{"kind":"call","span":{"start":[290,8],"end":[290,19],"bytes":[7608,7619]},"callee":null},"292:6":{"kind":"call","span":{"start":[292,6],"end":[292,16],"bytes":[7626,7636]},"callee":null},"293:11":{"kind":"call","span":{"start":[293,11],"end":[293,21],"bytes":[7649,7659]},"callee":null},"298:15":{"kind":"call","span":{"start":[298,15],"end":[298,26],"bytes":[7732,7743]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.Pos":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos","kind":"method","span":{"start":[303,1],"end":[307,2],"bytes":[7865,7950]},"signature":"github.com/benbjohnson/litestream.Replica.Pos","parameters":[],"return_type":"github.com/superfly/ltx.Pos","metrics":{"cyclomatic":1},"body":{"304:2":{"kind":"call","span":{"start":[304,2],"end":[304,14],"bytes":[7900,7912]},"callee":null},"305:8":{"kind":"call","span":{"start":[305,8],"end":[305,22],"bytes":[7920,7934]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.Restore":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","kind":"method","span":{"start":[608,1],"end":[799,2],"bytes":[16899,23659]},"signature":"github.com/benbjohnson/litestream.Replica.Restore","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[608,1],"end":[608,81],"bytes":[16899,16979]}},{"name":"opt","type":"github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[608,1],"end":[608,81],"bytes":[16899,16979]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":44},"body":{"611:10":{"kind":"call","span":{"start":[611,10],"end":[611,44],"bytes":[17038,17072]},"callee":null},"612:30":{"kind":"call","span":{"start":[612,30],"end":[612,52],"bytes":[17102,17124]},"callee":null},"613:10":{"kind":"call","span":{"start":[613,10],"end":[613,67],"bytes":[17136,17193]},"callee":null},"615:10":{"kind":"call","span":{"start":[615,10],"end":[615,57],"bytes":[17244,17291]},"callee":null},"616:27":{"kind":"call","span":{"start":[616,27],"end":[616,49],"bytes":[17318,17340]},"callee":null},"617:10":{"kind":"call","span":{"start":[617,10],"end":[617,62],"bytes":[17352,17404]},"callee":null},"619:10":{"kind":"call","span":{"start":[619,10],"end":[619,80],"bytes":[17557,17627]},"callee":null},"625:20":{"kind":"call","span":{"start":[625,20],"end":[625,43],"bytes":[17803,17826]},"callee":null},"626:21":{"kind":"call","span":{"start":[626,21],"end":[626,49],"bytes":[17865,17893]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ReadTXIDFile"},"628:12":{"kind":"call","span":{"start":[628,12],"end":[628,72],"bytes":[17928,17988]},"callee":null},"631:12":{"kind":"call","span":{"start":[631,12],"end":[631,147],"bytes":[18023,18158]},"callee":null},"636:27":{"kind":"call","span":{"start":[636,27],"end":[636,74],"bytes":[18372,18419]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"638:12":{"kind":"call","span":{"start":[638,12],"end":[638,83],"bytes":[18453,18524]},"callee":null},"642:8":{"kind":"call","span":{"start":[642,8],"end":[642,26],"bytes":[18574,18592]},"callee":null},"643:22":{"kind":"call","span":{"start":[643,22],"end":[643,40],"bytes":[18616,18634]},"callee":null},"645:14":{"kind":"call","span":{"start":[645,14],"end":[645,31],"bytes":[18653,18670]},"callee":null},"646:9":{"kind":"call","span":{"start":[646,9],"end":[646,28],"bytes":[18693,18712]},"callee":null},"647:12":{"kind":"call","span":{"start":[647,12],"end":[647,82],"bytes":[18724,18794]},"callee":null},"649:8":{"kind":"call","span":{"start":[649,8],"end":[649,27],"bytes":[18807,18826]},"callee":null},"653:13":{"kind":"call","span":{"start":[653,13],"end":[653,247],"bytes":[18909,19143]},"callee":null},"656:13":{"kind":"call","span":{"start":[656,13],"end":[656,208],"bytes":[19201,19396]},"callee":null},"660:4":{"kind":"call","span":{"start":[660,4],"end":[660,14],"bytes":[19412,19422]},"callee":null},"661:11":{"kind":"call","span":{"start":[661,11],"end":[661,66],"bytes":[19522,19577]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow"},"666:15":{"kind":"call","span":{"start":[666,15],"end":[666,38],"bytes":[19647,19670]},"callee":null},"667:10":{"kind":"call","span":{"start":[667,10],"end":[667,86],"bytes":[19694,19770]},"callee":null},"668:13":{"kind":"call","span":{"start":[668,13],"end":[668,31],"bytes":[19783,19801]},"callee":null},"676:18":{"kind":"call","span":{"start":[676,18],"end":[676,66],"bytes":[20103,20151]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore"},"681:12":{"kind":"call","span":{"start":[681,12],"end":[681,33],"bytes":[20216,20237]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3"},"686:16":{"kind":"call","span":{"start":[686,16],"end":[686,83],"bytes":[20266,20333]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan"},"686:72":{"kind":"call","span":{"start":[686,72],"end":[686,82],"bytes":[20322,20332]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger"},"688:10":{"kind":"call","span":{"start":[688,10],"end":[688,57],"bytes":[20360,20407]},"callee":null},"691:108":{"kind":"call","span":{"start":[691,108],"end":[691,118],"bytes":[20519,20529]},"callee":null},"691:2":{"kind":"call","span":{"start":[691,2],"end":[691,12],"bytes":[20413,20423]},"callee":null},"691:40":{"kind":"call","span":{"start":[691,40],"end":[691,50],"bytes":[20451,20461]},"callee":null},"691:66":{"kind":"call","span":{"start":[691,66],"end":[691,76],"bytes":[20477,20487]},"callee":null},"693:10":{"kind":"call","span":{"start":[693,10],"end":[693,42],"bytes":[20554,20586]},"callee":null},"693:31":{"kind":"call","span":{"start":[693,31],"end":[693,41],"bytes":[20575,20585]},"callee":null},"694:8":{"kind":"call","span":{"start":[694,8],"end":[700,5],"bytes":[20594,20708]},"callee":null},"705:11":{"kind":"call","span":{"start":[705,11],"end":[706,71],"bytes":[20854,21010]},"callee":null},"709:3":{"kind":"call","span":{"start":[709,3],"end":[709,13],"bytes":[21018,21028]},"callee":null},"711:10":{"kind":"call","span":{"start":[711,10],"end":[711,134],"bytes":[21140,21264]},"callee":null},"711:122":{"kind":"call","span":{"start":[711,122],"end":[711,132],"bytes":[21252,21262]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger"},"711:23":{"kind":"call","span":{"start":[711,23],"end":[711,133],"bytes":[21153,21263]},"callee":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.NewResumableReader"},"714:5":{"kind":"call","span":{"start":[714,5],"end":[714,14],"bytes":[21273,21282]},"callee":null},"715:10":{"kind":"call","span":{"start":[715,10],"end":[715,58],"bytes":[21299,21347]},"callee":null},"720:11":{"kind":"call","span":{"start":[720,11],"end":[720,17],"bytes":[21436,21442]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB"},"723:12":{"kind":"call","span":{"start":[723,12],"end":[723,68],"bytes":[21493,21549]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll"},"723:30":{"kind":"call","span":{"start":[723,30],"end":[723,58],"bytes":[21511,21539]},"callee":null},"724:10":{"kind":"call","span":{"start":[724,10],"end":[724,56],"bytes":[21573,21619]},"callee":null},"729:2":{"kind":"call","span":{"start":[729,2],"end":[729,12],"bytes":[21712,21722]},"callee":null},"729:75":{"kind":"call","span":{"start":[729,75],"end":[729,84],"bytes":[21785,21794]},"callee":null},"730:8":{"kind":"call","span":{"start":[730,8],"end":[730,49],"bytes":[21803,21844]},"callee":null},"732:12":{"kind":"call","span":{"start":[732,12],"end":[732,36],"bytes":[21857,21881]},"callee":null},"734:10":{"kind":"call","span":{"start":[734,10],"end":[734,58],"bytes":[21908,21956]},"callee":null},"736:8":{"kind":"call","span":{"start":[736,8],"end":[736,34],"bytes":[21967,21993]},"callee":null},"738:12":{"kind":"call","span":{"start":[738,12],"end":[738,21],"bytes":[22006,22015]},"callee":null},"740:5":{"kind":"call","span":{"start":[740,5],"end":[748,5],"bytes":[22021,22251]},"callee":null,"is_goroutine":true},"750:9":{"kind":"call","span":{"start":[750,9],"end":[750,27],"bytes":[22261,22279]},"callee":null},"751:12":{"kind":"call","span":{"start":[751,12],"end":[751,35],"bytes":[22291,22314]},"callee":null},"752:10":{"kind":"call","span":{"start":[752,10],"end":[752,48],"bytes":[22338,22376]},"callee":null},"755:12":{"kind":"call","span":{"start":[755,12],"end":[755,20],"bytes":[22392,22400]},"callee":null},"757:19":{"kind":"call","span":{"start":[757,19],"end":[757,28],"bytes":[22446,22455]},"callee":null},"762:2":{"kind":"call","span":{"start":[762,2],"end":[762,12],"bytes":[22521,22531]},"callee":null},"763:12":{"kind":"call","span":{"start":[763,12],"end":[763,52],"bytes":[22594,22634]},"callee":null},"766:12":{"kind":"call","span":{"start":[766,12],"end":[766,59],"bytes":[22676,22723]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir"},"766:30":{"kind":"call","span":{"start":[766,30],"end":[766,58],"bytes":[22694,22722]},"callee":null},"767:10":{"kind":"call","span":{"start":[767,10],"end":[767,56],"bytes":[22747,22793]},"callee":null},"771:13":{"kind":"call","span":{"start":[771,13],"end":[771,68],"bytes":[22857,22912]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkIntegrity"},"772:7":{"kind":"call","span":{"start":[772,7],"end":[772,16],"bytes":[22933,22942]},"callee":null},"773:9":{"kind":"call","span":{"start":[773,9],"end":[773,34],"bytes":[22960,22985]},"callee":null},"774:9":{"kind":"call","span":{"start":[774,9],"end":[774,43],"bytes":[22994,23028]},"callee":null},"775:9":{"kind":"call","span":{"start":[775,9],"end":[775,43],"bytes":[23037,23071]},"callee":null},"777:11":{"kind":"call","span":{"start":[777,11],"end":[777,62],"bytes":[23087,23138]},"callee":null},"779:3":{"kind":"call","span":{"start":[779,3],"end":[779,13],"bytes":[23145,23155]},"callee":null},"786:9":{"kind":"call","span":{"start":[786,9],"end":[786,23],"bytes":[23369,23383]},"callee":null},"791:20":{"kind":"call","span":{"start":[791,20],"end":[791,30],"bytes":[23426,23436]},"callee":null},"792:13":{"kind":"call","span":{"start":[792,13],"end":[792,51],"bytes":[23460,23498]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile"},"793:11":{"kind":"call","span":{"start":[793,11],"end":[793,57],"bytes":[23523,23569]},"callee":null},"795:10":{"kind":"call","span":{"start":[795,10],"end":[795,68],"bytes":[23583,23641]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow"}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore/github.com/benbjohnson/litestream.Replica.Restore.closure_1","kind":"function","span":{"start":[694,1],"end":[700,5],"bytes":[20587,20708]},"signature":"github.com/benbjohnson/litestream.Replica.Restore.closure_1","parameters":[],"body":{"697:9":{"kind":"call","span":{"start":[697,9],"end":[697,23],"bytes":[20680,20694]},"callee":null}}},"closure_2":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore/github.com/benbjohnson/litestream.Replica.Restore.closure_2","kind":"function","span":{"start":[730,1],"end":[730,49],"bytes":[21796,21844]},"signature":"github.com/benbjohnson/litestream.Replica.Restore.closure_2","parameters":[],"body":{"730:21":{"kind":"call","span":{"start":[730,21],"end":[730,45],"bytes":[21816,21840]},"callee":null}}},"closure_3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore/github.com/benbjohnson/litestream.Replica.Restore.closure_3","kind":"function","span":{"start":[736,1],"end":[736,34],"bytes":[21960,21993]},"signature":"github.com/benbjohnson/litestream.Replica.Restore.closure_3","parameters":[],"body":{"736:21":{"kind":"call","span":{"start":[736,21],"end":[736,30],"bytes":[21980,21989]},"callee":null}}},"closure_4":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore/github.com/benbjohnson/litestream.Replica.Restore.closure_4","kind":"function","span":{"start":[740,1],"end":[748,5],"bytes":[22017,22251]},"signature":"github.com/benbjohnson/litestream.Replica.Restore.closure_4","parameters":[],"body":{"741:13":{"kind":"call","span":{"start":[741,13],"end":[741,39],"bytes":[22042,22068]},"callee":null},"743:22":{"kind":"call","span":{"start":[743,22],"end":[743,62],"bytes":[22108,22148]},"callee":null},"743:4":{"kind":"call","span":{"start":[743,4],"end":[743,63],"bytes":[22090,22149]},"callee":null},"747:25":{"kind":"call","span":{"start":[747,25],"end":[747,39],"bytes":[22231,22245]},"callee":null},"747:7":{"kind":"call","span":{"start":[747,7],"end":[747,40],"bytes":[22213,22246]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.RestoreV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","kind":"method","span":{"start":[1067,1],"end":[1176,2],"bytes":[31404,34995]},"signature":"github.com/benbjohnson/litestream.Replica.RestoreV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1067,1],"end":[1067,77],"bytes":[31404,31480]}},{"name":"opt","type":"github.com/benbjohnson/litestream.RestoreOptions","span":{"start":[1067,1],"end":[1067,77],"bytes":[31404,31480]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":22},"body":{"1070:10":{"kind":"call","span":{"start":[1070,10],"end":[1070,70],"bytes":[31542,31602]},"callee":null},"1075:10":{"kind":"call","span":{"start":[1075,10],"end":[1075,44],"bytes":[31665,31699]},"callee":null},"1077:10":{"kind":"call","span":{"start":[1077,10],"end":[1077,80],"bytes":[31852,31922]},"callee":null},"1081:15":{"kind":"call","span":{"start":[1081,15],"end":[1081,38],"bytes":[31988,32011]},"callee":null},"1082:10":{"kind":"call","span":{"start":[1082,10],"end":[1082,86],"bytes":[32035,32111]},"callee":null},"1083:13":{"kind":"call","span":{"start":[1083,13],"end":[1083,31],"bytes":[32124,32142]},"callee":null},"1088:22":{"kind":"call","span":{"start":[1088,22],"end":[1088,47],"bytes":[32209,32234]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3"},"1090:10":{"kind":"call","span":{"start":[1090,10],"end":[1090,49],"bytes":[32261,32300]},"callee":null},"1092:5":{"kind":"call","span":{"start":[1092,5],"end":[1092,21],"bytes":[32308,32324]},"callee":null},"1099:21":{"kind":"call","span":{"start":[1099,21],"end":[1099,49],"bytes":[32500,32528]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3"},"1101:11":{"kind":"call","span":{"start":[1101,11],"end":[1101,71],"bytes":[32557,32617]},"callee":null},"1103:18":{"kind":"call","span":{"start":[1103,18],"end":[1103,52],"bytes":[32639,32673]},"callee":null},"1105:5":{"kind":"call","span":{"start":[1105,5],"end":[1105,22],"bytes":[32681,32698]},"callee":null},"1110:2":{"kind":"call","span":{"start":[1110,2],"end":[1110,42],"bytes":[32802,32842]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt"},"1113:14":{"kind":"call","span":{"start":[1113,14],"end":[1113,61],"bytes":[32947,32994]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.findBestSnapshotV3"},"1118:2":{"kind":"call","span":{"start":[1118,2],"end":[1118,12],"bytes":[33046,33056]},"callee":null},"1124:19":{"kind":"call","span":{"start":[1124,19],"end":[1124,65],"bytes":[33262,33308]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3"},"1126:10":{"kind":"call","span":{"start":[1126,10],"end":[1126,50],"bytes":[33335,33375]},"callee":null},"1128:13":{"kind":"call","span":{"start":[1128,13],"end":[1128,73],"bytes":[33391,33451]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.filterWALSegmentsV3"},"1130:2":{"kind":"call","span":{"start":[1130,2],"end":[1130,12],"bytes":[33454,33464]},"callee":null},"1130:53":{"kind":"call","span":{"start":[1130,53],"end":[1130,66],"bytes":[33505,33518]},"callee":null},"1134:11":{"kind":"call","span":{"start":[1134,11],"end":[1134,17],"bytes":[33605,33611]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB"},"1135:13":{"kind":"call","span":{"start":[1135,13],"end":[1135,25],"bytes":[33637,33649]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.DirInfo"},"1137:12":{"kind":"call","span":{"start":[1137,12],"end":[1137,68],"bytes":[33664,33720]},"callee":null},"1137:30":{"kind":"call","span":{"start":[1137,30],"end":[1137,58],"bytes":[33682,33710]},"callee":null},"1138:10":{"kind":"call","span":{"start":[1138,10],"end":[1138,56],"bytes":[33744,33790]},"callee":null},"1143:8":{"kind":"call","span":{"start":[1143,8],"end":[1143,43],"bytes":[33872,33907]},"callee":null},"1146:12":{"kind":"call","span":{"start":[1146,12],"end":[1146,91],"bytes":[33958,34037]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3"},"1147:10":{"kind":"call","span":{"start":[1147,10],"end":[1147,50],"bytes":[34061,34101]},"callee":null},"1151:12":{"kind":"call","span":{"start":[1151,12],"end":[1151,101],"bytes":[34141,34230]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3"},"1152:10":{"kind":"call","span":{"start":[1152,10],"end":[1152,51],"bytes":[34254,34295]},"callee":null},"1156:12":{"kind":"call","span":{"start":[1156,12],"end":[1156,46],"bytes":[34337,34371]},"callee":null},"1157:10":{"kind":"call","span":{"start":[1157,10],"end":[1157,54],"bytes":[34395,34439]},"callee":null},"1159:12":{"kind":"call","span":{"start":[1159,12],"end":[1159,59],"bytes":[34454,34501]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir"},"1159:30":{"kind":"call","span":{"start":[1159,30],"end":[1159,58],"bytes":[34472,34500]},"callee":null},"1160:10":{"kind":"call","span":{"start":[1160,10],"end":[1160,56],"bytes":[34525,34571]},"callee":null},"1164:13":{"kind":"call","span":{"start":[1164,13],"end":[1164,68],"bytes":[34635,34690]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkIntegrity"},"1165:7":{"kind":"call","span":{"start":[1165,7],"end":[1165,16],"bytes":[34711,34720]},"callee":null},"1166:9":{"kind":"call","span":{"start":[1166,9],"end":[1166,34],"bytes":[34738,34763]},"callee":null},"1167:9":{"kind":"call","span":{"start":[1167,9],"end":[1167,43],"bytes":[34772,34806]},"callee":null},"1168:9":{"kind":"call","span":{"start":[1168,9],"end":[1168,43],"bytes":[34815,34849]},"callee":null},"1170:11":{"kind":"call","span":{"start":[1170,11],"end":[1170,62],"bytes":[34865,34916]},"callee":null},"1172:3":{"kind":"call","span":{"start":[1172,3],"end":[1172,13],"bytes":[34923,34933]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3/github.com/benbjohnson/litestream.Replica.RestoreV3.closure_1","kind":"function","span":{"start":[1143,1],"end":[1143,43],"bytes":[33865,33907]},"signature":"github.com/benbjohnson/litestream.Replica.RestoreV3.closure_1","parameters":[],"body":{"1143:21":{"kind":"call","span":{"start":[1143,21],"end":[1143,39],"bytes":[33885,33903]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.SetPos":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.SetPos","kind":"method","span":{"start":[310,1],"end":[314,2],"bytes":[8000,8088]},"signature":"github.com/benbjohnson/litestream.Replica.SetPos","parameters":[{"name":"pos","type":"github.com/superfly/ltx.Pos","span":{"start":[310,1],"end":[310,40],"bytes":[8000,8039]}}],"metrics":{"cyclomatic":1},"body":{"311:2":{"kind":"call","span":{"start":[311,2],"end":[311,13],"bytes":[8041,8052]},"callee":null},"312:8":{"kind":"call","span":{"start":[312,8],"end":[312,21],"bytes":[8060,8073]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.Start":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Start","kind":"method","span":{"start":[104,1],"end":[121,2],"bytes":[2515,2900]},"signature":"github.com/benbjohnson/litestream.Replica.Start","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[104,1],"end":[104,53],"bytes":[2515,2567]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"111:2":{"kind":"call","span":{"start":[111,2],"end":[111,15],"bytes":[2691,2704]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Stop"},"114:18":{"kind":"call","span":{"start":[114,18],"end":[114,41],"bytes":[2758,2781]},"callee":null},"117:2":{"kind":"call","span":{"start":[117,2],"end":[117,13],"bytes":[2823,2834]},"callee":null},"118:5":{"kind":"call","span":{"start":[118,5],"end":[118,51],"bytes":[2839,2885]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Start/github.com/benbjohnson/litestream.Replica.Start.closure_1","kind":"function","span":{"start":[118,1],"end":[118,51],"bytes":[2835,2885]},"signature":"github.com/benbjohnson/litestream.Replica.Start.closure_1","parameters":[],"body":{"118:20":{"kind":"call","span":{"start":[118,20],"end":[118,31],"bytes":[2854,2865]},"callee":null},"118:33":{"kind":"call","span":{"start":[118,33],"end":[118,47],"bytes":[2867,2881]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.Stop":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Stop","kind":"method","span":{"start":[128,1],"end":[140,2],"bytes":[3160,3374]},"signature":"github.com/benbjohnson/litestream.Replica.Stop","parameters":[{"name":"hard","type":"bool","span":{"start":[128,1],"end":[128,48],"bytes":[3160,3207]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"129:2":{"kind":"call","span":{"start":[129,2],"end":[129,12],"bytes":[3209,3219]},"callee":null},"130:2":{"kind":"call","span":{"start":[130,2],"end":[130,13],"bytes":[3221,3232]},"callee":null},"132:2":{"kind":"call","span":{"start":[132,2],"end":[132,14],"bytes":[3235,3247]},"callee":null},"133:8":{"kind":"call","span":{"start":[133,8],"end":[133,22],"bytes":[3255,3269]},"callee":null},"135:11":{"kind":"call","span":{"start":[135,11],"end":[135,22],"bytes":[3305,3316]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.Sync":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync","kind":"method","span":{"start":[144,1],"end":[146,2],"bytes":[3532,3614]},"signature":"github.com/benbjohnson/litestream.Replica.Sync","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[144,1],"end":[144,58],"bytes":[3532,3589]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"145:9":{"kind":"call","span":{"start":[145,9],"end":[145,23],"bytes":[3598,3612]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.sync"}}},"github.com/benbjohnson/litestream.Replica.TimeBounds":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBounds","kind":"method","span":{"start":[538,1],"end":[559,2],"bytes":[14333,14951]},"signature":"github.com/benbjohnson/litestream.Replica.TimeBounds","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[538,1],"end":[538,96],"bytes":[14333,14428]}}],"return_type":"(time.Time, time.Time, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"540:15":{"kind":"call","span":{"start":[540,15],"end":[540,54],"bytes":[14494,14533]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"545:7":{"kind":"call","span":{"start":[545,7],"end":[545,17],"bytes":[14599,14609]},"callee":null},"546:12":{"kind":"call","span":{"start":[546,12],"end":[546,22],"bytes":[14623,14633]},"callee":null},"547:29":{"kind":"call","span":{"start":[547,29],"end":[547,61],"bytes":[14662,14694]},"callee":null},"547:7":{"kind":"call","span":{"start":[547,7],"end":[547,25],"bytes":[14640,14658]},"callee":null},"550:29":{"kind":"call","span":{"start":[550,29],"end":[550,60],"bytes":[14761,14792]},"callee":null},"550:7":{"kind":"call","span":{"start":[550,7],"end":[550,25],"bytes":[14739,14757]},"callee":null},"554:13":{"kind":"call","span":{"start":[554,13],"end":[554,24],"bytes":[14847,14858]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.TimeBoundsV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3","kind":"method","span":{"start":[1458,1],"end":[1493,2],"bytes":[43406,44399]},"signature":"github.com/benbjohnson/litestream.Replica.TimeBoundsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1458,1],"end":[1458,122],"bytes":[43406,43527]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClientV3","span":{"start":[1458,1],"end":[1458,122],"bytes":[43406,43527]}}],"return_type":"(time.Time, time.Time, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"1459:22":{"kind":"call","span":{"start":[1459,22],"end":[1459,47],"bytes":[43549,43574]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3"},"1465:21":{"kind":"call","span":{"start":[1465,21],"end":[1465,49],"bytes":[43690,43718]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3"},"1470:29":{"kind":"call","span":{"start":[1470,29],"end":[1470,61],"bytes":[43844,43876]},"callee":null},"1470:7":{"kind":"call","span":{"start":[1470,7],"end":[1470,25],"bytes":[43822,43840]},"callee":null},"1473:29":{"kind":"call","span":{"start":[1473,29],"end":[1473,60],"bytes":[43943,43974]},"callee":null},"1473:7":{"kind":"call","span":{"start":[1473,7],"end":[1473,25],"bytes":[43921,43939]},"callee":null},"1478:20":{"kind":"call","span":{"start":[1478,20],"end":[1478,50],"bytes":[44037,44067]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3"},"1483:29":{"kind":"call","span":{"start":[1483,29],"end":[1483,60],"bytes":[44191,44222]},"callee":null},"1483:7":{"kind":"call","span":{"start":[1483,7],"end":[1483,25],"bytes":[44169,44187]},"callee":null},"1486:29":{"kind":"call","span":{"start":[1486,29],"end":[1486,59],"bytes":[44288,44318]},"callee":null},"1486:7":{"kind":"call","span":{"start":[1486,7],"end":[1486,25],"bytes":[44266,44284]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.ValidateLevel":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.ValidateLevel","kind":"method","span":{"start":[1784,1],"end":[1846,2],"bytes":[52529,54262]},"signature":"github.com/benbjohnson/litestream.Replica.ValidateLevel","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1784,1],"end":[1784,93],"bytes":[52529,52621]}},{"name":"level","type":"int","span":{"start":[1784,1],"end":[1784,93],"bytes":[52529,52621]}}],"return_type":"([]github.com/benbjohnson/litestream.ValidationError, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"1785:14":{"kind":"call","span":{"start":[1785,14],"end":[1785,53],"bytes":[52635,52674]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"1787:15":{"kind":"call","span":{"start":[1787,15],"end":[1787,53],"bytes":[52706,52744]},"callee":null},"1789:8":{"kind":"call","span":{"start":[1789,8],"end":[1789,19],"bytes":[52755,52766]},"callee":null},"1794:6":{"kind":"call","span":{"start":[1794,6],"end":[1794,16],"bytes":[52832,52842]},"callee":null},"1795:11":{"kind":"call","span":{"start":[1795,11],"end":[1795,21],"bytes":[52855,52865]},"callee":null},"1805:13":{"kind":"call","span":{"start":[1805,13],"end":[1811,6],"bytes":[53093,53337]},"callee":null},"1808:15":{"kind":"call","span":{"start":[1808,15],"end":[1808,115],"bytes":[53186,53286]},"callee":null},"1820:14":{"kind":"call","span":{"start":[1820,14],"end":[1826,7],"bytes":[53582,53847]},"callee":null},"1823:16":{"kind":"call","span":{"start":[1823,16],"end":[1823,136],"bytes":[53673,53793]},"callee":null},"1828:14":{"kind":"call","span":{"start":[1828,14],"end":[1834,7],"bytes":[53873,54115]},"callee":null},"1831:16":{"kind":"call","span":{"start":[1831,16],"end":[1831,109],"bytes":[53968,54061]},"callee":null},"1841:12":{"kind":"call","span":{"start":[1841,12],"end":[1841,23],"bytes":[54159,54170]},"callee":null},"1842:15":{"kind":"call","span":{"start":[1842,15],"end":[1842,52],"bytes":[54199,54236]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.appendWALSegmentV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.appendWALSegmentV3","kind":"method","span":{"start":[1308,1],"end":[1317,2],"bytes":[38865,39215]},"signature":"github.com/benbjohnson/litestream.Replica.appendWALSegmentV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1308,1],"end":[1308,152],"bytes":[38865,39016]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClientV3","span":{"start":[1308,1],"end":[1308,152],"bytes":[38865,39016]}},{"name":"generation","type":"string","span":{"start":[1308,1],"end":[1308,152],"bytes":[38865,39016]}},{"name":"seg","type":"github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[1308,1],"end":[1308,152],"bytes":[38865,39016]}},{"name":"f","type":"*os.File","span":{"start":[1308,1],"end":[1308,152],"bytes":[38865,39016]}}],"return_type":"(int64, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1310:13":{"kind":"call","span":{"start":[1310,13],"end":[1310,76],"bytes":[39055,39118]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.OpenWALSegmentV3"},"1314:8":{"kind":"call","span":{"start":[1314,8],"end":[1314,35],"bytes":[39162,39189]},"callee":null},"1316:9":{"kind":"call","span":{"start":[1316,9],"end":[1316,23],"bytes":[39199,39213]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.appendWALSegmentV3/github.com/benbjohnson/litestream.Replica.appendWALSegmentV3.closure_1","kind":"function","span":{"start":[1314,1],"end":[1314,35],"bytes":[39155,39189]},"signature":"github.com/benbjohnson/litestream.Replica.appendWALSegmentV3.closure_1","parameters":[],"body":{"1314:21":{"kind":"call","span":{"start":[1314,21],"end":[1314,31],"bytes":[39175,39185]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.applyLTXFile":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile","kind":"method","span":{"start":[946,1],"end":[1000,2],"bytes":[28110,29522]},"signature":"github.com/benbjohnson/litestream.Replica.applyLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[946,1],"end":[946,109],"bytes":[28110,28218]}},{"name":"f","type":"*os.File","span":{"start":[946,1],"end":[946,109],"bytes":[28110,28218]}},{"name":"info","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[946,1],"end":[946,109],"bytes":[28110,28218]}},{"name":"pageSize","type":"uint32","span":{"start":[946,1],"end":[946,109],"bytes":[28110,28218]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"947:13":{"kind":"call","span":{"start":[947,13],"end":[947,84],"bytes":[28231,28302]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile"},"949:10":{"kind":"call","span":{"start":[949,10],"end":[949,46],"bytes":[28329,28365]},"callee":null},"951:8":{"kind":"call","span":{"start":[951,8],"end":[951,18],"bytes":[28376,28386]},"callee":null},"953:9":{"kind":"call","span":{"start":[953,9],"end":[953,27],"bytes":[28396,28414]},"callee":null},"954:12":{"kind":"call","span":{"start":[954,12],"end":[954,30],"bytes":[28426,28444]},"callee":null},"955:10":{"kind":"call","span":{"start":[955,10],"end":[955,46],"bytes":[28468,28504]},"callee":null},"958:9":{"kind":"call","span":{"start":[958,9],"end":[958,21],"bytes":[28517,28529]},"callee":null},"960:12":{"kind":"call","span":{"start":[960,12],"end":[960,41],"bytes":[28542,28571]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.LockFileExclusive"},"961:10":{"kind":"call","span":{"start":[961,10],"end":[961,55],"bytes":[28595,28640]},"callee":null},"963:8":{"kind":"call","span":{"start":[963,8],"end":[963,30],"bytes":[28651,28673]},"callee":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.UnlockFile"},"967:11":{"kind":"call","span":{"start":[967,11],"end":[967,33],"bytes":[28718,28740]},"callee":null},"968:13":{"kind":"call","span":{"start":[968,13],"end":[968,40],"bytes":[28753,28780]},"callee":null},"971:11":{"kind":"call","span":{"start":[971,11],"end":[971,45],"bytes":[28842,28876]},"callee":null},"974:24":{"kind":"call","span":{"start":[974,24],"end":[974,33],"bytes":[28905,28914]},"callee":null},"976:11":{"kind":"call","span":{"start":[976,11],"end":[976,33],"bytes":[28968,28990]},"callee":null},"979:10":{"kind":"call","span":{"start":[979,10],"end":[979,28],"bytes":[29005,29023]},"callee":null},"979:31":{"kind":"call","span":{"start":[979,31],"end":[979,46],"bytes":[29026,29041]},"callee":null},"980:16":{"kind":"call","span":{"start":[980,16],"end":[980,36],"bytes":[29057,29077]},"callee":null},"981:11":{"kind":"call","span":{"start":[981,11],"end":[981,58],"bytes":[29102,29149]},"callee":null},"986:13":{"kind":"call","span":{"start":[986,13],"end":[986,21],"bytes":[29191,29199]},"callee":null},"987:11":{"kind":"call","span":{"start":[987,11],"end":[987,54],"bytes":[29224,29267]},"callee":null},"989:14":{"kind":"call","span":{"start":[989,14],"end":[989,31],"bytes":[29285,29302]},"callee":null},"989:34":{"kind":"call","span":{"start":[989,34],"end":[989,49],"bytes":[29305,29320]},"callee":null},"990:13":{"kind":"call","span":{"start":[990,13],"end":[990,32],"bytes":[29333,29352]},"callee":null},"991:11":{"kind":"call","span":{"start":[991,11],"end":[991,42],"bytes":[29377,29408]},"callee":null},"995:12":{"kind":"call","span":{"start":[995,12],"end":[995,23],"bytes":[29428,29439]},"callee":null},"996:10":{"kind":"call","span":{"start":[996,10],"end":[996,46],"bytes":[29463,29499]},"callee":null},"999:9":{"kind":"call","span":{"start":[999,9],"end":[999,17],"bytes":[29512,29520]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.applyNewLTXFiles":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles","kind":"method","span":{"start":[865,1],"end":[936,2],"bytes":[25653,27624]},"signature":"github.com/benbjohnson/litestream.Replica.applyNewLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[865,1],"end":[865,125],"bytes":[25653,25777]}},{"name":"f","type":"*os.File","span":{"start":[865,1],"end":[865,125],"bytes":[25653,25777]}},{"name":"afterTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[865,1],"end":[865,125],"bytes":[25653,25777]}},{"name":"pageSize","type":"uint32","span":{"start":[865,1],"end":[865,125],"bytes":[25653,25777]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":15},"body":{"869:14":{"kind":"call","span":{"start":[869,14],"end":[869,61],"bytes":[25874,25921]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"871:23":{"kind":"call","span":{"start":[871,23],"end":[871,68],"bytes":[25961,26006]},"callee":null},"885:6":{"kind":"call","span":{"start":[885,6],"end":[885,16],"bytes":[26357,26367]},"callee":null},"887:11":{"kind":"call","span":{"start":[887,11],"end":[887,21],"bytes":[26399,26409]},"callee":null},"891:24":{"kind":"call","span":{"start":[891,24],"end":[891,84],"bytes":[26539,26599]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap"},"893:12":{"kind":"call","span":{"start":[893,12],"end":[893,28],"bytes":[26630,26646]},"callee":null},"902:12":{"kind":"call","span":{"start":[902,12],"end":[902,28],"bytes":[26844,26860]},"callee":null},"911:13":{"kind":"call","span":{"start":[911,13],"end":[911,51],"bytes":[26988,27026]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile"},"912:11":{"kind":"call","span":{"start":[912,11],"end":[915,6],"bytes":[27051,27182]},"callee":null},"912:23":{"kind":"call","span":{"start":[912,23],"end":[915,5],"bytes":[27063,27181]},"callee":null},"920:16":{"kind":"call","span":{"start":[920,16],"end":[920,25],"bytes":[27235,27244]},"callee":null},"921:10":{"kind":"call","span":{"start":[921,10],"end":[921,75],"bytes":[27272,27337]},"callee":null},"921:22":{"kind":"call","span":{"start":[921,22],"end":[921,74],"bytes":[27284,27336]},"callee":null},"923:15":{"kind":"call","span":{"start":[923,15],"end":[923,31],"bytes":[27355,27371]},"callee":null},"928:23":{"kind":"call","span":{"start":[928,23],"end":[928,84],"bytes":[27455,27516]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap"}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles.closure_1","kind":"function","span":{"start":[873,1],"end":[882,3],"bytes":[26010,26330]},"signature":"github.com/benbjohnson/litestream.Replica.applyNewLTXFiles.closure_1","parameters":[{"name":"retErr","type":"error","span":{"start":[873,1],"end":[873,55],"bytes":[26010,26064]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"body":{"874:18":{"kind":"call","span":{"start":[874,18],"end":[874,29],"bytes":[26082,26093]},"callee":null},"875:15":{"kind":"call","span":{"start":[875,15],"end":[875,69],"bytes":[26127,26181]},"callee":null},"877:25":{"kind":"call","span":{"start":[877,25],"end":[877,54],"bytes":[26228,26257]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3","kind":"method","span":{"start":[1244,1],"end":[1305,2],"bytes":[36970,38788]},"signature":"github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1244,1],"end":[1244,172],"bytes":[36970,37141]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClientV3","span":{"start":[1244,1],"end":[1244,172],"bytes":[36970,37141]}},{"name":"generation","type":"string","span":{"start":[1244,1],"end":[1244,172],"bytes":[36970,37141]}},{"name":"snapshotIndex","type":"int","span":{"start":[1244,1],"end":[1244,172],"bytes":[36970,37141]}},{"name":"segments","type":"[]github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[1244,1],"end":[1244,172],"bytes":[36970,37141]}},{"name":"dbPath","type":"string","span":{"start":[1244,1],"end":[1244,172],"bytes":[36970,37141]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"1245:5":{"kind":"call","span":{"start":[1245,5],"end":[1245,18],"bytes":[37146,37159]},"callee":null},"1254:12":{"kind":"call","span":{"start":[1254,12],"end":[1254,20],"bytes":[37330,37338]},"callee":null},"1256:8":{"kind":"call","span":{"start":[1256,8],"end":[1260,5],"bytes":[37347,37397]},"callee":null},"1281:13":{"kind":"call","span":{"start":[1281,13],"end":[1281,31],"bytes":[37919,37937]},"callee":null},"1285:12":{"kind":"call","span":{"start":[1285,12],"end":[1285,107],"bytes":[38018,38113]},"callee":null},"1289:16":{"kind":"call","span":{"start":[1289,16],"end":[1289,78],"bytes":[38174,38236]},"callee":null},"1294:11":{"kind":"call","span":{"start":[1294,11],"end":[1294,113],"bytes":[38335,38437]},"callee":null},"1296:16":{"kind":"call","span":{"start":[1296,16],"end":[1296,69],"bytes":[38457,38510]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.appendWALSegmentV3"},"1297:11":{"kind":"call","span":{"start":[1297,11],"end":[1297,80],"bytes":[38535,38604]},"callee":null},"1300:4":{"kind":"call","span":{"start":[1300,4],"end":[1300,14],"bytes":[38634,38644]},"callee":null},"1304:9":{"kind":"call","span":{"start":[1304,9],"end":[1304,27],"bytes":[38768,38786]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3.closure_1","kind":"function","span":{"start":[1256,1],"end":[1260,5],"bytes":[37340,37397]},"signature":"github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3.closure_1","parameters":[],"body":{"1258:8":{"kind":"call","span":{"start":[1258,8],"end":[1258,17],"bytes":[37379,37388]},"callee":null}}},"closure_2":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3.closure_2","kind":"function","span":{"start":[1262,1],"end":[1274,3],"bytes":[37399,37723]},"signature":"github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3.closure_2","parameters":[],"return_type":"error","error_channel":["error"],"body":{"1265:19":{"kind":"call","span":{"start":[1265,19],"end":[1265,28],"bytes":[37483,37492]},"callee":null},"1269:12":{"kind":"call","span":{"start":[1269,12],"end":[1269,32],"bytes":[37546,37566]},"callee":null},"1272:3":{"kind":"call","span":{"start":[1272,3],"end":[1272,13],"bytes":[37601,37611]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.calcPos":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.calcPos","kind":"method","span":{"start":[274,1],"end":[280,2],"bytes":[7015,7240]},"signature":"github.com/benbjohnson/litestream.Replica.calcPos","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[274,1],"end":[274,74],"bytes":[7015,7088]}}],"return_type":"(github.com/superfly/ltx.Pos, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"275:15":{"kind":"call","span":{"start":[275,15],"end":[275,39],"bytes":[7103,7127]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo"},"277:15":{"kind":"call","span":{"start":[277,15],"end":[277,50],"bytes":[7159,7194]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.downloadSnapshotV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3","kind":"method","span":{"start":[1224,1],"end":[1241,2],"bytes":[36463,36903]},"signature":"github.com/benbjohnson/litestream.Replica.downloadSnapshotV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1224,1],"end":[1224,137],"bytes":[36463,36599]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClientV3","span":{"start":[1224,1],"end":[1224,137],"bytes":[36463,36599]}},{"name":"generation","type":"string","span":{"start":[1224,1],"end":[1224,137],"bytes":[36463,36599]}},{"name":"index","type":"int","span":{"start":[1224,1],"end":[1224,137],"bytes":[36463,36599]}},{"name":"destPath","type":"string","span":{"start":[1224,1],"end":[1224,137],"bytes":[36463,36599]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1225:13":{"kind":"call","span":{"start":[1225,13],"end":[1225,58],"bytes":[36612,36657]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.OpenSnapshotV3"},"1229:8":{"kind":"call","span":{"start":[1229,8],"end":[1229,35],"bytes":[36698,36725]},"callee":null},"1231:12":{"kind":"call","span":{"start":[1231,12],"end":[1231,31],"bytes":[36738,36757]},"callee":null},"1235:8":{"kind":"call","span":{"start":[1235,8],"end":[1235,34],"bytes":[36798,36824]},"callee":null},"1237:15":{"kind":"call","span":{"start":[1237,15],"end":[1237,29],"bytes":[36840,36854]},"callee":null},"1240:9":{"kind":"call","span":{"start":[1240,9],"end":[1240,17],"bytes":[36893,36901]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3.closure_1","kind":"function","span":{"start":[1229,1],"end":[1229,35],"bytes":[36691,36725]},"signature":"github.com/benbjohnson/litestream.Replica.downloadSnapshotV3.closure_1","parameters":[],"body":{"1229:21":{"kind":"call","span":{"start":[1229,21],"end":[1229,31],"bytes":[36711,36721]},"callee":null}}},"closure_2":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3.closure_2","kind":"function","span":{"start":[1235,1],"end":[1235,34],"bytes":[36791,36824]},"signature":"github.com/benbjohnson/litestream.Replica.downloadSnapshotV3.closure_2","parameters":[],"body":{"1235:21":{"kind":"call","span":{"start":[1235,21],"end":[1235,30],"bytes":[36811,36820]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.fillFollowGap":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap","kind":"method","span":{"start":[1004,1],"end":[1064,2],"bytes":[29671,31351]},"signature":"github.com/benbjohnson/litestream.Replica.fillFollowGap","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1004,1],"end":[1004,143],"bytes":[29671,29813]}},{"name":"f","type":"*os.File","span":{"start":[1004,1],"end":[1004,143],"bytes":[29671,29813]}},{"name":"afterTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[1004,1],"end":[1004,143],"bytes":[29671,29813]}},{"name":"gapMinTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[1004,1],"end":[1004,143],"bytes":[29671,29813]}},{"name":"pageSize","type":"uint32","span":{"start":[1004,1],"end":[1004,143],"bytes":[29671,29813]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"1008:15":{"kind":"call","span":{"start":[1008,15],"end":[1008,54],"bytes":[29905,29944]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"1010:24":{"kind":"call","span":{"start":[1010,24],"end":[1010,77],"bytes":[29986,30039]},"callee":null},"1023:7":{"kind":"call","span":{"start":[1023,7],"end":[1023,17],"bytes":[30389,30399]},"callee":null},"1024:12":{"kind":"call","span":{"start":[1024,12],"end":[1024,22],"bytes":[30413,30423]},"callee":null},"1036:14":{"kind":"call","span":{"start":[1036,14],"end":[1036,52],"bytes":[30624,30662]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile"},"1037:12":{"kind":"call","span":{"start":[1037,12],"end":[1040,7],"bytes":[30688,30830]},"callee":null},"1037:23":{"kind":"call","span":{"start":[1037,23],"end":[1040,6],"bytes":[30699,30829]},"callee":null},"1046:12":{"kind":"call","span":{"start":[1046,12],"end":[1046,27],"bytes":[30963,30978]},"callee":null},"1050:17":{"kind":"call","span":{"start":[1050,17],"end":[1050,26],"bytes":[31005,31014]},"callee":null},"1051:11":{"kind":"call","span":{"start":[1051,11],"end":[1051,83],"bytes":[31043,31115]},"callee":null},"1051:22":{"kind":"call","span":{"start":[1051,22],"end":[1051,82],"bytes":[31054,31114]},"callee":null},"1053:16":{"kind":"call","span":{"start":[1053,16],"end":[1053,31],"bytes":[31135,31150]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap/github.com/benbjohnson/litestream.Replica.fillFollowGap.closure_1","kind":"function","span":{"start":[1012,1],"end":[1021,4],"bytes":[30044,30381]},"signature":"github.com/benbjohnson/litestream.Replica.fillFollowGap.closure_1","parameters":[{"name":"retErr","type":"error","span":{"start":[1012,1],"end":[1012,55],"bytes":[30044,30098]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"body":{"1013:19":{"kind":"call","span":{"start":[1013,19],"end":[1013,30],"bytes":[30117,30128]},"callee":null},"1014:16":{"kind":"call","span":{"start":[1014,16],"end":[1014,78],"bytes":[30163,30225]},"callee":null},"1016:26":{"kind":"call","span":{"start":[1016,26],"end":[1016,55],"bytes":[30274,30303]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp","kind":"method","span":{"start":[1497,1],"end":[1511,2],"bytes":[44538,45132]},"signature":"github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1497,1],"end":[1497,117],"bytes":[44538,44654]}},{"name":"timestamp","type":"time.Time","span":{"start":[1497,1],"end":[1497,117],"bytes":[44538,44654]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"1499:20":{"kind":"call","span":{"start":[1499,20],"end":[1501,4],"bytes":[44746,44886]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FindLTXFiles"},"1503:15":{"kind":"call","span":{"start":[1503,15],"end":[1503,56],"bytes":[44918,44959]},"callee":null},"1505:5":{"kind":"call","span":{"start":[1505,5],"end":[1505,19],"bytes":[44967,44981]},"callee":null},"1510:19":{"kind":"call","span":{"start":[1510,19],"end":[1510,33],"bytes":[45108,45122]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp/github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp.closure_1","kind":"function","span":{"start":[1499,1],"end":[1501,4],"bytes":[44727,44886]},"signature":"github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp.closure_1","parameters":[{"name":"info","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1499,1],"end":[1499,109],"bytes":[44727,44835]}}],"return_type":"(bool, error)","error_channel":["error"],"body":{"1500:10":{"kind":"call","span":{"start":[1500,10],"end":[1500,42],"bytes":[44845,44877]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","kind":"method","span":{"start":[1370,1],"end":[1396,2],"bytes":[40618,41423]},"signature":"github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1370,1],"end":[1370,142],"bytes":[40618,40759]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClientV3","span":{"start":[1370,1],"end":[1370,142],"bytes":[40618,40759]}},{"name":"timestamp","type":"time.Time","span":{"start":[1370,1],"end":[1370,142],"bytes":[40618,40759]}}],"return_type":"(*github.com/benbjohnson/litestream.SnapshotInfoV3, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"1371:22":{"kind":"call","span":{"start":[1371,22],"end":[1371,47],"bytes":[40781,40806]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3"},"1373:15":{"kind":"call","span":{"start":[1373,15],"end":[1373,61],"bytes":[40838,40884]},"callee":null},"1375:5":{"kind":"call","span":{"start":[1375,5],"end":[1375,21],"bytes":[40892,40908]},"callee":null},"1381:21":{"kind":"call","span":{"start":[1381,21],"end":[1381,49],"bytes":[41028,41056]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3"},"1383:16":{"kind":"call","span":{"start":[1383,16],"end":[1383,83],"bytes":[41090,41157]},"callee":null},"1385:18":{"kind":"call","span":{"start":[1385,18],"end":[1385,52],"bytes":[41179,41213]},"callee":null},"1388:5":{"kind":"call","span":{"start":[1388,5],"end":[1388,22],"bytes":[41222,41239]},"callee":null},"1393:2":{"kind":"call","span":{"start":[1393,2],"end":[1393,42],"bytes":[41323,41363]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt"},"1395:9":{"kind":"call","span":{"start":[1395,9],"end":[1395,52],"bytes":[41373,41416]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.findBestSnapshotV3"}}},"github.com/benbjohnson/litestream.Replica.follow":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow","kind":"method","span":{"start":[804,1],"end":[860,2],"bytes":[23868,25451]},"signature":"github.com/benbjohnson/litestream.Replica.follow","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[804,1],"end":[804,116],"bytes":[23868,23983]}},{"name":"outputPath","type":"string","span":{"start":[804,1],"end":[804,116],"bytes":[23868,23983]}},{"name":"lastTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[804,1],"end":[804,116],"bytes":[23868,23983]}},{"name":"interval","type":"time.Duration","span":{"start":[804,1],"end":[804,116],"bytes":[23868,23983]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"805:12":{"kind":"call","span":{"start":[805,12],"end":[805,49],"bytes":[23995,24032]},"callee":null},"807:10":{"kind":"call","span":{"start":[807,10],"end":[807,57],"bytes":[24059,24106]},"callee":null},"809:8":{"kind":"call","span":{"start":[809,8],"end":[812,5],"bytes":[24117,24161]},"callee":null},"816:15":{"kind":"call","span":{"start":[816,15],"end":[816,35],"bytes":[24266,24286]},"callee":null},"817:10":{"kind":"call","span":{"start":[817,10],"end":[817,68],"bytes":[24310,24368]},"callee":null},"819:14":{"kind":"call","span":{"start":[819,14],"end":[819,28],"bytes":[24385,24399]},"callee":null},"819:34":{"kind":"call","span":{"start":[819,34],"end":[819,48],"bytes":[24405,24419]},"callee":null},"828:2":{"kind":"call","span":{"start":[828,2],"end":[828,12],"bytes":[24523,24533]},"callee":null},"830:12":{"kind":"call","span":{"start":[830,12],"end":[830,36],"bytes":[24637,24661]},"callee":null},"831:8":{"kind":"call","span":{"start":[831,8],"end":[831,21],"bytes":[24669,24682]},"callee":null},"836:10":{"kind":"call","span":{"start":[836,10],"end":[836,20],"bytes":[24738,24748]},"callee":null},"837:4":{"kind":"call","span":{"start":[837,4],"end":[837,14],"bytes":[24753,24763]},"callee":null},"840:20":{"kind":"call","span":{"start":[840,20],"end":[840,66],"bytes":[24844,24890]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles"},"842:8":{"kind":"call","span":{"start":[842,8],"end":[842,17],"bytes":[24917,24926]},"callee":null},"843:6":{"kind":"call","span":{"start":[843,6],"end":[843,16],"bytes":[24941,24951]},"callee":null},"847:5":{"kind":"call","span":{"start":[847,5],"end":[847,15],"bytes":[25030,25040]},"callee":null},"851:15":{"kind":"call","span":{"start":[851,15],"end":[851,49],"bytes":[25193,25227]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile"},"852:13":{"kind":"call","span":{"start":[852,13],"end":[852,51],"bytes":[25254,25292]},"callee":null},"854:5":{"kind":"call","span":{"start":[854,5],"end":[854,15],"bytes":[25303,25313]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow/github.com/benbjohnson/litestream.Replica.follow.closure_1","kind":"function","span":{"start":[809,1],"end":[812,5],"bytes":[24110,24161]},"signature":"github.com/benbjohnson/litestream.Replica.follow.closure_1","parameters":[],"body":{"810:7":{"kind":"call","span":{"start":[810,7],"end":[810,15],"bytes":[24132,24140]},"callee":null},"811:7":{"kind":"call","span":{"start":[811,7],"end":[811,16],"bytes":[24147,24156]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.lockSync":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.lockSync","kind":"method","span":{"start":[236,1],"end":[246,2],"bytes":[5819,6108]},"signature":"github.com/benbjohnson/litestream.Replica.lockSync","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[236,1],"end":[236,56],"bytes":[5819,5874]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"237:5":{"kind":"call","span":{"start":[237,5],"end":[237,28],"bytes":[5879,5902]},"callee":null},"240:2":{"kind":"call","span":{"start":[240,2],"end":[240,22],"bytes":[5922,5942]},"callee":null},"241:8":{"kind":"call","span":{"start":[241,8],"end":[241,29],"bytes":[5950,5971]},"callee":null},"242:12":{"kind":"call","span":{"start":[242,12],"end":[242,37],"bytes":[5983,6008]},"callee":null},"243:10":{"kind":"call","span":{"start":[243,10],"end":[243,69],"bytes":[6032,6091]},"callee":null},"243:50":{"kind":"call","span":{"start":[243,50],"end":[243,68],"bytes":[6072,6090]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.monitor":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","kind":"method","span":{"start":[392,1],"end":[516,2],"bytes":[10236,13800]},"signature":"github.com/benbjohnson/litestream.Replica.monitor","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[392,1],"end":[392,49],"bytes":[10236,10284]}}],"metrics":{"cyclomatic":28},"body":{"393:12":{"kind":"call","span":{"start":[393,12],"end":[393,42],"bytes":[10296,10326]},"callee":null},"394:8":{"kind":"call","span":{"start":[394,8],"end":[394,21],"bytes":[10334,10347]},"callee":null},"396:12":{"kind":"call","span":{"start":[396,12],"end":[396,25],"bytes":[10360,10373]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Notify"},"406:11":{"kind":"call","span":{"start":[406,11],"end":[406,21],"bytes":[10588,10598]},"callee":null},"415:11":{"kind":"call","span":{"start":[415,11],"end":[415,21],"bytes":[10745,10755]},"callee":null},"417:11":{"kind":"call","span":{"start":[417,11],"end":[417,30],"bytes":[10778,10797]},"callee":null},"423:18":{"kind":"call","span":{"start":[423,18],"end":[423,28],"bytes":[10958,10968]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"423:56":{"kind":"call","span":{"start":[423,56],"end":[423,63],"bytes":[10996,11003]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos"},"427:11":{"kind":"call","span":{"start":[427,11],"end":[427,21],"bytes":[11159,11169]},"callee":null},"432:13":{"kind":"call","span":{"start":[432,13],"end":[432,22],"bytes":[11237,11246]},"callee":null},"437:12":{"kind":"call","span":{"start":[437,12],"end":[437,25],"bytes":[11337,11350]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Notify"},"440:13":{"kind":"call","span":{"start":[440,13],"end":[440,43],"bytes":[11428,11458]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.sync"},"441:7":{"kind":"call","span":{"start":[441,7],"end":[441,44],"bytes":[11479,11516]},"callee":null},"446:8":{"kind":"call","span":{"start":[446,8],"end":[446,33],"bytes":[11605,11630]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"461:8":{"kind":"call","span":{"start":[461,8],"end":[461,31],"bytes":[11987,12010]},"callee":null},"463:9":{"kind":"call","span":{"start":[463,9],"end":[463,32],"bytes":[12096,12119]},"callee":null},"465:8":{"kind":"call","span":{"start":[465,8],"end":[465,18],"bytes":[12182,12192]},"callee":null},"472:8":{"kind":"call","span":{"start":[472,8],"end":[472,18],"bytes":[12393,12403]},"callee":null},"478:21":{"kind":"call","span":{"start":[478,21],"end":[478,31],"bytes":[12581,12591]},"callee":null},"484:33":{"kind":"call","span":{"start":[484,33],"end":[484,59],"bytes":[12825,12851]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/github.com/benbjohnson/litestream.LTXError.IsAutoRecoverable"},"485:7":{"kind":"call","span":{"start":[485,7],"end":[485,17],"bytes":[12860,12870]},"callee":null},"486:22":{"kind":"call","span":{"start":[486,22],"end":[486,47],"bytes":[12945,12970]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState"},"487:8":{"kind":"call","span":{"start":[487,8],"end":[487,18],"bytes":[12997,13007]},"callee":null},"489:8":{"kind":"call","span":{"start":[489,8],"end":[489,18],"bytes":[13079,13089]},"callee":null},"497:9":{"kind":"call","span":{"start":[497,9],"end":[497,32],"bytes":[13350,13373]},"callee":null},"498:7":{"kind":"call","span":{"start":[498,7],"end":[498,17],"bytes":[13406,13416]},"callee":null},"502:21":{"kind":"call","span":{"start":[502,21],"end":[502,31],"bytes":[13554,13564]},"callee":null},"511:4":{"kind":"call","span":{"start":[511,4],"end":[511,14],"bytes":[13678,13688]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.shouldUseV3Restore":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","kind":"method","span":{"start":[1400,1],"end":[1454,2],"bytes":[41573,43295]},"signature":"github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1400,1],"end":[1400,119],"bytes":[41573,41691]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClientV3","span":{"start":[1400,1],"end":[1400,119],"bytes":[41573,41691]}},{"name":"timestamp","type":"time.Time","span":{"start":[1400,1],"end":[1400,119],"bytes":[41573,41691]}}],"return_type":"(bool, error)","error_channel":["error"],"metrics":{"cyclomatic":10},"body":{"1402:25":{"kind":"call","span":{"start":[1402,25],"end":[1402,52],"bytes":[41744,41771]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3"},"1404:17":{"kind":"call","span":{"start":[1404,17],"end":[1404,62],"bytes":[41805,41850]},"callee":null},"1408:26":{"kind":"call","span":{"start":[1408,26],"end":[1408,43],"bytes":[41905,41922]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBounds"},"1410:17":{"kind":"call","span":{"start":[1410,17],"end":[1410,59],"bytes":[41956,41998]},"callee":null},"1414:5":{"kind":"call","span":{"start":[1414,5],"end":[1414,25],"bytes":[42048,42068]},"callee":null},"1419:5":{"kind":"call","span":{"start":[1419,5],"end":[1419,26],"bytes":[42140,42161]},"callee":null},"1420:3":{"kind":"call","span":{"start":[1420,3],"end":[1420,13],"bytes":[42166,42176]},"callee":null},"1425:6":{"kind":"call","span":{"start":[1425,6],"end":[1425,24],"bytes":[42322,42340]},"callee":null},"1427:22":{"kind":"call","span":{"start":[1427,22],"end":[1427,78],"bytes":[42433,42489]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp"},"1429:18":{"kind":"call","span":{"start":[1429,18],"end":[1429,61],"bytes":[42525,42568]},"callee":null},"1432:23":{"kind":"call","span":{"start":[1432,23],"end":[1432,72],"bytes":[42596,42645]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp"},"1434:18":{"kind":"call","span":{"start":[1434,18],"end":[1434,58],"bytes":[42681,42721]},"callee":null},"1437:50":{"kind":"call","span":{"start":[1437,50],"end":[1437,99],"bytes":[42776,42825]},"callee":null},"1438:4":{"kind":"call","span":{"start":[1438,4],"end":[1438,14],"bytes":[42832,42842]},"callee":null},"1445:6":{"kind":"call","span":{"start":[1445,6],"end":[1445,37],"bytes":[43078,43109]},"callee":null},"1446:4":{"kind":"call","span":{"start":[1446,4],"end":[1446,14],"bytes":[43115,43125]},"callee":null}}},"github.com/benbjohnson/litestream.Replica.sync":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.sync","kind":"method","span":{"start":[148,1],"end":[157,2],"bytes":[3616,3830]},"signature":"github.com/benbjohnson/litestream.Replica.sync","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[148,1],"end":[148,73],"bytes":[3616,3688]}},{"name":"maxSyncLTXFiles","type":"int","span":{"start":[148,1],"end":[148,73],"bytes":[3616,3688]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"150:18":{"kind":"call","span":{"start":[150,18],"end":[150,50],"bytes":[3713,3745]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce"}}},"github.com/benbjohnson/litestream.Replica.syncOnce":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","kind":"method","span":{"start":[164,1],"end":[234,2],"bytes":[3895,5817]},"signature":"github.com/benbjohnson/litestream.Replica.syncOnce","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[164,1],"end":[164,109],"bytes":[3895,4003]}},{"name":"maxSyncLTXFiles","type":"int","span":{"start":[164,1],"end":[164,109],"bytes":[3895,4003]}}],"return_type":"(github.com/benbjohnson/litestream.replicaSyncResult, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"165:12":{"kind":"call","span":{"start":[165,12],"end":[165,27],"bytes":[4015,4030]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.lockSync"},"168:8":{"kind":"call","span":{"start":[168,8],"end":[168,28],"bytes":[4076,4096]},"callee":null},"171:8":{"kind":"call","span":{"start":[171,8],"end":[177,5],"bytes":[4164,4252]},"callee":null},"179:12":{"kind":"call","span":{"start":[179,12],"end":[179,21],"bytes":[4265,4274]},"callee":null},"180:18":{"kind":"call","span":{"start":[180,18],"end":[180,36],"bytes":[4306,4324]},"callee":null},"184:5":{"kind":"call","span":{"start":[184,5],"end":[184,12],"bytes":[4385,4392]},"callee":null},"185:15":{"kind":"call","span":{"start":[185,15],"end":[185,29],"bytes":[4418,4432]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.calcPos"},"187:19":{"kind":"call","span":{"start":[187,19],"end":[187,50],"bytes":[4469,4500]},"callee":null},"189:3":{"kind":"call","span":{"start":[189,3],"end":[189,16],"bytes":[4507,4520]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.SetPos"},"193:15":{"kind":"call","span":{"start":[193,15],"end":[193,25],"bytes":[4578,4588]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"195:18":{"kind":"call","span":{"start":[195,18],"end":[195,74],"bytes":[4623,4679]},"callee":null},"196:12":{"kind":"call","span":{"start":[196,12],"end":[196,25],"bytes":[4691,4704]},"callee":null},"200:2":{"kind":"call","span":{"start":[200,2],"end":[200,12],"bytes":[4751,4761]},"callee":null},"201:3":{"kind":"call","span":{"start":[201,3],"end":[204,4],"bytes":[4786,4900]},"callee":null},"202:27":{"kind":"call","span":{"start":[202,27],"end":[202,34],"bytes":[4831,4838]},"callee":null},"202:4":{"kind":"call","span":{"start":[202,4],"end":[202,49],"bytes":[4808,4853]},"callee":null},"203:22":{"kind":"call","span":{"start":[203,22],"end":[203,40],"bytes":[4876,4894]},"callee":null},"203:4":{"kind":"call","span":{"start":[203,4],"end":[203,41],"bytes":[4858,4895]},"callee":null},"207:27":{"kind":"call","span":{"start":[207,27],"end":[207,34],"bytes":[4989,4996]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos"},"207:72":{"kind":"call","span":{"start":[207,72],"end":[207,79],"bytes":[5034,5041]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos"},"212:4":{"kind":"call","span":{"start":[212,4],"end":[212,31],"bytes":[5264,5291]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.RecordSuccessfulSync"},"213:4":{"kind":"call","span":{"start":[213,4],"end":[213,14],"bytes":[5295,5305]},"callee":null},"215:16":{"kind":"call","span":{"start":[215,16],"end":[215,34],"bytes":[5384,5402]},"callee":null},"216:21":{"kind":"call","span":{"start":[216,21],"end":[216,28],"bytes":[5424,5431]},"callee":null},"219:13":{"kind":"call","span":{"start":[219,13],"end":[219,22],"bytes":[5485,5494]},"callee":null},"220:19":{"kind":"call","span":{"start":[220,19],"end":[220,37],"bytes":[5527,5545]},"callee":null},"222:13":{"kind":"call","span":{"start":[222,13],"end":[222,48],"bytes":[5562,5597]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile"},"225:3":{"kind":"call","span":{"start":[225,3],"end":[225,32],"bytes":[5640,5669]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.SetPos"},"231:2":{"kind":"call","span":{"start":[231,2],"end":[231,29],"bytes":[5767,5794]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.RecordSuccessfulSync"}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce/github.com/benbjohnson/litestream.Replica.syncOnce.closure_1","kind":"function","span":{"start":[171,1],"end":[177,5],"bytes":[4157,4252]},"signature":"github.com/benbjohnson/litestream.Replica.syncOnce.closure_1","parameters":[],"body":{"173:4":{"kind":"call","span":{"start":[173,4],"end":[173,15],"bytes":[4194,4205]},"callee":null},"175:4":{"kind":"call","span":{"start":[175,4],"end":[175,17],"bytes":[4230,4243]},"callee":null}}}}},"github.com/benbjohnson/litestream.Replica.uploadLTXFile":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile","kind":"method","span":{"start":[248,1],"end":[271,2],"bytes":[6110,6942]},"signature":"github.com/benbjohnson/litestream.Replica.uploadLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[248,1],"end":[248,105],"bytes":[6110,6214]}},{"name":"level","type":"int","span":{"start":[248,1],"end":[248,105],"bytes":[6110,6214]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[248,1],"end":[248,105],"bytes":[6110,6214]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[248,1],"end":[248,105],"bytes":[6110,6214]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"249:14":{"kind":"call","span":{"start":[249,14],"end":[249,51],"bytes":[6228,6265]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath"},"250:12":{"kind":"call","span":{"start":[250,12],"end":[250,29],"bytes":[6277,6294]},"callee":null},"252:10":{"kind":"call","span":{"start":[252,10],"end":[252,85],"bytes":[6321,6396]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError"},"252:47":{"kind":"call","span":{"start":[252,47],"end":[252,62],"bytes":[6358,6373]},"callee":null},"252:64":{"kind":"call","span":{"start":[252,64],"end":[252,79],"bytes":[6375,6390]},"callee":null},"254:8":{"kind":"call","span":{"start":[254,8],"end":[254,34],"bytes":[6407,6433]},"callee":null},"256:15":{"kind":"call","span":{"start":[256,15],"end":[256,69],"bytes":[6449,6503]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile"},"258:10":{"kind":"call","span":{"start":[258,10],"end":[258,47],"bytes":[6530,6567]},"callee":null},"260:2":{"kind":"call","span":{"start":[260,2],"end":[260,12],"bytes":[6572,6582]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile/github.com/benbjohnson/litestream.Replica.uploadLTXFile.closure_1","kind":"function","span":{"start":[254,1],"end":[254,34],"bytes":[6400,6433]},"signature":"github.com/benbjohnson/litestream.Replica.uploadLTXFile.closure_1","parameters":[],"body":{"254:21":{"kind":"call","span":{"start":[254,21],"end":[254,30],"bytes":[6420,6429]},"callee":null}}}}}},"fields":{"AutoRecoverEnabled":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/AutoRecoverEnabled","kind":"field","type":"bool","span":{"start":[68,1],"end":[68,25],"bytes":[1728,1752]}},"Client":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/Client","kind":"field","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[51,1],"end":[51,22],"bytes":[1038,1059]}},"MaxSyncLTXFiles":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/MaxSyncLTXFiles","kind":"field","type":"int","span":{"start":[58,1],"end":[58,21],"bytes":[1259,1279]}},"MonitorEnabled":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/MonitorEnabled","kind":"field","type":"bool","span":{"start":[62,1],"end":[62,21],"bytes":[1423,1443]}},"SyncInterval":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/SyncInterval","kind":"field","type":"time.Duration","span":{"start":[54,1],"end":[54,28],"bytes":[1105,1132]}},"cancel":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/cancel","kind":"field","type":"func()","span":{"start":[48,1],"end":[48,15],"bytes":[972,986]}},"db":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/db","kind":"field","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[36,1],"end":[36,8],"bytes":[665,672]}},"f":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/f","kind":"field","type":"*os.File","span":{"start":[45,1],"end":[45,75],"bytes":[873,947]}},"mu":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/mu","kind":"field","type":"sync.RWMutex","span":{"start":[38,1],"end":[38,18],"bytes":[674,691]}},"muf":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/muf","kind":"field","type":"sync.Mutex","span":{"start":[44,1],"end":[44,16],"bytes":[857,872]}},"pos":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/pos","kind":"field","type":"github.com/superfly/ltx.Pos","span":{"start":[39,1],"end":[39,44],"bytes":[692,735]}},"syncSem":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/syncSem","kind":"field","type":"*golang.org/x/sync/semaphore.Weighted","span":{"start":[41,1],"end":[41,33],"bytes":[737,769]}},"syncWaiters":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/syncWaiters","kind":"field","type":"sync/atomic.Int64","span":{"start":[42,1],"end":[42,86],"bytes":[770,855]}},"wg":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/wg","kind":"field","type":"sync.WaitGroup","span":{"start":[47,1],"end":[47,23],"bytes":[949,971]}}}},"ValidationError":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ValidationError","kind":"struct","span":{"start":[1774,1],"end":[1780,2],"bytes":[52102,52389]},"callables":{},"fields":{"CurrFile":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ValidationError/CurrFile","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1779,1],"end":[1779,58],"bytes":[52330,52387]}},"Level":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ValidationError/Level","kind":"field","type":"int","span":{"start":[1775,1],"end":[1775,44],"bytes":[52132,52175]}},"Message":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ValidationError/Message","kind":"field","type":"string","span":{"start":[1777,1],"end":[1777,54],"bytes":[52235,52288]}},"PrevFile":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ValidationError/PrevFile","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1778,1],"end":[1778,41],"bytes":[52289,52329]}},"Type":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ValidationError/Type","kind":"field","type":"string","span":{"start":[1776,1],"end":[1776,59],"bytes":[52176,52234]}}}},"replicaSyncResult":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.replicaSyncResult","kind":"struct","span":{"start":[159,1],"end":[162,2],"bytes":[3832,3893]},"callables":{},"fields":{"limited":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.replicaSyncResult/limited","kind":"field","type":"bool","span":{"start":[161,1],"end":[161,14],"bytes":[3878,3891]}},"synced":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.replicaSyncResult/synced","kind":"field","type":"bool","span":{"start":[160,1],"end":[160,14],"bytes":[3864,3877]}}}},"restoreLevelCursor":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor","kind":"struct","span":{"start":[1632,1],"end":[1641,2],"bytes":[48474,48864]},"callables":{"github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent","kind":"method","span":{"start":[1683,1],"end":[1697,2],"bytes":[49695,50019]},"signature":"github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1688:6":{"kind":"call","span":{"start":[1688,6],"end":[1688,18],"bytes":[49877,49889]},"callee":null},"1689:13":{"kind":"call","span":{"start":[1689,13],"end":[1689,24],"bytes":[49904,49915]},"callee":null},"1695:14":{"kind":"call","span":{"start":[1695,14],"end":[1695,26],"bytes":[49993,50005]},"callee":null}}},"github.com/benbjohnson/litestream.restoreLevelCursor.refresh":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.refresh","kind":"method","span":{"start":[1643,1],"end":[1681,2],"bytes":[48866,49693]},"signature":"github.com/benbjohnson/litestream.restoreLevelCursor.refresh","parameters":[{"name":"currentMax","type":"github.com/superfly/ltx.TXID","span":{"start":[1643,1],"end":[1643,93],"bytes":[48866,48958]}},{"name":"txID","type":"github.com/superfly/ltx.TXID","span":{"start":[1643,1],"end":[1643,93],"bytes":[48866,48958]}},{"name":"timestamp","type":"time.Time","span":{"start":[1643,1],"end":[1643,93],"bytes":[48866,48958]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"1654:13":{"kind":"call","span":{"start":[1654,13],"end":[1654,30],"bytes":[49230,49247]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent"},"1673:30":{"kind":"call","span":{"start":[1673,30],"end":[1673,62],"bytes":[49540,49572]},"callee":null},"1673:7":{"kind":"call","span":{"start":[1673,7],"end":[1673,25],"bytes":[49517,49535]},"callee":null},"1677:28":{"kind":"call","span":{"start":[1677,28],"end":[1677,69],"bytes":[49619,49660]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreCandidateBetter"}}}},"fields":{"candidate":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/candidate","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1638,1],"end":[1638,25],"bytes":[48764,48788]}},"current":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/current","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1636,1],"end":[1636,23],"bytes":[48667,48689]}},"done":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/done","kind":"field","type":"bool","span":{"start":[1640,1],"end":[1640,11],"bytes":[48852,48862]}},"itr":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/itr","kind":"field","type":"github.com/superfly/ltx.FileIterator","span":{"start":[1634,1],"end":[1634,22],"bytes":[48576,48597]}}}}},"functions":{"github.com/benbjohnson/litestream.CalcRestorePlan":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","kind":"function","span":{"start":[1514,1],"end":[1630,2],"bytes":[45226,48472]},"signature":"github.com/benbjohnson/litestream.CalcRestorePlan","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1514,1],"end":[1514,148],"bytes":[45226,45373]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[1514,1],"end":[1514,148],"bytes":[45226,45373]}},{"name":"txID","type":"github.com/superfly/ltx.TXID","span":{"start":[1514,1],"end":[1514,148],"bytes":[45226,45373]}},{"name":"timestamp","type":"time.Time","span":{"start":[1514,1],"end":[1514,148],"bytes":[45226,45373]}},{"name":"logger","type":"*log/slog.Logger","span":{"start":[1514,1],"end":[1514,148],"bytes":[45226,45373]}}],"return_type":"([]*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":27},"body":{"1515:19":{"kind":"call","span":{"start":[1515,19],"end":[1515,37],"bytes":[45392,45410]},"callee":null},"1516:15":{"kind":"call","span":{"start":[1516,15],"end":[1516,76],"bytes":[45427,45488]},"callee":null},"1520:11":{"kind":"call","span":{"start":[1520,11],"end":[1520,38],"bytes":[45532,45559]},"callee":null},"1525:22":{"kind":"call","span":{"start":[1525,22],"end":[1525,81],"bytes":[45767,45826]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"1525:62":{"kind":"call","span":{"start":[1525,62],"end":[1525,80],"bytes":[45807,45825]},"callee":null},"1529:6":{"kind":"call","span":{"start":[1529,6],"end":[1529,24],"bytes":[45870,45888]},"callee":null},"1530:11":{"kind":"call","span":{"start":[1530,11],"end":[1530,29],"bytes":[45901,45919]},"callee":null},"1531:3":{"kind":"call","span":{"start":[1531,3],"end":[1531,93],"bytes":[45922,46012]},"callee":null},"1535:30":{"kind":"call","span":{"start":[1535,30],"end":[1535,62],"bytes":[46098,46130]},"callee":null},"1535:7":{"kind":"call","span":{"start":[1535,7],"end":[1535,25],"bytes":[46075,46093]},"callee":null},"1540:12":{"kind":"call","span":{"start":[1540,12],"end":[1540,31],"bytes":[46181,46200]},"callee":null},"1544:3":{"kind":"call","span":{"start":[1544,3],"end":[1544,95],"bytes":[46260,46352]},"callee":null},"1545:11":{"kind":"call","span":{"start":[1545,11],"end":[1545,34],"bytes":[46363,46386]},"callee":null},"1551:15":{"kind":"call","span":{"start":[1551,15],"end":[1551,30],"bytes":[46583,46598]},"callee":null},"1557:13":{"kind":"call","span":{"start":[1557,13],"end":[1557,55],"bytes":[46698,46740]},"callee":null},"1559:3":{"kind":"call","span":{"start":[1559,3],"end":[1559,62],"bytes":[46789,46848]},"callee":null},"1560:15":{"kind":"call","span":{"start":[1560,15],"end":[1560,66],"bytes":[46863,46914]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"1560:47":{"kind":"call","span":{"start":[1560,47],"end":[1560,65],"bytes":[46895,46913]},"callee":null},"1564:13":{"kind":"call","span":{"start":[1564,13],"end":[1566,5],"bytes":[46968,47022]},"callee":null},"1568:8":{"kind":"call","span":{"start":[1568,8],"end":[1574,5],"bytes":[47033,47139]},"callee":null},"1579:14":{"kind":"call","span":{"start":[1579,14],"end":[1579,57],"bytes":[47227,47270]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.refresh"},"1585:22":{"kind":"call","span":{"start":[1585,22],"end":[1585,78],"bytes":[47381,47437]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreCandidateBetter"},"1599:3":{"kind":"call","span":{"start":[1599,3],"end":[1601,34],"bytes":[47613,47775]},"callee":null},"1600:16":{"kind":"call","span":{"start":[1600,16],"end":[1600,82],"bytes":[47674,47740]},"callee":null},"1602:11":{"kind":"call","span":{"start":[1602,11],"end":[1602,40],"bytes":[47786,47815]},"callee":null},"1611:36":{"kind":"call","span":{"start":[1611,36],"end":[1611,54],"bytes":[47969,47987]},"callee":null},"1611:5":{"kind":"call","span":{"start":[1611,5],"end":[1611,15],"bytes":[47938,47948]},"callee":null},"1613:14":{"kind":"call","span":{"start":[1613,14],"end":[1613,36],"bytes":[48038,48060]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent"},"1617:17":{"kind":"call","span":{"start":[1617,17],"end":[1617,133],"bytes":[48187,48303]},"callee":null},"1622:5":{"kind":"call","span":{"start":[1622,5],"end":[1622,15],"bytes":[48321,48331]},"callee":null},"1625:18":{"kind":"call","span":{"start":[1625,18],"end":[1625,33],"bytes":[48391,48406]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan/github.com/benbjohnson/litestream.CalcRestorePlan.closure_1","kind":"function","span":{"start":[1568,1],"end":[1574,5],"bytes":[47026,47139]},"signature":"github.com/benbjohnson/litestream.CalcRestorePlan.closure_1","parameters":[],"body":{"1571:9":{"kind":"call","span":{"start":[1571,9],"end":[1571,27],"bytes":[47107,47125]},"callee":null}}}}},"github.com/benbjohnson/litestream.NewReplica":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica","kind":"function","span":{"start":[71,1],"end":[83,2],"bytes":[1756,2005]},"signature":"github.com/benbjohnson/litestream.NewReplica","parameters":[{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[71,1],"end":[71,35],"bytes":[1756,1790]}}],"return_type":"*github.com/benbjohnson/litestream.Replica","metrics":{"cyclomatic":1},"body":{"74:12":{"kind":"call","span":{"start":[74,12],"end":[74,36],"bytes":[1833,1857]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica/github.com/benbjohnson/litestream.NewReplica.closure_1","kind":"function","span":{"start":[75,1],"end":[75,22],"bytes":[1859,1880]},"signature":"github.com/benbjohnson/litestream.NewReplica.closure_1","parameters":[],"body":{}}}},"github.com/benbjohnson/litestream.NewReplicaWithClient":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplicaWithClient","kind":"function","span":{"start":[85,1],"end":[89,2],"bytes":[2007,2125]},"signature":"github.com/benbjohnson/litestream.NewReplicaWithClient","parameters":[{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[85,1],"end":[85,67],"bytes":[2007,2073]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[85,1],"end":[85,67],"bytes":[2007,2073]}}],"return_type":"*github.com/benbjohnson/litestream.Replica","metrics":{"cyclomatic":1},"body":{"86:7":{"kind":"call","span":{"start":[86,7],"end":[86,21],"bytes":[2080,2094]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica"}}},"github.com/benbjohnson/litestream.ReadTXIDFile":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ReadTXIDFile","kind":"function","span":{"start":[1755,1],"end":[1771,2],"bytes":[51644,52043]},"signature":"github.com/benbjohnson/litestream.ReadTXIDFile","parameters":[{"name":"outputPath","type":"string","span":{"start":[1755,1],"end":[1755,57],"bytes":[51644,51700]}}],"return_type":"(github.com/superfly/ltx.TXID, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1756:14":{"kind":"call","span":{"start":[1756,14],"end":[1756,34],"bytes":[51714,51734]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.TXIDPath"},"1758:15":{"kind":"call","span":{"start":[1758,15],"end":[1758,36],"bytes":[51750,51771]},"callee":null},"1759:5":{"kind":"call","span":{"start":[1759,5],"end":[1759,23],"bytes":[51776,51794]},"callee":null},"1762:13":{"kind":"call","span":{"start":[1762,13],"end":[1762,50],"bytes":[51849,51886]},"callee":null},"1765:15":{"kind":"call","span":{"start":[1765,15],"end":[1765,61],"bytes":[51905,51951]},"callee":null},"1765:29":{"kind":"call","span":{"start":[1765,29],"end":[1765,60],"bytes":[51919,51950]},"callee":null},"1765:47":{"kind":"call","span":{"start":[1765,47],"end":[1765,59],"bytes":[51937,51949]},"callee":null},"1767:13":{"kind":"call","span":{"start":[1767,13],"end":[1767,51],"bytes":[51981,52019]},"callee":null}}},"github.com/benbjohnson/litestream.TXIDPath":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.TXIDPath","kind":"function","span":{"start":[1714,1],"end":[1716,2],"bytes":[50523,50595]},"signature":"github.com/benbjohnson/litestream.TXIDPath","parameters":[{"name":"outputPath","type":"string","span":{"start":[1714,1],"end":[1714,42],"bytes":[50523,50564]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.WriteTXIDFile":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile","kind":"function","span":{"start":[1720,1],"end":[1751,2],"bytes":[50732,51511]},"signature":"github.com/benbjohnson/litestream.WriteTXIDFile","parameters":[{"name":"outputPath","type":"string","span":{"start":[1720,1],"end":[1720,61],"bytes":[50732,50792]}},{"name":"txid","type":"github.com/superfly/ltx.TXID","span":{"start":[1720,1],"end":[1720,61],"bytes":[50732,50792]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"1721:14":{"kind":"call","span":{"start":[1721,14],"end":[1721,34],"bytes":[50806,50826]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.TXIDPath"},"1724:12":{"kind":"call","span":{"start":[1724,12],"end":[1724,30],"bytes":[50869,50887]},"callee":null},"1726:10":{"kind":"call","span":{"start":[1726,10],"end":[1726,54],"bytes":[50914,50958]},"callee":null},"1728:8":{"kind":"call","span":{"start":[1728,8],"end":[1728,17],"bytes":[50969,50978]},"callee":null},"1729:8":{"kind":"call","span":{"start":[1729,8],"end":[1729,26],"bytes":[50986,51004]},"callee":null},"1731:15":{"kind":"call","span":{"start":[1731,15],"end":[1731,36],"bytes":[51020,51041]},"callee":null},"1732:10":{"kind":"call","span":{"start":[1732,10],"end":[1732,43],"bytes":[51065,51098]},"callee":null},"1735:12":{"kind":"call","span":{"start":[1735,12],"end":[1735,20],"bytes":[51114,51122]},"callee":null},"1736:10":{"kind":"call","span":{"start":[1736,10],"end":[1736,47],"bytes":[51146,51183]},"callee":null},"1739:12":{"kind":"call","span":{"start":[1739,12],"end":[1739,21],"bytes":[51199,51208]},"callee":null},"1740:10":{"kind":"call","span":{"start":[1740,10],"end":[1740,48],"bytes":[51232,51270]},"callee":null},"1743:12":{"kind":"call","span":{"start":[1743,12],"end":[1743,40],"bytes":[51286,51314]},"callee":null},"1744:10":{"kind":"call","span":{"start":[1744,10],"end":[1744,49],"bytes":[51338,51377]},"callee":null},"1747:12":{"kind":"call","span":{"start":[1747,12],"end":[1747,53],"bytes":[51393,51434]},"callee":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir"},"1747:30":{"kind":"call","span":{"start":[1747,30],"end":[1747,52],"bytes":[51411,51433]},"callee":null},"1748:10":{"kind":"call","span":{"start":[1748,10],"end":[1748,46],"bytes":[51458,51494]},"callee":null}}},"github.com/benbjohnson/litestream.checkIntegrity":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkIntegrity","kind":"function","span":{"start":[1332,1],"end":[1366,2],"bytes":[39569,40477]},"signature":"github.com/benbjohnson/litestream.checkIntegrity","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1332,1],"end":[1332,89],"bytes":[39569,39657]}},{"name":"dbPath","type":"string","span":{"start":[1332,1],"end":[1332,89],"bytes":[39569,39657]}},{"name":"mode","type":"github.com/benbjohnson/litestream.IntegrityCheckMode","span":{"start":[1332,1],"end":[1332,89],"bytes":[39569,39657]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"1337:13":{"kind":"call","span":{"start":[1337,13],"end":[1337,39],"bytes":[39720,39746]},"callee":null},"1339:10":{"kind":"call","span":{"start":[1339,10],"end":[1339,66],"bytes":[39773,39829]},"callee":null},"1341:8":{"kind":"call","span":{"start":[1341,8],"end":[1341,35],"bytes":[39840,39867]},"callee":null},"1350:10":{"kind":"call","span":{"start":[1350,10],"end":[1350,66],"bytes":[40029,40085]},"callee":null},"1354:12":{"kind":"call","span":{"start":[1354,12],"end":[1354,53],"bytes":[40120,40161]},"callee":null},"1355:10":{"kind":"call","span":{"start":[1355,10],"end":[1355,48],"bytes":[40199,40237]},"callee":null},"1358:10":{"kind":"call","span":{"start":[1358,10],"end":[1358,58],"bytes":[40271,40319]},"callee":null},"1362:6":{"kind":"call","span":{"start":[1362,6],"end":[1362,32],"bytes":[40404,40430]},"callee":null},"1363:6":{"kind":"call","span":{"start":[1363,6],"end":[1363,32],"bytes":[40436,40462]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkIntegrity/github.com/benbjohnson/litestream.checkIntegrity.closure_1","kind":"function","span":{"start":[1341,1],"end":[1341,35],"bytes":[39833,39867]},"signature":"github.com/benbjohnson/litestream.checkIntegrity.closure_1","parameters":[],"body":{"1341:21":{"kind":"call","span":{"start":[1341,21],"end":[1341,31],"bytes":[39853,39863]},"callee":null}}}}},"github.com/benbjohnson/litestream.checkpointV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkpointV3","kind":"function","span":{"start":[1320,1],"end":[1329,2],"bytes":[39277,39492]},"signature":"github.com/benbjohnson/litestream.checkpointV3","parameters":[{"name":"dbPath","type":"string","span":{"start":[1320,1],"end":[1320,41],"bytes":[39277,39317]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"1321:13":{"kind":"call","span":{"start":[1321,13],"end":[1321,39],"bytes":[39330,39356]},"callee":null},"1325:8":{"kind":"call","span":{"start":[1325,8],"end":[1325,35],"bytes":[39397,39424]},"callee":null},"1327:11":{"kind":"call","span":{"start":[1327,11],"end":[1327,53],"bytes":[39436,39478]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkpointV3/github.com/benbjohnson/litestream.checkpointV3.closure_1","kind":"function","span":{"start":[1325,1],"end":[1325,35],"bytes":[39390,39424]},"signature":"github.com/benbjohnson/litestream.checkpointV3.closure_1","parameters":[],"body":{"1325:21":{"kind":"call","span":{"start":[1325,21],"end":[1325,31],"bytes":[39410,39420]},"callee":null}}}}},"github.com/benbjohnson/litestream.filterWALSegmentsV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.filterWALSegmentsV3","kind":"function","span":{"start":[1209,1],"end":[1221,2],"bytes":[36015,36369]},"signature":"github.com/benbjohnson/litestream.filterWALSegmentsV3","parameters":[{"name":"segments","type":"[]github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[1209,1],"end":[1209,115],"bytes":[36015,36129]}},{"name":"snapshotIndex","type":"int","span":{"start":[1209,1],"end":[1209,115],"bytes":[36015,36129]}},{"name":"timestamp","type":"time.Time","span":{"start":[1209,1],"end":[1209,115],"bytes":[36015,36129]}}],"return_type":"[]github.com/benbjohnson/litestream.WALSegmentInfoV3","metrics":{"cyclomatic":4},"body":{"1215:29":{"kind":"call","span":{"start":[1215,29],"end":[1215,59],"bytes":[36270,36300]},"callee":null},"1215:7":{"kind":"call","span":{"start":[1215,7],"end":[1215,25],"bytes":[36248,36266]},"callee":null},"1218:12":{"kind":"call","span":{"start":[1218,12],"end":[1218,31],"bytes":[36330,36349]},"callee":null}}},"github.com/benbjohnson/litestream.findBestSnapshotV3":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.findBestSnapshotV3","kind":"function","span":{"start":[1192,1],"end":[1205,2],"bytes":[35554,35889]},"signature":"github.com/benbjohnson/litestream.findBestSnapshotV3","parameters":[{"name":"snapshots","type":"[]github.com/benbjohnson/litestream.SnapshotInfoV3","span":{"start":[1192,1],"end":[1192,91],"bytes":[35554,35644]}},{"name":"timestamp","type":"time.Time","span":{"start":[1192,1],"end":[1192,91],"bytes":[35554,35644]}}],"return_type":"*github.com/benbjohnson/litestream.SnapshotInfoV3","metrics":{"cyclomatic":5},"body":{"1193:5":{"kind":"call","span":{"start":[1193,5],"end":[1193,19],"bytes":[35649,35663]},"callee":null},"1196:5":{"kind":"call","span":{"start":[1196,5],"end":[1196,23],"bytes":[35691,35709]},"callee":null},"1197:21":{"kind":"call","span":{"start":[1197,21],"end":[1197,35],"bytes":[35732,35746]},"callee":null},"1199:11":{"kind":"call","span":{"start":[1199,11],"end":[1199,25],"bytes":[35763,35777]},"callee":null},"1200:7":{"kind":"call","span":{"start":[1200,7],"end":[1200,46],"bytes":[35803,35842]},"callee":null}}},"github.com/benbjohnson/litestream.restoreCandidateBetter":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreCandidateBetter","kind":"function","span":{"start":[1699,1],"end":[1710,2],"bytes":[50021,50346]},"signature":"github.com/benbjohnson/litestream.restoreCandidateBetter","parameters":[{"name":"curr","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1699,1],"end":[1699,61],"bytes":[50021,50081]}},{"name":"next","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1699,1],"end":[1699,61],"bytes":[50021,50081]}}],"return_type":"bool","metrics":{"cyclomatic":4},"body":{"1709:9":{"kind":"call","span":{"start":[1709,9],"end":[1709,46],"bytes":[50307,50344]},"callee":null}}},"github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt":{"id":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt","kind":"function","span":{"start":[1179,1],"end":[1187,2],"bytes":[35080,35361]},"signature":"github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt","parameters":[{"name":"snapshots","type":"[]github.com/benbjohnson/litestream.SnapshotInfoV3","span":{"start":[1179,1],"end":[1179,62],"bytes":[35080,35141]}}],"metrics":{"cyclomatic":4},"body":{"1180:18":{"kind":"call","span":{"start":[1180,18],"end":[1180,32],"bytes":[35159,35173]},"callee":null},"1181:23":{"kind":"call","span":{"start":[1181,23],"end":[1181,37],"bytes":[35205,35219]},"callee":null},"1182:7":{"kind":"call","span":{"start":[1182,7],"end":[1182,59],"bytes":[35233,35285]},"callee":null}}}},"content_hash":"5dfbbc9a3eb6bbfb1922a8a17a47af6481e11432b2aeb8915dc60beec8f394db"},"replica_client.go":{"id":"can://go/litestream/replica_client.go","kind":"module","span":{"start":[1,1],"end":[168,1],"bytes":[0,6022]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\n\t\"github.com/superfly/ltx\"\n)\n\nvar ErrStopIter = errors.New(\"stop iterator\")\n\n// ReplicaClient represents client to connect to a Replica.\ntype ReplicaClient interface {\n\t// Type returns the type of client.\n\tType() string\n\n\t// Init initializes the replica client connection.\n\t// This may establish connections, validate configuration, etc.\n\t// Implementations should be idempotent (no-op if already initialized).\n\tInit(ctx context.Context) error\n\n\t// LTXFiles returns an iterator of all LTX files on the replica for a given level.\n\t// If seek is specified, the iterator start from the given TXID or the next available if not found.\n\t// If useMetadata is true, the iterator fetches accurate timestamps from metadata for timestamp-based restore.\n\t// When false, the iterator uses fast timestamps (LastModified/Created/ModTime) for normal operations.\n\tLTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error)\n\n\t// OpenLTXFile returns a reader that contains an LTX file at a given TXID.\n\t// If seek is specified, the reader will start at the given offset.\n\t// Returns an os.ErrNotFound error if the LTX file does not exist.\n\tOpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error)\n\n\t// WriteLTXFile writes an LTX file to the replica.\n\t// Returns metadata for the written file.\n\tWriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error)\n\n\t// DeleteLTXFiles deletes one or more LTX files.\n\tDeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error\n\n\t// DeleteAll deletes all files.\n\tDeleteAll(ctx context.Context) error\n\n\t// SetLogger sets the logger for the client.\n\tSetLogger(logger *slog.Logger)\n}\n\n// FindLTXFiles returns a list of files that match filter.\n// The useMetadata parameter is passed through to LTXFiles to control whether accurate timestamps\n// are fetched from metadata. When true (timestamp-based restore), accurate timestamps are required.\n// When false (normal operations), fast timestamps are sufficient.\nfunc FindLTXFiles(ctx context.Context, client ReplicaClient, level int, useMetadata bool, filter func(*ltx.FileInfo) (bool, error)) ([]*ltx.FileInfo, error) {\n\titr, err := client.LTXFiles(ctx, level, 0, useMetadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { _ = itr.Close() }()\n\n\tvar a []*ltx.FileInfo\n\tfor itr.Next() {\n\t\titem := itr.Item()\n\n\t\tmatch, err := filter(item)\n\t\tif match {\n\t\t\ta = append(a, item)\n\t\t}\n\n\t\tif errors.Is(err, ErrStopIter) {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn a, err\n\t\t}\n\t}\n\n\tif err := itr.Close(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn a, nil\n}\n\n// DefaultEstimatedPageIndexSize is size that is first fetched when fetching the page index.\n// If the fetch was smaller than the actual page index, another call is made to fetch the rest.\nconst DefaultEstimatedPageIndexSize = 32 * 1024 // 32KB\n\nfunc FetchPageIndex(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (map[uint32]ltx.PageIndexElem, error) {\n\trc, err := fetchPageIndexData(ctx, client, info)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rc.Close()\n\n\treturn ltx.DecodePageIndex(bufio.NewReader(rc), info.Level, info.MinTXID, info.MaxTXID)\n}\n\n// FetchLTXHeader reads \u0026 returns the LTX header for the given file info.\nfunc FetchLTXHeader(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (ltx.Header, error) {\n\trc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)\n\tif err != nil {\n\t\treturn ltx.Header{}, fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\tdefer rc.Close()\n\thdr, _, err := ltx.PeekHeader(rc)\n\tif err != nil {\n\t\treturn ltx.Header{}, fmt.Errorf(\"peek header: %w\", err)\n\t}\n\treturn hdr, nil\n}\n\n// fetchPageIndexData fetches a chunk of the end of the file to get the page index.\n// If the fetch was smaller than the actual page index, another call is made to fetch the rest.\nfunc fetchPageIndexData(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (io.ReadCloser, error) {\n\t// Fetch the end of the file to get the page index.\n\toffset := info.Size - DefaultEstimatedPageIndexSize\n\tif offset \u003c 0 {\n\t\toffset = 0\n\t}\n\n\tf, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, offset, 0)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\tdefer f.Close()\n\n\t// If we have read the full size of the page index, return the page index block as a reader.\n\tb, err := io.ReadAll(f)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read ltx page index: %w\", err)\n\t}\n\t// The replica may return fewer bytes than a page index footer occupies, for\n\t// example if the file is truncated or otherwise corrupt. Slicing the footer\n\t// off the end unchecked would index past the start of the buffer and panic.\n\tif len(b) \u003c ltx.TrailerSize+8 {\n\t\treturn nil, fmt.Errorf(\"ltx file too short to contain a page index: %d bytes\", len(b))\n\t}\n\n\tsize := binary.BigEndian.Uint64(b[len(b)-ltx.TrailerSize-8:])\n\tif off := len(b) - int(size) - ltx.TrailerSize - 8; off \u003e 0 {\n\t\treturn io.NopCloser(bytes.NewReader(b[off:])), nil\n\t}\n\n\t// Otherwise read the file from the start of the page index.\n\tf, err = client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, info.Size-ltx.TrailerSize-8-int64(size), 0)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\treturn f, nil\n}\n\n// FetchPage fetches and decodes a single page frame from an LTX file.\nfunc FetchPage(ctx context.Context, client ReplicaClient, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (ltx.PageHeader, []byte, error) {\n\tf, err := client.OpenLTXFile(ctx, level, minTXID, maxTXID, offset, size)\n\tif err != nil {\n\t\treturn ltx.PageHeader{}, nil, fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\tdefer f.Close()\n\n\tb, err := io.ReadAll(f)\n\tif err != nil {\n\t\treturn ltx.PageHeader{}, nil, fmt.Errorf(\"read ltx page frame: %w\", err)\n\t}\n\treturn ltx.DecodePageData(b)\n}\n","imports":[{"name":"bufio","path":"bufio","span":{"start":[4,1],"end":[4,9],"bytes":[29,37]}},{"name":"bytes","path":"bytes","span":{"start":[5,1],"end":[5,9],"bytes":[38,46]}},{"name":"context","path":"context","span":{"start":[6,1],"end":[6,11],"bytes":[47,57]}},{"name":"binary","path":"encoding/binary","span":{"start":[7,1],"end":[7,19],"bytes":[58,76]}},{"name":"errors","path":"errors","span":{"start":[8,1],"end":[8,10],"bytes":[77,86]}},{"name":"fmt","path":"fmt","span":{"start":[9,1],"end":[9,7],"bytes":[87,93]}},{"name":"io","path":"io","span":{"start":[10,1],"end":[10,6],"bytes":[94,99]}},{"name":"slog","path":"log/slog","span":{"start":[11,1],"end":[11,12],"bytes":[100,111]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[13,1],"end":[13,27],"bytes":[113,139]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient","kind":"interface","span":{"start":[19,1],"end":[51,2],"bytes":[250,1859]},"callables":{"github.com/benbjohnson/litestream.ReplicaClient.DeleteAll":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteAll","kind":"method","span":{"start":[47,1],"end":[47,38],"bytes":[1741,1778]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[47,1],"end":[47,38],"bytes":[1741,1778]}}],"return_type":"error","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[44,1],"end":[44,62],"bytes":[1645,1706]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[44,1],"end":[44,62],"bytes":[1645,1706]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[44,1],"end":[44,62],"bytes":[1645,1706]}}],"return_type":"error","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClient.Init":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Init","kind":"method","span":{"start":[26,1],"end":[26,33],"bytes":[524,556]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[26,1],"end":[26,33],"bytes":[524,556]}}],"return_type":"error","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClient.LTXFiles":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","kind":"method","span":{"start":[32,1],"end":[32,101],"bytes":[959,1059]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[32,1],"end":[32,101],"bytes":[959,1059]}},{"name":"level","type":"int","span":{"start":[32,1],"end":[32,101],"bytes":[959,1059]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[32,1],"end":[32,101],"bytes":[959,1059]}},{"name":"useMetadata","type":"bool","span":{"start":[32,1],"end":[32,101],"bytes":[959,1059]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]}},{"name":"level","type":"int","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]}},{"name":"offset","type":"int64","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]}},{"name":"size","type":"int64","span":{"start":[37,1],"end":[37,115],"bytes":[1274,1388]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClient.SetLogger":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.SetLogger","kind":"method","span":{"start":[50,1],"end":[50,32],"bytes":[1826,1857]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[50,1],"end":[50,32],"bytes":[1826,1857]}}],"body":{}},"github.com/benbjohnson/litestream.ReplicaClient.Type":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type","kind":"method","span":{"start":[21,1],"end":[21,15],"bytes":[318,332]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.Type","parameters":[],"return_type":"string","body":{}},"github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[41,1],"end":[41,109],"bytes":[1485,1593]},"signature":"github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[41,1],"end":[41,109],"bytes":[1485,1593]}},{"name":"level","type":"int","span":{"start":[41,1],"end":[41,109],"bytes":[1485,1593]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[41,1],"end":[41,109],"bytes":[1485,1593]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[41,1],"end":[41,109],"bytes":[1485,1593]}},{"name":"r","type":"io.Reader","span":{"start":[41,1],"end":[41,109],"bytes":[1485,1593]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"body":{}}}}},"functions":{"github.com/benbjohnson/litestream.FetchLTXHeader":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FetchLTXHeader","kind":"function","span":{"start":[101,1],"end":[112,2],"bytes":[3422,3851]},"signature":"github.com/benbjohnson/litestream.FetchLTXHeader","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[101,1],"end":[101,105],"bytes":[3422,3526]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[101,1],"end":[101,105],"bytes":[3422,3526]}},{"name":"info","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[101,1],"end":[101,105],"bytes":[3422,3526]}}],"return_type":"(github.com/superfly/ltx.Header, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"102:13":{"kind":"call","span":{"start":[102,13],"end":[102,95],"bytes":[3539,3621]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile"},"104:24":{"kind":"call","span":{"start":[104,24],"end":[104,60],"bytes":[3662,3698]},"callee":null},"106:8":{"kind":"call","span":{"start":[106,8],"end":[106,18],"bytes":[3709,3719]},"callee":null},"107:17":{"kind":"call","span":{"start":[107,17],"end":[107,35],"bytes":[3736,3754]},"callee":null},"109:24":{"kind":"call","span":{"start":[109,24],"end":[109,58],"bytes":[3795,3829]},"callee":null}}},"github.com/benbjohnson/litestream.FetchPage":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FetchPage","kind":"function","span":{"start":[155,1],"end":[167,2],"bytes":[5539,6021]},"signature":"github.com/benbjohnson/litestream.FetchPage","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}},{"name":"level","type":"int","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}},{"name":"offset","type":"int64","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}},{"name":"size","type":"int64","span":{"start":[155,1],"end":[155,150],"bytes":[5539,5688]}}],"return_type":"(github.com/superfly/ltx.PageHeader, []byte, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"156:12":{"kind":"call","span":{"start":[156,12],"end":[156,74],"bytes":[5700,5762]},"callee":null},"158:33":{"kind":"call","span":{"start":[158,33],"end":[158,69],"bytes":[5812,5848]},"callee":null},"160:8":{"kind":"call","span":{"start":[160,8],"end":[160,17],"bytes":[5859,5868]},"callee":null},"162:12":{"kind":"call","span":{"start":[162,12],"end":[162,25],"bytes":[5881,5894]},"callee":null},"164:33":{"kind":"call","span":{"start":[164,33],"end":[164,75],"bytes":[5944,5986]},"callee":null},"166:9":{"kind":"call","span":{"start":[166,9],"end":[166,30],"bytes":[5998,6019]},"callee":null}}},"github.com/benbjohnson/litestream.FetchPageIndex":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FetchPageIndex","kind":"function","span":{"start":[90,1],"end":[98,2],"bytes":[3026,3346]},"signature":"github.com/benbjohnson/litestream.FetchPageIndex","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[90,1],"end":[90,123],"bytes":[3026,3148]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[90,1],"end":[90,123],"bytes":[3026,3148]}},{"name":"info","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[90,1],"end":[90,123],"bytes":[3026,3148]}}],"return_type":"(map[uint32]github.com/superfly/ltx.PageIndexElem, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"91:13":{"kind":"call","span":{"start":[91,13],"end":[91,50],"bytes":[3161,3198]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.fetchPageIndexData"},"95:8":{"kind":"call","span":{"start":[95,8],"end":[95,18],"bytes":[3244,3254]},"callee":null},"97:29":{"kind":"call","span":{"start":[97,29],"end":[97,48],"bytes":[3284,3303]},"callee":null},"97:9":{"kind":"call","span":{"start":[97,9],"end":[97,89],"bytes":[3264,3344]},"callee":null}}},"github.com/benbjohnson/litestream.FindLTXFiles":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FindLTXFiles","kind":"function","span":{"start":[57,1],"end":[84,2],"bytes":[2186,2778]},"signature":"github.com/benbjohnson/litestream.FindLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[57,1],"end":[57,159],"bytes":[2186,2344]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[57,1],"end":[57,159],"bytes":[2186,2344]}},{"name":"level","type":"int","span":{"start":[57,1],"end":[57,159],"bytes":[2186,2344]}},{"name":"useMetadata","type":"bool","span":{"start":[57,1],"end":[57,159],"bytes":[2186,2344]}},{"name":"filter","type":"func(*github.com/superfly/ltx.FileInfo) (bool, error)","span":{"start":[57,1],"end":[57,159],"bytes":[2186,2344]}}],"return_type":"([]*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"58:14":{"kind":"call","span":{"start":[58,14],"end":[58,57],"bytes":[2358,2401]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles"},"62:8":{"kind":"call","span":{"start":[62,8],"end":[62,36],"bytes":[2447,2475]},"callee":null},"65:6":{"kind":"call","span":{"start":[65,6],"end":[65,16],"bytes":[2505,2515]},"callee":null},"66:11":{"kind":"call","span":{"start":[66,11],"end":[66,21],"bytes":[2528,2538]},"callee":null},"68:17":{"kind":"call","span":{"start":[68,17],"end":[68,29],"bytes":[2556,2568]},"callee":null},"70:8":{"kind":"call","span":{"start":[70,8],"end":[70,23],"bytes":[2589,2604]},"callee":null},"73:6":{"kind":"call","span":{"start":[73,6],"end":[73,33],"bytes":[2615,2642]},"callee":null},"80:12":{"kind":"call","span":{"start":[80,12],"end":[80,23],"bytes":[2715,2726]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FindLTXFiles/github.com/benbjohnson/litestream.FindLTXFiles.closure_1","kind":"function","span":{"start":[62,1],"end":[62,36],"bytes":[2440,2475]},"signature":"github.com/benbjohnson/litestream.FindLTXFiles.closure_1","parameters":[],"body":{"62:21":{"kind":"call","span":{"start":[62,21],"end":[62,32],"bytes":[2460,2471]},"callee":null}}}}},"github.com/benbjohnson/litestream.fetchPageIndexData":{"id":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.fetchPageIndexData","kind":"function","span":{"start":[116,1],"end":[152,2],"bytes":[4033,5466]},"signature":"github.com/benbjohnson/litestream.fetchPageIndexData","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[116,1],"end":[116,112],"bytes":[4033,4144]}},{"name":"client","type":"github.com/benbjohnson/litestream.ReplicaClient","span":{"start":[116,1],"end":[116,112],"bytes":[4033,4144]}},{"name":"info","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[116,1],"end":[116,112],"bytes":[4033,4144]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"123:12":{"kind":"call","span":{"start":[123,12],"end":[123,86],"bytes":[4296,4370]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile"},"125:15":{"kind":"call","span":{"start":[125,15],"end":[125,51],"bytes":[4402,4438]},"callee":null},"127:8":{"kind":"call","span":{"start":[127,8],"end":[127,17],"bytes":[4449,4458]},"callee":null},"130:12":{"kind":"call","span":{"start":[130,12],"end":[130,25],"bytes":[4565,4578]},"callee":null},"132:15":{"kind":"call","span":{"start":[132,15],"end":[132,57],"bytes":[4610,4652]},"callee":null},"137:5":{"kind":"call","span":{"start":[137,5],"end":[137,11],"bytes":[4894,4900]},"callee":null},"138:15":{"kind":"call","span":{"start":[138,15],"end":[138,89],"bytes":[4937,5011]},"callee":null},"138:82":{"kind":"call","span":{"start":[138,82],"end":[138,88],"bytes":[5004,5010]},"callee":null},"141:10":{"kind":"call","span":{"start":[141,10],"end":[141,63],"bytes":[5025,5078]},"callee":null},"141:36":{"kind":"call","span":{"start":[141,36],"end":[141,42],"bytes":[5051,5057]},"callee":null},"142:12":{"kind":"call","span":{"start":[142,12],"end":[142,18],"bytes":[5090,5096]},"callee":null},"142:21":{"kind":"call","span":{"start":[142,21],"end":[142,30],"bytes":[5099,5108]},"callee":null},"143:10":{"kind":"call","span":{"start":[143,10],"end":[143,48],"bytes":[5151,5189]},"callee":null},"143:23":{"kind":"call","span":{"start":[143,23],"end":[143,47],"bytes":[5164,5188]},"callee":null},"147:103":{"kind":"call","span":{"start":[147,103],"end":[147,114],"bytes":[5363,5374]},"callee":null},"147:11":{"kind":"call","span":{"start":[147,11],"end":[147,118],"bytes":[5271,5378]},"callee":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile"},"149:15":{"kind":"call","span":{"start":[149,15],"end":[149,51],"bytes":[5410,5446]},"callee":null}}}},"content_hash":"c57b00cfbdeb7e706a61acbdf3ca4d50075167c197082d38eea328b42a7388cb"},"replica_url.go":{"id":"can://go/litestream/replica_url.go","kind":"module","span":{"start":[1,1],"end":[441,1],"bytes":[0,12872]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"path\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// ReplicaClientFactory is a function that creates a ReplicaClient from URL components.\n// The userinfo parameter contains credentials from the URL (e.g., user:pass@host).\ntype ReplicaClientFactory func(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (ReplicaClient, error)\n\nvar (\n\treplicaClientFactories = make(map[string]ReplicaClientFactory)\n\treplicaClientFactoriesMu sync.RWMutex\n)\n\n// RegisterReplicaClientFactory registers a factory function for creating replica clients\n// for a given URL scheme. This is typically called from init() functions in backend packages.\nfunc RegisterReplicaClientFactory(scheme string, factory ReplicaClientFactory) {\n\treplicaClientFactoriesMu.Lock()\n\tdefer replicaClientFactoriesMu.Unlock()\n\treplicaClientFactories[scheme] = factory\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from a URL string.\n// The URL scheme determines which backend is used (s3, gs, abs, file, etc.).\nfunc NewReplicaClientFromURL(rawURL string) (ReplicaClient, error) {\n\tscheme, host, urlPath, query, userinfo, err := ParseReplicaURLWithQuery(rawURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Normalize webdavs to webdav\n\tfactoryScheme := scheme\n\tif factoryScheme == \"webdavs\" {\n\t\tfactoryScheme = \"webdav\"\n\t}\n\n\treplicaClientFactoriesMu.RLock()\n\tfactory, ok := replicaClientFactories[factoryScheme]\n\treplicaClientFactoriesMu.RUnlock()\n\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unsupported replica URL scheme: %q\", scheme)\n\t}\n\n\treturn factory(scheme, host, urlPath, query, userinfo)\n}\n\n// ReplicaTypeFromURL returns the replica type from a URL string.\n// Returns empty string if the URL is invalid or has no scheme.\nfunc ReplicaTypeFromURL(rawURL string) string {\n\tif !IsURL(rawURL) {\n\t\treturn \"\"\n\t}\n\tscheme, _, _, _ := ParseReplicaURL(rawURL)\n\tif scheme == \"\" {\n\t\treturn \"\"\n\t}\n\tif scheme == \"webdavs\" {\n\t\treturn \"webdav\"\n\t}\n\treturn scheme\n}\n\n// ParseReplicaURL parses a replica URL and returns the scheme, host, and path.\nfunc ParseReplicaURL(s string) (scheme, host, urlPath string, err error) {\n\tif strings.HasPrefix(strings.ToLower(s), \"s3://arn:\") {\n\t\tscheme, host, urlPath, _, err = parseS3AccessPointURL(s)\n\t\treturn scheme, host, urlPath, err\n\t}\n\n\tscheme, host, urlPath, _, _, err = ParseReplicaURLWithQuery(s)\n\treturn scheme, host, urlPath, err\n}\n\n// ParseReplicaURLWithQuery parses a replica URL and returns query parameters and userinfo.\nfunc ParseReplicaURLWithQuery(s string) (scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo, err error) {\n\t// Handle S3 Access Point ARNs which can't be parsed by standard url.Parse\n\tif strings.HasPrefix(strings.ToLower(s), \"s3://arn:\") {\n\t\tscheme, host, urlPath, query, err := parseS3AccessPointURL(s)\n\t\treturn scheme, host, urlPath, query, nil, err\n\t}\n\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", nil, nil, err\n\t}\n\n\tswitch u.Scheme {\n\tcase \"file\":\n\t\tscheme, u.Scheme = u.Scheme, \"\"\n\t\t// Remove query params from path for file URLs\n\t\tu.RawQuery = \"\"\n\t\treturn scheme, \"\", path.Clean(u.String()), nil, nil, nil\n\n\tcase \"\":\n\t\treturn u.Scheme, u.Host, u.Path, nil, nil, fmt.Errorf(\"replica url scheme required: %s\", s)\n\n\tdefault:\n\t\treturn u.Scheme, u.Host, strings.TrimPrefix(path.Clean(u.Path), \"/\"), u.Query(), u.User, nil\n\t}\n}\n\n// parseS3AccessPointURL parses an S3 Access Point URL (s3://arn:...).\nfunc parseS3AccessPointURL(s string) (scheme, host, urlPath string, query url.Values, err error) {\n\tconst prefix = \"s3://\"\n\tif !strings.HasPrefix(strings.ToLower(s), prefix) {\n\t\treturn \"\", \"\", \"\", nil, fmt.Errorf(\"invalid s3 access point url: %s\", s)\n\t}\n\n\tarnWithPath := s[len(prefix):]\n\n\t// Split off query string if present\n\tvar queryStr string\n\tif idx := strings.IndexByte(arnWithPath, '?'); idx != -1 {\n\t\tqueryStr = arnWithPath[idx+1:]\n\t\tarnWithPath = arnWithPath[:idx]\n\t}\n\n\tbucket, key, err := splitS3AccessPointARN(arnWithPath)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", nil, err\n\t}\n\n\t// Parse query string if present\n\tif queryStr != \"\" {\n\t\tquery, err = url.ParseQuery(queryStr)\n\t\tif err != nil {\n\t\t\treturn \"\", \"\", \"\", nil, fmt.Errorf(\"parse query string: %w\", err)\n\t\t}\n\t}\n\n\treturn \"s3\", bucket, CleanReplicaURLPath(key), query, nil\n}\n\n// splitS3AccessPointARN splits an S3 Access Point ARN into bucket and key components.\nfunc splitS3AccessPointARN(s string) (bucket, key string, err error) {\n\tlower := strings.ToLower(s)\n\tconst marker = \":accesspoint/\"\n\tidx := strings.Index(lower, marker)\n\tif idx == -1 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid s3 access point arn: %s\", s)\n\t}\n\n\tnameStart := idx + len(marker)\n\tif nameStart \u003e= len(s) {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid s3 access point arn: %s\", s)\n\t}\n\n\tremainder := s[nameStart:]\n\tslashIdx := strings.IndexByte(remainder, '/')\n\tif slashIdx == -1 {\n\t\treturn s, \"\", nil\n\t}\n\n\tbucketEnd := nameStart + slashIdx\n\tbucket = s[:bucketEnd]\n\tkey = remainder[slashIdx+1:]\n\treturn bucket, key, nil\n}\n\n// CleanReplicaURLPath cleans a URL path for use in replica storage.\nfunc CleanReplicaURLPath(p string) string {\n\tif p == \"\" {\n\t\treturn \"\"\n\t}\n\tcleaned := path.Clean(\"/\" + p)\n\tcleaned = strings.TrimPrefix(cleaned, \"/\")\n\tif cleaned == \".\" {\n\t\treturn \"\"\n\t}\n\treturn cleaned\n}\n\n// RegionFromS3ARN extracts the region from an S3 ARN.\nfunc RegionFromS3ARN(arn string) string {\n\tparts := strings.SplitN(arn, \":\", 6)\n\tif len(parts) \u003e= 4 {\n\t\treturn parts[3]\n\t}\n\treturn \"\"\n}\n\n// BoolQueryValue returns a boolean value from URL query parameters.\n// It checks multiple keys in order and returns the value and whether it was set.\nfunc BoolQueryValue(query url.Values, keys ...string) (value bool, ok bool) {\n\tif query == nil {\n\t\treturn false, false\n\t}\n\tfor _, key := range keys {\n\t\tif raw := query.Get(key); raw != \"\" {\n\t\t\tswitch strings.ToLower(raw) {\n\t\t\tcase \"true\", \"1\", \"t\", \"yes\":\n\t\t\t\treturn true, true\n\t\t\tcase \"false\", \"0\", \"f\", \"no\":\n\t\t\t\treturn false, true\n\t\t\tdefault:\n\t\t\t\treturn false, true\n\t\t\t}\n\t\t}\n\t}\n\treturn false, false\n}\n\n// IntQueryValue returns a positive integer value from URL query parameters.\n// It checks keys in order and returns the value and whether it was set.\n// Returns an error if a present value is not a positive integer.\nfunc IntQueryValue(query url.Values, keys ...string) (value int64, ok bool, err error) {\n\tif query == nil {\n\t\treturn 0, false, nil\n\t}\n\tfor _, key := range keys {\n\t\tif raw := query.Get(key); raw != \"\" {\n\t\t\tn, err := strconv.ParseInt(raw, 10, 64)\n\t\t\tif err != nil || n \u003c= 0 {\n\t\t\t\treturn 0, false, fmt.Errorf(\"invalid value for query parameter %q: %q (must be a positive integer)\", key, raw)\n\t\t\t}\n\t\t\treturn n, true, nil\n\t\t}\n\t}\n\treturn 0, false, nil\n}\n\n// IsHetznerEndpoint returns true if the endpoint is Hetzner object storage service.\nfunc IsHetznerEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn strings.HasSuffix(host, \".your-objectstorage.com\")\n}\n\n// IsTigrisEndpoint returns true if the endpoint is the Tigris object storage service.\nfunc IsTigrisEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn host == \"fly.storage.tigris.dev\" || host == \"t3.storage.dev\"\n}\n\n// IsGoogleCloudStorageEndpoint returns true if the endpoint is Google Cloud Storage.\nfunc IsGoogleCloudStorageEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\thost = strings.TrimSuffix(host, \".\")\n\n\tlabels := strings.Split(host, \".\")\n\tif len(labels) \u003c 3 ||\n\t\tlabels[len(labels)-2] != \"googleapis\" ||\n\t\tlabels[len(labels)-1] != \"com\" {\n\t\treturn false\n\t}\n\tfor _, label := range labels {\n\t\tif label == \"\" {\n\t\t\treturn false\n\t\t}\n\t}\n\tif len(labels) \u003e 3 {\n\t\tfor _, segment := range strings.Split(labels[1], \"-\") {\n\t\t\tif segment == \"storage\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\tfor _, segment := range strings.Split(labels[0], \"-\") {\n\t\tif segment == \"storage\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// IsDigitalOceanEndpoint returns true if the endpoint is Digital Ocean Spaces.\nfunc IsDigitalOceanEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn strings.HasSuffix(host, \".digitaloceanspaces.com\")\n}\n\n// IsBackblazeEndpoint returns true if the endpoint is Backblaze B2.\nfunc IsBackblazeEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn strings.HasSuffix(host, \".backblazeb2.com\")\n}\n\n// IsFilebaseEndpoint returns true if the endpoint is Filebase.\nfunc IsFilebaseEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn host == \"s3.filebase.com\"\n}\n\n// IsScalewayEndpoint returns true if the endpoint is Scaleway Object Storage.\nfunc IsScalewayEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn strings.HasSuffix(host, \".scw.cloud\")\n}\n\n// IsCloudflareR2Endpoint returns true if the endpoint is Cloudflare R2.\nfunc IsCloudflareR2Endpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn strings.HasSuffix(host, \".r2.cloudflarestorage.com\")\n}\n\n// IsSupabaseEndpoint returns true if the endpoint is Supabase Storage S3.\nfunc IsSupabaseEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\treturn strings.HasSuffix(host, \".supabase.co\")\n}\n\n// IsMinIOEndpoint returns true if the endpoint appears to be MinIO or similar\n// (a custom endpoint with a port number that is not a known cloud provider).\nfunc IsMinIOEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\tif u.Port() == \"\" {\n\t\treturn false\n\t}\n\t// Exclude known cloud providers\n\tif strings.Contains(host, \".amazonaws.com\") ||\n\t\tstrings.Contains(host, \".digitaloceanspaces.com\") ||\n\t\tstrings.Contains(host, \".backblazeb2.com\") ||\n\t\tstrings.Contains(host, \".filebase.com\") ||\n\t\tstrings.Contains(host, \".scw.cloud\") ||\n\t\tstrings.Contains(host, \".r2.cloudflarestorage.com\") ||\n\t\tstrings.Contains(host, \"tigris.dev\") ||\n\t\tstrings.Contains(host, \"t3.storage.dev\") ||\n\t\tstrings.Contains(host, \".supabase.co\") ||\n\t\tstrings.HasSuffix(host, \".your-objectstorage.com\") ||\n\t\tstrings.HasSuffix(host, \".googleapis.com\") {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// IsLocalEndpoint returns true if the endpoint appears to be a local development\n// endpoint (localhost, 127.0.0.1, or private network addresses).\n// These endpoints typically use HTTP instead of HTTPS.\nfunc IsLocalEndpoint(endpoint string) bool {\n\tu, err := parseEndpoint(endpoint)\n\tif err != nil {\n\t\treturn false\n\t}\n\thost := u.Hostname()\n\t// Check for common local/development hostnames\n\treturn host == \"localhost\" ||\n\t\thost == \"127.0.0.1\" ||\n\t\tstrings.HasPrefix(host, \"192.168.\") ||\n\t\tstrings.HasPrefix(host, \"10.\") ||\n\t\tstrings.HasPrefix(host, \"172.16.\") ||\n\t\tstrings.HasPrefix(host, \"172.17.\") ||\n\t\tstrings.HasPrefix(host, \"172.18.\") ||\n\t\tstrings.HasPrefix(host, \"172.19.\") ||\n\t\tstrings.HasPrefix(host, \"172.2\") || // 172.20-172.29\n\t\tstrings.HasPrefix(host, \"172.30.\") ||\n\t\tstrings.HasPrefix(host, \"172.31.\") ||\n\t\tstrings.HasSuffix(host, \".local\") ||\n\t\tstrings.HasSuffix(host, \".localhost\")\n}\n\n// EnsureEndpointScheme ensures an endpoint has an HTTP(S) scheme.\n// For local endpoints (localhost, private IPs), it defaults to http://.\n// For all other endpoints (cloud providers), it defaults to https://.\n// Returns the endpoint with scheme and a boolean indicating if a scheme was added.\nfunc EnsureEndpointScheme(endpoint string) (string, bool) {\n\tif endpoint == \"\" {\n\t\treturn \"\", false\n\t}\n\tif strings.HasPrefix(endpoint, \"http://\") || strings.HasPrefix(endpoint, \"https://\") {\n\t\treturn endpoint, false\n\t}\n\t// Default to HTTP for local development endpoints, HTTPS for everything else\n\tif IsLocalEndpoint(endpoint) {\n\t\treturn \"http://\" + endpoint, true\n\t}\n\treturn \"https://\" + endpoint, true\n}\n\nfunc parseEndpoint(endpoint string) (*url.URL, error) {\n\tendpoint = strings.TrimSpace(endpoint)\n\tif !strings.HasPrefix(strings.ToLower(endpoint), \"http://\") \u0026\u0026 !strings.HasPrefix(strings.ToLower(endpoint), \"https://\") {\n\t\tendpoint = \"//\" + endpoint\n\t}\n\tu, err := url.Parse(endpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif u.User != nil {\n\t\treturn nil, fmt.Errorf(\"endpoint must not contain user information\")\n\t}\n\tif u.Hostname() == \"\" {\n\t\treturn nil, fmt.Errorf(\"endpoint hostname required\")\n\t}\n\tu.Host = strings.ToLower(u.Host)\n\treturn u, nil\n}\n\n// IsURL returns true if s appears to be a URL (has a scheme).\nvar isURLRegex = regexp.MustCompile(`^\\w+:\\/\\/`)\n\nfunc IsURL(s string) bool {\n\treturn isURLRegex.MatchString(s)\n}\n","imports":[{"name":"fmt","path":"fmt","span":{"start":[4,1],"end":[4,7],"bytes":[29,35]}},{"name":"url","path":"net/url","span":{"start":[5,1],"end":[5,11],"bytes":[36,46]}},{"name":"path","path":"path","span":{"start":[6,1],"end":[6,8],"bytes":[47,54]}},{"name":"regexp","path":"regexp","span":{"start":[7,1],"end":[7,10],"bytes":[55,64]}},{"name":"strconv","path":"strconv","span":{"start":[8,1],"end":[8,11],"bytes":[65,75]}},{"name":"strings","path":"strings","span":{"start":[9,1],"end":[9,11],"bytes":[76,86]}},{"name":"sync","path":"sync","span":{"start":[10,1],"end":[10,8],"bytes":[87,94]}}],"types":{"ReplicaClientFactory":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ReplicaClientFactory","kind":"struct","span":{"start":[15,1],"end":[15,126],"bytes":[270,395]},"callables":{}}},"functions":{"github.com/benbjohnson/litestream.BoolQueryValue":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue","kind":"function","span":{"start":[192,1],"end":[209,2],"bytes":[5585,5988]},"signature":"github.com/benbjohnson/litestream.BoolQueryValue","parameters":[{"name":"query","type":"net/url.Values","span":{"start":[192,1],"end":[192,78],"bytes":[5585,5662]}},{"name":"keys","type":"[]string","span":{"start":[192,1],"end":[192,78],"bytes":[5585,5662]},"is_variadic":true}],"return_type":"(bool, bool)","metrics":{"cyclomatic":8},"body":{"197:13":{"kind":"call","span":{"start":[197,13],"end":[197,27],"bytes":[5747,5761]},"callee":null},"198:11":{"kind":"call","span":{"start":[198,11],"end":[198,31],"bytes":[5785,5805]},"callee":null}}},"github.com/benbjohnson/litestream.CleanReplicaURLPath":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.CleanReplicaURLPath","kind":"function","span":{"start":[169,1],"end":[179,2],"bytes":[5038,5240]},"signature":"github.com/benbjohnson/litestream.CleanReplicaURLPath","parameters":[{"name":"p","type":"string","span":{"start":[169,1],"end":[169,44],"bytes":[5038,5081]}}],"return_type":"string","metrics":{"cyclomatic":3},"body":{"173:13":{"kind":"call","span":{"start":[173,13],"end":[173,32],"bytes":[5123,5142]},"callee":null},"174:12":{"kind":"call","span":{"start":[174,12],"end":[174,44],"bytes":[5154,5186]},"callee":null}}},"github.com/benbjohnson/litestream.EnsureEndpointScheme":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme","kind":"function","span":{"start":[402,1],"end":[414,2],"bytes":[11737,12143]},"signature":"github.com/benbjohnson/litestream.EnsureEndpointScheme","parameters":[{"name":"endpoint","type":"string","span":{"start":[402,1],"end":[402,60],"bytes":[11737,11796]}}],"return_type":"(string, bool)","metrics":{"cyclomatic":4},"body":{"406:47":{"kind":"call","span":{"start":[406,47],"end":[406,86],"bytes":[11886,11925]},"callee":null},"406:5":{"kind":"call","span":{"start":[406,5],"end":[406,43],"bytes":[11844,11882]},"callee":null},"410:5":{"kind":"call","span":{"start":[410,5],"end":[410,30],"bytes":[12039,12064]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsLocalEndpoint"}}},"github.com/benbjohnson/litestream.IntQueryValue":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue","kind":"function","span":{"start":[214,1],"end":[228,2],"bytes":[6206,6653]},"signature":"github.com/benbjohnson/litestream.IntQueryValue","parameters":[{"name":"query","type":"net/url.Values","span":{"start":[214,1],"end":[214,89],"bytes":[6206,6294]}},{"name":"keys","type":"[]string","span":{"start":[214,1],"end":[214,89],"bytes":[6206,6294]},"is_variadic":true}],"return_type":"(int64, bool, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"219:13":{"kind":"call","span":{"start":[219,13],"end":[219,27],"bytes":[6380,6394]},"callee":null},"220:14":{"kind":"call","span":{"start":[220,14],"end":[220,43],"bytes":[6421,6450]},"callee":null},"222:22":{"kind":"call","span":{"start":[222,22],"end":[222,115],"bytes":[6501,6594]},"callee":null}}},"github.com/benbjohnson/litestream.IsBackblazeEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsBackblazeEndpoint","kind":"function","span":{"start":[296,1],"end":[303,2],"bytes":[8359,8553]},"signature":"github.com/benbjohnson/litestream.IsBackblazeEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[296,1],"end":[296,49],"bytes":[8359,8407]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"297:12":{"kind":"call","span":{"start":[297,12],"end":[297,35],"bytes":[8419,8442]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"301:10":{"kind":"call","span":{"start":[301,10],"end":[301,22],"bytes":[8487,8499]},"callee":null},"302:9":{"kind":"call","span":{"start":[302,9],"end":[302,52],"bytes":[8508,8551]},"callee":null}}},"github.com/benbjohnson/litestream.IsCloudflareR2Endpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsCloudflareR2Endpoint","kind":"function","span":{"start":[326,1],"end":[333,2],"bytes":[9137,9343]},"signature":"github.com/benbjohnson/litestream.IsCloudflareR2Endpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[326,1],"end":[326,52],"bytes":[9137,9188]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"327:12":{"kind":"call","span":{"start":[327,12],"end":[327,35],"bytes":[9200,9223]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"331:10":{"kind":"call","span":{"start":[331,10],"end":[331,22],"bytes":[9268,9280]},"callee":null},"332:9":{"kind":"call","span":{"start":[332,9],"end":[332,61],"bytes":[9289,9341]},"callee":null}}},"github.com/benbjohnson/litestream.IsDigitalOceanEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsDigitalOceanEndpoint","kind":"function","span":{"start":[286,1],"end":[293,2],"bytes":[8084,8288]},"signature":"github.com/benbjohnson/litestream.IsDigitalOceanEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[286,1],"end":[286,52],"bytes":[8084,8135]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"287:12":{"kind":"call","span":{"start":[287,12],"end":[287,35],"bytes":[8147,8170]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"291:10":{"kind":"call","span":{"start":[291,10],"end":[291,22],"bytes":[8215,8227]},"callee":null},"292:9":{"kind":"call","span":{"start":[292,9],"end":[292,59],"bytes":[8236,8286]},"callee":null}}},"github.com/benbjohnson/litestream.IsFilebaseEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsFilebaseEndpoint","kind":"function","span":{"start":[306,1],"end":[313,2],"bytes":[8619,8794]},"signature":"github.com/benbjohnson/litestream.IsFilebaseEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[306,1],"end":[306,48],"bytes":[8619,8666]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"307:12":{"kind":"call","span":{"start":[307,12],"end":[307,35],"bytes":[8678,8701]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"311:10":{"kind":"call","span":{"start":[311,10],"end":[311,22],"bytes":[8746,8758]},"callee":null}}},"github.com/benbjohnson/litestream.IsGoogleCloudStorageEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsGoogleCloudStorageEndpoint","kind":"function","span":{"start":[251,1],"end":[283,2],"bytes":[7324,8002]},"signature":"github.com/benbjohnson/litestream.IsGoogleCloudStorageEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[251,1],"end":[251,58],"bytes":[7324,7381]}}],"return_type":"bool","metrics":{"cyclomatic":10},"body":{"252:12":{"kind":"call","span":{"start":[252,12],"end":[252,35],"bytes":[7393,7416]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"256:10":{"kind":"call","span":{"start":[256,10],"end":[256,22],"bytes":[7461,7473]},"callee":null},"257:9":{"kind":"call","span":{"start":[257,9],"end":[257,38],"bytes":[7482,7511]},"callee":null},"259:12":{"kind":"call","span":{"start":[259,12],"end":[259,36],"bytes":[7524,7548]},"callee":null},"260:5":{"kind":"call","span":{"start":[260,5],"end":[260,16],"bytes":[7553,7564]},"callee":null},"261:10":{"kind":"call","span":{"start":[261,10],"end":[261,21],"bytes":[7581,7592]},"callee":null},"262:10":{"kind":"call","span":{"start":[262,10],"end":[262,21],"bytes":[7624,7635]},"callee":null},"270:5":{"kind":"call","span":{"start":[270,5],"end":[270,16],"bytes":[7746,7757]},"callee":null},"271:27":{"kind":"call","span":{"start":[271,27],"end":[271,56],"bytes":[7790,7819]},"callee":null},"277:26":{"kind":"call","span":{"start":[277,26],"end":[277,55],"bytes":[7905,7934]},"callee":null}}},"github.com/benbjohnson/litestream.IsHetznerEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsHetznerEndpoint","kind":"function","span":{"start":[231,1],"end":[238,2],"bytes":[6740,6939]},"signature":"github.com/benbjohnson/litestream.IsHetznerEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[231,1],"end":[231,47],"bytes":[6740,6786]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"232:12":{"kind":"call","span":{"start":[232,12],"end":[232,35],"bytes":[6798,6821]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"236:10":{"kind":"call","span":{"start":[236,10],"end":[236,22],"bytes":[6866,6878]},"callee":null},"237:9":{"kind":"call","span":{"start":[237,9],"end":[237,59],"bytes":[6887,6937]},"callee":null}}},"github.com/benbjohnson/litestream.IsLocalEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsLocalEndpoint","kind":"function","span":{"start":[376,1],"end":[396,2],"bytes":[10746,11440]},"signature":"github.com/benbjohnson/litestream.IsLocalEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[376,1],"end":[376,45],"bytes":[10746,10790]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"377:12":{"kind":"call","span":{"start":[377,12],"end":[377,35],"bytes":[10802,10825]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"381:10":{"kind":"call","span":{"start":[381,10],"end":[381,22],"bytes":[10870,10882]},"callee":null},"385:3":{"kind":"call","span":{"start":[385,3],"end":[385,38],"bytes":[10990,11025]},"callee":null},"386:3":{"kind":"call","span":{"start":[386,3],"end":[386,33],"bytes":[11031,11061]},"callee":null},"387:3":{"kind":"call","span":{"start":[387,3],"end":[387,37],"bytes":[11067,11101]},"callee":null},"388:3":{"kind":"call","span":{"start":[388,3],"end":[388,37],"bytes":[11107,11141]},"callee":null},"389:3":{"kind":"call","span":{"start":[389,3],"end":[389,37],"bytes":[11147,11181]},"callee":null},"390:3":{"kind":"call","span":{"start":[390,3],"end":[390,37],"bytes":[11187,11221]},"callee":null},"391:3":{"kind":"call","span":{"start":[391,3],"end":[391,35],"bytes":[11227,11259]},"callee":null},"392:3":{"kind":"call","span":{"start":[392,3],"end":[392,37],"bytes":[11282,11316]},"callee":null},"393:3":{"kind":"call","span":{"start":[393,3],"end":[393,37],"bytes":[11322,11356]},"callee":null},"394:3":{"kind":"call","span":{"start":[394,3],"end":[394,36],"bytes":[11362,11395]},"callee":null},"395:3":{"kind":"call","span":{"start":[395,3],"end":[395,40],"bytes":[11401,11438]},"callee":null}}},"github.com/benbjohnson/litestream.IsMinIOEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsMinIOEndpoint","kind":"function","span":{"start":[347,1],"end":[371,2],"bytes":[9768,10540]},"signature":"github.com/benbjohnson/litestream.IsMinIOEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[347,1],"end":[347,45],"bytes":[9768,9812]}}],"return_type":"bool","metrics":{"cyclomatic":4},"body":{"348:12":{"kind":"call","span":{"start":[348,12],"end":[348,35],"bytes":[9824,9847]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"352:10":{"kind":"call","span":{"start":[352,10],"end":[352,22],"bytes":[9892,9904]},"callee":null},"353:5":{"kind":"call","span":{"start":[353,5],"end":[353,13],"bytes":[9909,9917]},"callee":null},"357:5":{"kind":"call","span":{"start":[357,5],"end":[357,45],"bytes":[9982,10022]},"callee":null},"358:3":{"kind":"call","span":{"start":[358,3],"end":[358,52],"bytes":[10028,10077]},"callee":null},"359:3":{"kind":"call","span":{"start":[359,3],"end":[359,45],"bytes":[10083,10125]},"callee":null},"360:3":{"kind":"call","span":{"start":[360,3],"end":[360,42],"bytes":[10131,10170]},"callee":null},"361:3":{"kind":"call","span":{"start":[361,3],"end":[361,39],"bytes":[10176,10212]},"callee":null},"362:3":{"kind":"call","span":{"start":[362,3],"end":[362,54],"bytes":[10218,10269]},"callee":null},"363:3":{"kind":"call","span":{"start":[363,3],"end":[363,39],"bytes":[10275,10311]},"callee":null},"364:3":{"kind":"call","span":{"start":[364,3],"end":[364,43],"bytes":[10317,10357]},"callee":null},"365:3":{"kind":"call","span":{"start":[365,3],"end":[365,41],"bytes":[10363,10401]},"callee":null},"366:3":{"kind":"call","span":{"start":[366,3],"end":[366,53],"bytes":[10407,10457]},"callee":null},"367:3":{"kind":"call","span":{"start":[367,3],"end":[367,45],"bytes":[10463,10505]},"callee":null}}},"github.com/benbjohnson/litestream.IsScalewayEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsScalewayEndpoint","kind":"function","span":{"start":[316,1],"end":[323,2],"bytes":[8875,9062]},"signature":"github.com/benbjohnson/litestream.IsScalewayEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[316,1],"end":[316,48],"bytes":[8875,8922]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"317:12":{"kind":"call","span":{"start":[317,12],"end":[317,35],"bytes":[8934,8957]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"321:10":{"kind":"call","span":{"start":[321,10],"end":[321,22],"bytes":[9002,9014]},"callee":null},"322:9":{"kind":"call","span":{"start":[322,9],"end":[322,46],"bytes":[9023,9060]},"callee":null}}},"github.com/benbjohnson/litestream.IsSupabaseEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsSupabaseEndpoint","kind":"function","span":{"start":[336,1],"end":[343,2],"bytes":[9420,9609]},"signature":"github.com/benbjohnson/litestream.IsSupabaseEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[336,1],"end":[336,48],"bytes":[9420,9467]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"337:12":{"kind":"call","span":{"start":[337,12],"end":[337,35],"bytes":[9479,9502]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"341:10":{"kind":"call","span":{"start":[341,10],"end":[341,22],"bytes":[9547,9559]},"callee":null},"342:9":{"kind":"call","span":{"start":[342,9],"end":[342,48],"bytes":[9568,9607]},"callee":null}}},"github.com/benbjohnson/litestream.IsTigrisEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsTigrisEndpoint","kind":"function","span":{"start":[241,1],"end":[248,2],"bytes":[7028,7236]},"signature":"github.com/benbjohnson/litestream.IsTigrisEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[241,1],"end":[241,46],"bytes":[7028,7073]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"242:12":{"kind":"call","span":{"start":[242,12],"end":[242,35],"bytes":[7085,7108]},"callee":null},"246:10":{"kind":"call","span":{"start":[246,10],"end":[246,22],"bytes":[7153,7165]},"callee":null}}},"github.com/benbjohnson/litestream.IsURL":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL","kind":"function","span":{"start":[438,1],"end":[440,2],"bytes":[12808,12871]},"signature":"github.com/benbjohnson/litestream.IsURL","parameters":[{"name":"s","type":"string","span":{"start":[438,1],"end":[438,28],"bytes":[12808,12835]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"439:9":{"kind":"call","span":{"start":[439,9],"end":[439,34],"bytes":[12844,12869]},"callee":null}}},"github.com/benbjohnson/litestream.NewReplicaClientFromURL":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.NewReplicaClientFromURL","kind":"function","span":{"start":[32,1],"end":[53,2],"bytes":[1048,1625]},"signature":"github.com/benbjohnson/litestream.NewReplicaClientFromURL","parameters":[{"name":"rawURL","type":"string","span":{"start":[32,1],"end":[32,69],"bytes":[1048,1116]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"33:49":{"kind":"call","span":{"start":[33,49],"end":[33,81],"bytes":[1165,1197]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery"},"44:2":{"kind":"call","span":{"start":[44,2],"end":[44,34],"bytes":[1359,1391]},"callee":null},"46:2":{"kind":"call","span":{"start":[46,2],"end":[46,36],"bytes":[1447,1481]},"callee":null},"49:15":{"kind":"call","span":{"start":[49,15],"end":[49,71],"bytes":[1507,1563]},"callee":null},"52:9":{"kind":"call","span":{"start":[52,9],"end":[52,56],"bytes":[1576,1623]},"callee":null}}},"github.com/benbjohnson/litestream.ParseReplicaURL":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","kind":"function","span":{"start":[72,1],"end":[80,2],"bytes":[2064,2395]},"signature":"github.com/benbjohnson/litestream.ParseReplicaURL","parameters":[{"name":"s","type":"string","span":{"start":[72,1],"end":[72,75],"bytes":[2064,2138]}}],"return_type":"(string, string, string, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"73:23":{"kind":"call","span":{"start":[73,23],"end":[73,41],"bytes":[2161,2179]},"callee":null},"73:5":{"kind":"call","span":{"start":[73,5],"end":[73,55],"bytes":[2143,2193]},"callee":null},"74:35":{"kind":"call","span":{"start":[74,35],"end":[74,59],"bytes":[2230,2254]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL"},"78:37":{"kind":"call","span":{"start":[78,37],"end":[78,64],"bytes":[2331,2358]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery"}}},"github.com/benbjohnson/litestream.ParseReplicaURLWithQuery":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery","kind":"function","span":{"start":[83,1],"end":[108,2],"bytes":[2489,3352]},"signature":"github.com/benbjohnson/litestream.ParseReplicaURLWithQuery","parameters":[{"name":"s","type":"string","span":{"start":[83,1],"end":[83,126],"bytes":[2489,2614]}}],"return_type":"(string, string, string, net/url.Values, *net/url.Userinfo, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"100:22":{"kind":"call","span":{"start":[100,22],"end":[100,44],"bytes":[3099,3121]},"callee":null},"100:33":{"kind":"call","span":{"start":[100,33],"end":[100,43],"bytes":[3110,3120]},"callee":null},"103:46":{"kind":"call","span":{"start":[103,46],"end":[103,94],"bytes":[3193,3241]},"callee":null},"106:28":{"kind":"call","span":{"start":[106,28],"end":[106,71],"bytes":[3280,3323]},"callee":null},"106:47":{"kind":"call","span":{"start":[106,47],"end":[106,65],"bytes":[3299,3317]},"callee":null},"106:73":{"kind":"call","span":{"start":[106,73],"end":[106,82],"bytes":[3325,3334]},"callee":null},"85:23":{"kind":"call","span":{"start":[85,23],"end":[85,41],"bytes":[2713,2731]},"callee":null},"85:5":{"kind":"call","span":{"start":[85,5],"end":[85,55],"bytes":[2695,2745]},"callee":null},"86:40":{"kind":"call","span":{"start":[86,40],"end":[86,64],"bytes":[2787,2811]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL"},"90:12":{"kind":"call","span":{"start":[90,12],"end":[90,24],"bytes":[2875,2887]},"callee":null}}},"github.com/benbjohnson/litestream.RegionFromS3ARN":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegionFromS3ARN","kind":"function","span":{"start":[182,1],"end":[188,2],"bytes":[5297,5432]},"signature":"github.com/benbjohnson/litestream.RegionFromS3ARN","parameters":[{"name":"arn","type":"string","span":{"start":[182,1],"end":[182,42],"bytes":[5297,5338]}}],"return_type":"string","metrics":{"cyclomatic":2},"body":{"183:11":{"kind":"call","span":{"start":[183,11],"end":[183,38],"bytes":[5349,5376]},"callee":null},"184:5":{"kind":"call","span":{"start":[184,5],"end":[184,15],"bytes":[5381,5391]},"callee":null}}},"github.com/benbjohnson/litestream.RegisterReplicaClientFactory":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","kind":"function","span":{"start":[24,1],"end":[28,2],"bytes":[696,894]},"signature":"github.com/benbjohnson/litestream.RegisterReplicaClientFactory","parameters":[{"name":"scheme","type":"string","span":{"start":[24,1],"end":[24,81],"bytes":[696,776]}},{"name":"factory","type":"github.com/benbjohnson/litestream.ReplicaClientFactory","span":{"start":[24,1],"end":[24,81],"bytes":[696,776]}}],"metrics":{"cyclomatic":1},"body":{"25:2":{"kind":"call","span":{"start":[25,2],"end":[25,33],"bytes":[778,809]},"callee":null},"26:8":{"kind":"call","span":{"start":[26,8],"end":[26,41],"bytes":[817,850]},"callee":null}}},"github.com/benbjohnson/litestream.ReplicaTypeFromURL":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ReplicaTypeFromURL","kind":"function","span":{"start":[57,1],"end":[69,2],"bytes":[1757,1982]},"signature":"github.com/benbjohnson/litestream.ReplicaTypeFromURL","parameters":[{"name":"rawURL","type":"string","span":{"start":[57,1],"end":[57,48],"bytes":[1757,1804]}}],"return_type":"string","metrics":{"cyclomatic":4},"body":{"58:6":{"kind":"call","span":{"start":[58,6],"end":[58,19],"bytes":[1810,1823]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL"},"61:21":{"kind":"call","span":{"start":[61,21],"end":[61,44],"bytes":[1861,1884]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL"}}},"github.com/benbjohnson/litestream.parseEndpoint":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","kind":"function","span":{"start":[416,1],"end":[433,2],"bytes":[12145,12693]},"signature":"github.com/benbjohnson/litestream.parseEndpoint","parameters":[{"name":"endpoint","type":"string","span":{"start":[416,1],"end":[416,56],"bytes":[12145,12200]}}],"return_type":"(*net/url.URL, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"417:13":{"kind":"call","span":{"start":[417,13],"end":[417,40],"bytes":[12213,12240]},"callee":null},"418:24":{"kind":"call","span":{"start":[418,24],"end":[418,49],"bytes":[12264,12289]},"callee":null},"418:6":{"kind":"call","span":{"start":[418,6],"end":[418,61],"bytes":[12246,12301]},"callee":null},"418:66":{"kind":"call","span":{"start":[418,66],"end":[418,122],"bytes":[12306,12362]},"callee":null},"418:84":{"kind":"call","span":{"start":[418,84],"end":[418,109],"bytes":[12324,12349]},"callee":null},"421:12":{"kind":"call","span":{"start":[421,12],"end":[421,31],"bytes":[12408,12427]},"callee":null},"426:15":{"kind":"call","span":{"start":[426,15],"end":[426,71],"bytes":[12500,12556]},"callee":null},"428:5":{"kind":"call","span":{"start":[428,5],"end":[428,17],"bytes":[12564,12576]},"callee":null},"429:15":{"kind":"call","span":{"start":[429,15],"end":[429,55],"bytes":[12599,12639]},"callee":null},"431:11":{"kind":"call","span":{"start":[431,11],"end":[431,34],"bytes":[12653,12676]},"callee":null}}},"github.com/benbjohnson/litestream.parseS3AccessPointURL":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL","kind":"function","span":{"start":[111,1],"end":[140,2],"bytes":[3425,4260]},"signature":"github.com/benbjohnson/litestream.parseS3AccessPointURL","parameters":[{"name":"s","type":"string","span":{"start":[111,1],"end":[111,99],"bytes":[3425,3523]}}],"return_type":"(string, string, string, net/url.Values, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"113:24":{"kind":"call","span":{"start":[113,24],"end":[113,42],"bytes":[3571,3589]},"callee":null},"113:6":{"kind":"call","span":{"start":[113,6],"end":[113,51],"bytes":[3553,3598]},"callee":null},"114:27":{"kind":"call","span":{"start":[114,27],"end":[114,75],"bytes":[3627,3675]},"callee":null},"117:19":{"kind":"call","span":{"start":[117,19],"end":[117,30],"bytes":[3698,3709]},"callee":null},"121:12":{"kind":"call","span":{"start":[121,12],"end":[121,47],"bytes":[3783,3818]},"callee":null},"126:22":{"kind":"call","span":{"start":[126,22],"end":[126,56],"bytes":[3924,3958]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.splitS3AccessPointARN"},"133:16":{"kind":"call","span":{"start":[133,16],"end":[133,40],"bytes":[4080,4104]},"callee":null},"135:28":{"kind":"call","span":{"start":[135,28],"end":[135,69],"bytes":[4150,4191]},"callee":null},"139:23":{"kind":"call","span":{"start":[139,23],"end":[139,47],"bytes":[4222,4246]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.CleanReplicaURLPath"}}},"github.com/benbjohnson/litestream.splitS3AccessPointARN":{"id":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.splitS3AccessPointARN","kind":"function","span":{"start":[143,1],"end":[166,2],"bytes":[4349,4967]},"signature":"github.com/benbjohnson/litestream.splitS3AccessPointARN","parameters":[{"name":"s","type":"string","span":{"start":[143,1],"end":[143,71],"bytes":[4349,4419]}}],"return_type":"(string, string, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"144:11":{"kind":"call","span":{"start":[144,11],"end":[144,29],"bytes":[4430,4448]},"callee":null},"146:9":{"kind":"call","span":{"start":[146,9],"end":[146,37],"bytes":[4489,4517]},"callee":null},"148:18":{"kind":"call","span":{"start":[148,18],"end":[148,66],"bytes":[4551,4599]},"callee":null},"151:21":{"kind":"call","span":{"start":[151,21],"end":[151,32],"bytes":[4624,4635]},"callee":null},"152:18":{"kind":"call","span":{"start":[152,18],"end":[152,24],"bytes":[4653,4659]},"callee":null},"153:18":{"kind":"call","span":{"start":[153,18],"end":[153,66],"bytes":[4679,4727]},"callee":null},"157:14":{"kind":"call","span":{"start":[157,14],"end":[157,47],"bytes":[4773,4806]},"callee":null}}}},"content_hash":"3318488c0af38c6a7bbfc5a90fdc41b5a46c6c930a36298f5a909e5a1976d854"},"s3/leaser.go":{"id":"can://go/litestream/s3/leaser.go","kind":"module","span":{"start":[1,1],"end":[306,1],"bytes":[0,6926]},"package":"s3","source":"package s3\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3/types\"\n\t\"github.com/aws/smithy-go\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\nconst (\n\tDefaultLeaseTTL = 30 * time.Second\n\tDefaultLeasePath = \"lock.json\"\n\tLeaserType = \"s3\"\n)\n\nvar (\n\t_ litestream.Leaser = (*Leaser)(nil)\n\n\tErrLeaseRequired = errors.New(\"lease required\")\n\tErrLeaseETagRequired = errors.New(\"lease etag required\")\n\tErrLeaseAlreadyReleased = errors.New(\"lease already released\")\n)\n\n// S3API is the interface for S3 operations needed by Leaser.\ntype S3API interface {\n\tGetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)\n\tPutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)\n\tDeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error)\n}\n\ntype Leaser struct {\n\ts3 S3API\n\tlogger *slog.Logger\n\n\tBucket string\n\tPath string\n\tTTL time.Duration\n\tOwner string\n}\n\nfunc NewLeaser() *Leaser {\n\towner, _ := os.Hostname()\n\tif owner == \"\" {\n\t\towner = fmt.Sprintf(\"pid-%d\", os.Getpid())\n\t} else {\n\t\towner = fmt.Sprintf(\"%s:%d\", owner, os.Getpid())\n\t}\n\n\treturn \u0026Leaser{\n\t\tlogger: slog.Default().WithGroup(\"s3-leaser\"),\n\t\tTTL: DefaultLeaseTTL,\n\t\tOwner: owner,\n\t}\n}\n\nfunc (l *Leaser) SetLogger(logger *slog.Logger) {\n\tl.logger = logger.WithGroup(\"s3-leaser\")\n}\n\nfunc (l *Leaser) Client() S3API {\n\treturn l.s3\n}\n\nfunc (l *Leaser) SetClient(client S3API) {\n\tl.s3 = client\n}\n\nfunc (l *Leaser) Type() string {\n\treturn LeaserType\n}\n\nfunc (l *Leaser) lockKey() string {\n\tif l.Path == \"\" {\n\t\treturn DefaultLeasePath\n\t}\n\treturn l.Path + \"/\" + DefaultLeasePath\n}\n\nfunc (l *Leaser) AcquireLease(ctx context.Context) (*litestream.Lease, error) {\n\texisting, etag, err := l.readLease(ctx)\n\tif err != nil \u0026\u0026 !errors.Is(err, os.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"read existing lease: %w\", err)\n\t}\n\n\tif existing != nil \u0026\u0026 !existing.IsExpired() {\n\t\treturn nil, \u0026litestream.LeaseExistsError{\n\t\t\tOwner: existing.Owner,\n\t\t\tExpiresAt: existing.ExpiresAt,\n\t\t}\n\t}\n\n\tvar generation int64 = 1\n\tif existing != nil {\n\t\tgeneration = existing.Generation + 1\n\t}\n\n\tnewLease := \u0026litestream.Lease{\n\t\tGeneration: generation,\n\t\tExpiresAt: time.Now().Add(l.TTL),\n\t\tOwner: l.Owner,\n\t}\n\n\tnewETag, err := l.writeLease(ctx, newLease, etag)\n\tif err != nil {\n\t\tvar leaseErr *litestream.LeaseExistsError\n\t\tif errors.As(err, \u0026leaseErr) {\n\t\t\tif current, _, readErr := l.readLease(ctx); readErr == nil \u0026\u0026 current != nil {\n\t\t\t\treturn nil, \u0026litestream.LeaseExistsError{\n\t\t\t\t\tOwner: current.Owner,\n\t\t\t\t\tExpiresAt: current.ExpiresAt,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tnewLease.ETag = newETag\n\tl.logger.Debug(\"lease acquired\",\n\t\t\"generation\", newLease.Generation,\n\t\t\"owner\", newLease.Owner,\n\t\t\"expires_at\", newLease.ExpiresAt,\n\t\t\"etag\", newLease.ETag)\n\n\treturn newLease, nil\n}\n\nfunc (l *Leaser) RenewLease(ctx context.Context, lease *litestream.Lease) (*litestream.Lease, error) {\n\tif lease == nil {\n\t\treturn nil, ErrLeaseRequired\n\t}\n\tif lease.ETag == \"\" {\n\t\treturn nil, ErrLeaseETagRequired\n\t}\n\n\tnewLease := \u0026litestream.Lease{\n\t\tGeneration: lease.Generation,\n\t\tExpiresAt: time.Now().Add(l.TTL),\n\t\tOwner: l.Owner,\n\t}\n\n\tnewETag, err := l.writeLease(ctx, newLease, lease.ETag)\n\tif err != nil {\n\t\tvar leaseErr *litestream.LeaseExistsError\n\t\tif errors.As(err, \u0026leaseErr) {\n\t\t\treturn nil, litestream.ErrLeaseNotHeld\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tnewLease.ETag = newETag\n\tl.logger.Debug(\"lease renewed\",\n\t\t\"generation\", newLease.Generation,\n\t\t\"owner\", newLease.Owner,\n\t\t\"expires_at\", newLease.ExpiresAt,\n\t\t\"etag\", newLease.ETag)\n\n\treturn newLease, nil\n}\n\nfunc (l *Leaser) ReleaseLease(ctx context.Context, lease *litestream.Lease) error {\n\tif lease == nil {\n\t\treturn ErrLeaseRequired\n\t}\n\tif lease.ETag == \"\" {\n\t\treturn ErrLeaseETagRequired\n\t}\n\n\tkey := l.lockKey()\n\t_, err := l.s3.DeleteObject(ctx, \u0026s3.DeleteObjectInput{\n\t\tBucket: aws.String(l.Bucket),\n\t\tKey: aws.String(key),\n\t\tIfMatch: aws.String(lease.ETag),\n\t})\n\tif err != nil {\n\t\tif isNotExists(err) || isNotFoundError(err) {\n\t\t\treturn ErrLeaseAlreadyReleased\n\t\t}\n\t\tif isPreconditionFailed(err) {\n\t\t\treturn litestream.ErrLeaseNotHeld\n\t\t}\n\t\treturn fmt.Errorf(\"delete lease: %w\", err)\n\t}\n\n\tl.logger.Debug(\"lease released\",\n\t\t\"generation\", lease.Generation,\n\t\t\"owner\", lease.Owner)\n\n\treturn nil\n}\n\nfunc (l *Leaser) readLease(ctx context.Context) (*litestream.Lease, string, error) {\n\tkey := l.lockKey()\n\n\tout, err := l.s3.GetObject(ctx, \u0026s3.GetObjectInput{\n\t\tBucket: aws.String(l.Bucket),\n\t\tKey: aws.String(key),\n\t})\n\tif err != nil {\n\t\tif isNotExists(err) || isNotFoundError(err) {\n\t\t\treturn nil, \"\", os.ErrNotExist\n\t\t}\n\t\treturn nil, \"\", fmt.Errorf(\"get lock file: %w\", err)\n\t}\n\tdefer out.Body.Close()\n\n\tvar lease litestream.Lease\n\tif err := json.NewDecoder(out.Body).Decode(\u0026lease); err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"decode lock file: %w\", err)\n\t}\n\n\tetag := \"\"\n\tif out.ETag != nil {\n\t\tetag = *out.ETag\n\t}\n\tlease.ETag = etag\n\n\treturn \u0026lease, etag, nil\n}\n\nfunc (l *Leaser) writeLease(ctx context.Context, lease *litestream.Lease, etag string) (string, error) {\n\tkey := l.lockKey()\n\n\tdata, err := json.Marshal(lease)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"marshal lock file: %w\", err)\n\t}\n\n\tinput := \u0026s3.PutObjectInput{\n\t\tBucket: aws.String(l.Bucket),\n\t\tKey: aws.String(key),\n\t\tBody: bytes.NewReader(data),\n\t\tContentType: aws.String(\"application/json\"),\n\t}\n\n\tif etag == \"\" {\n\t\tinput.IfNoneMatch = aws.String(\"*\")\n\t} else {\n\t\tinput.IfMatch = aws.String(etag)\n\t}\n\n\tout, err := l.s3.PutObject(ctx, input)\n\tif err != nil {\n\t\tif isPreconditionFailed(err) {\n\t\t\treturn \"\", \u0026litestream.LeaseExistsError{}\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"put lock file: %w\", err)\n\t}\n\n\tnewETag := \"\"\n\tif out.ETag != nil {\n\t\tnewETag = *out.ETag\n\t}\n\n\treturn newETag, nil\n}\n\nfunc isPreconditionFailed(err error) bool {\n\tvar apiErr smithy.APIError\n\tif errors.As(err, \u0026apiErr) {\n\t\tcode := apiErr.ErrorCode()\n\t\treturn code == \"PreconditionFailed\" || code == \"412\"\n\t}\n\n\tvar respErr *smithy.OperationError\n\tif errors.As(err, \u0026respErr) {\n\t\tif httpErr, ok := respErr.Err.(interface{ HTTPStatusCode() int }); ok {\n\t\t\treturn httpErr.HTTPStatusCode() == 412\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc isNotFoundError(err error) bool {\n\tvar apiErr smithy.APIError\n\tif errors.As(err, \u0026apiErr) {\n\t\tcode := apiErr.ErrorCode()\n\t\treturn code == \"NoSuchKey\" || code == \"NotFound\" || code == \"404\"\n\t}\n\n\tvar noSuchKey *types.NoSuchKey\n\tif errors.As(err, \u0026noSuchKey) {\n\t\treturn true\n\t}\n\n\tvar respErr *smithy.OperationError\n\tif errors.As(err, \u0026respErr) {\n\t\tif httpErr, ok := respErr.Err.(interface{ HTTPStatusCode() int }); ok {\n\t\t\treturn httpErr.HTTPStatusCode() == 404\n\t\t}\n\t}\n\n\treturn false\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[21,29]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[30,40]}},{"name":"json","path":"encoding/json","span":{"start":[6,1],"end":[6,17],"bytes":[41,57]}},{"name":"errors","path":"errors","span":{"start":[7,1],"end":[7,10],"bytes":[58,67]}},{"name":"fmt","path":"fmt","span":{"start":[8,1],"end":[8,7],"bytes":[68,74]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[75,86]}},{"name":"os","path":"os","span":{"start":[10,1],"end":[10,6],"bytes":[87,92]}},{"name":"time","path":"time","span":{"start":[11,1],"end":[11,8],"bytes":[93,100]}},{"name":"aws","path":"github.com/aws/aws-sdk-go-v2/aws","span":{"start":[13,1],"end":[13,36],"bytes":[102,137]}},{"name":"s3","path":"github.com/aws/aws-sdk-go-v2/service/s3","span":{"start":[14,1],"end":[14,43],"bytes":[138,180]}},{"name":"types","path":"github.com/aws/aws-sdk-go-v2/service/s3/types","span":{"start":[15,1],"end":[15,49],"bytes":[181,229]}},{"name":"smithy-go","path":"github.com/aws/smithy-go","span":{"start":[16,1],"end":[16,28],"bytes":[230,257]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[18,1],"end":[18,37],"bytes":[259,295]}}],"types":{"Leaser":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser","kind":"struct","span":{"start":[42,1],"end":[50,2],"bytes":[1081,1206]},"callables":{"github.com/benbjohnson/litestream/s3.Leaser.AcquireLease":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.AcquireLease","kind":"method","span":{"start":[90,1],"end":[136,2],"bytes":[1894,3088]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.AcquireLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[90,1],"end":[90,80],"bytes":[1894,1973]}}],"return_type":"(*github.com/benbjohnson/litestream.Lease, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"110:15":{"kind":"call","span":{"start":[110,15],"end":[110,25],"bytes":[2454,2464]},"callee":null},"114:18":{"kind":"call","span":{"start":[114,18],"end":[114,51],"bytes":[2521,2554]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease"},"117:6":{"kind":"call","span":{"start":[117,6],"end":[117,31],"bytes":[2621,2646]},"callee":null},"118:30":{"kind":"call","span":{"start":[118,30],"end":[118,46],"bytes":[2678,2694]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease"},"129:2":{"kind":"call","span":{"start":[129,2],"end":[133,25],"bytes":[2906,3063]},"callee":null},"91:25":{"kind":"call","span":{"start":[91,25],"end":[91,41],"bytes":[1998,2014]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease"},"92:20":{"kind":"call","span":{"start":[92,20],"end":[92,50],"bytes":[2034,2064]},"callee":null},"93:15":{"kind":"call","span":{"start":[93,15],"end":[93,57],"bytes":[2081,2123]},"callee":null},"96:25":{"kind":"call","span":{"start":[96,25],"end":[96,45],"bytes":[2152,2172]},"callee":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/github.com/benbjohnson/litestream.Lease.IsExpired"}}},"github.com/benbjohnson/litestream/s3.Leaser.Client":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.Client","kind":"method","span":{"start":[71,1],"end":[73,2],"bytes":[1601,1649]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.Client","parameters":[],"return_type":"github.com/benbjohnson/litestream/s3.S3API","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","kind":"method","span":{"start":[171,1],"end":[200,2],"bytes":[3864,4562]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[171,1],"end":[171,84],"bytes":[3864,3947]}},{"name":"lease","type":"*github.com/benbjohnson/litestream.Lease","span":{"start":[171,1],"end":[171,84],"bytes":[3864,3947]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"179:9":{"kind":"call","span":{"start":[179,9],"end":[179,20],"bytes":[4061,4072]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey"},"180:12":{"kind":"call","span":{"start":[180,12],"end":[184,4],"bytes":[4084,4229]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.DeleteObject"},"181:12":{"kind":"call","span":{"start":[181,12],"end":[181,32],"bytes":[4141,4161]},"callee":null},"182:12":{"kind":"call","span":{"start":[182,12],"end":[182,27],"bytes":[4174,4189]},"callee":null},"183:12":{"kind":"call","span":{"start":[183,12],"end":[183,34],"bytes":[4202,4224]},"callee":null},"186:26":{"kind":"call","span":{"start":[186,26],"end":[186,46],"bytes":[4272,4292]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isNotFoundError"},"186:6":{"kind":"call","span":{"start":[186,6],"end":[186,22],"bytes":[4252,4268]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists"},"189:6":{"kind":"call","span":{"start":[189,6],"end":[189,31],"bytes":[4338,4363]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isPreconditionFailed"},"192:10":{"kind":"call","span":{"start":[192,10],"end":[192,45],"bytes":[4416,4451]},"callee":null},"195:2":{"kind":"call","span":{"start":[195,2],"end":[197,24],"bytes":[4457,4547]},"callee":null}}},"github.com/benbjohnson/litestream/s3.Leaser.RenewLease":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.RenewLease","kind":"method","span":{"start":[138,1],"end":[169,2],"bytes":[3090,3862]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.RenewLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[138,1],"end":[138,103],"bytes":[3090,3192]}},{"name":"lease","type":"*github.com/benbjohnson/litestream.Lease","span":{"start":[138,1],"end":[138,103],"bytes":[3090,3192]}}],"return_type":"(*github.com/benbjohnson/litestream.Lease, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"148:15":{"kind":"call","span":{"start":[148,15],"end":[148,25],"bytes":[3386,3396]},"callee":null},"152:18":{"kind":"call","span":{"start":[152,18],"end":[152,57],"bytes":[3453,3492]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease"},"155:6":{"kind":"call","span":{"start":[155,6],"end":[155,31],"bytes":[3559,3584]},"callee":null},"162:2":{"kind":"call","span":{"start":[162,2],"end":[166,25],"bytes":[3681,3837]},"callee":null}}},"github.com/benbjohnson/litestream/s3.Leaser.SetClient":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.SetClient","kind":"method","span":{"start":[75,1],"end":[77,2],"bytes":[1651,1710]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.SetClient","parameters":[{"name":"client","type":"github.com/benbjohnson/litestream/s3.S3API","span":{"start":[75,1],"end":[75,43],"bytes":[1651,1693]}}],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/s3.Leaser.SetLogger":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.SetLogger","kind":"method","span":{"start":[67,1],"end":[69,2],"bytes":[1506,1599]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[67,1],"end":[67,50],"bytes":[1506,1555]}}],"metrics":{"cyclomatic":1},"body":{"68:13":{"kind":"call","span":{"start":[68,13],"end":[68,42],"bytes":[1568,1597]},"callee":null}}},"github.com/benbjohnson/litestream/s3.Leaser.Type":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.Type","kind":"method","span":{"start":[79,1],"end":[81,2],"bytes":[1712,1765]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/s3.Leaser.lockKey":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey","kind":"method","span":{"start":[83,1],"end":[88,2],"bytes":[1767,1892]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.lockKey","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{}},"github.com/benbjohnson/litestream/s3.Leaser.readLease":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease","kind":"method","span":{"start":[202,1],"end":[229,2],"bytes":[4564,5231]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.readLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[202,1],"end":[202,85],"bytes":[4564,4648]}}],"return_type":"(*github.com/benbjohnson/litestream.Lease, string, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"203:9":{"kind":"call","span":{"start":[203,9],"end":[203,20],"bytes":[4657,4668]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey"},"205:14":{"kind":"call","span":{"start":[205,14],"end":[208,4],"bytes":[4683,4785]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.GetObject"},"206:11":{"kind":"call","span":{"start":[206,11],"end":[206,31],"bytes":[4733,4753]},"callee":null},"207:11":{"kind":"call","span":{"start":[207,11],"end":[207,26],"bytes":[4765,4780]},"callee":null},"210:26":{"kind":"call","span":{"start":[210,26],"end":[210,46],"bytes":[4828,4848]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isNotFoundError"},"210:6":{"kind":"call","span":{"start":[210,6],"end":[210,22],"bytes":[4808,4824]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists"},"213:19":{"kind":"call","span":{"start":[213,19],"end":[213,55],"bytes":[4907,4943]},"callee":null},"215:8":{"kind":"call","span":{"start":[215,8],"end":[215,24],"bytes":[4954,4970]},"callee":null},"218:12":{"kind":"call","span":{"start":[218,12],"end":[218,37],"bytes":[5011,5036]},"callee":null},"219:19":{"kind":"call","span":{"start":[219,19],"end":[219,58],"bytes":[5084,5123]},"callee":null}}},"github.com/benbjohnson/litestream/s3.Leaser.writeLease":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease","kind":"method","span":{"start":[231,1],"end":[266,2],"bytes":[5233,6038]},"signature":"github.com/benbjohnson/litestream/s3.Leaser.writeLease","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[231,1],"end":[231,105],"bytes":[5233,5337]}},{"name":"lease","type":"*github.com/benbjohnson/litestream.Lease","span":{"start":[231,1],"end":[231,105],"bytes":[5233,5337]}},{"name":"etag","type":"string","span":{"start":[231,1],"end":[231,105],"bytes":[5233,5337]}}],"return_type":"(string, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"232:9":{"kind":"call","span":{"start":[232,9],"end":[232,20],"bytes":[5346,5357]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey"},"234:15":{"kind":"call","span":{"start":[234,15],"end":[234,34],"bytes":[5373,5392]},"callee":null},"236:14":{"kind":"call","span":{"start":[236,14],"end":[236,54],"bytes":[5423,5463]},"callee":null},"240:16":{"kind":"call","span":{"start":[240,16],"end":[240,36],"bytes":[5513,5533]},"callee":null},"241:16":{"kind":"call","span":{"start":[241,16],"end":[241,31],"bytes":[5550,5565]},"callee":null},"242:16":{"kind":"call","span":{"start":[242,16],"end":[242,37],"bytes":[5582,5603]},"callee":null},"243:16":{"kind":"call","span":{"start":[243,16],"end":[243,46],"bytes":[5620,5650]},"callee":null},"247:23":{"kind":"call","span":{"start":[247,23],"end":[247,38],"bytes":[5695,5710]},"callee":null},"249:19":{"kind":"call","span":{"start":[249,19],"end":[249,35],"bytes":[5739,5755]},"callee":null},"252:14":{"kind":"call","span":{"start":[252,14],"end":[252,40],"bytes":[5773,5799]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.PutObject"},"254:6":{"kind":"call","span":{"start":[254,6],"end":[254,31],"bytes":[5822,5847]},"callee":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isPreconditionFailed"},"257:14":{"kind":"call","span":{"start":[257,14],"end":[257,50],"bytes":[5912,5948]},"callee":null}}}},"fields":{"Bucket":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/Bucket","kind":"field","type":"string","span":{"start":[46,1],"end":[46,15],"bytes":[1138,1152]}},"Owner":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/Owner","kind":"field","type":"string","span":{"start":[49,1],"end":[49,15],"bytes":[1190,1204]}},"Path":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/Path","kind":"field","type":"string","span":{"start":[47,1],"end":[47,15],"bytes":[1153,1167]}},"TTL":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/TTL","kind":"field","type":"time.Duration","span":{"start":[48,1],"end":[48,22],"bytes":[1168,1189]}},"logger":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[44,1],"end":[44,21],"bytes":[1116,1136]}},"s3":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/s3","kind":"field","type":"github.com/benbjohnson/litestream/s3.S3API","span":{"start":[43,1],"end":[43,14],"bytes":[1102,1115]}}}},"S3API":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API","kind":"interface","span":{"start":[36,1],"end":[40,2],"bytes":[695,1079]},"callables":{"github.com/benbjohnson/litestream/s3.S3API.DeleteObject":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.DeleteObject","kind":"method","span":{"start":[39,1],"end":[39,126],"bytes":[952,1077]},"signature":"github.com/benbjohnson/litestream/s3.S3API.DeleteObject","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[39,1],"end":[39,126],"bytes":[952,1077]}},{"name":"params","type":"*github.com/aws/aws-sdk-go-v2/service/s3.DeleteObjectInput","span":{"start":[39,1],"end":[39,126],"bytes":[952,1077]}},{"name":"optFns","type":"[]func(*github.com/aws/aws-sdk-go-v2/service/s3.Options)","span":{"start":[39,1],"end":[39,126],"bytes":[952,1077]},"is_variadic":true}],"return_type":"(*github.com/aws/aws-sdk-go-v2/service/s3.DeleteObjectOutput, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream/s3.S3API.GetObject":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.GetObject","kind":"method","span":{"start":[37,1],"end":[37,117],"bytes":[718,834]},"signature":"github.com/benbjohnson/litestream/s3.S3API.GetObject","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[37,1],"end":[37,117],"bytes":[718,834]}},{"name":"params","type":"*github.com/aws/aws-sdk-go-v2/service/s3.GetObjectInput","span":{"start":[37,1],"end":[37,117],"bytes":[718,834]}},{"name":"optFns","type":"[]func(*github.com/aws/aws-sdk-go-v2/service/s3.Options)","span":{"start":[37,1],"end":[37,117],"bytes":[718,834]},"is_variadic":true}],"return_type":"(*github.com/aws/aws-sdk-go-v2/service/s3.GetObjectOutput, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream/s3.S3API.PutObject":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.PutObject","kind":"method","span":{"start":[38,1],"end":[38,117],"bytes":[835,951]},"signature":"github.com/benbjohnson/litestream/s3.S3API.PutObject","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[38,1],"end":[38,117],"bytes":[835,951]}},{"name":"params","type":"*github.com/aws/aws-sdk-go-v2/service/s3.PutObjectInput","span":{"start":[38,1],"end":[38,117],"bytes":[835,951]}},{"name":"optFns","type":"[]func(*github.com/aws/aws-sdk-go-v2/service/s3.Options)","span":{"start":[38,1],"end":[38,117],"bytes":[835,951]},"is_variadic":true}],"return_type":"(*github.com/aws/aws-sdk-go-v2/service/s3.PutObjectOutput, error)","error_channel":["error"],"body":{}}}}},"functions":{"github.com/benbjohnson/litestream/s3.NewLeaser":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.NewLeaser","kind":"function","span":{"start":[52,1],"end":[65,2],"bytes":[1208,1504]},"signature":"github.com/benbjohnson/litestream/s3.NewLeaser","parameters":[],"return_type":"*github.com/benbjohnson/litestream/s3.Leaser","metrics":{"cyclomatic":2},"body":{"53:14":{"kind":"call","span":{"start":[53,14],"end":[53,27],"bytes":[1248,1261]},"callee":null},"55:11":{"kind":"call","span":{"start":[55,11],"end":[55,45],"bytes":[1290,1324]},"callee":null},"55:33":{"kind":"call","span":{"start":[55,33],"end":[55,44],"bytes":[1312,1323]},"callee":null},"57:11":{"kind":"call","span":{"start":[57,11],"end":[57,51],"bytes":[1345,1385]},"callee":null},"57:39":{"kind":"call","span":{"start":[57,39],"end":[57,50],"bytes":[1373,1384]},"callee":null},"61:11":{"kind":"call","span":{"start":[61,11],"end":[61,25],"bytes":[1417,1431]},"callee":null}}},"github.com/benbjohnson/litestream/s3.isNotFoundError":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isNotFoundError","kind":"function","span":{"start":[285,1],"end":[305,2],"bytes":[6438,6925]},"signature":"github.com/benbjohnson/litestream/s3.isNotFoundError","parameters":[{"name":"err","type":"error","span":{"start":[285,1],"end":[285,39],"bytes":[6438,6476]}}],"return_type":"bool","metrics":{"cyclomatic":5},"body":{"287:5":{"kind":"call","span":{"start":[287,5],"end":[287,28],"bytes":[6509,6532]},"callee":null},"288:11":{"kind":"call","span":{"start":[288,11],"end":[288,29],"bytes":[6545,6563]},"callee":null},"293:5":{"kind":"call","span":{"start":[293,5],"end":[293,31],"bytes":[6672,6698]},"callee":null},"298:5":{"kind":"call","span":{"start":[298,5],"end":[298,29],"bytes":[6759,6783]},"callee":null},"300:11":{"kind":"call","span":{"start":[300,11],"end":[300,35],"bytes":[6870,6894]},"callee":null}}},"github.com/benbjohnson/litestream/s3.isPreconditionFailed":{"id":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isPreconditionFailed","kind":"function","span":{"start":[268,1],"end":[283,2],"bytes":[6040,6436]},"signature":"github.com/benbjohnson/litestream/s3.isPreconditionFailed","parameters":[{"name":"err","type":"error","span":{"start":[268,1],"end":[268,44],"bytes":[6040,6083]}}],"return_type":"bool","metrics":{"cyclomatic":4},"body":{"270:5":{"kind":"call","span":{"start":[270,5],"end":[270,28],"bytes":[6116,6139]},"callee":null},"271:11":{"kind":"call","span":{"start":[271,11],"end":[271,29],"bytes":[6152,6170]},"callee":null},"276:5":{"kind":"call","span":{"start":[276,5],"end":[276,29],"bytes":[6270,6294]},"callee":null},"278:11":{"kind":"call","span":{"start":[278,11],"end":[278,35],"bytes":[6381,6405]},"callee":null}}}},"content_hash":"3afed52bb5e6a612b0bbc2455ed5859e2b178afbd4759edb491a0e127012b345"},"s3/replica_client.go":{"id":"can://go/litestream/s3/replica_client.go","kind":"module","span":{"start":[1,1],"end":[1894,1],"bytes":[0,58711]},"package":"s3","source":"package s3\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t\"crypto/md5\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/aws/ratelimit\"\n\t\"github.com/aws/aws-sdk-go-v2/aws/retry\"\n\tv4 \"github.com/aws/aws-sdk-go-v2/aws/signer/v4\"\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/aws/aws-sdk-go-v2/credentials\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/manager\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3/types\"\n\t\"github.com/aws/smithy-go\"\n\tsmithyxml \"github.com/aws/smithy-go/encoding/xml\"\n\t\"github.com/aws/smithy-go/middleware\"\n\tsmithytime \"github.com/aws/smithy-go/time\"\n\tsmithyhttp \"github.com/aws/smithy-go/transport/http\"\n\t\"github.com/superfly/ltx\"\n\t\"golang.org/x/sync/errgroup\"\n\t\"golang.org/x/sync/semaphore\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"s3\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"s3\"\n\n// MetadataKeyTimestamp is the metadata key for storing LTX file timestamps in S3.\nconst MetadataKeyTimestamp = \"litestream-timestamp\"\n\n// MaxKeys is the number of keys S3 can operate on per batch.\nconst MaxKeys = 1000\n\n// DefaultRegion is the region used if one is not specified.\nconst DefaultRegion = \"us-east-1\"\n\n// DefaultMetadataConcurrency is the default number of concurrent HeadObject calls\n// for fetching accurate timestamps during timestamp-based restore.\n// S3 can handle 5,500+ HEAD requests per second per prefix.\nconst DefaultMetadataConcurrency = 50\n\n// DefaultR2Concurrency is the default number of concurrent multipart upload\n// parts for Cloudflare R2, which has strict concurrent upload limits.\nconst DefaultR2Concurrency = 2\n\n// DefaultTigrisConcurrency is the default number of concurrent multipart\n// upload parts for Tigris, which has strict concurrent upload limits.\nconst DefaultTigrisConcurrency = 2\n\n// DefaultTigrisPartSize is the default multipart upload part size for Tigris.\nconst DefaultTigrisPartSize = 16 * 1024 * 1024\n\n// contentMD5StackKey is used to pass the precomputed Content-MD5 checksum\n// through the middleware stack from Serialize to Finalize phase.\ntype contentMD5StackKey struct{}\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\nvar _ litestream.ReplicaClientV3 = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files to S3.\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\ts3 *s3.Client // s3 service\n\tuploader *manager.Uploader\n\tlogger *slog.Logger\n\n\t// AWS authentication keys.\n\tAccessKeyID string\n\tSecretAccessKey string\n\n\t// S3 bucket information\n\tRegion string\n\tBucket string\n\tPath string\n\tEndpoint string\n\tForcePathStyle bool\n\tSkipVerify bool\n\tSignPayload bool\n\tSignAcceptEncoding bool\n\tRequireContentMD5 bool\n\tStorageClass string\n\n\t// Upload configuration\n\tPartSize int64 // Part size for multipart uploads (default: 5MB)\n\tConcurrency int // Number of concurrent parts to upload (default: 5)\n\n\t// MetadataConcurrency controls parallel HeadObject calls for timestamp-based restore.\n\t// Higher values improve restore speed for large backup histories.\n\t// Default: 50 (S3 can handle 5,500+ HEAD/s per prefix)\n\tMetadataConcurrency int\n\n\t// Server-Side Encryption - Customer Provided Keys (SSE-C)\n\t// Works with all S3-compatible providers (AWS, MinIO, Exoscale, etc.)\n\tSSECustomerAlgorithm string // Must be \"AES256\" if set\n\tSSECustomerKey string // Base64-encoded 256-bit (32 byte) encryption key\n\tSSECustomerKeyMD5 string // Base64-encoded MD5 of key (auto-computed if not set)\n\n\t// Server-Side Encryption - AWS KMS (SSE-KMS)\n\t// Only works with AWS S3 (not S3-compatible providers)\n\tSSEKMSKeyID string // KMS key ID, ARN, or alias\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t\tRequireContentMD5: true,\n\t\tSignPayload: true,\n\t\tSignAcceptEncoding: true,\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tclient := NewReplicaClient()\n\n\tvar (\n\t\tbucket string\n\t\tregion string\n\t\tendpoint string\n\t\tforcePathStyle bool\n\t\tskipVerify bool\n\t\tsignPayload bool\n\t\tsignPayloadSet bool\n\t\tsignAcceptEncoding bool\n\t\tsignAcceptEncodingSet bool\n\t\trequireMD5 bool\n\t\trequireMD5Set bool\n\t\tconcurrency int\n\t\tconcurrencySet bool\n\t\tpartSize int64\n\t\tstorageClass string\n\t)\n\n\t// Parse host for bucket and region\n\tif strings.HasPrefix(host, \"arn:\") {\n\t\tbucket = host\n\t\tregion = litestream.RegionFromS3ARN(host)\n\t} else {\n\t\tbucket, region, endpoint, forcePathStyle = ParseHost(host)\n\t}\n\n\t// Override with query parameters if provided\n\tif qEndpoint := query.Get(\"endpoint\"); qEndpoint != \"\" {\n\t\t// Ensure endpoint has a scheme (defaults to https:// for cloud, http:// for local)\n\t\tqEndpoint, _ = litestream.EnsureEndpointScheme(qEndpoint)\n\t\tendpoint = qEndpoint\n\t\t// Default to path style for custom endpoints unless explicitly set to false\n\t\tif v, ok := litestream.BoolQueryValue(query, \"forcePathStyle\", \"force-path-style\"); !ok || v {\n\t\t\tforcePathStyle = true\n\t\t}\n\t}\n\tif qRegion := query.Get(\"region\"); qRegion != \"\" {\n\t\tregion = qRegion\n\t}\n\tif v, ok := litestream.BoolQueryValue(query, \"forcePathStyle\", \"force-path-style\"); ok {\n\t\tforcePathStyle = v\n\t}\n\tif v, ok := litestream.BoolQueryValue(query, \"skipVerify\", \"skip-verify\"); ok {\n\t\tskipVerify = v\n\t}\n\tif v, ok := litestream.BoolQueryValue(query, \"signPayload\", \"sign-payload\"); ok {\n\t\tsignPayload = v\n\t\tsignPayloadSet = true\n\t}\n\tif v, ok := litestream.BoolQueryValue(query, \"signAcceptEncoding\", \"sign-accept-encoding\"); ok {\n\t\tsignAcceptEncoding = v\n\t\tsignAcceptEncodingSet = true\n\t}\n\tif v, ok := litestream.BoolQueryValue(query, \"requireContentMD5\", \"require-content-md5\"); ok {\n\t\trequireMD5 = v\n\t\trequireMD5Set = true\n\t}\n\tif v, ok, err := litestream.IntQueryValue(query, \"concurrency\"); err != nil {\n\t\treturn nil, err\n\t} else if ok {\n\t\tconcurrency = int(v)\n\t\tconcurrencySet = true\n\t}\n\tif v, ok, err := litestream.IntQueryValue(query, \"partSize\", \"part-size\"); err != nil {\n\t\treturn nil, err\n\t} else if ok {\n\t\tpartSize = v\n\t}\n\tif v := query.Get(\"storageClass\"); v != \"\" {\n\t\tstorageClass = v\n\t} else if v := query.Get(\"storage-class\"); v != \"\" {\n\t\tstorageClass = v\n\t}\n\n\t// Ensure bucket is set\n\tif bucket == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for s3 replica URL\")\n\t}\n\n\t// Track if forcePathStyle was explicitly set via query parameter.\n\tforcePathStyleSet := query.Get(\"forcePathStyle\") != \"\" || query.Get(\"force-path-style\") != \"\"\n\n\t// Read authentication from environment variables\n\tif v := os.Getenv(\"AWS_ACCESS_KEY_ID\"); v != \"\" {\n\t\tclient.AccessKeyID = v\n\t} else if v := os.Getenv(\"LITESTREAM_ACCESS_KEY_ID\"); v != \"\" {\n\t\tclient.AccessKeyID = v\n\t}\n\tif v := os.Getenv(\"AWS_SECRET_ACCESS_KEY\"); v != \"\" {\n\t\tclient.SecretAccessKey = v\n\t} else if v := os.Getenv(\"LITESTREAM_SECRET_ACCESS_KEY\"); v != \"\" {\n\t\tclient.SecretAccessKey = v\n\t}\n\n\tif endpoint == \"\" {\n\t\tif v := os.Getenv(\"LITESTREAM_S3_ENDPOINT\"); v != \"\" {\n\t\t\tendpoint, _ = litestream.EnsureEndpointScheme(v)\n\t\t\tif !forcePathStyleSet {\n\t\t\t\tforcePathStyle = true\n\t\t\t}\n\t\t}\n\t}\n\n\t// Detect S3-compatible provider endpoints for applying appropriate defaults.\n\tisHetzner := litestream.IsHetznerEndpoint(endpoint)\n\tisTigris := litestream.IsTigrisEndpoint(endpoint)\n\tisDigitalOcean := litestream.IsDigitalOceanEndpoint(endpoint)\n\tisBackblaze := litestream.IsBackblazeEndpoint(endpoint)\n\tisFilebase := litestream.IsFilebaseEndpoint(endpoint)\n\tisScaleway := litestream.IsScalewayEndpoint(endpoint)\n\tisCloudflareR2 := litestream.IsCloudflareR2Endpoint(endpoint)\n\tisMinIO := litestream.IsMinIOEndpoint(endpoint)\n\tisSupabase := litestream.IsSupabaseEndpoint(endpoint)\n\n\t// Apply provider-specific defaults for S3-compatible providers.\n\tif isTigris {\n\t\t// Tigris: requires signed payloads, no MD5\n\t\tif !signPayloadSet {\n\t\t\tsignPayload, signPayloadSet = true, true\n\t\t}\n\t\tif !requireMD5Set {\n\t\t\trequireMD5, requireMD5Set = false, true\n\t\t}\n\t\tif !concurrencySet {\n\t\t\tclient.Concurrency = DefaultTigrisConcurrency\n\t\t}\n\t\tif partSize == 0 {\n\t\t\tclient.PartSize = DefaultTigrisPartSize\n\t\t}\n\t}\n\tif isHetzner || isDigitalOcean || isBackblaze || isFilebase || isScaleway || isCloudflareR2 || isMinIO || isSupabase {\n\t\t// All these providers require signed payloads (don't support UNSIGNED-PAYLOAD)\n\t\tif !signPayloadSet {\n\t\t\tsignPayload, signPayloadSet = true, true\n\t\t}\n\t}\n\tif !forcePathStyleSet {\n\t\t// Filebase, Backblaze B2, MinIO, and Supabase require path-style URLs\n\t\tif isFilebase || isBackblaze || isMinIO || isSupabase {\n\t\t\tforcePathStyle = true\n\t\t}\n\t}\n\tif isCloudflareR2 {\n\t\tclient.Concurrency = DefaultR2Concurrency\n\t}\n\n\t// Configure client\n\tclient.Bucket = bucket\n\tclient.Path = urlPath\n\tclient.Region = region\n\tclient.Endpoint = endpoint\n\tclient.ForcePathStyle = forcePathStyle\n\tclient.SkipVerify = skipVerify\n\n\tif signPayloadSet {\n\t\tclient.SignPayload = signPayload\n\t}\n\tif signAcceptEncodingSet {\n\t\tclient.SignAcceptEncoding = signAcceptEncoding\n\t}\n\tif requireMD5Set {\n\t\tclient.RequireContentMD5 = requireMD5\n\t}\n\tif concurrencySet {\n\t\tclient.Concurrency = concurrency\n\t}\n\tif partSize \u003e 0 {\n\t\tclient.PartSize = partSize\n\t}\n\tclient.StorageClass = storageClass\n\n\t// Parse SSE-C parameters from query string\n\tif v := query.Get(\"sseCustomerAlgorithm\"); v != \"\" {\n\t\tclient.SSECustomerAlgorithm = v\n\t} else if v := query.Get(\"sse-customer-algorithm\"); v != \"\" {\n\t\tclient.SSECustomerAlgorithm = v\n\t}\n\tif v := query.Get(\"sseCustomerKey\"); v != \"\" {\n\t\tclient.SSECustomerKey = v\n\t} else if v := query.Get(\"sse-customer-key\"); v != \"\" {\n\t\tclient.SSECustomerKey = v\n\t}\n\tif v := query.Get(\"sseCustomerKeyMD5\"); v != \"\" {\n\t\tclient.SSECustomerKeyMD5 = v\n\t} else if v := query.Get(\"sse-customer-key-md5\"); v != \"\" {\n\t\tclient.SSECustomerKeyMD5 = v\n\t}\n\n\t// Parse SSE-KMS parameters from query string\n\tif v := query.Get(\"sseKmsKeyId\"); v != \"\" {\n\t\tclient.SSEKMSKeyID = v\n\t} else if v := query.Get(\"sse-kms-key-id\"); v != \"\" {\n\t\tclient.SSEKMSKeyID = v\n\t}\n\n\treturn client, nil\n}\n\n// Type returns \"s3\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to S3. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) (err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.s3 != nil {\n\t\treturn nil\n\t}\n\n\t// Validate required configuration\n\tif c.Bucket == \"\" {\n\t\treturn fmt.Errorf(\"s3: bucket name is required\")\n\t}\n\n\t// Validate SSE configuration\n\tif err := c.validateSSEConfig(); err != nil {\n\t\treturn err\n\t}\n\n\t// Look up region if not specified and no endpoint is used.\n\t// Endpoints are typically used for non-S3 object stores and do not\n\t// necessarily require a region.\n\tregion := c.Region\n\tif region == \"\" {\n\t\tif c.Endpoint == \"\" {\n\t\t\tif region, err = c.findBucketRegion(ctx, c.Bucket); err != nil {\n\t\t\t\treturn fmt.Errorf(\"s3: cannot lookup bucket region: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\tregion = DefaultRegion // default for non-S3 object stores\n\t\t}\n\t}\n\n\t// Create HTTP client with 24 hour timeout for long-running operations\n\thttpClient := \u0026http.Client{\n\t\tTimeout: 24 * time.Hour,\n\t}\n\n\t// Always configure custom HTTP Transport with controlled keepalive settings\n\t// to reduce idle CPU usage from default transport's aggressive keepalives.\n\t// See: https://github.com/benbjohnson/litestream/issues/992\n\thttpClient.Transport = \u0026http.Transport{\n\t\tProxy: http.ProxyFromEnvironment,\n\t\tDialContext: (\u0026net.Dialer{\n\t\t\tTimeout: 30 * time.Second,\n\t\t\tKeepAlive: 30 * time.Second,\n\t\t}).DialContext,\n\t\tForceAttemptHTTP2: true,\n\t\tMaxIdleConns: 100,\n\t\tIdleConnTimeout: 90 * time.Second,\n\t\tTLSHandshakeTimeout: 10 * time.Second,\n\t\tExpectContinueTimeout: 1 * time.Second,\n\t}\n\n\t// Configure TLS to skip verification if requested\n\tif c.SkipVerify {\n\t\thttpClient.Transport.(*http.Transport).TLSClientConfig = \u0026tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t}\n\t}\n\n\t// Build configuration options\n\tconfigOpts := []func(*config.LoadOptions) error{\n\t\tconfig.WithRegion(region),\n\t\tconfig.WithRetryer(newTransportRetryer),\n\t}\n\n\t// Add HTTP client with proper timeout\n\tconfigOpts = append(configOpts, config.WithHTTPClient(httpClient))\n\n\t// Add static credentials if provided, otherwise use default credential chain\n\t// Default credential chain includes:\n\t// - Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n\t// - Shared credentials file (~/.aws/credentials)\n\t// - EC2 Instance Profile credentials\n\t// - ECS Task Role credentials\n\t// - Web Identity Token credentials (for EKS)\n\tif c.AccessKeyID != \"\" \u0026\u0026 c.SecretAccessKey != \"\" {\n\t\tconfigOpts = append(configOpts, config.WithCredentialsProvider(\n\t\t\tcredentials.NewStaticCredentialsProvider(c.AccessKeyID, c.SecretAccessKey, \"\"),\n\t\t))\n\t}\n\n\t// Enable AWS SDK debug logging if LITESTREAM_S3_DEBUG is set.\n\t// Useful for debugging S3-compatible providers (signing issues, request/response bodies).\n\t// Supports comma-separated values: signing,request,retries\n\t// Values: signing, request, request-with-body, response, response-with-body, retries, all\n\tif logMode := parseS3DebugEnv(); logMode != 0 {\n\t\tconfigOpts = append(configOpts, config.WithClientLogMode(logMode))\n\t}\n\n\t// Load AWS configuration\n\tcfg, err := config.LoadDefaultConfig(ctx, configOpts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"s3: cannot load aws config: %w\", err)\n\t}\n\n\t// Create S3 client options\n\ts3Opts := []func(*s3.Options){\n\t\tfunc(o *s3.Options) {\n\t\t\to.UsePathStyle = c.ForcePathStyle\n\t\t\to.UseARNRegion = true\n\t\t\t// Add User-Agent and optional middleware.\n\t\t\to.APIOptions = append(o.APIOptions, c.middlewareOption())\n\t\t},\n\t}\n\n\t// S3-compatible providers (Tigris, Backblaze B2, MinIO, Filebase, etc.) don't\n\t// support aws-chunked content encoding used by default checksum calculation\n\t// in AWS SDK Go v2 v1.73.0+. Disable automatic checksum calculation and\n\t// response checksum validation for all custom endpoints.\n\t// See: https://github.com/benbjohnson/litestream/issues/918\n\t// See: https://github.com/benbjohnson/litestream/issues/947\n\tif c.Endpoint != \"\" {\n\t\ts3Opts = append(s3Opts, func(o *s3.Options) {\n\t\t\to.RequestChecksumCalculation = aws.RequestChecksumCalculationWhenRequired\n\t\t\to.ResponseChecksumValidation = aws.ResponseChecksumValidationWhenRequired\n\t\t})\n\t}\n\n\t// Add custom endpoint if specified\n\tc.configureEndpoint(\u0026s3Opts)\n\n\t// Create S3 client\n\tc.s3 = s3.NewFromConfig(cfg, s3Opts...)\n\n\t// Configure uploader with custom options if specified\n\tuploaderOpts := []func(*manager.Uploader){}\n\n\t// For S3-compatible providers, disable automatic checksum calculation on the Uploader.\n\t// The S3 client's RequestChecksumCalculation setting only affects single-part uploads.\n\t// Multipart uploads via the Uploader require this separate setting (added in s3/manager v1.20.0).\n\t// See: https://github.com/benbjohnson/litestream/issues/948\n\t// See: https://github.com/aws/aws-sdk-go-v2/issues/3007\n\tif c.Endpoint != \"\" {\n\t\tuploaderOpts = append(uploaderOpts, func(u *manager.Uploader) {\n\t\t\tu.RequestChecksumCalculation = aws.RequestChecksumCalculationWhenRequired\n\t\t})\n\t}\n\n\tif c.PartSize \u003e 0 {\n\t\tuploaderOpts = append(uploaderOpts, func(u *manager.Uploader) {\n\t\t\tu.PartSize = c.PartSize\n\t\t})\n\t}\n\tif c.Concurrency \u003e 0 {\n\t\tuploaderOpts = append(uploaderOpts, func(u *manager.Uploader) {\n\t\t\tu.Concurrency = c.Concurrency\n\t\t})\n\t}\n\tc.uploader = manager.NewUploader(c.s3, uploaderOpts...)\n\n\treturn nil\n}\n\n// configureEndpoint adds custom endpoint configuration to S3 client options if needed.\nfunc (c *ReplicaClient) configureEndpoint(opts *[]func(*s3.Options)) {\n\tif c.Endpoint != \"\" {\n\t\t*opts = append(*opts, func(o *s3.Options) {\n\t\t\to.UsePathStyle = c.ForcePathStyle\n\n\t\t\tendpoint := c.Endpoint\n\t\t\t// Add scheme if not present\n\t\t\tif !strings.HasPrefix(endpoint, \"http://\") \u0026\u0026 !strings.HasPrefix(endpoint, \"https://\") {\n\t\t\t\tendpoint = \"https://\" + endpoint\n\t\t\t}\n\n\t\t\to.BaseEndpoint = aws.String(endpoint)\n\t\t\t// For MinIO and other S3-compatible services\n\t\t\tif strings.HasPrefix(endpoint, \"http://\") {\n\t\t\t\to.EndpointOptions.DisableHTTPS = true\n\t\t\t}\n\t\t})\n\t}\n}\n\n// validateSSEConfig validates server-side encryption configuration.\nfunc (c *ReplicaClient) validateSSEConfig() error {\n\t// Check mutual exclusivity: SSE-C and SSE-KMS cannot both be set\n\tif c.SSECustomerKey != \"\" \u0026\u0026 c.SSEKMSKeyID != \"\" {\n\t\treturn fmt.Errorf(\"s3: cannot use both sse-customer-key and sse-kms-key-id; they are mutually exclusive\")\n\t}\n\n\t// Validate SSE-C configuration\n\tif c.SSECustomerKey != \"\" {\n\t\t// Algorithm must be AES256 (or default to it)\n\t\tif c.SSECustomerAlgorithm == \"\" {\n\t\t\tc.SSECustomerAlgorithm = \"AES256\"\n\t\t} else if c.SSECustomerAlgorithm != \"AES256\" {\n\t\t\treturn fmt.Errorf(\"s3: sse-customer-algorithm must be AES256, got %q\", c.SSECustomerAlgorithm)\n\t\t}\n\n\t\t// Validate key is valid base64 and correct length (256 bits = 32 bytes)\n\t\tkeyBytes, err := base64.StdEncoding.DecodeString(c.SSECustomerKey)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"s3: sse-customer-key must be valid base64: %w\", err)\n\t\t}\n\t\tif len(keyBytes) != 32 {\n\t\t\treturn fmt.Errorf(\"s3: sse-customer-key must be 256-bit (32 bytes) when decoded, got %d bytes\", len(keyBytes))\n\t\t}\n\n\t\t// Auto-compute MD5 if not provided\n\t\tif c.SSECustomerKeyMD5 == \"\" {\n\t\t\tsum := md5.Sum(keyBytes)\n\t\t\tc.SSECustomerKeyMD5 = base64.StdEncoding.EncodeToString(sum[:])\n\t\t}\n\n\t\t// SSE-C requires HTTPS (except for localhost/private networks for testing)\n\t\tif c.Endpoint != \"\" {\n\t\t\tendpoint := c.Endpoint\n\t\t\tif !strings.HasPrefix(endpoint, \"http://\") \u0026\u0026 !strings.HasPrefix(endpoint, \"https://\") {\n\t\t\t\tendpoint = \"https://\" + endpoint\n\t\t\t}\n\t\t\tif strings.HasPrefix(endpoint, \"http://\") {\n\t\t\t\tu, err := url.Parse(endpoint)\n\t\t\t\tif err == nil {\n\t\t\t\t\thost := u.Hostname()\n\t\t\t\t\t// Allow localhost by name\n\t\t\t\t\tif host == \"localhost\" {\n\t\t\t\t\t\t// OK - localhost is allowed\n\t\t\t\t\t} else if ip := net.ParseIP(host); ip != nil \u0026\u0026 (ip.IsLoopback() || ip.IsPrivate()) {\n\t\t\t\t\t\t// OK - loopback (127.x.x.x) or private RFC1918 ranges (10.x, 172.16-31.x, 192.168.x)\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn fmt.Errorf(\"s3: sse-customer-key requires HTTPS endpoint (HTTP only allowed for localhost/private networks)\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// transportRetryMaxAttempts caps total attempts per operation; exponential\n// backoff between attempts still bounds request rate during outages.\nconst transportRetryMaxAttempts = 10\n\n// newTransportRetryer returns a retryer that keeps retrying through sustained\n// object-store transport flaps. The SDK's default token bucket (and the\n// adaptive mode previously configured here) only refills retry quota on\n// successful responses, so a sustained provider flap drains it to zero and\n// every subsequent operation fails fast (\"retry quota exceeded, 0 available\")\n// precisely when retrying matters most for a replication tool.\nfunc newTransportRetryer() aws.Retryer {\n\treturn retry.NewStandard(func(o *retry.StandardOptions) {\n\t\to.MaxAttempts = transportRetryMaxAttempts\n\t\to.RateLimiter = ratelimit.None\n\t\t// S3-compatible providers (observed: Tigris) load-shed with HTTP 408\n\t\t// and api error code \"RequestCanceled\", neither of which the SDK\n\t\t// classifies as retryable by default. Genuine client-side context\n\t\t// cancellation stays non-retryable via the standard retryer's\n\t\t// canceled-context check, which runs before these.\n\t\to.Retryables = append(o.Retryables,\n\t\t\tretry.RetryableHTTPStatusCode{Codes: map[int]struct{}{\n\t\t\t\thttp.StatusRequestTimeout: {},\n\t\t\t}},\n\t\t\tretry.RetryableErrorCode{Codes: map[string]struct{}{\n\t\t\t\t\"RequestCanceled\": {},\n\t\t\t}},\n\t\t)\n\t})\n}\n\n// findBucketRegion looks up the AWS region for a bucket. Returns blank if non-S3.\nfunc (c *ReplicaClient) findBucketRegion(ctx context.Context, bucket string) (string, error) {\n\t// Build a config with credentials but no region\n\tconfigOpts := []func(*config.LoadOptions) error{\n\t\tconfig.WithRetryer(newTransportRetryer),\n\t}\n\n\t// Add static credentials if provided\n\tif c.AccessKeyID != \"\" \u0026\u0026 c.SecretAccessKey != \"\" {\n\t\tconfigOpts = append(configOpts, config.WithCredentialsProvider(\n\t\t\tcredentials.NewStaticCredentialsProvider(c.AccessKeyID, c.SecretAccessKey, \"\"),\n\t\t))\n\t}\n\n\t// Load AWS configuration\n\tcfg, err := config.LoadDefaultConfig(ctx, configOpts...)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"s3: cannot load aws config for region lookup: %w\", err)\n\t}\n\n\t// Use default region for initial region lookup\n\tcfg.Region = DefaultRegion\n\n\t// Create S3 client options\n\ts3Opts := []func(*s3.Options){}\n\n\t// Configure custom endpoint for region lookup\n\tc.configureEndpoint(\u0026s3Opts)\n\n\tclient := s3.NewFromConfig(cfg, s3Opts...)\n\n\t// Get bucket location\n\tout, err := client.GetBucketLocation(ctx, \u0026s3.GetBucketLocationInput{\n\t\tBucket: aws.String(bucket),\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Convert location constraint to region\n\tif out.LocationConstraint == \"\" {\n\t\treturn DefaultRegion, nil\n\t}\n\treturn string(out.LocationConstraint), nil\n}\n\n// LTXFiles returns an iterator over all LTX files on the replica for the given level.\n// When useMetadata is true, fetches accurate timestamps from S3 metadata via HeadObject.\n// This uses parallel batched requests (controlled by MetadataConcurrency) to avoid hangs\n// with large backup histories (see issue #930).\n// When false, uses fast LastModified timestamps from LIST operation.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\treturn newFileIterator(ctx, c, level, seek, useMetadata), nil\n}\n\n// OpenLTXFile returns a reader for an LTX file\n// Returns os.ErrNotExist if no matching index/offset is found.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar rangeStr string\n\tif size \u003e 0 {\n\t\trangeStr = fmt.Sprintf(\"bytes=%d-%d\", offset, offset+size-1)\n\t} else {\n\t\trangeStr = fmt.Sprintf(\"bytes=%d-\", offset)\n\t}\n\n\t// Build the key from the file info\n\tfilename := ltx.FormatFilename(minTXID, maxTXID)\n\tkey := c.Path + \"/\" + fmt.Sprintf(\"%04x/%s\", level, filename)\n\n\tinput := \u0026s3.GetObjectInput{\n\t\tBucket: aws.String(c.Bucket),\n\t\tKey: aws.String(key),\n\t\tRange: aws.String(rangeStr),\n\t}\n\n\t// Add SSE-C parameters if configured (required for reading SSE-C encrypted objects)\n\t// Note: SSE-KMS does not require parameters on read - decryption is automatic\n\tif c.SSECustomerKey != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(c.SSECustomerAlgorithm)\n\t\tinput.SSECustomerKey = aws.String(c.SSECustomerKey)\n\t\tinput.SSECustomerKeyMD5 = aws.String(c.SSECustomerKeyMD5)\n\t}\n\n\tout, err := c.s3.GetObject(ctx, input)\n\tif err != nil {\n\t\tif isNotExists(err) {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, fmt.Errorf(\"s3: get object %s: %w\", key, err)\n\t}\n\treturn out.Body, nil\n}\n\n// WriteLTXFile writes an LTX file to the replica.\n// Extracts timestamp from LTX header and stores it in S3 metadata to preserve original creation time.\n// Objects smaller than the part size are written with a single PutObject call\n// to avoid the multipart upload manager's 5 MiB part buffers (issue #1327).\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := ltx.FormatFilename(minTXID, maxTXID)\n\tkey := c.Path + \"/\" + fmt.Sprintf(\"%04x/%s\", level, filename)\n\n\tpartSize := int64(manager.DefaultUploadPartSize)\n\tif c.PartSize \u003e 0 {\n\t\tpartSize = c.PartSize\n\t}\n\n\t// The uploader rejects part sizes below the SDK minimum on every upload.\n\t// Fail identically here so the single-put path cannot mask the\n\t// misconfiguration for small objects.\n\tif partSize \u003c manager.MinUploadPartSize {\n\t\treturn nil, fmt.Errorf(\"s3: upload to %s: part size must be at least %d bytes\", key, manager.MinUploadPartSize)\n\t}\n\n\tsize, timestamp, etag, err := c.uploadLTX(ctx, key, r, partSize)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(size))\n\n\t// ETag indicates successful upload\n\tif etag == nil {\n\t\treturn nil, fmt.Errorf(\"s3: upload failed: no ETag returned\")\n\t}\n\n\treturn \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: size,\n\t\tCreatedAt: timestamp,\n\t}, nil\n}\n\n// uploadLTX uploads LTX data from r to key, choosing between a single\n// PutObject and the multipart uploader based on the object size.\nfunc (c *ReplicaClient) uploadLTX(ctx context.Context, key string, r io.Reader, partSize int64) (int64, time.Time, *string, error) {\n\t// The L0 replication path passes the local LTX file, so the size is\n\t// known up front and the body stays seekable for SDK retries.\n\tif rs, ok := r.(io.ReadSeeker); ok {\n\t\tif start, err := rs.Seek(0, io.SeekCurrent); err == nil {\n\t\t\treturn c.uploadSizedLTX(ctx, key, rs, start, partSize)\n\t\t}\n\t\t// Reader does not support seeking (e.g. piped file descriptor);\n\t\t// nothing has been consumed, so treat it as an unsized stream.\n\t}\n\treturn c.uploadStreamedLTX(ctx, key, r, partSize)\n}\n\n// uploadSizedLTX uploads from a seekable reader whose size is known.\nfunc (c *ReplicaClient) uploadSizedLTX(ctx context.Context, key string, rs io.ReadSeeker, start, partSize int64) (int64, time.Time, *string, error) {\n\tend, err := rs.Seek(0, io.SeekEnd)\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: size ltx file %s: %w\", key, err)\n\t}\n\tsize := end - start\n\tif _, err := rs.Seek(start, io.SeekStart); err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: rewind ltx file %s: %w\", key, err)\n\t}\n\n\t// Extract timestamp from LTX header, then rewind so the upload sees the\n\t// full file.\n\thdr, _, err := ltx.PeekHeader(rs)\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\tif _, err := rs.Seek(start, io.SeekStart); err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: rewind ltx file %s: %w\", key, err)\n\t}\n\n\tinput := c.putObjectInput(key, timestamp)\n\tinput.Body = rs\n\n\tif size \u003c partSize {\n\t\tinput.ContentLength = aws.Int64(size)\n\t\tout, err := c.s3.PutObject(ctx, input)\n\t\tif err != nil {\n\t\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: put object %s: %w\", key, err)\n\t\t}\n\t\treturn size, timestamp, out.ETag, nil\n\t}\n\n\t// At or above the part size the uploader splits the seekable body into\n\t// section readers without copying it into part buffers.\n\tout, err := c.uploader.Upload(ctx, input)\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: upload to %s: %w\", key, err)\n\t}\n\treturn size, timestamp, out.ETag, nil\n}\n\n// uploadStreamedLTX uploads from a reader of unknown size, buffering up to\n// the part size to determine whether the object fits in a single PutObject.\nfunc (c *ReplicaClient) uploadStreamedLTX(ctx context.Context, key string, r io.Reader, partSize int64) (int64, time.Time, *string, error) {\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\thdr, _, err := ltx.PeekHeader(io.TeeReader(r, \u0026buf))\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\tif _, err := io.CopyN(\u0026buf, r, partSize-int64(buf.Len())); err != nil \u0026\u0026 !errors.Is(err, io.EOF) {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: buffer ltx stream %s: %w\", key, err)\n\t}\n\n\tinput := c.putObjectInput(key, timestamp)\n\n\t// Reaching EOF before the part size means the whole object is buffered.\n\tif size := int64(buf.Len()); size \u003c partSize {\n\t\tinput.Body = bytes.NewReader(buf.Bytes())\n\t\tinput.ContentLength = aws.Int64(size)\n\t\tout, err := c.s3.PutObject(ctx, input)\n\t\tif err != nil {\n\t\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: put object %s: %w\", key, err)\n\t\t}\n\t\treturn size, timestamp, out.ETag, nil\n\t}\n\n\t// Combine buffered prefix with rest of reader so nothing is re-read.\n\trc := internal.NewReadCounter(io.MultiReader(bytes.NewReader(buf.Bytes()), r))\n\tinput.Body = rc\n\tout, err := c.uploader.Upload(ctx, input)\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: upload to %s: %w\", key, err)\n\t}\n\treturn rc.N(), timestamp, out.ETag, nil\n}\n\n// putObjectInput returns a PutObjectInput populated with the client's write\n// parameters so the single-put and multipart paths stay in parity.\nfunc (c *ReplicaClient) putObjectInput(key string, timestamp time.Time) *s3.PutObjectInput {\n\tinput := \u0026s3.PutObjectInput{\n\t\tBucket: aws.String(c.Bucket),\n\t\tKey: aws.String(key),\n\t\t// Store timestamp in S3 metadata for accurate timestamp retrieval\n\t\tMetadata: map[string]string{\n\t\t\tMetadataKeyTimestamp: timestamp.Format(time.RFC3339Nano),\n\t\t},\n\t}\n\tif c.StorageClass != \"\" {\n\t\tinput.StorageClass = types.StorageClass(c.StorageClass)\n\t}\n\n\t// Add SSE-C parameters if configured\n\tif c.SSECustomerKey != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(c.SSECustomerAlgorithm)\n\t\tinput.SSECustomerKey = aws.String(c.SSECustomerKey)\n\t\tinput.SSECustomerKeyMD5 = aws.String(c.SSECustomerKeyMD5)\n\t}\n\n\t// Add SSE-KMS parameters if configured\n\tif c.SSEKMSKeyID != \"\" {\n\t\tinput.ServerSideEncryption = types.ServerSideEncryptionAwsKms\n\t\tinput.SSEKMSKeyId = aws.String(c.SSEKMSKeyID)\n\t}\n\n\treturn input\n}\n\nfunc (c *ReplicaClient) middlewareOption() func(*middleware.Stack) error {\n\treturn func(stack *middleware.Stack) error {\n\t\tif c.RequireContentMD5 {\n\t\t\tif err := stack.Serialize.Add(\n\t\t\t\tmiddleware.SerializeMiddlewareFunc(\n\t\t\t\t\t\"LitestreamComputeDeleteContentMD5\",\n\t\t\t\t\tfunc(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (\n\t\t\t\t\t\tout middleware.SerializeOutput, metadata middleware.Metadata, err error,\n\t\t\t\t\t) {\n\t\t\t\t\t\tif middleware.GetOperationName(ctx) != \"DeleteObjects\" {\n\t\t\t\t\t\t\treturn next.HandleSerialize(ctx, in)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tinput, ok := in.Parameters.(*s3.DeleteObjectsInput)\n\t\t\t\t\t\tif !ok || input == nil || input.Delete == nil || len(input.Delete.Objects) == 0 {\n\t\t\t\t\t\t\treturn next.HandleSerialize(ctx, in)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tchecksum, err := computeDeleteObjectsContentMD5(input.Delete)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn out, metadata, err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif checksum != \"\" {\n\t\t\t\t\t\t\tctx = middleware.WithStackValue(ctx, contentMD5StackKey{}, checksum)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn next.HandleSerialize(ctx, in)\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\tmiddleware.Before,\n\t\t\t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif err := stack.Build.Add(\n\t\t\tmiddleware.BuildMiddlewareFunc(\n\t\t\t\t\"LitestreamUserAgent\",\n\t\t\t\tfunc(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (\n\t\t\t\t\tout middleware.BuildOutput, metadata middleware.Metadata, err error,\n\t\t\t\t) {\n\t\t\t\t\tif req, ok := in.Request.(*smithyhttp.Request); ok {\n\t\t\t\t\t\tcurrent := req.Header.Get(\"User-Agent\")\n\t\t\t\t\t\tif current == \"\" {\n\t\t\t\t\t\t\treq.Header.Set(\"User-Agent\", \"litestream\")\n\t\t\t\t\t\t} else if !strings.Contains(current, \"litestream\") {\n\t\t\t\t\t\t\treq.Header.Set(\"User-Agent\", \"litestream \"+current)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn next.HandleBuild(ctx, in)\n\t\t\t\t},\n\t\t\t),\n\t\t\tmiddleware.After,\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif litestream.IsTigrisEndpoint(c.Endpoint) {\n\t\t\tif err := stack.Build.Add(\n\t\t\t\tmiddleware.BuildMiddlewareFunc(\n\t\t\t\t\t\"LitestreamTigrisConsistent\",\n\t\t\t\t\tfunc(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (\n\t\t\t\t\t\tout middleware.BuildOutput, metadata middleware.Metadata, err error,\n\t\t\t\t\t) {\n\t\t\t\t\t\tif req, ok := in.Request.(*smithyhttp.Request); ok {\n\t\t\t\t\t\t\treq.Header.Set(\"X-Tigris-Consistent\", \"true\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn next.HandleBuild(ctx, in)\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\tmiddleware.After,\n\t\t\t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif !c.SignAcceptEncoding || litestream.IsGoogleCloudStorageEndpoint(c.Endpoint) {\n\t\t\tif err := stack.Finalize.Insert(\n\t\t\t\tmiddleware.FinalizeMiddlewareFunc(\n\t\t\t\t\t\"LitestreamRemoveAcceptEncoding\",\n\t\t\t\t\tfunc(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (\n\t\t\t\t\t\tout middleware.FinalizeOutput, metadata middleware.Metadata, err error,\n\t\t\t\t\t) {\n\t\t\t\t\t\tif req, ok := in.Request.(*smithyhttp.Request); ok {\n\t\t\t\t\t\t\treq.Header.Del(\"Accept-Encoding\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn next.HandleFinalize(ctx, in)\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\t\"Signing\",\n\t\t\t\tmiddleware.Before,\n\t\t\t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\t// Many S3-compatible providers (e.g. Filebase) do not support SigV4\n\t\t// payload hashing. Switching to unsigned payload matches the behavior\n\t\t// of the AWS SDK v1 client used in Litestream v0.3.x and restores\n\t\t// compatibility.\n\t\tif !c.SignPayload {\n\t\t\t_ = v4.RemoveComputePayloadSHA256Middleware(stack)\n\t\t\tif err := v4.AddUnsignedPayloadMiddleware(stack); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_ = v4.RemoveContentSHA256HeaderMiddleware(stack)\n\t\t\tif err := v4.AddContentSHA256HeaderMiddleware(stack); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\t// Disable AWS SDK v2's trailing checksum middleware which uses\n\t\t// aws-chunked encoding. This is required for:\n\t\t// 1. UNSIGNED-PAYLOAD requests (aws-chunked + UNSIGNED-PAYLOAD is rejected by AWS)\n\t\t// 2. S3-compatible providers (Filebase, MinIO, Backblaze B2, etc.) that don't\n\t\t// support aws-chunked encoding at all\n\t\t// See: https://github.com/aws/aws-sdk-go-v2/discussions/2960\n\t\t// See: https://github.com/benbjohnson/litestream/issues/895\n\t\tif !c.SignPayload || c.Endpoint != \"\" {\n\t\t\tstack.Finalize.Remove(\"addInputChecksumTrailer\")\n\t\t}\n\n\t\t// Add debug logging middleware at the end of Finalize phase\n\t\t// so all headers (including X-Tigris-Consistent) are set\n\t\tif err := stack.Finalize.Add(\n\t\t\tmiddleware.FinalizeMiddlewareFunc(\n\t\t\t\t\"LitestreamDebugLogging\",\n\t\t\t\tfunc(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (\n\t\t\t\t\tout middleware.FinalizeOutput, metadata middleware.Metadata, err error,\n\t\t\t\t) {\n\t\t\t\t\tif req, ok := in.Request.(*smithyhttp.Request); ok {\n\t\t\t\t\t\tc.logger.Debug(\"s3 request\",\n\t\t\t\t\t\t\t\"method\", req.Method,\n\t\t\t\t\t\t\t\"url\", req.URL.String(),\n\t\t\t\t\t\t\t\"x-tigris-consistent\", req.Header.Get(\"X-Tigris-Consistent\"),\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\treturn next.HandleFinalize(ctx, in)\n\t\t\t\t},\n\t\t\t),\n\t\t\tmiddleware.After,\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !c.RequireContentMD5 {\n\t\t\treturn nil\n\t\t}\n\n\t\tmd5Middleware := func() middleware.FinalizeMiddleware {\n\t\t\treturn middleware.FinalizeMiddlewareFunc(\n\t\t\t\t\"LitestreamDeleteContentMD5\",\n\t\t\t\tfunc(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (\n\t\t\t\t\tout middleware.FinalizeOutput, metadata middleware.Metadata, err error,\n\t\t\t\t) {\n\t\t\t\t\tif middleware.GetOperationName(ctx) != \"DeleteObjects\" {\n\t\t\t\t\t\treturn next.HandleFinalize(ctx, in)\n\t\t\t\t\t}\n\n\t\t\t\t\tchecksum, _ := middleware.GetStackValue(ctx, contentMD5StackKey{}).(string)\n\t\t\t\t\tif checksum == \"\" {\n\t\t\t\t\t\treturn next.HandleFinalize(ctx, in)\n\t\t\t\t\t}\n\n\t\t\t\t\treq, ok := in.Request.(*smithyhttp.Request)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn next.HandleFinalize(ctx, in)\n\t\t\t\t\t}\n\t\t\t\t\tif req.Header.Get(\"Content-MD5\") == \"\" {\n\t\t\t\t\t\treq.Header.Set(\"Content-MD5\", checksum)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn next.HandleFinalize(ctx, in)\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\n\t\t// Try to insert before AWS's checksum middleware for optimal ordering.\n\t\t// If that middleware doesn't exist (e.g., different SDK version), add at the end.\n\t\t// Our middleware checks if Content-MD5 is already set, so order is not critical.\n\t\tif err := stack.Finalize.Insert(md5Middleware(), \"AWSChecksum:ComputeInputPayloadChecksum\", middleware.Before); err != nil {\n\t\t\tif err := stack.Finalize.Add(md5Middleware(), middleware.After); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc computeDeleteObjectsContentMD5(deleteInput *types.Delete) (string, error) {\n\tif deleteInput == nil {\n\t\treturn \"\", nil\n\t}\n\n\tpayload, err := marshalDeleteObjects(deleteInput)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(payload) == 0 {\n\t\treturn \"\", nil\n\t}\n\n\tsum := md5.Sum(payload)\n\treturn base64.StdEncoding.EncodeToString(sum[:]), nil\n}\n\nfunc marshalDeleteObjects(deleteInput *types.Delete) ([]byte, error) {\n\tif deleteInput == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar buf bytes.Buffer\n\tencoder := smithyxml.NewEncoder(\u0026buf)\n\troot := smithyxml.StartElement{\n\t\tName: smithyxml.Name{\n\t\t\tLocal: \"Delete\",\n\t\t},\n\t\tAttr: []smithyxml.Attr{\n\t\t\tsmithyxml.NewNamespaceAttribute(\"\", \"http://s3.amazonaws.com/doc/2006-03-01/\"),\n\t\t},\n\t}\n\n\tif err := encodeDeleteDocument(deleteInput, encoder.RootElement(root)); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn encoder.Bytes(), nil\n}\n\nfunc encodeDeleteDocument(v *types.Delete, value smithyxml.Value) error {\n\tdefer value.Close()\n\tif v.Objects != nil {\n\t\troot := smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"Object\",\n\t\t\t},\n\t\t}\n\t\tel := value.FlattenedElement(root)\n\t\tif err := encodeObjectIdentifierList(v.Objects, el); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif v.Quiet != nil {\n\t\troot := smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"Quiet\",\n\t\t\t},\n\t\t}\n\t\tel := value.MemberElement(root)\n\t\tel.Boolean(*v.Quiet)\n\t}\n\treturn nil\n}\n\nfunc encodeObjectIdentifierList(v []types.ObjectIdentifier, value smithyxml.Value) error {\n\tif !value.IsFlattened() {\n\t\tdefer value.Close()\n\t}\n\n\tarray := value.Array()\n\tfor i := range v {\n\t\tmember := array.Member()\n\t\tif err := encodeObjectIdentifier(\u0026v[i], member); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// encodeObjectIdentifier mirrors the AWS SDK's XML serializer for DeleteObjects.\n// This ensures our precomputed Content-MD5 matches the actual request body.\n// Includes all ObjectIdentifier fields as of AWS SDK v2 (2024).\nfunc encodeObjectIdentifier(v *types.ObjectIdentifier, value smithyxml.Value) error {\n\tdefer value.Close()\n\tif v.ETag != nil {\n\t\tel := value.MemberElement(smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"ETag\",\n\t\t\t},\n\t\t})\n\t\tel.String(*v.ETag)\n\t}\n\tif v.Key != nil {\n\t\tel := value.MemberElement(smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"Key\",\n\t\t\t},\n\t\t})\n\t\tel.String(*v.Key)\n\t}\n\tif v.LastModifiedTime != nil {\n\t\tel := value.MemberElement(smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"LastModifiedTime\",\n\t\t\t},\n\t\t})\n\t\tel.String(smithytime.FormatHTTPDate(*v.LastModifiedTime))\n\t}\n\tif v.Size != nil {\n\t\tel := value.MemberElement(smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"Size\",\n\t\t\t},\n\t\t})\n\t\tel.Long(*v.Size)\n\t}\n\tif v.VersionId != nil {\n\t\tel := value.MemberElement(smithyxml.StartElement{\n\t\t\tName: smithyxml.Name{\n\t\t\t\tLocal: \"VersionId\",\n\t\t\t},\n\t\t})\n\t\tel.String(*v.VersionId)\n\t}\n\treturn nil\n}\n\n// DeleteLTXFiles deletes one or more LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tif len(a) == 0 {\n\t\treturn nil\n\t}\n\n\t// Convert file infos to object identifiers\n\tobjIDs := make([]types.ObjectIdentifier, 0, len(a))\n\tfor _, info := range a {\n\t\tfilename := ltx.FormatFilename(info.MinTXID, info.MaxTXID)\n\t\tkey := c.Path + \"/\" + fmt.Sprintf(\"%04x/%s\", info.Level, filename)\n\t\tobjIDs = append(objIDs, types.ObjectIdentifier{Key: aws.String(key)})\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"key\", key)\n\t}\n\n\t// Delete in batches\n\tfor len(objIDs) \u003e 0 {\n\t\tn := min(len(objIDs), MaxKeys)\n\n\t\tc.logger.Debug(\"deleting ltx files batch\", \"count\", n)\n\n\t\tstart := time.Now()\n\t\tout, err := c.s3.DeleteObjects(ctx, \u0026s3.DeleteObjectsInput{\n\t\t\tBucket: aws.String(c.Bucket),\n\t\t\tDelete: \u0026types.Delete{Objects: objIDs[:n]},\n\t\t})\n\t\tduration := time.Since(start)\n\t\tinternal.OperationDurationHistogramVec.WithLabelValues(ReplicaClientType, \"DELETE\").Observe(duration.Seconds())\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"s3: delete batch of %d objects: %w\", n, err)\n\t\t}\n\n\t\tdeleted := 0\n\t\tif out != nil {\n\t\t\tdeleted = len(out.Deleted)\n\t\t}\n\t\tc.logger.Debug(\"delete batch completed\",\n\t\t\t\"requested\", n,\n\t\t\t\"deleted\", deleted,\n\t\t\t\"errors\", len(out.Errors),\n\t\t\t\"duration_ms\", duration.Milliseconds())\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Add(float64(deleted))\n\n\t\tif len(out.Errors) \u003e 0 {\n\t\t\tfor i, e := range out.Errors {\n\t\t\t\tcode := aws.ToString(e.Code)\n\t\t\t\tinternal.OperationErrorCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\", code).Inc()\n\n\t\t\t\tif i \u003c 5 {\n\t\t\t\t\tc.logger.Warn(\"delete object failed\",\n\t\t\t\t\t\t\"key\", aws.ToString(e.Key),\n\t\t\t\t\t\t\"code\", code,\n\t\t\t\t\t\t\"message\", aws.ToString(e.Message))\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(out.Errors) \u003e 5 {\n\t\t\t\tc.logger.Warn(\"additional delete errors suppressed\", \"count\", len(out.Errors)-5)\n\t\t\t}\n\t\t}\n\n\t\tif err := deleteOutputError(out); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tobjIDs = objIDs[n:]\n\t}\n\n\treturn nil\n}\n\n// DeleteAll deletes all files.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tvar objIDs []types.ObjectIdentifier\n\n\t// Create paginator for listing objects\n\tpaginator := s3.NewListObjectsV2Paginator(c.s3, \u0026s3.ListObjectsV2Input{\n\t\tBucket: aws.String(c.Bucket),\n\t\tPrefix: aws.String(c.Path + \"/\"),\n\t})\n\n\t// Iterate through all pages\n\tfor paginator.HasMorePages() {\n\t\tpage, err := paginator.NextPage(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"s3: list objects page: %w\", err)\n\t\t}\n\n\t\t// Collect object identifiers\n\t\tfor _, obj := range page.Contents {\n\t\t\tobjIDs = append(objIDs, types.ObjectIdentifier{Key: obj.Key})\n\t\t}\n\t}\n\n\t// Delete all collected objects in batches\n\tfor len(objIDs) \u003e 0 {\n\t\tn := min(len(objIDs), MaxKeys)\n\n\t\tout, err := c.s3.DeleteObjects(ctx, \u0026s3.DeleteObjectsInput{\n\t\t\tBucket: aws.String(c.Bucket),\n\t\t\tDelete: \u0026types.Delete{Objects: objIDs[:n], Quiet: aws.Bool(true)},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"s3: delete all batch of %d objects: %w\", n, err)\n\t\t} else if err := deleteOutputError(out); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tobjIDs = objIDs[n:]\n\t}\n\n\treturn nil\n}\n\n// GenerationsV3 returns a list of v0.3.x generation IDs in the replica.\nfunc (c *ReplicaClient) GenerationsV3(ctx context.Context) ([]string, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tprefix := litestream.GenerationsPathV3(c.Path) + \"/\"\n\n\t// Use CommonPrefixes with delimiter to list \"directories\"\n\tpaginator := s3.NewListObjectsV2Paginator(c.s3, \u0026s3.ListObjectsV2Input{\n\t\tBucket: aws.String(c.Bucket),\n\t\tPrefix: aws.String(prefix),\n\t\tDelimiter: aws.String(\"/\"),\n\t})\n\n\tvar generations []string\n\tfor paginator.HasMorePages() {\n\t\tpage, err := paginator.NextPage(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"s3: list generations: %w\", err)\n\t\t}\n\n\t\tfor _, cp := range page.CommonPrefixes {\n\t\t\t// Extract generation ID from prefix (e.g., \"path/generations/abc123def456/\" -\u003e \"abc123def456\")\n\t\t\tp := aws.ToString(cp.Prefix)\n\t\t\tp = strings.TrimPrefix(p, prefix)\n\t\t\tp = strings.TrimSuffix(p, \"/\")\n\t\t\tif litestream.IsGenerationIDV3(p) {\n\t\t\t\tgenerations = append(generations, p)\n\t\t\t}\n\t\t}\n\t}\n\n\tslices.Sort(generations)\n\treturn generations, nil\n}\n\n// SnapshotsV3 returns snapshots for a generation, sorted by index.\nfunc (c *ReplicaClient) SnapshotsV3(ctx context.Context, generation string) ([]litestream.SnapshotInfoV3, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tprefix := litestream.SnapshotsPathV3(c.Path, generation) + \"/\"\n\n\tpaginator := s3.NewListObjectsV2Paginator(c.s3, \u0026s3.ListObjectsV2Input{\n\t\tBucket: aws.String(c.Bucket),\n\t\tPrefix: aws.String(prefix),\n\t})\n\n\tvar snapshots []litestream.SnapshotInfoV3\n\tfor paginator.HasMorePages() {\n\t\tpage, err := paginator.NextPage(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"s3: list snapshots: %w\", err)\n\t\t}\n\n\t\tfor _, obj := range page.Contents {\n\t\t\tkey := path.Base(aws.ToString(obj.Key))\n\t\t\tindex, err := litestream.ParseSnapshotFilenameV3(key)\n\t\t\tif err != nil {\n\t\t\t\tcontinue // skip invalid filenames\n\t\t\t}\n\n\t\t\tsnapshots = append(snapshots, litestream.SnapshotInfoV3{\n\t\t\t\tGeneration: generation,\n\t\t\t\tIndex: index,\n\t\t\t\tSize: aws.ToInt64(obj.Size),\n\t\t\t\tCreatedAt: aws.ToTime(obj.LastModified).UTC(),\n\t\t\t})\n\t\t}\n\t}\n\n\tslices.SortFunc(snapshots, func(a, b litestream.SnapshotInfoV3) int {\n\t\treturn a.Index - b.Index\n\t})\n\treturn snapshots, nil\n}\n\n// WALSegmentsV3 returns WAL segments for a generation, sorted by index then offset.\nfunc (c *ReplicaClient) WALSegmentsV3(ctx context.Context, generation string) ([]litestream.WALSegmentInfoV3, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tprefix := litestream.WALPathV3(c.Path, generation) + \"/\"\n\n\tpaginator := s3.NewListObjectsV2Paginator(c.s3, \u0026s3.ListObjectsV2Input{\n\t\tBucket: aws.String(c.Bucket),\n\t\tPrefix: aws.String(prefix),\n\t})\n\n\tvar segments []litestream.WALSegmentInfoV3\n\tfor paginator.HasMorePages() {\n\t\tpage, err := paginator.NextPage(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"s3: list wal segments: %w\", err)\n\t\t}\n\n\t\tfor _, obj := range page.Contents {\n\t\t\tkey := path.Base(aws.ToString(obj.Key))\n\t\t\tindex, offset, err := litestream.ParseWALSegmentFilenameV3(key)\n\t\t\tif err != nil {\n\t\t\t\tcontinue // skip invalid filenames\n\t\t\t}\n\n\t\t\tsegments = append(segments, litestream.WALSegmentInfoV3{\n\t\t\t\tGeneration: generation,\n\t\t\t\tIndex: index,\n\t\t\t\tOffset: offset,\n\t\t\t\tSize: aws.ToInt64(obj.Size),\n\t\t\t\tCreatedAt: aws.ToTime(obj.LastModified).UTC(),\n\t\t\t})\n\t\t}\n\t}\n\n\tslices.SortFunc(segments, func(a, b litestream.WALSegmentInfoV3) int {\n\t\tif a.Index != b.Index {\n\t\t\treturn a.Index - b.Index\n\t\t}\n\t\treturn cmp.Compare(a.Offset, b.Offset)\n\t})\n\treturn segments, nil\n}\n\n// OpenSnapshotV3 opens a v0.3.x snapshot file for reading.\n// The returned reader provides LZ4-decompressed data.\nfunc (c *ReplicaClient) OpenSnapshotV3(ctx context.Context, generation string, index int) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := litestream.SnapshotPathV3(c.Path, generation, index)\n\n\tinput := \u0026s3.GetObjectInput{\n\t\tBucket: aws.String(c.Bucket),\n\t\tKey: aws.String(key),\n\t}\n\n\t// Add SSE-C parameters if configured\n\tif c.SSECustomerKey != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(c.SSECustomerAlgorithm)\n\t\tinput.SSECustomerKey = aws.String(c.SSECustomerKey)\n\t\tinput.SSECustomerKeyMD5 = aws.String(c.SSECustomerKeyMD5)\n\t}\n\n\tout, err := c.s3.GetObject(ctx, input)\n\tif err != nil {\n\t\tif isNotExists(err) {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, fmt.Errorf(\"s3: get snapshot %s: %w\", key, err)\n\t}\n\n\treturn internal.NewLZ4Reader(out.Body), nil\n}\n\n// OpenWALSegmentV3 opens a v0.3.x WAL segment file for reading.\n// The returned reader provides LZ4-decompressed data.\nfunc (c *ReplicaClient) OpenWALSegmentV3(ctx context.Context, generation string, index int, offset int64) (io.ReadCloser, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := litestream.WALSegmentPathV3(c.Path, generation, index, offset)\n\n\tinput := \u0026s3.GetObjectInput{\n\t\tBucket: aws.String(c.Bucket),\n\t\tKey: aws.String(key),\n\t}\n\n\t// Add SSE-C parameters if configured\n\tif c.SSECustomerKey != \"\" {\n\t\tinput.SSECustomerAlgorithm = aws.String(c.SSECustomerAlgorithm)\n\t\tinput.SSECustomerKey = aws.String(c.SSECustomerKey)\n\t\tinput.SSECustomerKeyMD5 = aws.String(c.SSECustomerKeyMD5)\n\t}\n\n\tout, err := c.s3.GetObject(ctx, input)\n\tif err != nil {\n\t\tif isNotExists(err) {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, fmt.Errorf(\"s3: get wal segment %s: %w\", key, err)\n\t}\n\n\treturn internal.NewLZ4Reader(out.Body), nil\n}\n\n// fileIterator represents an iterator over LTX files in S3.\ntype fileIterator struct {\n\tctx context.Context\n\tcancel context.CancelFunc\n\tclient *ReplicaClient\n\tlevel int\n\tseek ltx.TXID\n\tprefix string\n\n\tuseMetadata bool // When true, fetch accurate timestamps from metadata\n\tmetadataCache map[string]time.Time // key -\u003e timestamp cache for batch fetches\n\n\tpaginator *s3.ListObjectsV2Paginator\n\tpage *s3.ListObjectsV2Output\n\tpageIndex int\n\n\tclosed bool\n\terr error\n\tinfo *ltx.FileInfo\n}\n\nfunc newFileIterator(ctx context.Context, client *ReplicaClient, level int, seek ltx.TXID, useMetadata bool) *fileIterator {\n\tctx, cancel := context.WithCancel(ctx)\n\n\tprefix := client.Path + \"/\" + fmt.Sprintf(\"%04x/\", level)\n\titr := \u0026fileIterator{\n\t\tctx: ctx,\n\t\tcancel: cancel,\n\t\tclient: client,\n\t\tlevel: level,\n\t\tseek: seek,\n\t\tprefix: prefix,\n\t\tuseMetadata: useMetadata,\n\t\tmetadataCache: make(map[string]time.Time),\n\t}\n\n\t// Create paginator for listing objects with level prefix\n\titr.paginator = s3.NewListObjectsV2Paginator(client.s3, \u0026s3.ListObjectsV2Input{\n\t\tBucket: aws.String(client.Bucket),\n\t\tPrefix: aws.String(prefix),\n\t})\n\n\treturn itr\n}\n\n// fetchMetadataBatch fetches timestamps from S3 metadata for a batch of keys in parallel.\nfunc (itr *fileIterator) fetchMetadataBatch(keys []string) error {\n\tif len(keys) == 0 {\n\t\treturn nil\n\t}\n\n\t// Determine concurrency limit\n\tconcurrency := itr.client.MetadataConcurrency\n\tif concurrency \u003c= 0 {\n\t\tconcurrency = DefaultMetadataConcurrency\n\t}\n\n\t// Pre-allocate results map to avoid lock contention during writes\n\tresults := make(map[string]time.Time, len(keys))\n\tvar mu sync.Mutex\n\n\t// Use x/sync/semaphore for precise concurrency control with context support\n\tsem := semaphore.NewWeighted(int64(concurrency))\n\tg, ctx := errgroup.WithContext(itr.ctx)\n\n\tfor _, key := range keys {\n\t\tkey := key // capture for goroutine\n\n\t\tg.Go(func() error {\n\t\t\t// Acquire semaphore slot (blocking with context cancellation)\n\t\t\tif err := sem.Acquire(ctx, 1); err != nil {\n\t\t\t\treturn err // context cancelled\n\t\t\t}\n\t\t\tdefer sem.Release(1)\n\n\t\t\theadInput := \u0026s3.HeadObjectInput{\n\t\t\t\tBucket: aws.String(itr.client.Bucket),\n\t\t\t\tKey: aws.String(key),\n\t\t\t}\n\n\t\t\t// Add SSE-C parameters if configured (required for reading SSE-C encrypted objects)\n\t\t\t// Note: SSE-KMS does not require parameters on HeadObject - access is automatic\n\t\t\tif itr.client.SSECustomerKey != \"\" {\n\t\t\t\theadInput.SSECustomerAlgorithm = aws.String(itr.client.SSECustomerAlgorithm)\n\t\t\t\theadInput.SSECustomerKey = aws.String(itr.client.SSECustomerKey)\n\t\t\t\theadInput.SSECustomerKeyMD5 = aws.String(itr.client.SSECustomerKeyMD5)\n\t\t\t}\n\n\t\t\thead, err := itr.client.s3.HeadObject(ctx, headInput)\n\t\t\tif err != nil {\n\t\t\t\t// Non-fatal: file might not have metadata, use LastModified\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif head.Metadata != nil {\n\t\t\t\tif ts, ok := head.Metadata[MetadataKeyTimestamp]; ok {\n\t\t\t\t\tif parsed, err := time.Parse(time.RFC3339Nano, ts); err == nil {\n\t\t\t\t\t\tmu.Lock()\n\t\t\t\t\t\tresults[key] = parsed\n\t\t\t\t\t\tmu.Unlock()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\n\tif err := g.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\t// Merge results into cache\n\tfor k, v := range results {\n\t\titr.metadataCache[k] = v\n\t}\n\treturn nil\n}\n\n// Close stops iteration and returns any error that occurred during iteration.\nfunc (itr *fileIterator) Close() (err error) {\n\titr.closed = true\n\titr.cancel()\n\treturn itr.err\n}\n\n// Next returns the next file. Returns false when no more files are available.\nfunc (itr *fileIterator) Next() bool {\n\tif itr.closed || itr.err != nil {\n\t\treturn false\n\t}\n\n\t// Process objects until we find a valid LTX file\n\tfor {\n\t\t// Load next page if needed\n\t\tif itr.page == nil || itr.pageIndex \u003e= len(itr.page.Contents) {\n\t\t\tif !itr.paginator.HasMorePages() {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tvar err error\n\t\t\titr.page, err = itr.paginator.NextPage(itr.ctx)\n\t\t\tif err != nil {\n\t\t\t\titr.err = err\n\t\t\t\treturn false\n\t\t\t}\n\t\t\titr.pageIndex = 0\n\n\t\t\t// Log page contents for debugging.\n\t\t\tif len(itr.page.Contents) \u003e 0 {\n\t\t\t\tkeys := make([]string, 0, len(itr.page.Contents))\n\t\t\t\tfor _, obj := range itr.page.Contents {\n\t\t\t\t\tkeys = append(keys, path.Base(aws.ToString(obj.Key)))\n\t\t\t\t}\n\t\t\t\titr.client.logger.Debug(\"s3 LIST page\", \"prefix\", itr.prefix, \"keys\", keys)\n\t\t\t}\n\n\t\t\t// Batch fetch metadata for the entire page when useMetadata is true.\n\t\t\t// This uses parallel HeadObject calls controlled by MetadataConcurrency\n\t\t\t// to avoid the O(N) sequential calls that caused restore hangs (issue #930).\n\t\t\tif itr.useMetadata \u0026\u0026 len(itr.page.Contents) \u003e 0 {\n\t\t\t\tkeys := make([]string, 0, len(itr.page.Contents))\n\t\t\t\tfor _, obj := range itr.page.Contents {\n\t\t\t\t\tkeys = append(keys, aws.ToString(obj.Key))\n\t\t\t\t}\n\t\t\t\tif err := itr.fetchMetadataBatch(keys); err != nil {\n\t\t\t\t\titr.err = err\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Process current object\n\t\tif itr.pageIndex \u003c len(itr.page.Contents) {\n\t\t\tobj := itr.page.Contents[itr.pageIndex]\n\t\t\titr.pageIndex++\n\n\t\t\t// Extract file info from key\n\t\t\tfullKey := aws.ToString(obj.Key)\n\t\t\tkey := path.Base(fullKey)\n\t\t\tminTXID, maxTXID, err := ltx.ParseFilename(key)\n\t\t\tif err != nil {\n\t\t\t\tcontinue // Skip non-LTX files\n\t\t\t}\n\n\t\t\t// Build file info\n\t\t\tinfo := \u0026ltx.FileInfo{\n\t\t\t\tLevel: itr.level,\n\t\t\t\tMinTXID: minTXID,\n\t\t\t\tMaxTXID: maxTXID,\n\t\t\t}\n\n\t\t\t// Skip if below seek TXID\n\t\t\tif info.MinTXID \u003c itr.seek {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Skip if wrong level\n\t\t\tif info.Level != itr.level {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Set file info\n\t\t\tinfo.Size = aws.ToInt64(obj.Size)\n\n\t\t\t// Use cached metadata timestamp if available (from batch fetch),\n\t\t\t// otherwise fallback to LastModified from LIST operation.\n\t\t\tif itr.useMetadata {\n\t\t\t\tif ts, ok := itr.metadataCache[fullKey]; ok {\n\t\t\t\t\tinfo.CreatedAt = ts\n\t\t\t\t} else {\n\t\t\t\t\tinfo.CreatedAt = aws.ToTime(obj.LastModified).UTC()\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tinfo.CreatedAt = aws.ToTime(obj.LastModified).UTC()\n\t\t\t}\n\n\t\t\titr.info = info\n\t\t\treturn true\n\t\t}\n\t}\n}\n\n// Item returns the metadata for the current file.\nfunc (itr *fileIterator) Item() *ltx.FileInfo {\n\treturn itr.info\n}\n\n// Err returns any error that occurred during iteration.\nfunc (itr *fileIterator) Err() error {\n\treturn itr.err\n}\n\n// ParseURL parses an S3 URL into its host and path parts.\n// If endpoint is set, it can override the host.\nfunc ParseURL(s, endpoint string) (bucket, region, key string, err error) {\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", err\n\t}\n\n\tif u.Scheme != \"s3\" {\n\t\treturn \"\", \"\", \"\", fmt.Errorf(\"s3: invalid url scheme\")\n\t}\n\n\t// Special handling for filebase.com\n\tif u.Host == \"filebase.com\" {\n\t\tparts := strings.SplitN(strings.TrimPrefix(u.Path, \"/\"), \"/\", 2)\n\t\tif len(parts) == 0 {\n\t\t\treturn \"\", \"\", \"\", fmt.Errorf(\"s3: bucket required\")\n\t\t}\n\t\tbucket = parts[0]\n\t\tif len(parts) \u003e 1 {\n\t\t\tkey = parts[1]\n\t\t}\n\t\treturn bucket, \"\", key, nil\n\t}\n\n\t// For other hosts, check if it's a special endpoint\n\tbucket, region, _, _ = ParseHost(u.Host)\n\tif bucket == \"\" {\n\t\tbucket = u.Host\n\t}\n\n\tkey = strings.TrimPrefix(u.Path, \"/\")\n\treturn bucket, region, key, nil\n}\n\n// ParseHost parses the host/endpoint for an S3-like storage system.\n// Endpoints: https://docs.aws.amazon.com/general/latest/gr/s3.html\nfunc ParseHost(host string) (bucket, region, endpoint string, forcePathStyle bool) {\n\t// Check for MinIO-style hosts (bucket.host:port)\n\tif strings.Contains(host, \":\") \u0026\u0026 !strings.Contains(host, \".com\") {\n\t\tparts := strings.SplitN(host, \".\", 2)\n\t\tif len(parts) == 2 {\n\t\t\t// Extract bucket from bucket.host:port format\n\t\t\tbucket = parts[0]\n\t\t\tendpoint = \"http://\" + parts[1]\n\t\t\treturn bucket, DefaultRegion, endpoint, true\n\t\t}\n\t\t// No bucket in host, just host:port\n\t\treturn \"\", \"\", \"http://\" + host, true\n\t}\n\n\t// Check common object storage providers\n\t// Check for AWS S3 URLs first\n\tif a := awsS3Regex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tbucket = a[1]\n\t\tif len(a) \u003e 2 \u0026\u0026 a[2] != \"\" {\n\t\t\tregion = a[2]\n\t\t}\n\t\treturn bucket, region, \"\", false\n\t} else if a := digitaloceanRegex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tbucket = a[1]\n\t\tregion = a[2]\n\t\treturn bucket, region, fmt.Sprintf(\"https://%s.digitaloceanspaces.com\", region), false\n\t} else if a := backblazeRegex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tregion = a[2]\n\t\tbucket = a[1]\n\t\tendpoint = fmt.Sprintf(\"https://s3.%s.backblazeb2.com\", region)\n\t\treturn bucket, region, endpoint, true\n\t} else if a := filebaseRegex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tbucket = a[1]\n\t\tendpoint = \"s3.filebase.com\"\n\t\treturn bucket, \"\", endpoint, false\n\t} else if a := scalewayRegex.FindStringSubmatch(host); len(a) \u003e 1 {\n\t\tregion = a[2]\n\t\tbucket = a[1]\n\t\tendpoint = fmt.Sprintf(\"s3.%s.scw.cloud\", region)\n\t\treturn bucket, region, endpoint, false\n\t}\n\n\t// For standard S3, the host is the bucket name\n\treturn host, \"\", \"\", false\n}\n\nvar (\n\tawsS3Regex = regexp.MustCompile(`^(.+)\\.s3(?:\\.([^.]+))?\\.amazonaws\\.com$`)\n\tdigitaloceanRegex = regexp.MustCompile(`^(?:(.+)\\.)?([^.]+)\\.digitaloceanspaces.com$`)\n\tbackblazeRegex = regexp.MustCompile(`^(?:(.+)\\.)?s3.([^.]+)\\.backblazeb2.com$`)\n\tfilebaseRegex = regexp.MustCompile(`^(?:(.+)\\.)?s3.filebase.com$`)\n\tscalewayRegex = regexp.MustCompile(`^(?:(.+)\\.)?s3.([^.]+)\\.scw\\.cloud$`)\n)\n\nfunc isNotExists(err error) bool {\n\tvar apiErr smithy.APIError\n\tif errors.As(err, \u0026apiErr) {\n\t\treturn apiErr.ErrorCode() == \"NoSuchKey\"\n\t}\n\treturn false\n}\n\nfunc deleteOutputError(out *s3.DeleteObjectsOutput) error {\n\tif len(out.Errors) == 0 {\n\t\treturn nil\n\t}\n\n\t// Build generic error\n\tvar b strings.Builder\n\tb.WriteString(\"failed to delete files:\")\n\tfor _, err := range out.Errors {\n\t\tfmt.Fprintf(\u0026b, \"\\n%s: %s\", aws.ToString(err.Key), aws.ToString(err.Message))\n\t}\n\treturn errors.New(b.String())\n}\n\n// parseS3DebugEnv parses the LITESTREAM_S3_DEBUG environment variable and returns\n// the corresponding AWS SDK ClientLogMode. Supports comma-separated values.\nfunc parseS3DebugEnv() aws.ClientLogMode {\n\tv := os.Getenv(\"LITESTREAM_S3_DEBUG\")\n\tif v == \"\" {\n\t\treturn 0\n\t}\n\n\tvar logMode aws.ClientLogMode\n\tfor _, mode := range strings.Split(v, \",\") {\n\t\tswitch strings.ToLower(strings.TrimSpace(mode)) {\n\t\tcase \"signing\":\n\t\t\tlogMode |= aws.LogSigning\n\t\tcase \"request\":\n\t\t\tlogMode |= aws.LogRequest\n\t\tcase \"request-with-body\":\n\t\t\tlogMode |= aws.LogRequestWithBody\n\t\tcase \"response\":\n\t\t\tlogMode |= aws.LogResponse\n\t\tcase \"response-with-body\":\n\t\t\tlogMode |= aws.LogResponseWithBody\n\t\tcase \"retries\":\n\t\t\tlogMode |= aws.LogRetries\n\t\tcase \"all\":\n\t\t\tlogMode |= aws.LogSigning | aws.LogRequest | aws.LogRequestWithBody |\n\t\t\t\taws.LogResponse | aws.LogResponseWithBody | aws.LogRetries\n\t\tdefault:\n\t\t\tslog.Warn(\"unknown LITESTREAM_S3_DEBUG value, expected: signing, request, request-with-body, response, response-with-body, retries, all\", \"value\", mode)\n\t\t}\n\t}\n\treturn logMode\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[21,29]}},{"name":"cmp","path":"cmp","span":{"start":[5,1],"end":[5,7],"bytes":[30,36]}},{"name":"context","path":"context","span":{"start":[6,1],"end":[6,11],"bytes":[37,47]}},{"name":"md5","path":"crypto/md5","span":{"start":[7,1],"end":[7,14],"bytes":[48,61]}},{"name":"tls","path":"crypto/tls","span":{"start":[8,1],"end":[8,14],"bytes":[62,75]}},{"name":"base64","path":"encoding/base64","span":{"start":[9,1],"end":[9,19],"bytes":[76,94]}},{"name":"errors","path":"errors","span":{"start":[10,1],"end":[10,10],"bytes":[95,104]}},{"name":"fmt","path":"fmt","span":{"start":[11,1],"end":[11,7],"bytes":[105,111]}},{"name":"io","path":"io","span":{"start":[12,1],"end":[12,6],"bytes":[112,117]}},{"name":"slog","path":"log/slog","span":{"start":[13,1],"end":[13,12],"bytes":[118,129]}},{"name":"net","path":"net","span":{"start":[14,1],"end":[14,7],"bytes":[130,136]}},{"name":"http","path":"net/http","span":{"start":[15,1],"end":[15,12],"bytes":[137,148]}},{"name":"url","path":"net/url","span":{"start":[16,1],"end":[16,11],"bytes":[149,159]}},{"name":"os","path":"os","span":{"start":[17,1],"end":[17,6],"bytes":[160,165]}},{"name":"path","path":"path","span":{"start":[18,1],"end":[18,8],"bytes":[166,173]}},{"name":"regexp","path":"regexp","span":{"start":[19,1],"end":[19,10],"bytes":[174,183]}},{"name":"slices","path":"slices","span":{"start":[20,1],"end":[20,10],"bytes":[184,193]}},{"name":"strings","path":"strings","span":{"start":[21,1],"end":[21,11],"bytes":[194,204]}},{"name":"sync","path":"sync","span":{"start":[22,1],"end":[22,8],"bytes":[205,212]}},{"name":"time","path":"time","span":{"start":[23,1],"end":[23,8],"bytes":[213,220]}},{"name":"aws","path":"github.com/aws/aws-sdk-go-v2/aws","span":{"start":[25,1],"end":[25,36],"bytes":[222,257]}},{"name":"ratelimit","path":"github.com/aws/aws-sdk-go-v2/aws/ratelimit","span":{"start":[26,1],"end":[26,46],"bytes":[258,303]}},{"name":"retry","path":"github.com/aws/aws-sdk-go-v2/aws/retry","span":{"start":[27,1],"end":[27,42],"bytes":[304,345]}},{"name":"v4","path":"github.com/aws/aws-sdk-go-v2/aws/signer/v4","alias":"v4","span":{"start":[28,1],"end":[28,49],"bytes":[346,394]}},{"name":"config","path":"github.com/aws/aws-sdk-go-v2/config","span":{"start":[29,1],"end":[29,39],"bytes":[395,433]}},{"name":"credentials","path":"github.com/aws/aws-sdk-go-v2/credentials","span":{"start":[30,1],"end":[30,44],"bytes":[434,477]}},{"name":"manager","path":"github.com/aws/aws-sdk-go-v2/feature/s3/manager","span":{"start":[31,1],"end":[31,51],"bytes":[478,528]}},{"name":"s3","path":"github.com/aws/aws-sdk-go-v2/service/s3","span":{"start":[32,1],"end":[32,43],"bytes":[529,571]}},{"name":"types","path":"github.com/aws/aws-sdk-go-v2/service/s3/types","span":{"start":[33,1],"end":[33,49],"bytes":[572,620]}},{"name":"smithy-go","path":"github.com/aws/smithy-go","span":{"start":[34,1],"end":[34,28],"bytes":[621,648]}},{"name":"xml","path":"github.com/aws/smithy-go/encoding/xml","alias":"smithyxml","span":{"start":[35,1],"end":[35,51],"bytes":[649,699]}},{"name":"middleware","path":"github.com/aws/smithy-go/middleware","span":{"start":[36,1],"end":[36,39],"bytes":[700,738]}},{"name":"time","path":"github.com/aws/smithy-go/time","alias":"smithytime","span":{"start":[37,1],"end":[37,44],"bytes":[739,782]}},{"name":"http","path":"github.com/aws/smithy-go/transport/http","alias":"smithyhttp","span":{"start":[38,1],"end":[38,54],"bytes":[783,836]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[39,1],"end":[39,27],"bytes":[837,863]}},{"name":"errgroup","path":"golang.org/x/sync/errgroup","span":{"start":[40,1],"end":[40,30],"bytes":[864,893]}},{"name":"semaphore","path":"golang.org/x/sync/semaphore","span":{"start":[41,1],"end":[41,31],"bytes":[894,924]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[43,1],"end":[43,37],"bytes":[926,962]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[44,1],"end":[44,46],"bytes":[963,1008]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient","kind":"struct","span":{"start":[87,1],"end":[127,2],"bytes":[2592,4007]},"callables":{"github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteAll":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteAll","kind":"method","span":{"start":[1282,1],"end":[1326,2],"bytes":[41353,42491]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1282,1],"end":[1282,63],"bytes":[41353,41415]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"1283:12":{"kind":"call","span":{"start":[1283,12],"end":[1283,23],"bytes":[41427,41438]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1290:15":{"kind":"call","span":{"start":[1290,15],"end":[1293,4],"bytes":[41563,41693]},"callee":null},"1291:11":{"kind":"call","span":{"start":[1291,11],"end":[1291,31],"bytes":[41632,41652]},"callee":null},"1292:11":{"kind":"call","span":{"start":[1292,11],"end":[1292,35],"bytes":[41664,41688]},"callee":null},"1296:6":{"kind":"call","span":{"start":[1296,6],"end":[1296,30],"bytes":[41730,41754]},"callee":null},"1297:16":{"kind":"call","span":{"start":[1297,16],"end":[1297,39],"bytes":[41772,41795]},"callee":null},"1299:11":{"kind":"call","span":{"start":[1299,11],"end":[1299,55],"bytes":[41824,41868]},"callee":null},"1304:13":{"kind":"call","span":{"start":[1304,13],"end":[1304,65],"bytes":[41956,42008]},"callee":null},"1309:6":{"kind":"call","span":{"start":[1309,6],"end":[1309,17],"bytes":[42066,42077]},"callee":null},"1310:12":{"kind":"call","span":{"start":[1310,12],"end":[1310,23],"bytes":[42095,42106]},"callee":null},"1310:8":{"kind":"call","span":{"start":[1310,8],"end":[1310,33],"bytes":[42091,42116]},"callee":null},"1312:15":{"kind":"call","span":{"start":[1312,15],"end":[1315,5],"bytes":[42132,42287]},"callee":null},"1313:12":{"kind":"call","span":{"start":[1313,12],"end":[1313,32],"bytes":[42191,42211]},"callee":null},"1314:54":{"kind":"call","span":{"start":[1314,54],"end":[1314,68],"bytes":[42266,42280]},"callee":null},"1317:11":{"kind":"call","span":{"start":[1317,11],"end":[1317,71],"bytes":[42316,42376]},"callee":null},"1318:20":{"kind":"call","span":{"start":[1318,20],"end":[1318,42],"bytes":[42396,42418]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.deleteOutputError"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[1206,1],"end":[1279,2],"bytes":[39234,41319]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1206,1],"end":[1206,87],"bytes":[39234,39320]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[1206,1],"end":[1206,87],"bytes":[39234,39320]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"1207:12":{"kind":"call","span":{"start":[1207,12],"end":[1207,23],"bytes":[39332,39343]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1211:5":{"kind":"call","span":{"start":[1211,5],"end":[1211,11],"bytes":[39379,39385]},"callee":null},"1216:12":{"kind":"call","span":{"start":[1216,12],"end":[1216,53],"bytes":[39466,39507]},"callee":null},"1216:46":{"kind":"call","span":{"start":[1216,46],"end":[1216,52],"bytes":[39500,39506]},"callee":null},"1218:15":{"kind":"call","span":{"start":[1218,15],"end":[1218,61],"bytes":[39548,39594]},"callee":null},"1219:25":{"kind":"call","span":{"start":[1219,25],"end":[1219,69],"bytes":[39619,39663]},"callee":null},"1220:12":{"kind":"call","span":{"start":[1220,12],"end":[1220,72],"bytes":[39675,39735]},"callee":null},"1220:55":{"kind":"call","span":{"start":[1220,55],"end":[1220,70],"bytes":[39718,39733]},"callee":null},"1222:3":{"kind":"call","span":{"start":[1222,3],"end":[1222,121],"bytes":[39739,39857]},"callee":null},"1226:6":{"kind":"call","span":{"start":[1226,6],"end":[1226,17],"bytes":[39889,39900]},"callee":null},"1227:12":{"kind":"call","span":{"start":[1227,12],"end":[1227,23],"bytes":[39918,39929]},"callee":null},"1227:8":{"kind":"call","span":{"start":[1227,8],"end":[1227,33],"bytes":[39914,39939]},"callee":null},"1229:3":{"kind":"call","span":{"start":[1229,3],"end":[1229,57],"bytes":[39943,39997]},"callee":null},"1231:12":{"kind":"call","span":{"start":[1231,12],"end":[1231,22],"bytes":[40010,40020]},"callee":null},"1232:15":{"kind":"call","span":{"start":[1232,15],"end":[1235,5],"bytes":[40035,40167]},"callee":null},"1233:12":{"kind":"call","span":{"start":[1233,12],"end":[1233,32],"bytes":[40094,40114]},"callee":null},"1236:15":{"kind":"call","span":{"start":[1236,15],"end":[1236,32],"bytes":[40182,40199]},"callee":null},"1237:3":{"kind":"call","span":{"start":[1237,3],"end":[1237,86],"bytes":[40202,40285]},"callee":null},"1237:95":{"kind":"call","span":{"start":[1237,95],"end":[1237,113],"bytes":[40294,40312]},"callee":null},"1240:11":{"kind":"call","span":{"start":[1240,11],"end":[1240,67],"bytes":[40343,40399]},"callee":null},"1245:14":{"kind":"call","span":{"start":[1245,14],"end":[1245,30],"bytes":[40451,40467]},"callee":null},"1247:3":{"kind":"call","span":{"start":[1247,3],"end":[1251,43],"bytes":[40474,40629]},"callee":null},"1250:14":{"kind":"call","span":{"start":[1250,14],"end":[1250,29],"bytes":[40570,40585]},"callee":null},"1251:19":{"kind":"call","span":{"start":[1251,19],"end":[1251,42],"bytes":[40605,40628]},"callee":null},"1252:3":{"kind":"call","span":{"start":[1252,3],"end":[1252,81],"bytes":[40632,40710]},"callee":null},"1252:86":{"kind":"call","span":{"start":[1252,86],"end":[1252,102],"bytes":[40715,40731]},"callee":null},"1254:6":{"kind":"call","span":{"start":[1254,6],"end":[1254,21],"bytes":[40739,40754]},"callee":null},"1256:13":{"kind":"call","span":{"start":[1256,13],"end":[1256,33],"bytes":[40807,40827]},"callee":null},"1257:5":{"kind":"call","span":{"start":[1257,5],"end":[1257,89],"bytes":[40832,40916]},"callee":null},"1260:6":{"kind":"call","span":{"start":[1260,6],"end":[1263,42],"bytes":[40944,41077]},"callee":null},"1261:14":{"kind":"call","span":{"start":[1261,14],"end":[1261,33],"bytes":[40995,41014]},"callee":null},"1263:18":{"kind":"call","span":{"start":[1263,18],"end":[1263,41],"bytes":[41053,41076]},"callee":null},"1266:7":{"kind":"call","span":{"start":[1266,7],"end":[1266,22],"bytes":[41095,41110]},"callee":null},"1267:5":{"kind":"call","span":{"start":[1267,5],"end":[1267,85],"bytes":[41121,41201]},"callee":null},"1267:67":{"kind":"call","span":{"start":[1267,67],"end":[1267,82],"bytes":[41183,41198]},"callee":null},"1271:13":{"kind":"call","span":{"start":[1271,13],"end":[1271,35],"bytes":[41224,41246]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.deleteOutputError"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.GenerationsV3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.GenerationsV3","kind":"method","span":{"start":[1329,1],"end":[1363,2],"bytes":[42566,43564]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.GenerationsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1329,1],"end":[1329,79],"bytes":[42566,42644]}}],"return_type":"([]string, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"1330:12":{"kind":"call","span":{"start":[1330,12],"end":[1330,23],"bytes":[42656,42667]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1334:12":{"kind":"call","span":{"start":[1334,12],"end":[1334,48],"bytes":[42715,42751]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.GenerationsPathV3"},"1337:15":{"kind":"call","span":{"start":[1337,15],"end":[1341,4],"bytes":[42833,42993]},"callee":null},"1338:14":{"kind":"call","span":{"start":[1338,14],"end":[1338,34],"bytes":[42905,42925]},"callee":null},"1339:14":{"kind":"call","span":{"start":[1339,14],"end":[1339,32],"bytes":[42940,42958]},"callee":null},"1340:14":{"kind":"call","span":{"start":[1340,14],"end":[1340,29],"bytes":[42973,42988]},"callee":null},"1344:6":{"kind":"call","span":{"start":[1344,6],"end":[1344,30],"bytes":[43026,43050]},"callee":null},"1345:16":{"kind":"call","span":{"start":[1345,16],"end":[1345,39],"bytes":[43068,43091]},"callee":null},"1347:16":{"kind":"call","span":{"start":[1347,16],"end":[1347,59],"bytes":[43125,43168]},"callee":null},"1352:9":{"kind":"call","span":{"start":[1352,9],"end":[1352,32],"bytes":[43324,43347]},"callee":null},"1353:8":{"kind":"call","span":{"start":[1353,8],"end":[1353,37],"bytes":[43355,43384]},"callee":null},"1354:8":{"kind":"call","span":{"start":[1354,8],"end":[1354,34],"bytes":[43392,43418]},"callee":null},"1355:7":{"kind":"call","span":{"start":[1355,7],"end":[1355,37],"bytes":[43425,43455]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.IsGenerationIDV3"},"1356:19":{"kind":"call","span":{"start":[1356,19],"end":[1356,41],"bytes":[43476,43498]},"callee":null},"1361:2":{"kind":"call","span":{"start":[1361,2],"end":[1361,26],"bytes":[43513,43537]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.Init":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","kind":"method","span":{"start":[352,1],"end":[504,2],"bytes":[10937,16109]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[352,1],"end":[352,64],"bytes":[10937,11000]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":15},"body":{"353:2":{"kind":"call","span":{"start":[353,2],"end":[353,13],"bytes":[11002,11013]},"callee":null},"354:8":{"kind":"call","span":{"start":[354,8],"end":[354,21],"bytes":[11021,11034]},"callee":null},"362:10":{"kind":"call","span":{"start":[362,10],"end":[362,51],"bytes":[11137,11178]},"callee":null},"366:12":{"kind":"call","span":{"start":[366,12],"end":[366,33],"bytes":[11225,11246]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.validateSSEConfig"},"376:21":{"kind":"call","span":{"start":[376,21],"end":[376,54],"bytes":[11525,11558]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.findBucketRegion"},"377:12":{"kind":"call","span":{"start":[377,12],"end":[377,66],"bytes":[11584,11638]},"callee":null},"414:3":{"kind":"call","span":{"start":[414,3],"end":[414,28],"bytes":[12719,12744]},"callee":null},"415:3":{"kind":"call","span":{"start":[415,3],"end":[415,42],"bytes":[12748,12787]},"callee":null},"419:15":{"kind":"call","span":{"start":[419,15],"end":[419,68],"bytes":[12847,12900]},"callee":null},"419:34":{"kind":"call","span":{"start":[419,34],"end":[419,67],"bytes":[12866,12899]},"callee":null},"429:16":{"kind":"call","span":{"start":[429,16],"end":[431,5],"bytes":[13328,13466]},"callee":null},"429:35":{"kind":"call","span":{"start":[429,35],"end":[431,4],"bytes":[13347,13465]},"callee":null},"430:4":{"kind":"call","span":{"start":[430,4],"end":[430,82],"bytes":[13382,13460]},"callee":null},"438:16":{"kind":"call","span":{"start":[438,16],"end":[438,33],"bytes":[13795,13812]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.parseS3DebugEnv"},"439:16":{"kind":"call","span":{"start":[439,16],"end":[439,69],"bytes":[13844,13897]},"callee":null},"439:35":{"kind":"call","span":{"start":[439,35],"end":[439,68],"bytes":[13863,13896]},"callee":null},"443:14":{"kind":"call","span":{"start":[443,14],"end":[443,58],"bytes":[13942,13986]},"callee":null},"445:10":{"kind":"call","span":{"start":[445,10],"end":[445,59],"bytes":[14013,14062]},"callee":null},"465:12":{"kind":"call","span":{"start":[465,12],"end":[468,5],"bytes":[14779,14974]},"callee":null},"472:2":{"kind":"call","span":{"start":[472,2],"end":[472,30],"bytes":[15017,15045]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint"},"475:9":{"kind":"call","span":{"start":[475,9],"end":[475,41],"bytes":[15076,15108]},"callee":null},"486:18":{"kind":"call","span":{"start":[486,18],"end":[488,5],"bytes":[15650,15780]},"callee":null},"492:18":{"kind":"call","span":{"start":[492,18],"end":[494,5],"bytes":[15823,15903]},"callee":null},"497:18":{"kind":"call","span":{"start":[497,18],"end":[499,5],"bytes":[15948,16034]},"callee":null},"501:15":{"kind":"call","span":{"start":[501,15],"end":[501,57],"bytes":[16052,16094]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init/github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_1","kind":"function","span":{"start":[450,1],"end":[455,5],"bytes":[14128,14325]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_1","parameters":[{"name":"o","type":"*github.com/aws/aws-sdk-go-v2/service/s3.Options","span":{"start":[450,1],"end":[450,24],"bytes":[14128,14151]}}],"body":{"454:19":{"kind":"call","span":{"start":[454,19],"end":[454,61],"bytes":[14278,14320]},"callee":null},"454:40":{"kind":"call","span":{"start":[454,40],"end":[454,60],"bytes":[14299,14319]},"callee":null}}},"closure_2":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init/github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_2","kind":"function","span":{"start":[465,1],"end":[468,5],"bytes":[14768,14974]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_2","parameters":[{"name":"o","type":"*github.com/aws/aws-sdk-go-v2/service/s3.Options","span":{"start":[465,1],"end":[465,48],"bytes":[14768,14815]}}],"body":{}},"closure_3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init/github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_3","kind":"function","span":{"start":[486,1],"end":[488,5],"bytes":[15633,15780]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_3","parameters":[{"name":"u","type":"*github.com/aws/aws-sdk-go-v2/feature/s3/manager.Uploader","span":{"start":[486,1],"end":[486,66],"bytes":[15633,15698]}}],"body":{}},"closure_4":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init/github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_4","kind":"function","span":{"start":[492,1],"end":[494,5],"bytes":[15806,15903]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_4","parameters":[{"name":"u","type":"*github.com/aws/aws-sdk-go-v2/feature/s3/manager.Uploader","span":{"start":[492,1],"end":[492,66],"bytes":[15806,15871]}}],"body":{}},"closure_5":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init/github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_5","kind":"function","span":{"start":[497,1],"end":[499,5],"bytes":[15931,16034]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Init.closure_5","parameters":[{"name":"u","type":"*github.com/aws/aws-sdk-go-v2/feature/s3/manager.Uploader","span":{"start":[497,1],"end":[497,66],"bytes":[15931,15996]}}],"body":{}}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.LTXFiles":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.LTXFiles","kind":"method","span":{"start":[665,1],"end":[670,2],"bytes":[21965,22213]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[665,1],"end":[665,126],"bytes":[21965,22090]}},{"name":"level","type":"int","span":{"start":[665,1],"end":[665,126],"bytes":[21965,22090]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[665,1],"end":[665,126],"bytes":[21965,22090]}},{"name":"useMetadata","type":"bool","span":{"start":[665,1],"end":[665,126],"bytes":[21965,22090]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"666:12":{"kind":"call","span":{"start":[666,12],"end":[666,23],"bytes":[22102,22113]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"669:9":{"kind":"call","span":{"start":[669,9],"end":[669,58],"bytes":[22157,22206]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.newFileIterator"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[674,1],"end":[712,2],"bytes":[22327,23541]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[674,1],"end":[674,140],"bytes":[22327,22466]}},{"name":"level","type":"int","span":{"start":[674,1],"end":[674,140],"bytes":[22327,22466]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[674,1],"end":[674,140],"bytes":[22327,22466]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[674,1],"end":[674,140],"bytes":[22327,22466]}},{"name":"offset","type":"int64","span":{"start":[674,1],"end":[674,140],"bytes":[22327,22466]}},{"name":"size","type":"int64","span":{"start":[674,1],"end":[674,140],"bytes":[22327,22466]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"675:12":{"kind":"call","span":{"start":[675,12],"end":[675,23],"bytes":[22478,22489]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"681:14":{"kind":"call","span":{"start":[681,14],"end":[681,63],"bytes":[22575,22624]},"callee":null},"683:14":{"kind":"call","span":{"start":[683,14],"end":[683,46],"bytes":[22648,22680]},"callee":null},"687:14":{"kind":"call","span":{"start":[687,14],"end":[687,50],"bytes":[22735,22771]},"callee":null},"688:24":{"kind":"call","span":{"start":[688,24],"end":[688,63],"bytes":[22795,22834]},"callee":null},"691:11":{"kind":"call","span":{"start":[691,11],"end":[691,31],"bytes":[22876,22896]},"callee":null},"692:11":{"kind":"call","span":{"start":[692,11],"end":[692,26],"bytes":[22908,22923]},"callee":null},"693:11":{"kind":"call","span":{"start":[693,11],"end":[693,31],"bytes":[22935,22955]},"callee":null},"699:32":{"kind":"call","span":{"start":[699,32],"end":[699,66],"bytes":[23187,23221]},"callee":null},"700:26":{"kind":"call","span":{"start":[700,26],"end":[700,54],"bytes":[23247,23275]},"callee":null},"701:29":{"kind":"call","span":{"start":[701,29],"end":[701,60],"bytes":[23304,23335]},"callee":null},"704:14":{"kind":"call","span":{"start":[704,14],"end":[704,40],"bytes":[23353,23379]},"callee":null},"706:6":{"kind":"call","span":{"start":[706,6],"end":[706,22],"bytes":[23402,23418]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists"},"709:15":{"kind":"call","span":{"start":[709,15],"end":[709,60],"bytes":[23469,23514]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3","kind":"method","span":{"start":[1455,1],"end":[1483,2],"bytes":[46180,46988]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1455,1],"end":[1455,115],"bytes":[46180,46294]}},{"name":"generation","type":"string","span":{"start":[1455,1],"end":[1455,115],"bytes":[46180,46294]}},{"name":"index","type":"int","span":{"start":[1455,1],"end":[1455,115],"bytes":[46180,46294]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"1456:12":{"kind":"call","span":{"start":[1456,12],"end":[1456,23],"bytes":[46306,46317]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1460:9":{"kind":"call","span":{"start":[1460,9],"end":[1460,61],"bytes":[46362,46414]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotPathV3"},"1463:11":{"kind":"call","span":{"start":[1463,11],"end":[1463,31],"bytes":[46456,46476]},"callee":null},"1464:11":{"kind":"call","span":{"start":[1464,11],"end":[1464,26],"bytes":[46488,46503]},"callee":null},"1469:32":{"kind":"call","span":{"start":[1469,32],"end":[1469,66],"bytes":[46608,46642]},"callee":null},"1470:26":{"kind":"call","span":{"start":[1470,26],"end":[1470,54],"bytes":[46668,46696]},"callee":null},"1471:29":{"kind":"call","span":{"start":[1471,29],"end":[1471,60],"bytes":[46725,46756]},"callee":null},"1474:14":{"kind":"call","span":{"start":[1474,14],"end":[1474,40],"bytes":[46774,46800]},"callee":null},"1476:6":{"kind":"call","span":{"start":[1476,6],"end":[1476,22],"bytes":[46823,46839]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists"},"1479:15":{"kind":"call","span":{"start":[1479,15],"end":[1479,62],"bytes":[46890,46937]},"callee":null},"1482:9":{"kind":"call","span":{"start":[1482,9],"end":[1482,40],"bytes":[46950,46981]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3","kind":"method","span":{"start":[1487,1],"end":[1515,2],"bytes":[47110,47947]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1487,1],"end":[1487,131],"bytes":[47110,47240]}},{"name":"generation","type":"string","span":{"start":[1487,1],"end":[1487,131],"bytes":[47110,47240]}},{"name":"index","type":"int","span":{"start":[1487,1],"end":[1487,131],"bytes":[47110,47240]}},{"name":"offset","type":"int64","span":{"start":[1487,1],"end":[1487,131],"bytes":[47110,47240]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"1488:12":{"kind":"call","span":{"start":[1488,12],"end":[1488,23],"bytes":[47252,47263]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1492:9":{"kind":"call","span":{"start":[1492,9],"end":[1492,71],"bytes":[47308,47370]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentPathV3"},"1495:11":{"kind":"call","span":{"start":[1495,11],"end":[1495,31],"bytes":[47412,47432]},"callee":null},"1496:11":{"kind":"call","span":{"start":[1496,11],"end":[1496,26],"bytes":[47444,47459]},"callee":null},"1501:32":{"kind":"call","span":{"start":[1501,32],"end":[1501,66],"bytes":[47564,47598]},"callee":null},"1502:26":{"kind":"call","span":{"start":[1502,26],"end":[1502,54],"bytes":[47624,47652]},"callee":null},"1503:29":{"kind":"call","span":{"start":[1503,29],"end":[1503,60],"bytes":[47681,47712]},"callee":null},"1506:14":{"kind":"call","span":{"start":[1506,14],"end":[1506,40],"bytes":[47730,47756]},"callee":null},"1508:6":{"kind":"call","span":{"start":[1508,6],"end":[1508,22],"bytes":[47779,47795]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists"},"1511:15":{"kind":"call","span":{"start":[1511,15],"end":[1511,65],"bytes":[47846,47896]},"callee":null},"1514:9":{"kind":"call","span":{"start":[1514,9],"end":[1514,40],"bytes":[47909,47940]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.SetLogger":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.SetLogger","kind":"method","span":{"start":[139,1],"end":[141,2],"bytes":[4292,4398]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[139,1],"end":[139,57],"bytes":[4292,4348]}}],"metrics":{"cyclomatic":1},"body":{"140:13":{"kind":"call","span":{"start":[140,13],"end":[140,48],"bytes":[4361,4396]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3","kind":"method","span":{"start":[1366,1],"end":[1405,2],"bytes":[43634,44751]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1366,1],"end":[1366,115],"bytes":[43634,43748]}},{"name":"generation","type":"string","span":{"start":[1366,1],"end":[1366,115],"bytes":[43634,43748]}}],"return_type":"([]github.com/benbjohnson/litestream.SnapshotInfoV3, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"1367:12":{"kind":"call","span":{"start":[1367,12],"end":[1367,23],"bytes":[43760,43771]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1371:12":{"kind":"call","span":{"start":[1371,12],"end":[1371,58],"bytes":[43819,43865]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotsPathV3"},"1373:15":{"kind":"call","span":{"start":[1373,15],"end":[1376,4],"bytes":[43887,44011]},"callee":null},"1374:11":{"kind":"call","span":{"start":[1374,11],"end":[1374,31],"bytes":[43956,43976]},"callee":null},"1375:11":{"kind":"call","span":{"start":[1375,11],"end":[1375,29],"bytes":[43988,44006]},"callee":null},"1379:6":{"kind":"call","span":{"start":[1379,6],"end":[1379,30],"bytes":[44061,44085]},"callee":null},"1380:16":{"kind":"call","span":{"start":[1380,16],"end":[1380,39],"bytes":[44103,44126]},"callee":null},"1382:16":{"kind":"call","span":{"start":[1382,16],"end":[1382,57],"bytes":[44160,44201]},"callee":null},"1386:11":{"kind":"call","span":{"start":[1386,11],"end":[1386,43],"bytes":[44255,44287]},"callee":null},"1386:21":{"kind":"call","span":{"start":[1386,21],"end":[1386,42],"bytes":[44265,44286]},"callee":null},"1387:18":{"kind":"call","span":{"start":[1387,18],"end":[1387,57],"bytes":[44305,44344]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseSnapshotFilenameV3"},"1392:16":{"kind":"call","span":{"start":[1392,16],"end":[1397,6],"bytes":[44424,44616]},"callee":null},"1395:17":{"kind":"call","span":{"start":[1395,17],"end":[1395,38],"bytes":[44536,44557]},"callee":null},"1396:17":{"kind":"call","span":{"start":[1396,17],"end":[1396,45],"bytes":[44575,44603]},"callee":null},"1401:2":{"kind":"call","span":{"start":[1401,2],"end":[1403,4],"bytes":[44626,44726]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3/github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3.closure_1","kind":"function","span":{"start":[1401,1],"end":[1403,4],"bytes":[44625,44726]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3.closure_1","parameters":[{"name":"a","type":"github.com/benbjohnson/litestream.SnapshotInfoV3","span":{"start":[1401,1],"end":[1401,71],"bytes":[44625,44695]}},{"name":"b","type":"github.com/benbjohnson/litestream.SnapshotInfoV3","span":{"start":[1401,1],"end":[1401,71],"bytes":[44625,44695]}}],"return_type":"int","body":{}}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.Type":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Type","kind":"method","span":{"start":[347,1],"end":[349,2],"bytes":[10796,10863]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3","kind":"method","span":{"start":[1408,1],"end":[1451,2],"bytes":[44838,46063]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1408,1],"end":[1408,119],"bytes":[44838,44956]}},{"name":"generation","type":"string","span":{"start":[1408,1],"end":[1408,119],"bytes":[44838,44956]}}],"return_type":"([]github.com/benbjohnson/litestream.WALSegmentInfoV3, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"1409:12":{"kind":"call","span":{"start":[1409,12],"end":[1409,23],"bytes":[44968,44979]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"1413:12":{"kind":"call","span":{"start":[1413,12],"end":[1413,52],"bytes":[45027,45067]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALPathV3"},"1415:15":{"kind":"call","span":{"start":[1415,15],"end":[1418,4],"bytes":[45089,45213]},"callee":null},"1416:11":{"kind":"call","span":{"start":[1416,11],"end":[1416,31],"bytes":[45158,45178]},"callee":null},"1417:11":{"kind":"call","span":{"start":[1417,11],"end":[1417,29],"bytes":[45190,45208]},"callee":null},"1421:6":{"kind":"call","span":{"start":[1421,6],"end":[1421,30],"bytes":[45264,45288]},"callee":null},"1422:16":{"kind":"call","span":{"start":[1422,16],"end":[1422,39],"bytes":[45306,45329]},"callee":null},"1424:16":{"kind":"call","span":{"start":[1424,16],"end":[1424,60],"bytes":[45363,45407]},"callee":null},"1428:11":{"kind":"call","span":{"start":[1428,11],"end":[1428,43],"bytes":[45461,45493]},"callee":null},"1428:21":{"kind":"call","span":{"start":[1428,21],"end":[1428,42],"bytes":[45471,45492]},"callee":null},"1429:26":{"kind":"call","span":{"start":[1429,26],"end":[1429,67],"bytes":[45519,45560]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3"},"1434:15":{"kind":"call","span":{"start":[1434,15],"end":[1440,6],"bytes":[45639,45856]},"callee":null},"1438:17":{"kind":"call","span":{"start":[1438,17],"end":[1438,38],"bytes":[45776,45797]},"callee":null},"1439:17":{"kind":"call","span":{"start":[1439,17],"end":[1439,45],"bytes":[45815,45843]},"callee":null},"1444:2":{"kind":"call","span":{"start":[1444,2],"end":[1449,4],"bytes":[45866,46039]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3/github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3.closure_1","kind":"function","span":{"start":[1444,1],"end":[1449,4],"bytes":[45865,46039]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3.closure_1","parameters":[{"name":"a","type":"github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[1444,1],"end":[1444,72],"bytes":[45865,45936]}},{"name":"b","type":"github.com/benbjohnson/litestream.WALSegmentInfoV3","span":{"start":[1444,1],"end":[1444,72],"bytes":[45865,45936]}}],"return_type":"int","body":{"1448:10":{"kind":"call","span":{"start":[1448,10],"end":[1448,41],"bytes":[46004,46035]},"callee":null}}}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[718,1],"end":[758,2],"bytes":[23853,25147]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[718,1],"end":[718,134],"bytes":[23853,23986]}},{"name":"level","type":"int","span":{"start":[718,1],"end":[718,134],"bytes":[23853,23986]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[718,1],"end":[718,134],"bytes":[23853,23986]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[718,1],"end":[718,134],"bytes":[23853,23986]}},{"name":"r","type":"io.Reader","span":{"start":[718,1],"end":[718,134],"bytes":[23853,23986]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"719:12":{"kind":"call","span":{"start":[719,12],"end":[719,23],"bytes":[23998,24009]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init"},"723:14":{"kind":"call","span":{"start":[723,14],"end":[723,50],"bytes":[24059,24095]},"callee":null},"724:24":{"kind":"call","span":{"start":[724,24],"end":[724,63],"bytes":[24119,24158]},"callee":null},"726:14":{"kind":"call","span":{"start":[726,14],"end":[726,50],"bytes":[24173,24209]},"callee":null},"735:15":{"kind":"call","span":{"start":[735,15],"end":[735,114],"bytes":[24496,24595]},"callee":null},"738:32":{"kind":"call","span":{"start":[738,32],"end":[738,66],"bytes":[24631,24665]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX"},"743:2":{"kind":"call","span":{"start":[743,2],"end":[743,77],"bytes":[24706,24781]},"callee":null},"744:2":{"kind":"call","span":{"start":[744,2],"end":[744,77],"bytes":[24789,24864]},"callee":null},"744:82":{"kind":"call","span":{"start":[744,82],"end":[744,95],"bytes":[24869,24882]},"callee":null},"748:15":{"kind":"call","span":{"start":[748,15],"end":[748,64],"bytes":[24954,25003]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint","kind":"method","span":{"start":[507,1],"end":[525,2],"bytes":[16199,16763]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint","parameters":[{"name":"opts","type":"*[]func(*github.com/aws/aws-sdk-go-v2/service/s3.Options)","span":{"start":[507,1],"end":[507,71],"bytes":[16199,16269]}}],"metrics":{"cyclomatic":4},"body":{"509:11":{"kind":"call","span":{"start":[509,11],"end":[523,5],"bytes":[16303,16758]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint.closure_1","kind":"function","span":{"start":[509,1],"end":[523,5],"bytes":[16293,16758]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint.closure_1","parameters":[{"name":"o","type":"*github.com/aws/aws-sdk-go-v2/service/s3.Options","span":{"start":[509,1],"end":[509,46],"bytes":[16293,16338]}}],"body":{"514:51":{"kind":"call","span":{"start":[514,51],"end":[514,90],"bytes":[16485,16524]},"callee":null},"514:8":{"kind":"call","span":{"start":[514,8],"end":[514,46],"bytes":[16442,16480]},"callee":null},"518:21":{"kind":"call","span":{"start":[518,21],"end":[518,41],"bytes":[16590,16610]},"callee":null},"520:7":{"kind":"call","span":{"start":[520,7],"end":[520,45],"bytes":[16666,16704]},"callee":null}}}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.findBucketRegion":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.findBucketRegion","kind":"method","span":{"start":[615,1],"end":[658,2],"bytes":[20313,21577]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.findBucketRegion","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[615,1],"end":[615,95],"bytes":[20313,20407]}},{"name":"bucket","type":"string","span":{"start":[615,1],"end":[615,95],"bytes":[20313,20407]}}],"return_type":"(string, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"618:3":{"kind":"call","span":{"start":[618,3],"end":[618,42],"bytes":[20510,20549]},"callee":null},"623:16":{"kind":"call","span":{"start":[623,16],"end":[625,5],"bytes":[20662,20800]},"callee":null},"623:35":{"kind":"call","span":{"start":[623,35],"end":[625,4],"bytes":[20681,20799]},"callee":null},"624:4":{"kind":"call","span":{"start":[624,4],"end":[624,82],"bytes":[20716,20794]},"callee":null},"629:14":{"kind":"call","span":{"start":[629,14],"end":[629,58],"bytes":[20845,20889]},"callee":null},"631:14":{"kind":"call","span":{"start":[631,14],"end":[631,81],"bytes":[20920,20987]},"callee":null},"641:2":{"kind":"call","span":{"start":[641,2],"end":[641,30],"bytes":[21182,21210]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint"},"643:12":{"kind":"call","span":{"start":[643,12],"end":[643,44],"bytes":[21223,21255]},"callee":null},"646:14":{"kind":"call","span":{"start":[646,14],"end":[648,4],"bytes":[21294,21385]},"callee":null},"647:11":{"kind":"call","span":{"start":[647,11],"end":[647,29],"bytes":[21362,21380]},"callee":null},"657:9":{"kind":"call","span":{"start":[657,9],"end":[657,39],"bytes":[21540,21570]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption","kind":"method","span":{"start":[887,1],"end":[1075,2],"bytes":[30071,36313]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption","parameters":[],"return_type":"func(*github.com/aws/smithy-go/middleware.Stack) error","metrics":{"cyclomatic":30},"body":{},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1","kind":"function","span":{"start":[888,1],"end":[1074,3],"bytes":[30146,36311]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1","parameters":[{"name":"stack","type":"*github.com/aws/smithy-go/middleware.Stack","span":{"start":[888,1],"end":[888,46],"bytes":[30146,30191]}}],"return_type":"error","error_channel":["error"],"body":{"1006:4":{"kind":"call","span":{"start":[1006,4],"end":[1006,52],"bytes":[34113,34161]},"callee":null},"1011:13":{"kind":"call","span":{"start":[1011,13],"end":[1028,4],"bytes":[34302,34885]},"callee":null},"1012:4":{"kind":"call","span":{"start":[1012,4],"end":[1026,5],"bytes":[34325,34859]},"callee":null},"1067:13":{"kind":"call","span":{"start":[1067,13],"end":[1067,113],"bytes":[36075,36175]},"callee":null},"1067:35":{"kind":"call","span":{"start":[1067,35],"end":[1067,50],"bytes":[36097,36112]},"callee":null},"1068:14":{"kind":"call","span":{"start":[1068,14],"end":[1068,67],"bytes":[36203,36256]},"callee":null},"1068:33":{"kind":"call","span":{"start":[1068,33],"end":[1068,48],"bytes":[36222,36237]},"callee":null},"890:14":{"kind":"call","span":{"start":[890,14],"end":[917,5],"bytes":[30232,31162]},"callee":null},"891:5":{"kind":"call","span":{"start":[891,5],"end":[915,6],"bytes":[30257,31133]},"callee":null},"922:13":{"kind":"call","span":{"start":[922,13],"end":[940,4],"bytes":[31214,31850]},"callee":null},"923:4":{"kind":"call","span":{"start":[923,4],"end":[938,5],"bytes":[31234,31824]},"callee":null},"944:6":{"kind":"call","span":{"start":[944,6],"end":[944,45],"bytes":[31889,31928]},"callee":null},"945:14":{"kind":"call","span":{"start":[945,14],"end":[958,5],"bytes":[31944,32405]},"callee":null},"946:5":{"kind":"call","span":{"start":[946,5],"end":[956,6],"bytes":[31965,32377]},"callee":null},"963:31":{"kind":"call","span":{"start":[963,31],"end":[963,82],"bytes":[32475,32526]},"callee":null},"964:14":{"kind":"call","span":{"start":[964,14],"end":[978,5],"bytes":[32542,33032]},"callee":null},"965:5":{"kind":"call","span":{"start":[965,5],"end":[975,6],"bytes":[32569,32988]},"callee":null},"988:8":{"kind":"call","span":{"start":[988,8],"end":[988,54],"bytes":[33334,33380]},"callee":null},"989:14":{"kind":"call","span":{"start":[989,14],"end":[989,52],"bytes":[33394,33432]},"callee":null},"992:8":{"kind":"call","span":{"start":[992,8],"end":[992,53],"bytes":[33474,33519]},"callee":null},"993:14":{"kind":"call","span":{"start":[993,14],"end":[993,56],"bytes":[33533,33575]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_1","kind":"function","span":{"start":[893,1],"end":[914,8],"bytes":[30335,31127]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[893,1],"end":[893,97],"bytes":[30335,30431]}},{"name":"in","type":"github.com/aws/smithy-go/middleware.SerializeInput","span":{"start":[893,1],"end":[893,97],"bytes":[30335,30431]}},{"name":"next","type":"github.com/aws/smithy-go/middleware.SerializeHandler","span":{"start":[893,1],"end":[893,97],"bytes":[30335,30431]}}],"return_type":"(github.com/aws/smithy-go/middleware.SerializeOutput, github.com/aws/smithy-go/middleware.Metadata, error)","error_channel":["error"],"body":{"896:10":{"kind":"call","span":{"start":[896,10],"end":[896,42],"bytes":[30529,30561]},"callee":null},"897:15":{"kind":"call","span":{"start":[897,15],"end":[897,44],"bytes":[30597,30626]},"callee":null},"901:56":{"kind":"call","span":{"start":[901,56],"end":[901,81],"bytes":[30749,30774]},"callee":null},"902:15":{"kind":"call","span":{"start":[902,15],"end":[902,44],"bytes":[30796,30825]},"callee":null},"905:24":{"kind":"call","span":{"start":[905,24],"end":[905,68],"bytes":[30858,30902]},"callee":null},"910:14":{"kind":"call","span":{"start":[910,14],"end":[910,76],"bytes":[31005,31067]},"callee":null},"913:14":{"kind":"call","span":{"start":[913,14],"end":[913,43],"bytes":[31090,31119]},"callee":null}}},"closure_2":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_2","kind":"function","span":{"start":[925,1],"end":[937,7],"bytes":[31293,31819]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_2","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[925,1],"end":[925,88],"bytes":[31293,31380]}},{"name":"in","type":"github.com/aws/smithy-go/middleware.BuildInput","span":{"start":[925,1],"end":[925,88],"bytes":[31293,31380]}},{"name":"next","type":"github.com/aws/smithy-go/middleware.BuildHandler","span":{"start":[925,1],"end":[925,88],"bytes":[31293,31380]}}],"return_type":"(github.com/aws/smithy-go/middleware.BuildOutput, github.com/aws/smithy-go/middleware.Metadata, error)","error_channel":["error"],"body":{"929:18":{"kind":"call","span":{"start":[929,18],"end":[929,46],"bytes":[31538,31566]},"callee":null},"931:8":{"kind":"call","span":{"start":[931,8],"end":[931,50],"bytes":[31599,31641]},"callee":null},"932:18":{"kind":"call","span":{"start":[932,18],"end":[932,57],"bytes":[31659,31698]},"callee":null},"933:8":{"kind":"call","span":{"start":[933,8],"end":[933,59],"bytes":[31708,31759]},"callee":null},"936:13":{"kind":"call","span":{"start":[936,13],"end":[936,38],"bytes":[31787,31812]},"callee":null}}},"closure_3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_3","kind":"function","span":{"start":[948,1],"end":[955,8],"bytes":[32032,32371]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[948,1],"end":[948,89],"bytes":[32032,32120]}},{"name":"in","type":"github.com/aws/smithy-go/middleware.BuildInput","span":{"start":[948,1],"end":[948,89],"bytes":[32032,32120]}},{"name":"next","type":"github.com/aws/smithy-go/middleware.BuildHandler","span":{"start":[948,1],"end":[948,89],"bytes":[32032,32120]}}],"return_type":"(github.com/aws/smithy-go/middleware.BuildOutput, github.com/aws/smithy-go/middleware.Metadata, error)","error_channel":["error"],"body":{"952:8":{"kind":"call","span":{"start":[952,8],"end":[952,53],"bytes":[32271,32316]},"callee":null},"954:14":{"kind":"call","span":{"start":[954,14],"end":[954,39],"bytes":[32338,32363]},"callee":null}}},"closure_4":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_4","kind":"function","span":{"start":[967,1],"end":[974,8],"bytes":[32643,32982]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_4","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[967,1],"end":[967,95],"bytes":[32643,32737]}},{"name":"in","type":"github.com/aws/smithy-go/middleware.FinalizeInput","span":{"start":[967,1],"end":[967,95],"bytes":[32643,32737]}},{"name":"next","type":"github.com/aws/smithy-go/middleware.FinalizeHandler","span":{"start":[967,1],"end":[967,95],"bytes":[32643,32737]}}],"return_type":"(github.com/aws/smithy-go/middleware.FinalizeOutput, github.com/aws/smithy-go/middleware.Metadata, error)","error_channel":["error"],"body":{"971:8":{"kind":"call","span":{"start":[971,8],"end":[971,41],"bytes":[32891,32924]},"callee":null},"973:14":{"kind":"call","span":{"start":[973,14],"end":[973,42],"bytes":[32946,32974]},"callee":null}}},"closure_5":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_5","kind":"function","span":{"start":[1014,1],"end":[1025,7],"bytes":[34390,34854]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_5","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1014,1],"end":[1014,94],"bytes":[34390,34483]}},{"name":"in","type":"github.com/aws/smithy-go/middleware.FinalizeInput","span":{"start":[1014,1],"end":[1014,94],"bytes":[34390,34483]}},{"name":"next","type":"github.com/aws/smithy-go/middleware.FinalizeHandler","span":{"start":[1014,1],"end":[1014,94],"bytes":[34390,34483]}}],"return_type":"(github.com/aws/smithy-go/middleware.FinalizeOutput, github.com/aws/smithy-go/middleware.Metadata, error)","error_channel":["error"],"body":{"1018:7":{"kind":"call","span":{"start":[1018,7],"end":[1022,8],"bytes":[34633,34799]},"callee":null},"1020:15":{"kind":"call","span":{"start":[1020,15],"end":[1020,31],"bytes":[34705,34721]},"callee":null},"1021:31":{"kind":"call","span":{"start":[1021,31],"end":[1021,68],"bytes":[34753,34790]},"callee":null},"1024:13":{"kind":"call","span":{"start":[1024,13],"end":[1024,41],"bytes":[34819,34847]},"callee":null}}},"closure_6":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_6","kind":"function","span":{"start":[1036,1],"end":[1062,4],"bytes":[34966,35818]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_6","parameters":[],"return_type":"github.com/aws/smithy-go/middleware.FinalizeMiddleware","body":{"1037:11":{"kind":"call","span":{"start":[1037,11],"end":[1061,5],"bytes":[35034,35814]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_6/github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_6.closure_1","kind":"function","span":{"start":[1039,1],"end":[1060,7],"bytes":[35103,35809]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.middlewareOption.closure_1.closure_6.closure_1","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1039,1],"end":[1039,94],"bytes":[35103,35196]}},{"name":"in","type":"github.com/aws/smithy-go/middleware.FinalizeInput","span":{"start":[1039,1],"end":[1039,94],"bytes":[35103,35196]}},{"name":"next","type":"github.com/aws/smithy-go/middleware.FinalizeHandler","span":{"start":[1039,1],"end":[1039,94],"bytes":[35103,35196]}}],"return_type":"(github.com/aws/smithy-go/middleware.FinalizeOutput, github.com/aws/smithy-go/middleware.Metadata, error)","error_channel":["error"],"body":{"1042:9":{"kind":"call","span":{"start":[1042,9],"end":[1042,41],"bytes":[35290,35322]},"callee":null},"1043:14":{"kind":"call","span":{"start":[1043,14],"end":[1043,42],"bytes":[35357,35385]},"callee":null},"1046:21":{"kind":"call","span":{"start":[1046,21],"end":[1046,72],"bytes":[35414,35465]},"callee":null},"1048:14":{"kind":"call","span":{"start":[1048,14],"end":[1048,42],"bytes":[35513,35541]},"callee":null},"1053:14":{"kind":"call","span":{"start":[1053,14],"end":[1053,42],"bytes":[35626,35654]},"callee":null},"1055:9":{"kind":"call","span":{"start":[1055,9],"end":[1055,38],"bytes":[35670,35699]},"callee":null},"1056:7":{"kind":"call","span":{"start":[1056,7],"end":[1056,46],"bytes":[35714,35753]},"callee":null},"1059:13":{"kind":"call","span":{"start":[1059,13],"end":[1059,41],"bytes":[35774,35802]},"callee":null}}}}}}}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput","kind":"method","span":{"start":[858,1],"end":[885,2],"bytes":[29179,30069]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput","parameters":[{"name":"key","type":"string","span":{"start":[858,1],"end":[858,93],"bytes":[29179,29271]}},{"name":"timestamp","type":"time.Time","span":{"start":[858,1],"end":[858,93],"bytes":[29179,29271]}}],"return_type":"*github.com/aws/aws-sdk-go-v2/service/s3.PutObjectInput","metrics":{"cyclomatic":4},"body":{"860:11":{"kind":"call","span":{"start":[860,11],"end":[860,31],"bytes":[29312,29332]},"callee":null},"861:11":{"kind":"call","span":{"start":[861,11],"end":[861,26],"bytes":[29344,29359]},"callee":null},"864:26":{"kind":"call","span":{"start":[864,26],"end":[864,60],"bytes":[29486,29520]},"callee":null},"868:24":{"kind":"call","span":{"start":[868,24],"end":[868,58],"bytes":[29580,29614]},"callee":null},"873:32":{"kind":"call","span":{"start":[873,32],"end":[873,66],"bytes":[29718,29752]},"callee":null},"874:26":{"kind":"call","span":{"start":[874,26],"end":[874,54],"bytes":[29778,29806]},"callee":null},"875:29":{"kind":"call","span":{"start":[875,29],"end":[875,60],"bytes":[29835,29866]},"callee":null},"881:23":{"kind":"call","span":{"start":[881,23],"end":[881,48],"bytes":[30024,30049]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX","kind":"method","span":{"start":[762,1],"end":[773,2],"bytes":[25286,25901]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[762,1],"end":[762,133],"bytes":[25286,25418]}},{"name":"key","type":"string","span":{"start":[762,1],"end":[762,133],"bytes":[25286,25418]}},{"name":"r","type":"io.Reader","span":{"start":[762,1],"end":[762,133],"bytes":[25286,25418]}},{"name":"partSize","type":"int64","span":{"start":[762,1],"end":[762,133],"bytes":[25286,25418]}}],"return_type":"(int64, time.Time, *string, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"766:20":{"kind":"call","span":{"start":[766,20],"end":[766,46],"bytes":[25610,25636]},"callee":null},"767:11":{"kind":"call","span":{"start":[767,11],"end":[767,58],"bytes":[25661,25708]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadSizedLTX"},"772:9":{"kind":"call","span":{"start":[772,9],"end":[772,51],"bytes":[25857,25899]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.uploadSizedLTX":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadSizedLTX","kind":"method","span":{"start":[776,1],"end":[816,2],"bytes":[25973,27460]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.uploadSizedLTX","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[776,1],"end":[776,150],"bytes":[25973,26122]}},{"name":"key","type":"string","span":{"start":[776,1],"end":[776,150],"bytes":[25973,26122]}},{"name":"rs","type":"io.ReadSeeker","span":{"start":[776,1],"end":[776,150],"bytes":[25973,26122]}},{"name":"start","type":"int64","span":{"start":[776,1],"end":[776,150],"bytes":[25973,26122]}},{"name":"partSize","type":"int64","span":{"start":[776,1],"end":[776,150],"bytes":[25973,26122]}}],"return_type":"(int64, time.Time, *string, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"777:14":{"kind":"call","span":{"start":[777,14],"end":[777,36],"bytes":[26136,26158]},"callee":null},"779:31":{"kind":"call","span":{"start":[779,31],"end":[779,79],"bytes":[26206,26254]},"callee":null},"782:15":{"kind":"call","span":{"start":[782,15],"end":[782,43],"bytes":[26293,26321]},"callee":null},"783:31":{"kind":"call","span":{"start":[783,31],"end":[783,81],"bytes":[26366,26416]},"callee":null},"788:17":{"kind":"call","span":{"start":[788,17],"end":[788,35],"bytes":[26526,26544]},"callee":null},"790:31":{"kind":"call","span":{"start":[790,31],"end":[790,87],"bytes":[26592,26648]},"callee":null},"792:15":{"kind":"call","span":{"start":[792,15],"end":[792,44],"bytes":[26666,26695]},"callee":null},"793:15":{"kind":"call","span":{"start":[793,15],"end":[793,43],"bytes":[26716,26744]},"callee":null},"794:31":{"kind":"call","span":{"start":[794,31],"end":[794,81],"bytes":[26789,26839]},"callee":null},"797:11":{"kind":"call","span":{"start":[797,11],"end":[797,43],"bytes":[26854,26886]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput"},"801:25":{"kind":"call","span":{"start":[801,25],"end":[801,40],"bytes":[26951,26966]},"callee":null},"802:15":{"kind":"call","span":{"start":[802,15],"end":[802,41],"bytes":[26981,27007]},"callee":null},"804:32":{"kind":"call","span":{"start":[804,32],"end":[804,77],"bytes":[27057,27102]},"callee":null},"811:14":{"kind":"call","span":{"start":[811,14],"end":[811,43],"bytes":[27295,27324]},"callee":null},"813:31":{"kind":"call","span":{"start":[813,31],"end":[813,75],"bytes":[27372,27416]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX","kind":"method","span":{"start":[820,1],"end":[854,2],"bytes":[27615,29032]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[820,1],"end":[820,141],"bytes":[27615,27755]}},{"name":"key","type":"string","span":{"start":[820,1],"end":[820,141],"bytes":[27615,27755]}},{"name":"r","type":"io.Reader","span":{"start":[820,1],"end":[820,141],"bytes":[27615,27755]}},{"name":"partSize","type":"int64","span":{"start":[820,1],"end":[820,141],"bytes":[27615,27755]}}],"return_type":"(int64, time.Time, *string, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"823:17":{"kind":"call","span":{"start":[823,17],"end":[823,54],"bytes":[27867,27904]},"callee":null},"823:32":{"kind":"call","span":{"start":[823,32],"end":[823,53],"bytes":[27882,27903]},"callee":null},"825:31":{"kind":"call","span":{"start":[825,31],"end":[825,87],"bytes":[27952,28008]},"callee":null},"827:15":{"kind":"call","span":{"start":[827,15],"end":[827,44],"bytes":[28026,28055]},"callee":null},"829:15":{"kind":"call","span":{"start":[829,15],"end":[829,59],"bytes":[28077,28121]},"callee":null},"829:42":{"kind":"call","span":{"start":[829,42],"end":[829,58],"bytes":[28104,28120]},"callee":null},"829:48":{"kind":"call","span":{"start":[829,48],"end":[829,57],"bytes":[28110,28119]},"callee":null},"829:76":{"kind":"call","span":{"start":[829,76],"end":[829,98],"bytes":[28138,28160]},"callee":null},"830:31":{"kind":"call","span":{"start":[830,31],"end":[830,83],"bytes":[28193,28245]},"callee":null},"833:11":{"kind":"call","span":{"start":[833,11],"end":[833,43],"bytes":[28260,28292]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput"},"836:13":{"kind":"call","span":{"start":[836,13],"end":[836,29],"bytes":[28380,28396]},"callee":null},"836:19":{"kind":"call","span":{"start":[836,19],"end":[836,28],"bytes":[28386,28395]},"callee":null},"837:16":{"kind":"call","span":{"start":[837,16],"end":[837,44],"bytes":[28431,28459]},"callee":null},"837:32":{"kind":"call","span":{"start":[837,32],"end":[837,43],"bytes":[28447,28458]},"callee":null},"838:25":{"kind":"call","span":{"start":[838,25],"end":[838,40],"bytes":[28484,28499]},"callee":null},"839:15":{"kind":"call","span":{"start":[839,15],"end":[839,41],"bytes":[28514,28540]},"callee":null},"841:32":{"kind":"call","span":{"start":[841,32],"end":[841,77],"bytes":[28590,28635]},"callee":null},"847:32":{"kind":"call","span":{"start":[847,32],"end":[847,79],"bytes":[28786,28833]},"callee":null},"847:47":{"kind":"call","span":{"start":[847,47],"end":[847,75],"bytes":[28801,28829]},"callee":null},"847:63":{"kind":"call","span":{"start":[847,63],"end":[847,74],"bytes":[28817,28828]},"callee":null},"847:8":{"kind":"call","span":{"start":[847,8],"end":[847,80],"bytes":[28762,28834]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter"},"849:14":{"kind":"call","span":{"start":[849,14],"end":[849,43],"bytes":[28865,28894]},"callee":null},"851:31":{"kind":"call","span":{"start":[851,31],"end":[851,75],"bytes":[28942,28986]},"callee":null},"853:9":{"kind":"call","span":{"start":[853,9],"end":[853,15],"bytes":[28998,29004]},"callee":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N"}}},"github.com/benbjohnson/litestream/s3.ReplicaClient.validateSSEConfig":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.validateSSEConfig","kind":"method","span":{"start":[528,1],"end":[582,2],"bytes":[16834,18856]},"signature":"github.com/benbjohnson/litestream/s3.ReplicaClient.validateSSEConfig","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":14},"body":{"531:10":{"kind":"call","span":{"start":[531,10],"end":[531,108],"bytes":[17014,17112]},"callee":null},"540:11":{"kind":"call","span":{"start":[540,11],"end":[540,98],"bytes":[17360,17447]},"callee":null},"544:20":{"kind":"call","span":{"start":[544,20],"end":[544,69],"bytes":[17547,17596]},"callee":null},"546:11":{"kind":"call","span":{"start":[546,11],"end":[546,75],"bytes":[17625,17689]},"callee":null},"548:6":{"kind":"call","span":{"start":[548,6],"end":[548,19],"bytes":[17699,17712]},"callee":null},"549:100":{"kind":"call","span":{"start":[549,100],"end":[549,113],"bytes":[17820,17833]},"callee":null},"549:11":{"kind":"call","span":{"start":[549,11],"end":[549,114],"bytes":[17731,17834]},"callee":null},"554:11":{"kind":"call","span":{"start":[554,11],"end":[554,28],"bytes":[17921,17938]},"callee":null},"555:26":{"kind":"call","span":{"start":[555,26],"end":[555,67],"bytes":[17964,18005]},"callee":null},"561:51":{"kind":"call","span":{"start":[561,51],"end":[561,90],"bytes":[18189,18228]},"callee":null},"561:8":{"kind":"call","span":{"start":[561,8],"end":[561,46],"bytes":[18146,18184]},"callee":null},"564:7":{"kind":"call","span":{"start":[564,7],"end":[564,45],"bytes":[18279,18317]},"callee":null},"565:15":{"kind":"call","span":{"start":[565,15],"end":[565,34],"bytes":[18334,18353]},"callee":null},"567:14":{"kind":"call","span":{"start":[567,14],"end":[567,26],"bytes":[18387,18399]},"callee":null},"571:22":{"kind":"call","span":{"start":[571,22],"end":[571,39],"bytes":[18518,18535]},"callee":null},"571:55":{"kind":"call","span":{"start":[571,55],"end":[571,70],"bytes":[18551,18566]},"callee":null},"571:74":{"kind":"call","span":{"start":[571,74],"end":[571,88],"bytes":[18570,18584]},"callee":null},"574:14":{"kind":"call","span":{"start":[574,14],"end":[574,123],"bytes":[18707,18816]},"callee":null}}}},"fields":{"AccessKeyID":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/AccessKeyID","kind":"field","type":"string","span":{"start":[94,1],"end":[94,24],"bytes":[2757,2780]}},"Bucket":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/Bucket","kind":"field","type":"string","span":{"start":[99,1],"end":[99,27],"bytes":[2859,2885]}},"Concurrency":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/Concurrency","kind":"field","type":"int","span":{"start":[111,1],"end":[111,72],"bytes":[3187,3258]}},"Endpoint":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/Endpoint","kind":"field","type":"string","span":{"start":[101,1],"end":[101,27],"bytes":[2913,2939]}},"ForcePathStyle":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/ForcePathStyle","kind":"field","type":"bool","span":{"start":[102,1],"end":[102,25],"bytes":[2940,2964]}},"MetadataConcurrency":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/MetadataConcurrency","kind":"field","type":"int","span":{"start":[116,1],"end":[116,25],"bytes":[3473,3497]}},"PartSize":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/PartSize","kind":"field","type":"int64","span":{"start":[110,1],"end":[110,69],"bytes":[3118,3186]}},"Path":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[100,1],"end":[100,27],"bytes":[2886,2912]}},"Region":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/Region","kind":"field","type":"string","span":{"start":[98,1],"end":[98,27],"bytes":[2832,2858]}},"RequireContentMD5":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/RequireContentMD5","kind":"field","type":"bool","span":{"start":[106,1],"end":[106,25],"bytes":[3040,3064]}},"SSECustomerAlgorithm":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SSECustomerAlgorithm","kind":"field","type":"string","span":{"start":[120,1],"end":[120,56],"bytes":[3631,3686]}},"SSECustomerKey":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SSECustomerKey","kind":"field","type":"string","span":{"start":[121,1],"end":[121,80],"bytes":[3687,3766]}},"SSECustomerKeyMD5":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SSECustomerKeyMD5","kind":"field","type":"string","span":{"start":[122,1],"end":[122,85],"bytes":[3767,3851]}},"SSEKMSKeyID":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SSEKMSKeyID","kind":"field","type":"string","span":{"start":[126,1],"end":[126,49],"bytes":[3957,4005]}},"SecretAccessKey":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SecretAccessKey","kind":"field","type":"string","span":{"start":[95,1],"end":[95,24],"bytes":[2781,2804]}},"SignAcceptEncoding":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SignAcceptEncoding","kind":"field","type":"bool","span":{"start":[105,1],"end":[105,25],"bytes":[3015,3039]}},"SignPayload":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SignPayload","kind":"field","type":"bool","span":{"start":[104,1],"end":[104,25],"bytes":[2990,3014]}},"SkipVerify":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/SkipVerify","kind":"field","type":"bool","span":{"start":[103,1],"end":[103,25],"bytes":[2965,2989]}},"StorageClass":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/StorageClass","kind":"field","type":"string","span":{"start":[107,1],"end":[107,27],"bytes":[3065,3091]}},"logger":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[91,1],"end":[91,23],"bytes":[2704,2726]}},"mu":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[88,1],"end":[88,21],"bytes":[2620,2640]}},"s3":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/s3","kind":"field","type":"*github.com/aws/aws-sdk-go-v2/service/s3.Client","span":{"start":[89,1],"end":[89,35],"bytes":[2641,2675]}},"uploader":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/uploader","kind":"field","type":"*github.com/aws/aws-sdk-go-v2/feature/s3/manager.Uploader","span":{"start":[90,1],"end":[90,28],"bytes":[2676,2703]}}}},"contentMD5StackKey":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.contentMD5StackKey","kind":"struct","span":{"start":[81,1],"end":[81,33],"bytes":[2387,2419]},"callables":{}},"fileIterator":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator","kind":"struct","span":{"start":[1518,1],"end":[1536,2],"bytes":[48010,48466]},"callables":{"github.com/benbjohnson/litestream/s3.fileIterator.Close":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.Close","kind":"method","span":{"start":[1636,1],"end":[1640,2],"bytes":[51301,51398]},"signature":"github.com/benbjohnson/litestream/s3.fileIterator.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"1638:2":{"kind":"call","span":{"start":[1638,2],"end":[1638,14],"bytes":[51368,51380]},"callee":null}}},"github.com/benbjohnson/litestream/s3.fileIterator.Err":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.Err","kind":"method","span":{"start":[1745,1],"end":[1747,2],"bytes":[54089,54145]},"signature":"github.com/benbjohnson/litestream/s3.fileIterator.Err","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/s3.fileIterator.Item":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.Item","kind":"method","span":{"start":[1740,1],"end":[1742,2],"bytes":[53964,54030]},"signature":"github.com/benbjohnson/litestream/s3.fileIterator.Item","parameters":[],"return_type":"*github.com/superfly/ltx.FileInfo","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/s3.fileIterator.Next":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.Next","kind":"method","span":{"start":[1643,1],"end":[1737,2],"bytes":[51479,53911]},"signature":"github.com/benbjohnson/litestream/s3.fileIterator.Next","parameters":[],"return_type":"bool","metrics":{"cyclomatic":17},"body":{"1651:42":{"kind":"call","span":{"start":[1651,42],"end":[1651,64],"bytes":[51701,51723]},"callee":null},"1652:8":{"kind":"call","span":{"start":[1652,8],"end":[1652,36],"bytes":[51733,51761]},"callee":null},"1657:20":{"kind":"call","span":{"start":[1657,20],"end":[1657,51],"bytes":[51823,51854]},"callee":null},"1665:7":{"kind":"call","span":{"start":[1665,7],"end":[1665,29],"bytes":[51981,52003]},"callee":null},"1666:13":{"kind":"call","span":{"start":[1666,13],"end":[1666,54],"bytes":[52022,52063]},"callee":null},"1666:31":{"kind":"call","span":{"start":[1666,31],"end":[1666,53],"bytes":[52040,52062]},"callee":null},"1668:13":{"kind":"call","span":{"start":[1668,13],"end":[1668,59],"bytes":[52120,52166]},"callee":null},"1668:26":{"kind":"call","span":{"start":[1668,26],"end":[1668,58],"bytes":[52133,52165]},"callee":null},"1668:36":{"kind":"call","span":{"start":[1668,36],"end":[1668,57],"bytes":[52143,52164]},"callee":null},"1670:5":{"kind":"call","span":{"start":[1670,5],"end":[1670,80],"bytes":[52177,52252]},"callee":null},"1676:26":{"kind":"call","span":{"start":[1676,26],"end":[1676,48],"bytes":[52514,52536]},"callee":null},"1677:13":{"kind":"call","span":{"start":[1677,13],"end":[1677,54],"bytes":[52555,52596]},"callee":null},"1677:31":{"kind":"call","span":{"start":[1677,31],"end":[1677,53],"bytes":[52573,52595]},"callee":null},"1679:13":{"kind":"call","span":{"start":[1679,13],"end":[1679,48],"bytes":[52653,52688]},"callee":null},"1679:26":{"kind":"call","span":{"start":[1679,26],"end":[1679,47],"bytes":[52666,52687]},"callee":null},"1681:15":{"kind":"call","span":{"start":[1681,15],"end":[1681,43],"bytes":[52709,52737]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch"},"1689:22":{"kind":"call","span":{"start":[1689,22],"end":[1689,44],"bytes":[52854,52876]},"callee":null},"1694:15":{"kind":"call","span":{"start":[1694,15],"end":[1694,36],"bytes":[52989,53010]},"callee":null},"1695:11":{"kind":"call","span":{"start":[1695,11],"end":[1695,29],"bytes":[53021,53039]},"callee":null},"1696:29":{"kind":"call","span":{"start":[1696,29],"end":[1696,51],"bytes":[53068,53090]},"callee":null},"1719:16":{"kind":"call","span":{"start":[1719,16],"end":[1719,37],"bytes":[53466,53487]},"callee":null},"1727:23":{"kind":"call","span":{"start":[1727,23],"end":[1727,51],"bytes":[53754,53782]},"callee":null},"1730:22":{"kind":"call","span":{"start":[1730,22],"end":[1730,50],"bytes":[53828,53856]},"callee":null}}},"github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch","kind":"method","span":{"start":[1563,1],"end":[1633,2],"bytes":[49257,51220]},"signature":"github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch","parameters":[{"name":"keys","type":"[]string","span":{"start":[1563,1],"end":[1563,67],"bytes":[49257,49323]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"1564:5":{"kind":"call","span":{"start":[1564,5],"end":[1564,14],"bytes":[49328,49337]},"callee":null},"1575:13":{"kind":"call","span":{"start":[1575,13],"end":[1575,50],"bytes":[49591,49628]},"callee":null},"1575:40":{"kind":"call","span":{"start":[1575,40],"end":[1575,49],"bytes":[49618,49627]},"callee":null},"1579:31":{"kind":"call","span":{"start":[1579,31],"end":[1579,49],"bytes":[49757,49775]},"callee":null},"1579:9":{"kind":"call","span":{"start":[1579,9],"end":[1579,50],"bytes":[49735,49776]},"callee":null},"1580:12":{"kind":"call","span":{"start":[1580,12],"end":[1580,41],"bytes":[49788,49817]},"callee":null},"1585:3":{"kind":"call","span":{"start":[1585,3],"end":[1621,5],"bytes":[49888,51063]},"callee":null},"1624:12":{"kind":"call","span":{"start":[1624,12],"end":[1624,20],"bytes":[51079,51087]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch/github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch.closure_1","kind":"function","span":{"start":[1585,1],"end":[1621,5],"bytes":[49886,51063]},"signature":"github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch.closure_1","parameters":[],"return_type":"error","error_channel":["error"],"body":{"1587:14":{"kind":"call","span":{"start":[1587,14],"end":[1587,33],"bytes":[49987,50006]},"callee":null},"1590:10":{"kind":"call","span":{"start":[1590,10],"end":[1590,24],"bytes":[50071,50085]},"callee":null},"1593:13":{"kind":"call","span":{"start":[1593,13],"end":[1593,42],"bytes":[50136,50165]},"callee":null},"1594:13":{"kind":"call","span":{"start":[1594,13],"end":[1594,28],"bytes":[50179,50194]},"callee":null},"1600:38":{"kind":"call","span":{"start":[1600,38],"end":[1600,81],"bytes":[50451,50494]},"callee":null},"1601:32":{"kind":"call","span":{"start":[1601,32],"end":[1601,69],"bytes":[50526,50563]},"callee":null},"1602:35":{"kind":"call","span":{"start":[1602,35],"end":[1602,75],"bytes":[50598,50638]},"callee":null},"1605:17":{"kind":"call","span":{"start":[1605,17],"end":[1605,57],"bytes":[50661,50701]},"callee":null},"1613:24":{"kind":"call","span":{"start":[1613,24],"end":[1613,56],"bytes":[50918,50950]},"callee":null},"1614:7":{"kind":"call","span":{"start":[1614,7],"end":[1614,16],"bytes":[50971,50980]},"callee":null},"1616:7":{"kind":"call","span":{"start":[1616,7],"end":[1616,18],"bytes":[51015,51026]},"callee":null}}}}}},"fields":{"cancel":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/cancel","kind":"field","type":"context.CancelFunc","span":{"start":[1520,1],"end":[1520,27],"bytes":[48061,48087]}},"client":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/client","kind":"field","type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","span":{"start":[1521,1],"end":[1521,23],"bytes":[48088,48110]}},"closed":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/closed","kind":"field","type":"bool","span":{"start":[1533,1],"end":[1533,13],"bytes":[48416,48428]}},"ctx":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/ctx","kind":"field","type":"context.Context","span":{"start":[1519,1],"end":[1519,24],"bytes":[48037,48060]}},"err":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/err","kind":"field","type":"error","span":{"start":[1534,1],"end":[1534,14],"bytes":[48429,48442]}},"info":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/info","kind":"field","type":"*github.com/superfly/ltx.FileInfo","span":{"start":[1535,1],"end":[1535,22],"bytes":[48443,48464]}},"level":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/level","kind":"field","type":"int","span":{"start":[1522,1],"end":[1522,12],"bytes":[48111,48122]}},"metadataCache":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/metadataCache","kind":"field","type":"map[string]time.Time","span":{"start":[1527,1],"end":[1527,80],"bytes":[48246,48325]}},"page":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/page","kind":"field","type":"*github.com/aws/aws-sdk-go-v2/service/s3.ListObjectsV2Output","span":{"start":[1530,1],"end":[1530,35],"bytes":[48365,48399]}},"pageIndex":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/pageIndex","kind":"field","type":"int","span":{"start":[1531,1],"end":[1531,15],"bytes":[48400,48414]}},"paginator":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/paginator","kind":"field","type":"*github.com/aws/aws-sdk-go-v2/service/s3.ListObjectsV2Paginator","span":{"start":[1529,1],"end":[1529,38],"bytes":[48327,48364]}},"prefix":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/prefix","kind":"field","type":"string","span":{"start":[1524,1],"end":[1524,15],"bytes":[48140,48154]}},"seek":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/seek","kind":"field","type":"github.com/superfly/ltx.TXID","span":{"start":[1523,1],"end":[1523,17],"bytes":[48123,48139]}},"useMetadata":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/useMetadata","kind":"field","type":"bool","span":{"start":[1526,1],"end":[1526,90],"bytes":[48156,48245]}}}}},"functions":{"github.com/benbjohnson/litestream/s3.NewReplicaClient":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","kind":"function","span":{"start":[130,1],"end":[137,2],"bytes":[4070,4290]},"signature":"github.com/benbjohnson/litestream/s3.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","metrics":{"cyclomatic":1},"body":{"132:23":{"kind":"call","span":{"start":[132,23],"end":[132,37],"bytes":[4157,4171]},"callee":null}}},"github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","kind":"function","span":{"start":[145,1],"end":[344,2],"bytes":[4536,10753]},"signature":"github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[145,1],"end":[145,137],"bytes":[4536,4672]}},{"name":"host","type":"string","span":{"start":[145,1],"end":[145,137],"bytes":[4536,4672]}},{"name":"urlPath","type":"string","span":{"start":[145,1],"end":[145,137],"bytes":[4536,4672]}},{"name":"query","type":"net/url.Values","span":{"start":[145,1],"end":[145,137],"bytes":[4536,4672]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[145,1],"end":[145,137],"bytes":[4536,4672]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":47},"body":{"146:12":{"kind":"call","span":{"start":[146,12],"end":[146,30],"bytes":[4684,4702]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient"},"167:5":{"kind":"call","span":{"start":[167,5],"end":[167,36],"bytes":[5199,5230]},"callee":null},"169:12":{"kind":"call","span":{"start":[169,12],"end":[169,44],"bytes":[5260,5292]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegionFromS3ARN"},"171:46":{"kind":"call","span":{"start":[171,46],"end":[171,61],"bytes":[5348,5363]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost"},"175:18":{"kind":"call","span":{"start":[175,18],"end":[175,39],"bytes":[5432,5453]},"callee":null},"177:18":{"kind":"call","span":{"start":[177,18],"end":[177,60],"bytes":[5576,5618]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme"},"180:15":{"kind":"call","span":{"start":[180,15],"end":[180,85],"bytes":[5735,5805]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"184:16":{"kind":"call","span":{"start":[184,16],"end":[184,35],"bytes":[5865,5884]},"callee":null},"187:14":{"kind":"call","span":{"start":[187,14],"end":[187,84],"bytes":[5937,6007]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"190:14":{"kind":"call","span":{"start":[190,14],"end":[190,75],"bytes":[6051,6112]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"193:14":{"kind":"call","span":{"start":[193,14],"end":[193,77],"bytes":[6152,6215]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"197:14":{"kind":"call","span":{"start":[197,14],"end":[197,92],"bytes":[6280,6358]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"201:14":{"kind":"call","span":{"start":[201,14],"end":[201,90],"bytes":[6437,6513]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue"},"205:19":{"kind":"call","span":{"start":[205,19],"end":[205,65],"bytes":[6581,6627]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue"},"208:17":{"kind":"call","span":{"start":[208,17],"end":[208,23],"bytes":[6692,6698]},"callee":null},"211:19":{"kind":"call","span":{"start":[211,19],"end":[211,75],"bytes":[6744,6800]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue"},"216:10":{"kind":"call","span":{"start":[216,10],"end":[216,35],"bytes":[6876,6901]},"callee":null},"218:17":{"kind":"call","span":{"start":[218,17],"end":[218,43],"bytes":[6948,6974]},"callee":null},"224:15":{"kind":"call","span":{"start":[224,15],"end":[224,63],"bytes":[7067,7115]},"callee":null},"228:23":{"kind":"call","span":{"start":[228,23],"end":[228,50],"bytes":[7210,7237]},"callee":null},"228:60":{"kind":"call","span":{"start":[228,60],"end":[228,89],"bytes":[7247,7276]},"callee":null},"231:10":{"kind":"call","span":{"start":[231,10],"end":[231,40],"bytes":[7344,7374]},"callee":null},"233:17":{"kind":"call","span":{"start":[233,17],"end":[233,54],"bytes":[7427,7464]},"callee":null},"236:10":{"kind":"call","span":{"start":[236,10],"end":[236,44],"bytes":[7513,7547]},"callee":null},"238:17":{"kind":"call","span":{"start":[238,17],"end":[238,58],"bytes":[7604,7645]},"callee":null},"243:11":{"kind":"call","span":{"start":[243,11],"end":[243,46],"bytes":[7721,7756]},"callee":null},"244:18":{"kind":"call","span":{"start":[244,18],"end":[244,52],"bytes":[7785,7819]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme"},"252:15":{"kind":"call","span":{"start":[252,15],"end":[252,53],"bytes":[7979,8017]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsHetznerEndpoint"},"253:14":{"kind":"call","span":{"start":[253,14],"end":[253,51],"bytes":[8031,8068]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsTigrisEndpoint"},"254:20":{"kind":"call","span":{"start":[254,20],"end":[254,63],"bytes":[8088,8131]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsDigitalOceanEndpoint"},"255:17":{"kind":"call","span":{"start":[255,17],"end":[255,57],"bytes":[8148,8188]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsBackblazeEndpoint"},"256:16":{"kind":"call","span":{"start":[256,16],"end":[256,55],"bytes":[8204,8243]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsFilebaseEndpoint"},"257:16":{"kind":"call","span":{"start":[257,16],"end":[257,55],"bytes":[8259,8298]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsScalewayEndpoint"},"258:20":{"kind":"call","span":{"start":[258,20],"end":[258,63],"bytes":[8318,8361]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsCloudflareR2Endpoint"},"259:13":{"kind":"call","span":{"start":[259,13],"end":[259,49],"bytes":[8374,8410]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsMinIOEndpoint"},"260:16":{"kind":"call","span":{"start":[260,16],"end":[260,55],"bytes":[8426,8465]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsSupabaseEndpoint"},"320:10":{"kind":"call","span":{"start":[320,10],"end":[320,43],"bytes":[10010,10043]},"callee":null},"322:17":{"kind":"call","span":{"start":[322,17],"end":[322,52],"bytes":[10105,10140]},"callee":null},"325:10":{"kind":"call","span":{"start":[325,10],"end":[325,37],"bytes":[10198,10225]},"callee":null},"327:17":{"kind":"call","span":{"start":[327,17],"end":[327,46],"bytes":[10281,10310]},"callee":null},"330:10":{"kind":"call","span":{"start":[330,10],"end":[330,40],"bytes":[10362,10392]},"callee":null},"332:17":{"kind":"call","span":{"start":[332,17],"end":[332,50],"bytes":[10451,10484]},"callee":null},"337:10":{"kind":"call","span":{"start":[337,10],"end":[337,34],"bytes":[10587,10611]},"callee":null},"339:17":{"kind":"call","span":{"start":[339,17],"end":[339,44],"bytes":[10664,10691]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ParseHost":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost","kind":"function","span":{"start":[1786,1],"end":[1830,2],"bytes":[55152,56729]},"signature":"github.com/benbjohnson/litestream/s3.ParseHost","parameters":[{"name":"host","type":"string","span":{"start":[1786,1],"end":[1786,85],"bytes":[55152,55236]}}],"return_type":"(string, string, string, bool)","metrics":{"cyclomatic":9},"body":{"1788:37":{"kind":"call","span":{"start":[1788,37],"end":[1788,67],"bytes":[55324,55354]},"callee":null},"1788:5":{"kind":"call","span":{"start":[1788,5],"end":[1788,32],"bytes":[55292,55319]},"callee":null},"1789:12":{"kind":"call","span":{"start":[1789,12],"end":[1789,40],"bytes":[55368,55396]},"callee":null},"1790:6":{"kind":"call","span":{"start":[1790,6],"end":[1790,16],"bytes":[55402,55412]},"callee":null},"1802:10":{"kind":"call","span":{"start":[1802,10],"end":[1802,45],"bytes":[55744,55779]},"callee":null},"1802:47":{"kind":"call","span":{"start":[1802,47],"end":[1802,53],"bytes":[55781,55787]},"callee":null},"1804:6":{"kind":"call","span":{"start":[1804,6],"end":[1804,12],"bytes":[55815,55821]},"callee":null},"1808:17":{"kind":"call","span":{"start":[1808,17],"end":[1808,59],"bytes":[55914,55956]},"callee":null},"1808:61":{"kind":"call","span":{"start":[1808,61],"end":[1808,67],"bytes":[55958,55964]},"callee":null},"1811:26":{"kind":"call","span":{"start":[1811,26],"end":[1811,82],"bytes":[56028,56084]},"callee":null},"1812:17":{"kind":"call","span":{"start":[1812,17],"end":[1812,56],"bytes":[56108,56147]},"callee":null},"1812:58":{"kind":"call","span":{"start":[1812,58],"end":[1812,64],"bytes":[56149,56155]},"callee":null},"1815:14":{"kind":"call","span":{"start":[1815,14],"end":[1815,66],"bytes":[56207,56259]},"callee":null},"1817:17":{"kind":"call","span":{"start":[1817,17],"end":[1817,55],"bytes":[56316,56354]},"callee":null},"1817:57":{"kind":"call","span":{"start":[1817,57],"end":[1817,63],"bytes":[56356,56362]},"callee":null},"1821:17":{"kind":"call","span":{"start":[1821,17],"end":[1821,55],"bytes":[56469,56507]},"callee":null},"1821:57":{"kind":"call","span":{"start":[1821,57],"end":[1821,63],"bytes":[56509,56515]},"callee":null},"1824:14":{"kind":"call","span":{"start":[1824,14],"end":[1824,52],"bytes":[56567,56605]},"callee":null}}},"github.com/benbjohnson/litestream/s3.ParseURL":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseURL","kind":"function","span":{"start":[1751,1],"end":[1782,2],"bytes":[54255,55013]},"signature":"github.com/benbjohnson/litestream/s3.ParseURL","parameters":[{"name":"s","type":"string","span":{"start":[1751,1],"end":[1751,76],"bytes":[54255,54330]}},{"name":"endpoint","type":"string","span":{"start":[1751,1],"end":[1751,76],"bytes":[54255,54330]}}],"return_type":"(string, string, string, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"1752:12":{"kind":"call","span":{"start":[1752,12],"end":[1752,24],"bytes":[54342,54354]},"callee":null},"1758:22":{"kind":"call","span":{"start":[1758,22],"end":[1758,58],"bytes":[54445,54481]},"callee":null},"1763:12":{"kind":"call","span":{"start":[1763,12],"end":[1763,67],"bytes":[54566,54621]},"callee":null},"1763:27":{"kind":"call","span":{"start":[1763,27],"end":[1763,58],"bytes":[54581,54612]},"callee":null},"1764:6":{"kind":"call","span":{"start":[1764,6],"end":[1764,16],"bytes":[54627,54637]},"callee":null},"1765:23":{"kind":"call","span":{"start":[1765,23],"end":[1765,56],"bytes":[54667,54700]},"callee":null},"1768:6":{"kind":"call","span":{"start":[1768,6],"end":[1768,16],"bytes":[54730,54740]},"callee":null},"1775:25":{"kind":"call","span":{"start":[1775,25],"end":[1775,42],"bytes":[54881,54898]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost"},"1780:8":{"kind":"call","span":{"start":[1780,8],"end":[1780,39],"bytes":[54947,54978]},"callee":null}}},"github.com/benbjohnson/litestream/s3.computeDeleteObjectsContentMD5":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.computeDeleteObjectsContentMD5","kind":"function","span":{"start":[1077,1],"end":[1092,2],"bytes":[36315,36656]},"signature":"github.com/benbjohnson/litestream/s3.computeDeleteObjectsContentMD5","parameters":[{"name":"deleteInput","type":"*github.com/aws/aws-sdk-go-v2/service/s3/types.Delete","span":{"start":[1077,1],"end":[1077,81],"bytes":[36315,36395]}}],"return_type":"(string, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1082:18":{"kind":"call","span":{"start":[1082,18],"end":[1082,51],"bytes":[36459,36492]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.marshalDeleteObjects"},"1086:5":{"kind":"call","span":{"start":[1086,5],"end":[1086,17],"bytes":[36534,36546]},"callee":null},"1090:9":{"kind":"call","span":{"start":[1090,9],"end":[1090,25],"bytes":[36583,36599]},"callee":null},"1091:9":{"kind":"call","span":{"start":[1091,9],"end":[1091,50],"bytes":[36608,36649]},"callee":null}}},"github.com/benbjohnson/litestream/s3.deleteOutputError":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.deleteOutputError","kind":"function","span":{"start":[1848,1],"end":[1860,2],"bytes":[57303,57645]},"signature":"github.com/benbjohnson/litestream/s3.deleteOutputError","parameters":[{"name":"out","type":"*github.com/aws/aws-sdk-go-v2/service/s3.DeleteObjectsOutput","span":{"start":[1848,1],"end":[1848,60],"bytes":[57303,57362]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"1849:5":{"kind":"call","span":{"start":[1849,5],"end":[1849,20],"bytes":[57367,57382]},"callee":null},"1855:2":{"kind":"call","span":{"start":[1855,2],"end":[1855,42],"bytes":[57455,57495]},"callee":null},"1857:3":{"kind":"call","span":{"start":[1857,3],"end":[1857,80],"bytes":[57532,57609]},"callee":null},"1857:31":{"kind":"call","span":{"start":[1857,31],"end":[1857,52],"bytes":[57560,57581]},"callee":null},"1857:54":{"kind":"call","span":{"start":[1857,54],"end":[1857,79],"bytes":[57583,57608]},"callee":null},"1859:20":{"kind":"call","span":{"start":[1859,20],"end":[1859,30],"bytes":[57632,57642]},"callee":null},"1859:9":{"kind":"call","span":{"start":[1859,9],"end":[1859,31],"bytes":[57621,57643]},"callee":null}}},"github.com/benbjohnson/litestream/s3.encodeDeleteDocument":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeDeleteDocument","kind":"function","span":{"start":[1117,1],"end":[1140,2],"bytes":[37177,37694]},"signature":"github.com/benbjohnson/litestream/s3.encodeDeleteDocument","parameters":[{"name":"v","type":"*github.com/aws/aws-sdk-go-v2/service/s3/types.Delete","span":{"start":[1117,1],"end":[1117,74],"bytes":[37177,37250]}},{"name":"value","type":"github.com/aws/smithy-go/encoding/xml.Value","span":{"start":[1117,1],"end":[1117,74],"bytes":[37177,37250]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1118:8":{"kind":"call","span":{"start":[1118,8],"end":[1118,21],"bytes":[37258,37271]},"callee":null},"1125:9":{"kind":"call","span":{"start":[1125,9],"end":[1125,37],"bytes":[37393,37421]},"callee":null},"1126:13":{"kind":"call","span":{"start":[1126,13],"end":[1126,54],"bytes":[37434,37475]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifierList"},"1136:9":{"kind":"call","span":{"start":[1136,9],"end":[1136,34],"bytes":[37629,37654]},"callee":null},"1137:3":{"kind":"call","span":{"start":[1137,3],"end":[1137,23],"bytes":[37657,37677]},"callee":null}}},"github.com/benbjohnson/litestream/s3.encodeObjectIdentifier":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifier","kind":"function","span":{"start":[1160,1],"end":[1203,2],"bytes":[38235,39183]},"signature":"github.com/benbjohnson/litestream/s3.encodeObjectIdentifier","parameters":[{"name":"v","type":"*github.com/aws/aws-sdk-go-v2/service/s3/types.ObjectIdentifier","span":{"start":[1160,1],"end":[1160,86],"bytes":[38235,38320]}},{"name":"value","type":"github.com/aws/smithy-go/encoding/xml.Value","span":{"start":[1160,1],"end":[1160,86],"bytes":[38235,38320]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"1161:8":{"kind":"call","span":{"start":[1161,8],"end":[1161,21],"bytes":[38328,38341]},"callee":null},"1163:9":{"kind":"call","span":{"start":[1163,9],"end":[1167,5],"bytes":[38370,38468]},"callee":null},"1168:3":{"kind":"call","span":{"start":[1168,3],"end":[1168,21],"bytes":[38471,38489]},"callee":null},"1171:9":{"kind":"call","span":{"start":[1171,9],"end":[1175,5],"bytes":[38520,38617]},"callee":null},"1176:3":{"kind":"call","span":{"start":[1176,3],"end":[1176,20],"bytes":[38620,38637]},"callee":null},"1179:9":{"kind":"call","span":{"start":[1179,9],"end":[1183,5],"bytes":[38681,38791]},"callee":null},"1184:13":{"kind":"call","span":{"start":[1184,13],"end":[1184,59],"bytes":[38804,38850]},"callee":null},"1184:3":{"kind":"call","span":{"start":[1184,3],"end":[1184,60],"bytes":[38794,38851]},"callee":null},"1187:9":{"kind":"call","span":{"start":[1187,9],"end":[1191,5],"bytes":[38883,38981]},"callee":null},"1192:3":{"kind":"call","span":{"start":[1192,3],"end":[1192,19],"bytes":[38984,39000]},"callee":null},"1195:9":{"kind":"call","span":{"start":[1195,9],"end":[1199,5],"bytes":[39037,39140]},"callee":null},"1200:3":{"kind":"call","span":{"start":[1200,3],"end":[1200,26],"bytes":[39143,39166]},"callee":null}}},"github.com/benbjohnson/litestream/s3.encodeObjectIdentifierList":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifierList","kind":"function","span":{"start":[1142,1],"end":[1155,2],"bytes":[37696,38009]},"signature":"github.com/benbjohnson/litestream/s3.encodeObjectIdentifierList","parameters":[{"name":"v","type":"[]github.com/aws/aws-sdk-go-v2/service/s3/types.ObjectIdentifier","span":{"start":[1142,1],"end":[1142,91],"bytes":[37696,37786]}},{"name":"value","type":"github.com/aws/smithy-go/encoding/xml.Value","span":{"start":[1142,1],"end":[1142,91],"bytes":[37696,37786]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"1143:6":{"kind":"call","span":{"start":[1143,6],"end":[1143,25],"bytes":[37792,37811]},"callee":null},"1144:9":{"kind":"call","span":{"start":[1144,9],"end":[1144,22],"bytes":[37822,37835]},"callee":null},"1147:11":{"kind":"call","span":{"start":[1147,11],"end":[1147,24],"bytes":[37850,37863]},"callee":null},"1149:13":{"kind":"call","span":{"start":[1149,13],"end":[1149,27],"bytes":[37896,37910]},"callee":null},"1150:13":{"kind":"call","span":{"start":[1150,13],"end":[1150,50],"bytes":[37923,37960]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifier"}}},"github.com/benbjohnson/litestream/s3.init":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.init","kind":"function","span":{"start":[47,1],"end":[49,2],"bytes":[1012,1099]},"signature":"github.com/benbjohnson/litestream/s3.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"48:2":{"kind":"call","span":{"start":[48,2],"end":[48,72],"bytes":[1027,1097]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}},"github.com/benbjohnson/litestream/s3.isNotExists":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists","kind":"function","span":{"start":[1840,1],"end":[1846,2],"bytes":[57147,57301]},"signature":"github.com/benbjohnson/litestream/s3.isNotExists","parameters":[{"name":"err","type":"error","span":{"start":[1840,1],"end":[1840,35],"bytes":[57147,57181]}}],"return_type":"bool","metrics":{"cyclomatic":2},"body":{"1842:5":{"kind":"call","span":{"start":[1842,5],"end":[1842,28],"bytes":[57214,57237]},"callee":null},"1843:10":{"kind":"call","span":{"start":[1843,10],"end":[1843,28],"bytes":[57249,57267]},"callee":null}}},"github.com/benbjohnson/litestream/s3.marshalDeleteObjects":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.marshalDeleteObjects","kind":"function","span":{"start":[1094,1],"end":[1115,2],"bytes":[36658,37175]},"signature":"github.com/benbjohnson/litestream/s3.marshalDeleteObjects","parameters":[{"name":"deleteInput","type":"*github.com/aws/aws-sdk-go-v2/service/s3/types.Delete","span":{"start":[1094,1],"end":[1094,71],"bytes":[36658,36728]}}],"return_type":"([]byte, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"1100:13":{"kind":"call","span":{"start":[1100,13],"end":[1100,39],"bytes":[36810,36836]},"callee":null},"1106:4":{"kind":"call","span":{"start":[1106,4],"end":[1106,82],"bytes":[36948,37026]},"callee":null},"1110:12":{"kind":"call","span":{"start":[1110,12],"end":[1110,72],"bytes":[37048,37108]},"callee":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeDeleteDocument"},"1110:46":{"kind":"call","span":{"start":[1110,46],"end":[1110,71],"bytes":[37082,37107]},"callee":null},"1114:9":{"kind":"call","span":{"start":[1114,9],"end":[1114,24],"bytes":[37153,37168]},"callee":null}}},"github.com/benbjohnson/litestream/s3.newFileIterator":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.newFileIterator","kind":"function","span":{"start":[1538,1],"end":[1560,2],"bytes":[48468,49164]},"signature":"github.com/benbjohnson/litestream/s3.newFileIterator","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[1538,1],"end":[1538,125],"bytes":[48468,48592]}},{"name":"client","type":"*github.com/benbjohnson/litestream/s3.ReplicaClient","span":{"start":[1538,1],"end":[1538,125],"bytes":[48468,48592]}},{"name":"level","type":"int","span":{"start":[1538,1],"end":[1538,125],"bytes":[48468,48592]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[1538,1],"end":[1538,125],"bytes":[48468,48592]}},{"name":"useMetadata","type":"bool","span":{"start":[1538,1],"end":[1538,125],"bytes":[48468,48592]}}],"return_type":"*github.com/benbjohnson/litestream/s3.fileIterator","metrics":{"cyclomatic":1},"body":{"1539:17":{"kind":"call","span":{"start":[1539,17],"end":[1539,40],"bytes":[48609,48632]},"callee":null},"1541:32":{"kind":"call","span":{"start":[1541,32],"end":[1541,59],"bytes":[48665,48692]},"callee":null},"1550:18":{"kind":"call","span":{"start":[1550,18],"end":[1550,44],"bytes":[48907,48933]},"callee":null},"1554:18":{"kind":"call","span":{"start":[1554,18],"end":[1557,4],"bytes":[49015,49149]},"callee":null},"1555:11":{"kind":"call","span":{"start":[1555,11],"end":[1555,36],"bytes":[49089,49114]},"callee":null},"1556:11":{"kind":"call","span":{"start":[1556,11],"end":[1556,29],"bytes":[49126,49144]},"callee":null}}},"github.com/benbjohnson/litestream/s3.newTransportRetryer":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.newTransportRetryer","kind":"function","span":{"start":[594,1],"end":[612,2],"bytes":[19486,20228]},"signature":"github.com/benbjohnson/litestream/s3.newTransportRetryer","parameters":[],"return_type":"github.com/aws/aws-sdk-go-v2/aws.Retryer","metrics":{"cyclomatic":1},"body":{"595:9":{"kind":"call","span":{"start":[595,9],"end":[611,4],"bytes":[19535,20226]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.newTransportRetryer/github.com/benbjohnson/litestream/s3.newTransportRetryer.closure_1","kind":"function","span":{"start":[595,1],"end":[611,4],"bytes":[19527,20226]},"signature":"github.com/benbjohnson/litestream/s3.newTransportRetryer.closure_1","parameters":[{"name":"o","type":"*github.com/aws/aws-sdk-go-v2/aws/retry.StandardOptions","span":{"start":[595,1],"end":[595,59],"bytes":[19527,19585]}}],"body":{"603:18":{"kind":"call","span":{"start":[603,18],"end":[610,4],"bytes":[20008,20222]},"callee":null}}}}},"github.com/benbjohnson/litestream/s3.parseS3DebugEnv":{"id":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.parseS3DebugEnv","kind":"function","span":{"start":[1864,1],"end":[1893,2],"bytes":[57807,58710]},"signature":"github.com/benbjohnson/litestream/s3.parseS3DebugEnv","parameters":[],"return_type":"github.com/aws/aws-sdk-go-v2/aws.ClientLogMode","metrics":{"cyclomatic":12},"body":{"1865:7":{"kind":"call","span":{"start":[1865,7],"end":[1865,39],"bytes":[57856,57888]},"callee":null},"1871:23":{"kind":"call","span":{"start":[1871,23],"end":[1871,44],"bytes":[57971,57992]},"callee":null},"1872:10":{"kind":"call","span":{"start":[1872,10],"end":[1872,50],"bytes":[58004,58044]},"callee":null},"1872:26":{"kind":"call","span":{"start":[1872,26],"end":[1872,49],"bytes":[58020,58043]},"callee":null},"1889:4":{"kind":"call","span":{"start":[1889,4],"end":[1889,156],"bytes":[58533,58685]},"callee":null}}}},"content_hash":"067ee2bdceaeb371ea22bf7d32034bcfa8e86ff8c0292bcb38d3d784db69cc78"},"server.go":{"id":"can://go/litestream/server.go","kind":"module","span":{"start":[1,1],"end":[681,1],"bytes":[0,17841]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/pprof\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n)\n\n// SocketConfig configures the Unix socket for control commands.\ntype SocketConfig struct {\n\tEnabled bool `yaml:\"enabled\"`\n\tPath string `yaml:\"path\"`\n\tPermissions uint32 `yaml:\"permissions\"`\n}\n\n// DefaultSocketConfig returns the default socket configuration.\nfunc DefaultSocketConfig() SocketConfig {\n\treturn SocketConfig{\n\t\tEnabled: false,\n\t\tPath: \"/var/run/litestream.sock\",\n\t\tPermissions: 0600,\n\t}\n}\n\n// Server manages runtime control via Unix socket using HTTP.\ntype Server struct {\n\tstore *Store\n\n\t// SocketPath is the path to the Unix socket.\n\tSocketPath string\n\n\t// SocketPerms is the file permissions for the socket.\n\tSocketPerms uint32\n\n\t// PathExpander optionally expands paths (e.g., ~ expansion).\n\t// If nil, paths are used as-is.\n\tPathExpander func(string) (string, error)\n\n\t// Version is the version string to report in /info.\n\tVersion string\n\n\t// startedAt is set when the server starts.\n\tstartedAt time.Time\n\n\tsocketListener net.Listener\n\thttpServer *http.Server\n\n\tctx context.Context\n\tcancel context.CancelFunc\n\twg sync.WaitGroup\n\n\tlogger *slog.Logger\n}\n\n// NewServer creates a new Server instance.\nfunc NewServer(store *Store) *Server {\n\tctx, cancel := context.WithCancel(context.Background())\n\ts := \u0026Server{\n\t\tstore: store,\n\t\tSocketPerms: 0600,\n\t\tctx: ctx,\n\t\tcancel: cancel,\n\t\tlogger: slog.Default().With(LogKeySystem, LogSystemServer),\n\t}\n\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"POST /start\", s.handleStart)\n\tmux.HandleFunc(\"POST /stop\", s.handleStop)\n\tmux.HandleFunc(\"GET /txid\", s.handleTXID)\n\tmux.HandleFunc(\"POST /register\", s.handleRegister)\n\tmux.HandleFunc(\"POST /unregister\", s.handleUnregister)\n\tmux.HandleFunc(\"POST /sync\", s.handleSync)\n\tmux.HandleFunc(\"GET /list\", s.handleList)\n\tmux.HandleFunc(\"GET /info\", s.handleInfo)\n\tmux.HandleFunc(\"GET /debug/sync-status\", s.handleSyncStatus)\n\n\t// pprof endpoints\n\tmux.HandleFunc(\"GET /debug/pprof/\", pprof.Index)\n\tmux.HandleFunc(\"GET /debug/pprof/cmdline\", pprof.Cmdline)\n\tmux.HandleFunc(\"GET /debug/pprof/profile\", pprof.Profile)\n\tmux.HandleFunc(\"GET /debug/pprof/symbol\", pprof.Symbol)\n\tmux.HandleFunc(\"GET /debug/pprof/trace\", pprof.Trace)\n\n\ts.httpServer = \u0026http.Server{Handler: mux}\n\n\treturn s\n}\n\n// Start begins listening for control connections.\nfunc (s *Server) Start() error {\n\tif s.SocketPath == \"\" {\n\t\treturn fmt.Errorf(\"socket path required\")\n\t}\n\n\t// Check if socket file exists and is actually a socket before removing\n\tif info, err := os.Lstat(s.SocketPath); err == nil {\n\t\tif info.Mode()\u0026os.ModeSocket != 0 {\n\t\t\tif err := os.Remove(s.SocketPath); err != nil {\n\t\t\t\treturn fmt.Errorf(\"remove existing socket: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"socket path exists but is not a socket: %s\", s.SocketPath)\n\t\t}\n\t} else if !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"check socket path: %w\", err)\n\t}\n\n\tlistener, err := net.Listen(\"unix\", s.SocketPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"listen on unix socket: %w\", err)\n\t}\n\ts.socketListener = listener\n\n\tif err := os.Chmod(s.SocketPath, os.FileMode(s.SocketPerms)); err != nil {\n\t\tlistener.Close()\n\t\treturn fmt.Errorf(\"chmod socket: %w\", err)\n\t}\n\n\t// Set startedAt after successful socket setup to ensure uptime reflects\n\t// the actual time the server became available.\n\ts.startedAt = time.Now()\n\n\ts.logger.Info(\"control socket listening\", \"path\", s.SocketPath)\n\n\ts.wg.Add(1)\n\tgo func() {\n\t\tdefer s.wg.Done()\n\t\tif err := s.httpServer.Serve(listener); err != nil \u0026\u0026 err != http.ErrServerClosed {\n\t\t\ts.logger.Error(\"http server error\", \"error\", err)\n\t\t}\n\t}()\n\n\treturn nil\n}\n\n// Close gracefully shuts down the control server.\nfunc (s *Server) Close() error {\n\ts.cancel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\tif s.httpServer != nil {\n\t\tif err := s.httpServer.Shutdown(ctx); err != nil {\n\t\t\ts.logger.Error(\"http server shutdown error\", \"error\", err)\n\t\t}\n\t}\n\ts.wg.Wait()\n\treturn nil\n}\n\n// expandPath expands the path using PathExpander if set.\nfunc (s *Server) expandPath(path string) (string, error) {\n\tif s.PathExpander != nil {\n\t\treturn s.PathExpander(path)\n\t}\n\treturn path, nil\n}\n\nfunc (s *Server) handleStart(w http.ResponseWriter, r *http.Request) {\n\tvar req StartRequest\n\tif err := json.NewDecoder(r.Body).Decode(\u0026req); err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, \"invalid request body\", err.Error())\n\t\treturn\n\t}\n\n\tif req.Path == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"path required\", nil)\n\t\treturn\n\t}\n\n\texpandedPath, err := s.expandPath(req.Path)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\treturn\n\t}\n\n\tctx := s.ctx\n\tif req.Timeout \u003e 0 {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(s.ctx, time.Duration(req.Timeout)*time.Second)\n\t\tdefer cancel()\n\t}\n\n\tdb := s.store.FindDB(expandedPath)\n\tif db == nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, fmt.Sprintf(\"database not found: %s\", expandedPath), nil)\n\t\treturn\n\t}\n\n\tstatus := \"started\"\n\tif db.IsOpen() {\n\t\tstatus = \"already_running\"\n\t} else {\n\t\tif err := s.store.EnableDB(ctx, expandedPath); err != nil {\n\t\t\twriteJSONError(w, http.StatusInternalServerError, err.Error(), nil)\n\t\t\treturn\n\t\t}\n\t}\n\ttxID, err := s.storeTXID(expandedPath)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, err.Error(), nil)\n\t\treturn\n\t}\n\n\twriteJSON(w, http.StatusOK, StartResponse{\n\t\tStatus: status,\n\t\tPath: expandedPath,\n\t\tTXID: txID,\n\t})\n}\n\nfunc (s *Server) handleStop(w http.ResponseWriter, r *http.Request) {\n\tvar req StopRequest\n\tif err := json.NewDecoder(r.Body).Decode(\u0026req); err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, \"invalid request body\", err.Error())\n\t\treturn\n\t}\n\n\tif req.Path == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"path required\", nil)\n\t\treturn\n\t}\n\n\texpandedPath, err := s.expandPath(req.Path)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\treturn\n\t}\n\n\ttimeout := req.Timeout\n\tif timeout == 0 {\n\t\ttimeout = 30\n\t}\n\tctx, cancel := context.WithTimeout(s.ctx, time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\tdb := s.store.FindDB(expandedPath)\n\tif db == nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, fmt.Sprintf(\"database not found: %s\", expandedPath), nil)\n\t\treturn\n\t}\n\n\tstatus := \"stopped\"\n\tif !db.IsOpen() {\n\t\tstatus = \"already_stopped\"\n\t} else {\n\t\tif err := s.store.DisableDB(ctx, expandedPath); err != nil {\n\t\t\twriteJSONError(w, http.StatusInternalServerError, err.Error(), nil)\n\t\t\treturn\n\t\t}\n\t}\n\ttxID, err := s.storeTXID(expandedPath)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, err.Error(), nil)\n\t\treturn\n\t}\n\n\twriteJSON(w, http.StatusOK, StopResponse{\n\t\tStatus: status,\n\t\tPath: expandedPath,\n\t\tTXID: txID,\n\t})\n}\n\nfunc (s *Server) storeTXID(path string) (uint64, error) {\n\tdb := s.store.FindDB(path)\n\tif db == nil {\n\t\treturn 0, fmt.Errorf(\"database not found: %s\", path)\n\t}\n\n\t_, maxTXID, err := db.MaxLTX()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"read txid: %w\", err)\n\t}\n\treturn uint64(maxTXID), nil\n}\n\nfunc (s *Server) handleTXID(w http.ResponseWriter, r *http.Request) {\n\tpath := r.URL.Query().Get(\"path\")\n\tif path == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"path required\", nil)\n\t\treturn\n\t}\n\n\texpandedPath, err := s.expandPath(path)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\treturn\n\t}\n\n\tdb := s.store.FindDB(expandedPath)\n\tif db == nil {\n\t\twriteJSONError(w, http.StatusNotFound, \"database not found\", nil)\n\t\treturn\n\t}\n\n\tpos, err := db.Pos()\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, err.Error(), nil)\n\t\treturn\n\t}\n\n\twriteJSON(w, http.StatusOK, TXIDResponse{\n\t\tTXID: uint64(pos.TXID),\n\t})\n}\n\nfunc (s *Server) handleSyncStatus(w http.ResponseWriter, r *http.Request) {\n\tpath := r.URL.Query().Get(\"path\")\n\tif path != \"\" {\n\t\texpandedPath, err := s.expandPath(path)\n\t\tif err != nil {\n\t\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\t\treturn\n\t\t}\n\n\t\tdb := s.store.FindDB(expandedPath)\n\t\tif db == nil {\n\t\t\twriteJSONError(w, http.StatusNotFound, \"database not found\", nil)\n\t\t\treturn\n\t\t}\n\n\t\twriteJSON(w, http.StatusOK, SyncDiagnosticsResponse{\n\t\t\tDatabases: []SyncDiagnostic{db.SyncDiagnostic()},\n\t\t})\n\t\treturn\n\t}\n\n\tdbs := s.store.DBs()\n\tdiagnostics := make([]SyncDiagnostic, 0, len(dbs))\n\tfor _, db := range dbs {\n\t\tdiagnostics = append(diagnostics, db.SyncDiagnostic())\n\t}\n\twriteJSON(w, http.StatusOK, SyncDiagnosticsResponse{Databases: diagnostics})\n}\n\nfunc writeJSON(w http.ResponseWriter, status int, v interface{}) {\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(status)\n\tjson.NewEncoder(w).Encode(v)\n}\n\nfunc writeJSONError(w http.ResponseWriter, status int, message string, details interface{}) {\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(status)\n\tjson.NewEncoder(w).Encode(ErrorResponse{\n\t\tError: message,\n\t\tDetails: details,\n\t})\n}\n\n// StartRequest is the request body for the /start endpoint.\ntype StartRequest struct {\n\tPath string `json:\"path\"`\n\tTimeout int `json:\"timeout,omitempty\"`\n}\n\n// StartResponse is the response body for the /start endpoint.\ntype StartResponse struct {\n\tStatus string `json:\"status\"`\n\tPath string `json:\"path\"`\n\tTXID uint64 `json:\"txid\"`\n}\n\n// StopRequest is the request body for the /stop endpoint.\ntype StopRequest struct {\n\tPath string `json:\"path\"`\n\tTimeout int `json:\"timeout,omitempty\"`\n}\n\n// StopResponse is the response body for the /stop endpoint.\ntype StopResponse struct {\n\tStatus string `json:\"status\"`\n\tPath string `json:\"path\"`\n\tTXID uint64 `json:\"txid\"`\n}\n\n// ErrorResponse is returned when an error occurs.\ntype ErrorResponse struct {\n\tError string `json:\"error\"`\n\tDetails interface{} `json:\"details,omitempty\"`\n}\n\n// TXIDResponse is the response body for the /txid endpoint.\ntype TXIDResponse struct {\n\tTXID uint64 `json:\"txid\"`\n}\n\n// SyncDiagnosticsResponse is the response body for /debug/sync-status.\ntype SyncDiagnosticsResponse struct {\n\tDatabases []SyncDiagnostic `json:\"databases\"`\n}\n\nfunc (s *Server) handleSync(w http.ResponseWriter, r *http.Request) {\n\tvar req SyncRequest\n\tif err := json.NewDecoder(r.Body).Decode(\u0026req); err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, \"invalid request body\", err.Error())\n\t\treturn\n\t}\n\n\tif req.Path == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"path required\", nil)\n\t\treturn\n\t}\n\n\texpandedPath, err := s.expandPath(req.Path)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\treturn\n\t}\n\n\tctx := s.ctx\n\tif req.Wait \u0026\u0026 req.Timeout == 0 {\n\t\treq.Timeout = 30\n\t}\n\tif req.Timeout \u003e 0 {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(s.ctx, time.Duration(req.Timeout)*time.Second)\n\t\tdefer cancel()\n\t}\n\n\tresult, err := s.store.SyncDB(ctx, expandedPath, req.Wait)\n\tif err != nil {\n\t\tswitch {\n\t\tcase errors.Is(err, ErrDatabaseNotFound):\n\t\t\twriteJSONError(w, http.StatusNotFound, err.Error(), nil)\n\t\tcase errors.Is(err, ErrDatabaseNotOpen):\n\t\t\twriteJSONError(w, http.StatusConflict, err.Error(), nil)\n\t\tdefault:\n\t\t\twriteJSONError(w, http.StatusInternalServerError, err.Error(), nil)\n\t\t}\n\t\treturn\n\t}\n\n\tvar status string\n\tif !result.Changed {\n\t\tstatus = \"no_change\"\n\t} else if req.Wait {\n\t\tstatus = \"synced\"\n\t} else {\n\t\tstatus = \"synced_local\"\n\t}\n\n\twriteJSON(w, http.StatusOK, SyncResponse{\n\t\tStatus: status,\n\t\tPath: expandedPath,\n\t\tTXID: result.TXID,\n\t\tReplicatedTXID: result.ReplicatedTXID,\n\t})\n}\n\n// SyncRequest is the request body for the /sync endpoint.\ntype SyncRequest struct {\n\tPath string `json:\"path\"`\n\tWait bool `json:\"wait,omitempty\"`\n\tTimeout int `json:\"timeout,omitempty\"`\n}\n\n// SyncResponse is the response body for the /sync endpoint.\ntype SyncResponse struct {\n\tStatus string `json:\"status\"`\n\tPath string `json:\"path\"`\n\tTXID uint64 `json:\"txid\"`\n\tReplicatedTXID uint64 `json:\"replicated_txid\"`\n}\n\nfunc (s *Server) handleList(w http.ResponseWriter, _ *http.Request) {\n\tdbs := s.store.DBs()\n\tresp := ListResponse{\n\t\tDatabases: make([]DatabaseSummary, 0, len(dbs)),\n\t}\n\n\tfor _, db := range dbs {\n\t\tvar status string\n\t\tif db.IsOpen() {\n\t\t\tif db.Replica != nil \u0026\u0026 db.Replica.MonitorEnabled {\n\t\t\t\tstatus = \"replicating\"\n\t\t\t} else {\n\t\t\t\tstatus = \"open\"\n\t\t\t}\n\t\t} else {\n\t\t\tstatus = \"stopped\"\n\t\t}\n\n\t\tsummary := DatabaseSummary{\n\t\t\tPath: db.Path(),\n\t\t\tStatus: status,\n\t\t}\n\n\t\tif t := db.LastSuccessfulSyncAt(); !t.IsZero() {\n\t\t\tsummary.LastSyncAt = \u0026t\n\t\t}\n\n\t\tresp.Databases = append(resp.Databases, summary)\n\t}\n\n\twriteJSON(w, http.StatusOK, resp)\n}\n\nfunc (s *Server) handleInfo(w http.ResponseWriter, _ *http.Request) {\n\tresp := InfoResponse{\n\t\tVersion: s.Version,\n\t\tPID: os.Getpid(),\n\t\tStartedAt: s.startedAt,\n\t\tUptimeSeconds: int64(time.Since(s.startedAt).Seconds()),\n\t\tDatabaseCount: len(s.store.DBs()),\n\t}\n\n\twriteJSON(w, http.StatusOK, resp)\n}\n\n// ListResponse is the response body for the /list endpoint.\ntype ListResponse struct {\n\tDatabases []DatabaseSummary `json:\"databases\"`\n}\n\n// DatabaseSummary contains summary information about a database.\ntype DatabaseSummary struct {\n\tPath string `json:\"path\"`\n\tStatus string `json:\"status\"`\n\n\t// LastSyncAt is the timestamp of the last successful replica sync.\n\t// This reflects when data was last successfully uploaded to the replica\n\t// storage backend, not just when the local WAL was processed.\n\tLastSyncAt *time.Time `json:\"last_sync_at,omitempty\"`\n}\n\n// InfoResponse is the response body for the /info endpoint.\ntype InfoResponse struct {\n\tVersion string `json:\"version\"`\n\tPID int `json:\"pid\"`\n\tUptimeSeconds int64 `json:\"uptime_seconds\"`\n\tStartedAt time.Time `json:\"started_at\"`\n\tDatabaseCount int `json:\"database_count\"`\n}\n\n// RegisterDatabaseRequest is the request body for the /register endpoint.\ntype RegisterDatabaseRequest struct {\n\tPath string `json:\"path\"`\n\tReplicaURL string `json:\"replica_url\"`\n}\n\n// RegisterDatabaseResponse is the response body for the /register endpoint.\ntype RegisterDatabaseResponse struct {\n\tStatus string `json:\"status\"`\n\tPath string `json:\"path\"`\n}\n\n// UnregisterDatabaseRequest is the request body for the /unregister endpoint.\ntype UnregisterDatabaseRequest struct {\n\tPath string `json:\"path\"`\n\tTimeout int `json:\"timeout,omitempty\"`\n}\n\n// UnregisterDatabaseResponse is the response body for the /unregister endpoint.\ntype UnregisterDatabaseResponse struct {\n\tStatus string `json:\"status\"`\n\tPath string `json:\"path\"`\n\tTXID uint64 `json:\"txid\"`\n}\n\nfunc (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {\n\tvar req RegisterDatabaseRequest\n\tif err := json.NewDecoder(r.Body).Decode(\u0026req); err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, \"invalid request body\", err.Error())\n\t\treturn\n\t}\n\n\tif req.Path == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"path required\", nil)\n\t\treturn\n\t}\n\n\tif req.ReplicaURL == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"replica_url required\", nil)\n\t\treturn\n\t}\n\n\texpandedPath, err := s.expandPath(req.Path)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\treturn\n\t}\n\n\t// Check if database already exists.\n\tif existing := s.store.FindDB(expandedPath); existing != nil {\n\t\twriteJSON(w, http.StatusOK, RegisterDatabaseResponse{\n\t\t\tStatus: \"already_registered\",\n\t\t\tPath: expandedPath,\n\t\t})\n\t\treturn\n\t}\n\n\t// Create replica client from URL.\n\tclient, err := NewReplicaClientFromURL(req.ReplicaURL)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid replica url: %v\", err), nil)\n\t\treturn\n\t}\n\n\t// Create new database.\n\tdb := NewDB(expandedPath)\n\n\t// Create replica and attach client.\n\treplica := NewReplica(db)\n\treplica.Client = client\n\tdb.Replica = replica\n\n\t// Register database with store (this also opens the database).\n\tif err := s.store.RegisterDB(db); err != nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, fmt.Sprintf(\"failed to register database: %v\", err), nil)\n\t\treturn\n\t}\n\n\twriteJSON(w, http.StatusOK, RegisterDatabaseResponse{\n\t\tStatus: \"registered\",\n\t\tPath: expandedPath,\n\t})\n}\n\nfunc (s *Server) handleUnregister(w http.ResponseWriter, r *http.Request) {\n\tvar req UnregisterDatabaseRequest\n\tif err := json.NewDecoder(r.Body).Decode(\u0026req); err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, \"invalid request body\", err.Error())\n\t\treturn\n\t}\n\n\tif req.Path == \"\" {\n\t\twriteJSONError(w, http.StatusBadRequest, \"path required\", nil)\n\t\treturn\n\t}\n\n\texpandedPath, err := s.expandPath(req.Path)\n\tif err != nil {\n\t\twriteJSONError(w, http.StatusBadRequest, fmt.Sprintf(\"invalid path: %v\", err), nil)\n\t\treturn\n\t}\n\n\t// Set up timeout context. Treat non-positive values as default.\n\ttimeout := req.Timeout\n\tif timeout \u003c= 0 {\n\t\ttimeout = 30\n\t}\n\tctx, cancel := context.WithTimeout(s.ctx, time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\tdb := s.store.FindDB(expandedPath)\n\n\t// Remove database from store (this also closes it).\n\tif err := s.store.UnregisterDB(ctx, expandedPath); err != nil {\n\t\twriteJSONError(w, http.StatusInternalServerError, fmt.Sprintf(\"failed to unregister database: %v\", err), nil)\n\t\treturn\n\t}\n\tvar txID uint64\n\tstatus := \"already_unregistered\"\n\tif db != nil {\n\t\t_, maxTXID, err := db.MaxLTX()\n\t\tif err != nil {\n\t\t\twriteJSONError(w, http.StatusInternalServerError, fmt.Sprintf(\"failed to read final txid: %v\", err), nil)\n\t\t\treturn\n\t\t}\n\t\ttxID = uint64(maxTXID)\n\t\tstatus = \"unregistered\"\n\t}\n\n\twriteJSON(w, http.StatusOK, UnregisterDatabaseResponse{\n\t\tStatus: status,\n\t\tPath: expandedPath,\n\t\tTXID: txID,\n\t})\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"json","path":"encoding/json","span":{"start":[5,1],"end":[5,17],"bytes":[40,56]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[57,66]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[67,73]}},{"name":"slog","path":"log/slog","span":{"start":[8,1],"end":[8,12],"bytes":[74,85]}},{"name":"net","path":"net","span":{"start":[9,1],"end":[9,7],"bytes":[86,92]}},{"name":"http","path":"net/http","span":{"start":[10,1],"end":[10,12],"bytes":[93,104]}},{"name":"pprof","path":"net/http/pprof","span":{"start":[11,1],"end":[11,18],"bytes":[105,122]}},{"name":"os","path":"os","span":{"start":[12,1],"end":[12,6],"bytes":[123,128]}},{"name":"sync","path":"sync","span":{"start":[13,1],"end":[13,8],"bytes":[129,136]}},{"name":"time","path":"time","span":{"start":[14,1],"end":[14,8],"bytes":[137,144]}}],"types":{"DatabaseSummary":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DatabaseSummary","kind":"struct","span":{"start":[527,1],"end":[535,2],"bytes":[13353,13707]},"callables":{},"fields":{"LastSyncAt":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DatabaseSummary/LastSyncAt","kind":"field","type":"*time.Time","span":{"start":[534,1],"end":[534,55],"bytes":[13651,13705]}},"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DatabaseSummary/Path","kind":"field","type":"string","span":{"start":[528,1],"end":[528,29],"bytes":[13383,13411]}},"Status":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DatabaseSummary/Status","kind":"field","type":"string","span":{"start":[529,1],"end":[529,31],"bytes":[13412,13442]}}}},"ErrorResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.ErrorResponse","kind":"struct","span":{"start":[388,1],"end":[391,2],"bytes":[9884,9997]},"callables":{},"fields":{"Details":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.ErrorResponse/Details","kind":"field","type":"interface{}","span":{"start":[390,1],"end":[390,48],"bytes":[9948,9995]}},"Error":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.ErrorResponse/Error","kind":"field","type":"string","span":{"start":[389,1],"end":[389,36],"bytes":[9912,9947]}}}},"InfoResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.InfoResponse","kind":"struct","span":{"start":[538,1],"end":[544,2],"bytes":[13770,14021]},"callables":{},"fields":{"DatabaseCount":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.InfoResponse/DatabaseCount","kind":"field","type":"int","span":{"start":[543,1],"end":[543,49],"bytes":[13971,14019]}},"PID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.InfoResponse/PID","kind":"field","type":"int","span":{"start":[540,1],"end":[540,38],"bytes":[13839,13876]}},"StartedAt":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.InfoResponse/StartedAt","kind":"field","type":"time.Time","span":{"start":[542,1],"end":[542,45],"bytes":[13926,13970]}},"UptimeSeconds":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.InfoResponse/UptimeSeconds","kind":"field","type":"int64","span":{"start":[541,1],"end":[541,49],"bytes":[13877,13925]}},"Version":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.InfoResponse/Version","kind":"field","type":"string","span":{"start":[539,1],"end":[539,42],"bytes":[13797,13838]}}}},"ListResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.ListResponse","kind":"struct","span":{"start":[522,1],"end":[524,2],"bytes":[13209,13285]},"callables":{},"fields":{"Databases":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.ListResponse/Databases","kind":"field","type":"[]github.com/benbjohnson/litestream.DatabaseSummary","span":{"start":[523,1],"end":[523,48],"bytes":[13236,13283]}}}},"RegisterDatabaseRequest":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.RegisterDatabaseRequest","kind":"struct","span":{"start":[547,1],"end":[550,2],"bytes":[14098,14210]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.RegisterDatabaseRequest/Path","kind":"field","type":"string","span":{"start":[548,1],"end":[548,33],"bytes":[14136,14168]}},"ReplicaURL":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.RegisterDatabaseRequest/ReplicaURL","kind":"field","type":"string","span":{"start":[549,1],"end":[549,40],"bytes":[14169,14208]}}}},"RegisterDatabaseResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.RegisterDatabaseResponse","kind":"struct","span":{"start":[553,1],"end":[556,2],"bytes":[14289,14389]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.RegisterDatabaseResponse/Path","kind":"field","type":"string","span":{"start":[555,1],"end":[555,29],"bytes":[14359,14387]}},"Status":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.RegisterDatabaseResponse/Status","kind":"field","type":"string","span":{"start":[554,1],"end":[554,31],"bytes":[14328,14358]}}}},"Server":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server","kind":"struct","span":{"start":[34,1],"end":[61,2],"bytes":[638,1253]},"callables":{"github.com/benbjohnson/litestream.Server.Close":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Close","kind":"method","span":{"start":[145,1],"end":[158,2],"bytes":[3780,4090]},"signature":"github.com/benbjohnson/litestream.Server.Close","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"146:2":{"kind":"call","span":{"start":[146,2],"end":[146,12],"bytes":[3814,3824]},"callee":null},"148:17":{"kind":"call","span":{"start":[148,17],"end":[148,73],"bytes":[3842,3898]},"callee":null},"148:37":{"kind":"call","span":{"start":[148,37],"end":[148,57],"bytes":[3862,3882]},"callee":null},"149:8":{"kind":"call","span":{"start":[149,8],"end":[149,16],"bytes":[3906,3914]},"callee":null},"152:13":{"kind":"call","span":{"start":[152,13],"end":[152,39],"bytes":[3954,3980]},"callee":null},"153:4":{"kind":"call","span":{"start":[153,4],"end":[153,62],"bytes":[3998,4056]},"callee":null},"156:2":{"kind":"call","span":{"start":[156,2],"end":[156,13],"bytes":[4065,4076]},"callee":null}}},"github.com/benbjohnson/litestream.Server.Start":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Start","kind":"method","span":{"start":[98,1],"end":[142,2],"bytes":[2437,3727]},"signature":"github.com/benbjohnson/litestream.Server.Start","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"100:10":{"kind":"call","span":{"start":[100,10],"end":[100,44],"bytes":[2504,2538]},"callee":null},"104:18":{"kind":"call","span":{"start":[104,18],"end":[104,40],"bytes":[2633,2655]},"callee":null},"105:6":{"kind":"call","span":{"start":[105,6],"end":[105,17],"bytes":[2675,2686]},"callee":null},"106:14":{"kind":"call","span":{"start":[106,14],"end":[106,37],"bytes":[2721,2744]},"callee":null},"107:12":{"kind":"call","span":{"start":[107,12],"end":[107,57],"bytes":[2770,2815]},"callee":null},"110:11":{"kind":"call","span":{"start":[110,11],"end":[110,81],"bytes":[2842,2912]},"callee":null},"112:13":{"kind":"call","span":{"start":[112,13],"end":[112,31],"bytes":[2929,2947]},"callee":null},"113:10":{"kind":"call","span":{"start":[113,10],"end":[113,50],"bytes":[2959,2999]},"callee":null},"116:19":{"kind":"call","span":{"start":[116,19],"end":[116,51],"bytes":[3022,3054]},"callee":null},"118:10":{"kind":"call","span":{"start":[118,10],"end":[118,54],"bytes":[3081,3125]},"callee":null},"122:12":{"kind":"call","span":{"start":[122,12],"end":[122,62],"bytes":[3170,3220]},"callee":null},"122:35":{"kind":"call","span":{"start":[122,35],"end":[122,61],"bytes":[3193,3219]},"callee":null},"123:3":{"kind":"call","span":{"start":[123,3],"end":[123,19],"bytes":[3237,3253]},"callee":null},"124:10":{"kind":"call","span":{"start":[124,10],"end":[124,45],"bytes":[3263,3298]},"callee":null},"129:16":{"kind":"call","span":{"start":[129,16],"end":[129,26],"bytes":[3441,3451]},"callee":null},"131:2":{"kind":"call","span":{"start":[131,2],"end":[131,65],"bytes":[3454,3517]},"callee":null},"133:2":{"kind":"call","span":{"start":[133,2],"end":[133,13],"bytes":[3520,3531]},"callee":null},"134:5":{"kind":"call","span":{"start":[134,5],"end":[139,5],"bytes":[3536,3712]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Start/github.com/benbjohnson/litestream.Server.Start.closure_1","kind":"function","span":{"start":[134,1],"end":[139,5],"bytes":[3532,3712]},"signature":"github.com/benbjohnson/litestream.Server.Start.closure_1","parameters":[],"body":{"135:9":{"kind":"call","span":{"start":[135,9],"end":[135,20],"bytes":[3553,3564]},"callee":null},"136:13":{"kind":"call","span":{"start":[136,13],"end":[136,41],"bytes":[3577,3605]},"callee":null},"137:4":{"kind":"call","span":{"start":[137,4],"end":[137,53],"bytes":[3654,3703]},"callee":null}}}}},"github.com/benbjohnson/litestream.Server.expandPath":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","kind":"method","span":{"start":[161,1],"end":[166,2],"bytes":[4150,4289]},"signature":"github.com/benbjohnson/litestream.Server.expandPath","parameters":[{"name":"path","type":"string","span":{"start":[161,1],"end":[161,59],"bytes":[4150,4208]}}],"return_type":"(string, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"163:10":{"kind":"call","span":{"start":[163,10],"end":[163,30],"bytes":[4246,4266]},"callee":null}}},"github.com/benbjohnson/litestream.Server.handleInfo":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleInfo","kind":"method","span":{"start":[509,1],"end":[519,2],"bytes":[12829,13146]},"signature":"github.com/benbjohnson/litestream.Server.handleInfo","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[509,1],"end":[509,70],"bytes":[12829,12898]}},{"name":"_","type":"*net/http.Request","span":{"start":[509,1],"end":[509,70],"bytes":[12829,12898]}}],"metrics":{"cyclomatic":1},"body":{"512:18":{"kind":"call","span":{"start":[512,18],"end":[512,29],"bytes":[12967,12978]},"callee":null},"514:18":{"kind":"call","span":{"start":[514,18],"end":[514,58],"bytes":[13027,13067]},"callee":null},"514:24":{"kind":"call","span":{"start":[514,24],"end":[514,47],"bytes":[13033,13056]},"callee":null},"515:18":{"kind":"call","span":{"start":[515,18],"end":[515,36],"bytes":[13086,13104]},"callee":null},"515:22":{"kind":"call","span":{"start":[515,22],"end":[515,35],"bytes":[13090,13103]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"518:2":{"kind":"call","span":{"start":[518,2],"end":[518,35],"bytes":[13111,13144]},"callee":null}}},"github.com/benbjohnson/litestream.Server.handleList":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleList","kind":"method","span":{"start":[476,1],"end":[507,2],"bytes":[12185,12827]},"signature":"github.com/benbjohnson/litestream.Server.handleList","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[476,1],"end":[476,70],"bytes":[12185,12254]}},{"name":"_","type":"*net/http.Request","span":{"start":[476,1],"end":[476,70],"bytes":[12185,12254]}}],"metrics":{"cyclomatic":5},"body":{"477:9":{"kind":"call","span":{"start":[477,9],"end":[477,22],"bytes":[12263,12276]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"479:14":{"kind":"call","span":{"start":[479,14],"end":[479,50],"bytes":[12313,12349]},"callee":null},"479:41":{"kind":"call","span":{"start":[479,41],"end":[479,49],"bytes":[12340,12348]},"callee":null},"484:6":{"kind":"call","span":{"start":[484,6],"end":[484,17],"bytes":[12406,12417]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"495:12":{"kind":"call","span":{"start":[495,12],"end":[495,21],"bytes":[12618,12627]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"499:11":{"kind":"call","span":{"start":[499,11],"end":[499,36],"bytes":[12663,12688]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt"},"499:39":{"kind":"call","span":{"start":[499,39],"end":[499,49],"bytes":[12691,12701]},"callee":null},"503:20":{"kind":"call","span":{"start":[503,20],"end":[503,51],"bytes":[12755,12786]},"callee":null},"506:2":{"kind":"call","span":{"start":[506,2],"end":[506,35],"bytes":[12792,12825]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.handleRegister":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","kind":"method","span":{"start":[571,1],"end":[628,2],"bytes":[14800,16393]},"signature":"github.com/benbjohnson/litestream.Server.handleRegister","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[571,1],"end":[571,74],"bytes":[14800,14873]}},{"name":"r","type":"*net/http.Request","span":{"start":[571,1],"end":[571,74],"bytes":[14800,14873]}}],"metrics":{"cyclomatic":8},"body":{"573:12":{"kind":"call","span":{"start":[573,12],"end":[573,35],"bytes":[14918,14941]},"callee":null},"574:3":{"kind":"call","span":{"start":[574,3],"end":[574,80],"bytes":[14971,15048]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"574:68":{"kind":"call","span":{"start":[574,68],"end":[574,79],"bytes":[15036,15047]},"callee":null},"579:3":{"kind":"call","span":{"start":[579,3],"end":[579,65],"bytes":[15085,15147]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"584:3":{"kind":"call","span":{"start":[584,3],"end":[584,72],"bytes":[15190,15259]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"588:23":{"kind":"call","span":{"start":[588,23],"end":[588,45],"bytes":[15295,15317]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath"},"590:3":{"kind":"call","span":{"start":[590,3],"end":[590,86],"bytes":[15337,15420]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"590:44":{"kind":"call","span":{"start":[590,44],"end":[590,80],"bytes":[15378,15414]},"callee":null},"595:17":{"kind":"call","span":{"start":[595,17],"end":[595,45],"bytes":[15488,15516]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"596:3":{"kind":"call","span":{"start":[596,3],"end":[599,5],"bytes":[15538,15654]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"},"604:17":{"kind":"call","span":{"start":[604,17],"end":[604,56],"bytes":[15720,15759]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.NewReplicaClientFromURL"},"606:3":{"kind":"call","span":{"start":[606,3],"end":[606,93],"bytes":[15779,15869]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"606:44":{"kind":"call","span":{"start":[606,44],"end":[606,87],"bytes":[15820,15863]},"callee":null},"611:8":{"kind":"call","span":{"start":[611,8],"end":[611,27],"bytes":[15915,15934]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB"},"614:13":{"kind":"call","span":{"start":[614,13],"end":[614,27],"bytes":[15986,16000]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica"},"619:12":{"kind":"call","span":{"start":[619,12],"end":[619,34],"bytes":[16125,16147]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB"},"620:3":{"kind":"call","span":{"start":[620,3],"end":[620,110],"bytes":[16164,16271]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"620:53":{"kind":"call","span":{"start":[620,53],"end":[620,104],"bytes":[16214,16265]},"callee":null},"624:2":{"kind":"call","span":{"start":[624,2],"end":[627,4],"bytes":[16286,16391]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.handleStart":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","kind":"method","span":{"start":[168,1],"end":[219,2],"bytes":[4291,5621]},"signature":"github.com/benbjohnson/litestream.Server.handleStart","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[168,1],"end":[168,71],"bytes":[4291,4361]}},{"name":"r","type":"*net/http.Request","span":{"start":[168,1],"end":[168,71],"bytes":[4291,4361]}}],"metrics":{"cyclomatic":9},"body":{"170:12":{"kind":"call","span":{"start":[170,12],"end":[170,35],"bytes":[4395,4418]},"callee":null},"171:3":{"kind":"call","span":{"start":[171,3],"end":[171,80],"bytes":[4448,4525]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"171:68":{"kind":"call","span":{"start":[171,68],"end":[171,79],"bytes":[4513,4524]},"callee":null},"176:3":{"kind":"call","span":{"start":[176,3],"end":[176,65],"bytes":[4562,4624]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"180:23":{"kind":"call","span":{"start":[180,23],"end":[180,45],"bytes":[4660,4682]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath"},"182:3":{"kind":"call","span":{"start":[182,3],"end":[182,86],"bytes":[4702,4785]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"182:44":{"kind":"call","span":{"start":[182,44],"end":[182,80],"bytes":[4743,4779]},"callee":null},"189:17":{"kind":"call","span":{"start":[189,17],"end":[189,83],"bytes":[4883,4949]},"callee":null},"189:44":{"kind":"call","span":{"start":[189,44],"end":[189,70],"bytes":[4910,4936]},"callee":null},"190:9":{"kind":"call","span":{"start":[190,9],"end":[190,17],"bytes":[4958,4966]},"callee":null},"193:8":{"kind":"call","span":{"start":[193,8],"end":[193,36],"bytes":[4978,5006]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"195:3":{"kind":"call","span":{"start":[195,3],"end":[195,110],"bytes":[5025,5132]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"195:53":{"kind":"call","span":{"start":[195,53],"end":[195,104],"bytes":[5075,5126]},"callee":null},"200:5":{"kind":"call","span":{"start":[200,5],"end":[200,16],"bytes":[5171,5182]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"203:13":{"kind":"call","span":{"start":[203,13],"end":[203,48],"bytes":[5236,5271]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnableDB"},"204:4":{"kind":"call","span":{"start":[204,4],"end":[204,71],"bytes":[5289,5356]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"204:54":{"kind":"call","span":{"start":[204,54],"end":[204,65],"bytes":[5339,5350]},"callee":null},"208:15":{"kind":"call","span":{"start":[208,15],"end":[208,40],"bytes":[5388,5413]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID"},"210:3":{"kind":"call","span":{"start":[210,3],"end":[210,70],"bytes":[5433,5500]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"210:53":{"kind":"call","span":{"start":[210,53],"end":[210,64],"bytes":[5483,5494]},"callee":null},"214:2":{"kind":"call","span":{"start":[214,2],"end":[218,4],"bytes":[5515,5619]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.handleStop":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","kind":"method","span":{"start":[221,1],"end":[272,2],"bytes":[5623,6937]},"signature":"github.com/benbjohnson/litestream.Server.handleStop","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[221,1],"end":[221,70],"bytes":[5623,5692]}},{"name":"r","type":"*net/http.Request","span":{"start":[221,1],"end":[221,70],"bytes":[5623,5692]}}],"metrics":{"cyclomatic":9},"body":{"223:12":{"kind":"call","span":{"start":[223,12],"end":[223,35],"bytes":[5725,5748]},"callee":null},"224:3":{"kind":"call","span":{"start":[224,3],"end":[224,80],"bytes":[5778,5855]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"224:68":{"kind":"call","span":{"start":[224,68],"end":[224,79],"bytes":[5843,5854]},"callee":null},"229:3":{"kind":"call","span":{"start":[229,3],"end":[229,65],"bytes":[5892,5954]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"233:23":{"kind":"call","span":{"start":[233,23],"end":[233,45],"bytes":[5990,6012]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath"},"235:3":{"kind":"call","span":{"start":[235,3],"end":[235,86],"bytes":[6032,6115]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"235:44":{"kind":"call","span":{"start":[235,44],"end":[235,80],"bytes":[6073,6109]},"callee":null},"243:17":{"kind":"call","span":{"start":[243,17],"end":[243,79],"bytes":[6206,6268]},"callee":null},"243:44":{"kind":"call","span":{"start":[243,44],"end":[243,66],"bytes":[6233,6255]},"callee":null},"244:8":{"kind":"call","span":{"start":[244,8],"end":[244,16],"bytes":[6276,6284]},"callee":null},"246:8":{"kind":"call","span":{"start":[246,8],"end":[246,36],"bytes":[6293,6321]},"callee":null},"248:3":{"kind":"call","span":{"start":[248,3],"end":[248,110],"bytes":[6340,6447]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"248:53":{"kind":"call","span":{"start":[248,53],"end":[248,104],"bytes":[6390,6441]},"callee":null},"253:6":{"kind":"call","span":{"start":[253,6],"end":[253,17],"bytes":[6487,6498]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"256:13":{"kind":"call","span":{"start":[256,13],"end":[256,49],"bytes":[6552,6588]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DisableDB"},"257:4":{"kind":"call","span":{"start":[257,4],"end":[257,71],"bytes":[6606,6673]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"257:54":{"kind":"call","span":{"start":[257,54],"end":[257,65],"bytes":[6656,6667]},"callee":null},"261:15":{"kind":"call","span":{"start":[261,15],"end":[261,40],"bytes":[6705,6730]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID"},"263:3":{"kind":"call","span":{"start":[263,3],"end":[263,70],"bytes":[6750,6817]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"263:53":{"kind":"call","span":{"start":[263,53],"end":[263,64],"bytes":[6800,6811]},"callee":null},"267:2":{"kind":"call","span":{"start":[267,2],"end":[271,4],"bytes":[6832,6935]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.handleSync":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSync","kind":"method","span":{"start":[403,1],"end":[459,2],"bytes":[10277,11730]},"signature":"github.com/benbjohnson/litestream.Server.handleSync","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[403,1],"end":[403,70],"bytes":[10277,10346]}},{"name":"r","type":"*net/http.Request","span":{"start":[403,1],"end":[403,70],"bytes":[10277,10346]}}],"metrics":{"cyclomatic":13},"body":{"405:12":{"kind":"call","span":{"start":[405,12],"end":[405,35],"bytes":[10379,10402]},"callee":null},"406:3":{"kind":"call","span":{"start":[406,3],"end":[406,80],"bytes":[10432,10509]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"406:68":{"kind":"call","span":{"start":[406,68],"end":[406,79],"bytes":[10497,10508]},"callee":null},"411:3":{"kind":"call","span":{"start":[411,3],"end":[411,65],"bytes":[10546,10608]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"415:23":{"kind":"call","span":{"start":[415,23],"end":[415,45],"bytes":[10644,10666]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath"},"417:3":{"kind":"call","span":{"start":[417,3],"end":[417,86],"bytes":[10686,10769]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"417:44":{"kind":"call","span":{"start":[417,44],"end":[417,80],"bytes":[10727,10763]},"callee":null},"427:17":{"kind":"call","span":{"start":[427,17],"end":[427,83],"bytes":[10924,10990]},"callee":null},"427:44":{"kind":"call","span":{"start":[427,44],"end":[427,70],"bytes":[10951,10977]},"callee":null},"428:9":{"kind":"call","span":{"start":[428,9],"end":[428,17],"bytes":[10999,11007]},"callee":null},"431:17":{"kind":"call","span":{"start":[431,17],"end":[431,60],"bytes":[11028,11071]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB"},"434:8":{"kind":"call","span":{"start":[434,8],"end":[434,43],"bytes":[11107,11142]},"callee":null},"435:4":{"kind":"call","span":{"start":[435,4],"end":[435,60],"bytes":[11147,11203]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"435:43":{"kind":"call","span":{"start":[435,43],"end":[435,54],"bytes":[11186,11197]},"callee":null},"436:8":{"kind":"call","span":{"start":[436,8],"end":[436,42],"bytes":[11211,11245]},"callee":null},"437:4":{"kind":"call","span":{"start":[437,4],"end":[437,60],"bytes":[11250,11306]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"437:43":{"kind":"call","span":{"start":[437,43],"end":[437,54],"bytes":[11289,11300]},"callee":null},"439:4":{"kind":"call","span":{"start":[439,4],"end":[439,71],"bytes":[11321,11388]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"439:54":{"kind":"call","span":{"start":[439,54],"end":[439,65],"bytes":[11371,11382]},"callee":null},"453:2":{"kind":"call","span":{"start":[453,2],"end":[458,4],"bytes":[11553,11728]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.handleSyncStatus":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","kind":"method","span":{"start":[317,1],"end":[344,2],"bytes":[7917,8710]},"signature":"github.com/benbjohnson/litestream.Server.handleSyncStatus","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[317,1],"end":[317,76],"bytes":[7917,7992]}},{"name":"r","type":"*net/http.Request","span":{"start":[317,1],"end":[317,76],"bytes":[7917,7992]}}],"metrics":{"cyclomatic":5},"body":{"318:10":{"kind":"call","span":{"start":[318,10],"end":[318,23],"bytes":[8002,8015]},"callee":null},"320:24":{"kind":"call","span":{"start":[320,24],"end":[320,42],"bytes":[8068,8086]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath"},"322:4":{"kind":"call","span":{"start":[322,4],"end":[322,87],"bytes":[8108,8191]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"322:45":{"kind":"call","span":{"start":[322,45],"end":[322,81],"bytes":[8149,8185]},"callee":null},"326:9":{"kind":"call","span":{"start":[326,9],"end":[326,37],"bytes":[8215,8243]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"328:4":{"kind":"call","span":{"start":[328,4],"end":[328,69],"bytes":[8264,8329]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"332:3":{"kind":"call","span":{"start":[332,3],"end":[334,5],"bytes":[8347,8457]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"},"333:32":{"kind":"call","span":{"start":[333,32],"end":[333,51],"bytes":[8431,8450]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncDiagnostic"},"338:9":{"kind":"call","span":{"start":[338,9],"end":[338,22],"bytes":[8479,8492]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"339:17":{"kind":"call","span":{"start":[339,17],"end":[339,52],"bytes":[8509,8544]},"callee":null},"339:43":{"kind":"call","span":{"start":[339,43],"end":[339,51],"bytes":[8535,8543]},"callee":null},"341:17":{"kind":"call","span":{"start":[341,17],"end":[341,57],"bytes":[8587,8627]},"callee":null},"341:37":{"kind":"call","span":{"start":[341,37],"end":[341,56],"bytes":[8607,8626]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncDiagnostic"},"343:2":{"kind":"call","span":{"start":[343,2],"end":[343,78],"bytes":[8632,8708]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.handleTXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleTXID","kind":"method","span":{"start":[287,1],"end":[315,2],"bytes":[7229,7915]},"signature":"github.com/benbjohnson/litestream.Server.handleTXID","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[287,1],"end":[287,70],"bytes":[7229,7298]}},{"name":"r","type":"*net/http.Request","span":{"start":[287,1],"end":[287,70],"bytes":[7229,7298]}}],"metrics":{"cyclomatic":5},"body":{"288:10":{"kind":"call","span":{"start":[288,10],"end":[288,23],"bytes":[7308,7321]},"callee":null},"290:3":{"kind":"call","span":{"start":[290,3],"end":[290,65],"bytes":[7353,7415]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"294:23":{"kind":"call","span":{"start":[294,23],"end":[294,41],"bytes":[7451,7469]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath"},"296:3":{"kind":"call","span":{"start":[296,3],"end":[296,86],"bytes":[7489,7572]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"296:44":{"kind":"call","span":{"start":[296,44],"end":[296,80],"bytes":[7530,7566]},"callee":null},"300:8":{"kind":"call","span":{"start":[300,8],"end":[300,36],"bytes":[7593,7621]},"callee":null},"302:3":{"kind":"call","span":{"start":[302,3],"end":[302,68],"bytes":[7640,7705]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"306:14":{"kind":"call","span":{"start":[306,14],"end":[306,22],"bytes":[7732,7740]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"308:3":{"kind":"call","span":{"start":[308,3],"end":[308,70],"bytes":[7760,7827]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"308:53":{"kind":"call","span":{"start":[308,53],"end":[308,64],"bytes":[7810,7821]},"callee":null},"312:2":{"kind":"call","span":{"start":[312,2],"end":[314,4],"bytes":[7842,7913]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"},"313:9":{"kind":"call","span":{"start":[313,9],"end":[313,25],"bytes":[7892,7908]},"callee":null}}},"github.com/benbjohnson/litestream.Server.handleUnregister":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleUnregister","kind":"method","span":{"start":[630,1],"end":[680,2],"bytes":[16395,17840]},"signature":"github.com/benbjohnson/litestream.Server.handleUnregister","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[630,1],"end":[630,76],"bytes":[16395,16470]}},{"name":"r","type":"*net/http.Request","span":{"start":[630,1],"end":[630,76],"bytes":[16395,16470]}}],"metrics":{"cyclomatic":8},"body":{"632:12":{"kind":"call","span":{"start":[632,12],"end":[632,35],"bytes":[16517,16540]},"callee":null},"633:3":{"kind":"call","span":{"start":[633,3],"end":[633,80],"bytes":[16570,16647]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"633:68":{"kind":"call","span":{"start":[633,68],"end":[633,79],"bytes":[16635,16646]},"callee":null},"638:3":{"kind":"call","span":{"start":[638,3],"end":[638,65],"bytes":[16684,16746]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"642:23":{"kind":"call","span":{"start":[642,23],"end":[642,45],"bytes":[16782,16804]},"callee":null},"644:3":{"kind":"call","span":{"start":[644,3],"end":[644,86],"bytes":[16824,16907]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"644:44":{"kind":"call","span":{"start":[644,44],"end":[644,80],"bytes":[16865,16901]},"callee":null},"653:17":{"kind":"call","span":{"start":[653,17],"end":[653,79],"bytes":[17064,17126]},"callee":null},"653:44":{"kind":"call","span":{"start":[653,44],"end":[653,66],"bytes":[17091,17113]},"callee":null},"654:8":{"kind":"call","span":{"start":[654,8],"end":[654,16],"bytes":[17134,17142]},"callee":null},"656:8":{"kind":"call","span":{"start":[656,8],"end":[656,36],"bytes":[17151,17179]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"659:12":{"kind":"call","span":{"start":[659,12],"end":[659,51],"bytes":[17246,17285]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB"},"660:3":{"kind":"call","span":{"start":[660,3],"end":[660,112],"bytes":[17302,17411]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"660:53":{"kind":"call","span":{"start":[660,53],"end":[660,106],"bytes":[17352,17405]},"callee":null},"666:22":{"kind":"call","span":{"start":[666,22],"end":[666,33],"bytes":[17512,17523]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX"},"668:4":{"kind":"call","span":{"start":[668,4],"end":[668,109],"bytes":[17545,17650]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"668:54":{"kind":"call","span":{"start":[668,54],"end":[668,103],"bytes":[17595,17644]},"callee":null},"671:10":{"kind":"call","span":{"start":[671,10],"end":[671,25],"bytes":[17674,17689]},"callee":null},"675:2":{"kind":"call","span":{"start":[675,2],"end":[679,4],"bytes":[17721,17838]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"}}},"github.com/benbjohnson/litestream.Server.storeTXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID","kind":"method","span":{"start":[274,1],"end":[285,2],"bytes":[6939,7227]},"signature":"github.com/benbjohnson/litestream.Server.storeTXID","parameters":[{"name":"path","type":"string","span":{"start":[274,1],"end":[274,58],"bytes":[6939,6996]}}],"return_type":"(uint64, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"275:8":{"kind":"call","span":{"start":[275,8],"end":[275,28],"bytes":[7004,7024]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"277:13":{"kind":"call","span":{"start":[277,13],"end":[277,55],"bytes":[7053,7095]},"callee":null},"280:21":{"kind":"call","span":{"start":[280,21],"end":[280,32],"bytes":[7120,7131]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX"},"282:13":{"kind":"call","span":{"start":[282,13],"end":[282,45],"bytes":[7161,7193]},"callee":null},"284:9":{"kind":"call","span":{"start":[284,9],"end":[284,24],"bytes":[7205,7220]},"callee":null}}}},"fields":{"PathExpander":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/PathExpander","kind":"field","type":"func(string) (string, error)","span":{"start":[45,1],"end":[45,43],"bytes":[915,957]}},"SocketPath":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/SocketPath","kind":"field","type":"string","span":{"start":[38,1],"end":[38,19],"bytes":[721,739]}},"SocketPerms":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/SocketPerms","kind":"field","type":"uint32","span":{"start":[41,1],"end":[41,20],"bytes":[797,816]}},"Version":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/Version","kind":"field","type":"string","span":{"start":[48,1],"end":[48,16],"bytes":[1013,1028]}},"cancel":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/cancel","kind":"field","type":"context.CancelFunc","span":{"start":[57,1],"end":[57,27],"bytes":[1180,1206]}},"ctx":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/ctx","kind":"field","type":"context.Context","span":{"start":[56,1],"end":[56,24],"bytes":[1156,1179]}},"httpServer":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/httpServer","kind":"field","type":"*net/http.Server","span":{"start":[54,1],"end":[54,29],"bytes":[1126,1154]}},"logger":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[60,1],"end":[60,21],"bytes":[1231,1251]}},"socketListener":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/socketListener","kind":"field","type":"net.Listener","span":{"start":[53,1],"end":[53,29],"bytes":[1097,1125]}},"startedAt":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/startedAt","kind":"field","type":"time.Time","span":{"start":[51,1],"end":[51,21],"bytes":[1075,1095]}},"store":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/store","kind":"field","type":"*github.com/benbjohnson/litestream.Store","span":{"start":[35,1],"end":[35,14],"bytes":[659,672]}},"wg":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/wg","kind":"field","type":"sync.WaitGroup","span":{"start":[58,1],"end":[58,23],"bytes":[1207,1229]}}}},"SocketConfig":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SocketConfig","kind":"struct","span":{"start":[18,1],"end":[22,2],"bytes":[213,353]},"callables":{},"fields":{"Enabled":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SocketConfig/Enabled","kind":"field","type":"bool","span":{"start":[19,1],"end":[19,37],"bytes":[240,276]}},"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SocketConfig/Path","kind":"field","type":"string","span":{"start":[20,1],"end":[20,34],"bytes":[277,310]}},"Permissions":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SocketConfig/Permissions","kind":"field","type":"uint32","span":{"start":[21,1],"end":[21,41],"bytes":[311,351]}}}},"StartRequest":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartRequest","kind":"struct","span":{"start":[362,1],"end":[365,2],"bytes":[9206,9307]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartRequest/Path","kind":"field","type":"string","span":{"start":[363,1],"end":[363,30],"bytes":[9233,9262]}},"Timeout":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartRequest/Timeout","kind":"field","type":"int","span":{"start":[364,1],"end":[364,43],"bytes":[9263,9305]}}}},"StartResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartResponse","kind":"struct","span":{"start":[368,1],"end":[372,2],"bytes":[9372,9490]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartResponse/Path","kind":"field","type":"string","span":{"start":[370,1],"end":[370,29],"bytes":[9431,9459]}},"Status":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartResponse/Status","kind":"field","type":"string","span":{"start":[369,1],"end":[369,31],"bytes":[9400,9430]}},"TXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StartResponse/TXID","kind":"field","type":"uint64","span":{"start":[371,1],"end":[371,29],"bytes":[9460,9488]}}}},"StopRequest":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopRequest","kind":"struct","span":{"start":[375,1],"end":[378,2],"bytes":[9551,9651]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopRequest/Path","kind":"field","type":"string","span":{"start":[376,1],"end":[376,30],"bytes":[9577,9606]}},"Timeout":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopRequest/Timeout","kind":"field","type":"int","span":{"start":[377,1],"end":[377,43],"bytes":[9607,9649]}}}},"StopResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopResponse","kind":"struct","span":{"start":[381,1],"end":[385,2],"bytes":[9714,9831]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopResponse/Path","kind":"field","type":"string","span":{"start":[383,1],"end":[383,29],"bytes":[9772,9800]}},"Status":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopResponse/Status","kind":"field","type":"string","span":{"start":[382,1],"end":[382,31],"bytes":[9741,9771]}},"TXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.StopResponse/TXID","kind":"field","type":"uint64","span":{"start":[384,1],"end":[384,29],"bytes":[9801,9829]}}}},"SyncDiagnosticsResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncDiagnosticsResponse","kind":"struct","span":{"start":[399,1],"end":[401,2],"bytes":[10189,10275]},"callables":{},"fields":{"Databases":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncDiagnosticsResponse/Databases","kind":"field","type":"[]github.com/benbjohnson/litestream.SyncDiagnostic","span":{"start":[400,1],"end":[400,47],"bytes":[10227,10273]}}}},"SyncRequest":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncRequest","kind":"struct","span":{"start":[462,1],"end":[466,2],"bytes":[11791,11931]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncRequest/Path","kind":"field","type":"string","span":{"start":[463,1],"end":[463,30],"bytes":[11817,11846]}},"Timeout":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncRequest/Timeout","kind":"field","type":"int","span":{"start":[465,1],"end":[465,43],"bytes":[11887,11929]}},"Wait":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncRequest/Wait","kind":"field","type":"bool","span":{"start":[464,1],"end":[464,40],"bytes":[11847,11886]}}}},"SyncResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncResponse","kind":"struct","span":{"start":[469,1],"end":[474,2],"bytes":[11994,12183]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncResponse/Path","kind":"field","type":"string","span":{"start":[471,1],"end":[471,37],"bytes":[12060,12096]}},"ReplicatedTXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncResponse/ReplicatedTXID","kind":"field","type":"uint64","span":{"start":[473,1],"end":[473,48],"bytes":[12134,12181]}},"Status":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncResponse/Status","kind":"field","type":"string","span":{"start":[470,1],"end":[470,39],"bytes":[12021,12059]}},"TXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.SyncResponse/TXID","kind":"field","type":"uint64","span":{"start":[472,1],"end":[472,37],"bytes":[12097,12133]}}}},"TXIDResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.TXIDResponse","kind":"struct","span":{"start":[394,1],"end":[396,2],"bytes":[10060,10115]},"callables":{},"fields":{"TXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.TXIDResponse/TXID","kind":"field","type":"uint64","span":{"start":[395,1],"end":[395,27],"bytes":[10087,10113]}}}},"UnregisterDatabaseRequest":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseRequest","kind":"struct","span":{"start":[559,1],"end":[562,2],"bytes":[14470,14584]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseRequest/Path","kind":"field","type":"string","span":{"start":[560,1],"end":[560,30],"bytes":[14510,14539]}},"Timeout":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseRequest/Timeout","kind":"field","type":"int","span":{"start":[561,1],"end":[561,43],"bytes":[14540,14582]}}}},"UnregisterDatabaseResponse":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseResponse","kind":"struct","span":{"start":[565,1],"end":[569,2],"bytes":[14667,14798]},"callables":{},"fields":{"Path":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseResponse/Path","kind":"field","type":"string","span":{"start":[567,1],"end":[567,29],"bytes":[14739,14767]}},"Status":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseResponse/Status","kind":"field","type":"string","span":{"start":[566,1],"end":[566,31],"bytes":[14708,14738]}},"TXID":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.UnregisterDatabaseResponse/TXID","kind":"field","type":"uint64","span":{"start":[568,1],"end":[568,29],"bytes":[14768,14796]}}}}},"functions":{"github.com/benbjohnson/litestream.DefaultSocketConfig":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DefaultSocketConfig","kind":"function","span":{"start":[25,1],"end":[31,2],"bytes":[420,574]},"signature":"github.com/benbjohnson/litestream.DefaultSocketConfig","parameters":[],"return_type":"github.com/benbjohnson/litestream.SocketConfig","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.NewServer":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.NewServer","kind":"function","span":{"start":[64,1],"end":[95,2],"bytes":[1299,2384]},"signature":"github.com/benbjohnson/litestream.NewServer","parameters":[{"name":"store","type":"*github.com/benbjohnson/litestream.Store","span":{"start":[64,1],"end":[64,39],"bytes":[1299,1337]}}],"return_type":"*github.com/benbjohnson/litestream.Server","metrics":{"cyclomatic":1},"body":{"65:17":{"kind":"call","span":{"start":[65,17],"end":[65,57],"bytes":[1354,1394]},"callee":null},"65:36":{"kind":"call","span":{"start":[65,36],"end":[65,56],"bytes":[1373,1393]},"callee":null},"71:16":{"kind":"call","span":{"start":[71,16],"end":[71,30],"bytes":[1511,1525]},"callee":null},"74:9":{"kind":"call","span":{"start":[74,9],"end":[74,27],"bytes":[1575,1593]},"callee":null},"75:2":{"kind":"call","span":{"start":[75,2],"end":[75,46],"bytes":[1595,1639]},"callee":null},"76:2":{"kind":"call","span":{"start":[76,2],"end":[76,44],"bytes":[1641,1683]},"callee":null},"77:2":{"kind":"call","span":{"start":[77,2],"end":[77,43],"bytes":[1685,1726]},"callee":null},"78:2":{"kind":"call","span":{"start":[78,2],"end":[78,52],"bytes":[1728,1778]},"callee":null},"79:2":{"kind":"call","span":{"start":[79,2],"end":[79,56],"bytes":[1780,1834]},"callee":null},"80:2":{"kind":"call","span":{"start":[80,2],"end":[80,44],"bytes":[1836,1878]},"callee":null},"81:2":{"kind":"call","span":{"start":[81,2],"end":[81,43],"bytes":[1880,1921]},"callee":null},"82:2":{"kind":"call","span":{"start":[82,2],"end":[82,43],"bytes":[1923,1964]},"callee":null},"83:2":{"kind":"call","span":{"start":[83,2],"end":[83,62],"bytes":[1966,2026]},"callee":null},"86:2":{"kind":"call","span":{"start":[86,2],"end":[86,50],"bytes":[2049,2097]},"callee":null},"87:2":{"kind":"call","span":{"start":[87,2],"end":[87,59],"bytes":[2099,2156]},"callee":null},"88:2":{"kind":"call","span":{"start":[88,2],"end":[88,59],"bytes":[2158,2215]},"callee":null},"89:2":{"kind":"call","span":{"start":[89,2],"end":[89,57],"bytes":[2217,2272]},"callee":null},"90:2":{"kind":"call","span":{"start":[90,2],"end":[90,55],"bytes":[2274,2327]},"callee":null}}},"github.com/benbjohnson/litestream.writeJSON":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","kind":"function","span":{"start":[346,1],"end":[350,2],"bytes":[8712,8885]},"signature":"github.com/benbjohnson/litestream.writeJSON","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[346,1],"end":[346,67],"bytes":[8712,8778]}},{"name":"status","type":"int","span":{"start":[346,1],"end":[346,67],"bytes":[8712,8778]}},{"name":"v","type":"interface{}","span":{"start":[346,1],"end":[346,67],"bytes":[8712,8778]}}],"metrics":{"cyclomatic":1},"body":{"347:2":{"kind":"call","span":{"start":[347,2],"end":[347,12],"bytes":[8780,8790]},"callee":null},"348:2":{"kind":"call","span":{"start":[348,2],"end":[348,23],"bytes":[8832,8853]},"callee":null},"349:2":{"kind":"call","span":{"start":[349,2],"end":[349,20],"bytes":[8855,8873]},"callee":null}}},"github.com/benbjohnson/litestream.writeJSONError":{"id":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","kind":"function","span":{"start":[352,1],"end":[359,2],"bytes":[8887,9143]},"signature":"github.com/benbjohnson/litestream.writeJSONError","parameters":[{"name":"w","type":"net/http.ResponseWriter","span":{"start":[352,1],"end":[352,94],"bytes":[8887,8980]}},{"name":"status","type":"int","span":{"start":[352,1],"end":[352,94],"bytes":[8887,8980]}},{"name":"message","type":"string","span":{"start":[352,1],"end":[352,94],"bytes":[8887,8980]}},{"name":"details","type":"interface{}","span":{"start":[352,1],"end":[352,94],"bytes":[8887,8980]}}],"metrics":{"cyclomatic":1},"body":{"353:2":{"kind":"call","span":{"start":[353,2],"end":[353,12],"bytes":[8982,8992]},"callee":null},"354:2":{"kind":"call","span":{"start":[354,2],"end":[354,23],"bytes":[9034,9055]},"callee":null},"355:2":{"kind":"call","span":{"start":[355,2],"end":[355,20],"bytes":[9057,9075]},"callee":null}}}},"content_hash":"e3f807d05a146d30c5e210067903fb9f8756128b292c0e8c140648ec80767946"},"sftp/replica_client.go":{"id":"can://go/litestream/sftp/replica_client.go","kind":"module","span":{"start":[1,1],"end":[431,1],"bytes":[0,11844]},"package":"sftp","source":"package sftp\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/pkg/sftp\"\n\t\"github.com/superfly/ltx\"\n\t\"golang.org/x/crypto/ssh\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"sftp\", NewReplicaClientFromURL)\n}\n\n// ReplicaClientType is the client type for this package.\nconst ReplicaClientType = \"sftp\"\n\n// Default settings for replica client.\nconst (\n\tDefaultDialTimeout = 30 * time.Second\n)\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\n// ReplicaClient is a client for writing LTX files over SFTP.\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\tsshClient *ssh.Client\n\tsftpClient *sftp.Client\n\tlogger *slog.Logger\n\n\t// SFTP connection info\n\tHost string\n\tUser string\n\tPassword string\n\tPath string\n\tKeyPath string\n\tHostKey string\n\tDialTimeout time.Duration\n\n\t// ConcurrentWrites enables concurrent writes for better performance.\n\t// Note: This makes resuming failed transfers unsafe.\n\tConcurrentWrites bool\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t\tDialTimeout: DefaultDialTimeout,\n\t\tConcurrentWrites: true, // Default to true for better performance\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\n// URL format: sftp://[user[:password]@]host[:port]/path\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tclient := NewReplicaClient()\n\n\t// Extract credentials from userinfo\n\tif userinfo != nil {\n\t\tclient.User = userinfo.Username()\n\t\tclient.Password, _ = userinfo.Password()\n\t}\n\n\tclient.Host = host\n\tclient.Path = urlPath\n\n\tif client.Host == \"\" {\n\t\treturn nil, fmt.Errorf(\"host required for sftp replica URL\")\n\t}\n\tif client.User == \"\" {\n\t\treturn nil, fmt.Errorf(\"user required for sftp replica URL\")\n\t}\n\n\treturn client, nil\n}\n\n// Type returns \"sftp\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to SFTP. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) error {\n\t_, err := c.init(ctx)\n\treturn err\n}\n\n// init initializes the connection and returns the SFTP client.\nfunc (c *ReplicaClient) init(ctx context.Context) (_ *sftp.Client, err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.sftpClient != nil {\n\t\treturn c.sftpClient, nil\n\t}\n\n\tif c.User == \"\" {\n\t\treturn nil, fmt.Errorf(\"sftp user required\")\n\t}\n\n\t// Build SSH configuration \u0026 auth methods\n\tvar hostkey ssh.HostKeyCallback\n\tif c.HostKey != \"\" {\n\t\tvar pubkey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(c.HostKey))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot parse sftp host key: %w\", err)\n\t\t}\n\t\thostkey = ssh.FixedHostKey(pubkey)\n\t} else {\n\t\tslog.Warn(\"sftp host key not verified\", \"host\", c.Host)\n\t\thostkey = ssh.InsecureIgnoreHostKey()\n\t}\n\tconfig := \u0026ssh.ClientConfig{\n\t\tUser: c.User,\n\t\tHostKeyCallback: hostkey,\n\t\tBannerCallback: ssh.BannerDisplayStderr(),\n\t}\n\tif c.Password != \"\" {\n\t\tconfig.Auth = append(config.Auth, ssh.Password(c.Password))\n\t}\n\n\tif c.KeyPath != \"\" {\n\t\tbuf, err := os.ReadFile(c.KeyPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"sftp: cannot read sftp key path: %w\", err)\n\t\t}\n\n\t\tsigner, err := ssh.ParsePrivateKey(buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"sftp: cannot parse sftp key path: %w\", err)\n\t\t}\n\t\tconfig.Auth = append(config.Auth, ssh.PublicKeys(signer))\n\t}\n\n\t// Append standard port, if necessary.\n\thost := c.Host\n\tif _, _, err := net.SplitHostPort(c.Host); err != nil {\n\t\thost = net.JoinHostPort(c.Host, \"22\")\n\t}\n\n\t// Connect via SSH.\n\tif c.sshClient, err = ssh.Dial(\"tcp\", host, config); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Wrap connection with an SFTP client.\n\t// Configure options based on client settings\n\topts := []sftp.ClientOption{}\n\tif c.ConcurrentWrites {\n\t\topts = append(opts, sftp.UseConcurrentWrites(true))\n\t}\n\n\tif c.sftpClient, err = sftp.NewClient(c.sshClient, opts...); err != nil {\n\t\tc.sshClient.Close()\n\t\tc.sshClient = nil\n\t\treturn nil, err\n\t}\n\n\treturn c.sftpClient, nil\n}\n\n// DeleteAll deletes all LTX files.\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) (err error) {\n\tdefer func() { c.resetOnConnError(err) }()\n\n\tsftpClient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar dirs []string\n\twalker := sftpClient.Walk(c.Path)\n\tfor walker.Step() {\n\t\tif err := walker.Err(); os.IsNotExist(err) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"sftp: cannot walk path %q: %w\", walker.Path(), err)\n\t\t}\n\t\tif walker.Stat().IsDir() {\n\t\t\tdirs = append(dirs, walker.Path())\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := sftpClient.Remove(walker.Path()); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"sftp: cannot delete file %q: %w\", walker.Path(), err)\n\t\t}\n\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\t// Remove directories in reverse order after they have been emptied.\n\tfor i := len(dirs) - 1; i \u003e= 0; i-- {\n\t\tfilename := dirs[i]\n\t\tif err := sftpClient.RemoveDirectory(filename); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"sftp: cannot delete directory %q: %w\", filename, err)\n\t\t}\n\t}\n\n\t// log.Printf(\"%s(%s): retainer: deleting all\", r.db.Path(), r.Name())\n\n\treturn nil\n}\n\n// LTXFiles returns an iterator over all available LTX files for a level.\n// SFTP uses file ModTime for timestamps, which is set via Chtimes() to preserve original timestamp.\n// The useMetadata parameter is ignored since ModTime always contains the accurate timestamp.\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, _ bool) (_ ltx.FileIterator, err error) {\n\tdefer func() { c.resetOnConnError(err) }()\n\n\tsftpClient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdir := litestream.LTXLevelDir(c.Path, level)\n\tfis, err := sftpClient.ReadDir(dir)\n\tif os.IsNotExist(err) {\n\t\treturn ltx.NewFileInfoSliceIterator(nil), nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Iterate over every file and convert to metadata.\n\tinfos := make([]*ltx.FileInfo, 0, len(fis))\n\tfor _, fi := range fis {\n\t\tminTXID, maxTXID, err := ltx.ParseFilename(path.Base(fi.Name()))\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t} else if minTXID \u003c seek {\n\t\t\tcontinue\n\t\t}\n\n\t\tinfos = append(infos, \u0026ltx.FileInfo{\n\t\t\tLevel: level,\n\t\t\tMinTXID: minTXID,\n\t\t\tMaxTXID: maxTXID,\n\t\t\tSize: fi.Size(),\n\t\t\tCreatedAt: fi.ModTime().UTC(), // ModTime contains accurate timestamp from Chtimes()\n\t\t})\n\t}\n\n\treturn ltx.NewFileInfoSliceIterator(infos), nil\n}\n\n// WriteLTXFile writes a LTX file from rd into a remote file.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {\n\tdefer func() { c.resetOnConnError(err) }()\n\n\tsftpClient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(rd, \u0026buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\tfullReader := io.MultiReader(\u0026buf, rd)\n\n\tif err := sftpClient.MkdirAll(path.Dir(filename)); err != nil {\n\t\treturn nil, fmt.Errorf(\"sftp: cannot make parent snapshot directory %q: %w\", path.Dir(filename), err)\n\t}\n\n\ttmpFilename := fmt.Sprintf(\"%s.%d.%d.tmp\", filename, os.Getpid(), time.Now().UnixNano())\n\tf, err := sftpClient.OpenFile(tmpFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"sftp: cannot open temporary snapshot file for writing: %w\", err)\n\t}\n\tdefer func() {\n\t\tif f != nil {\n\t\t\t_ = f.Close()\n\t\t}\n\t\tif err != nil {\n\t\t\t_ = sftpClient.Remove(tmpFilename)\n\t\t}\n\t}()\n\n\tn, err := io.Copy(f, fullReader)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Flush the data to stable storage before the rename so a server crash\n\t// cannot leave an empty file at the final path. Servers that do not\n\t// advertise fsync@openssh.com are left to their own write-back policy.\n\tif _, ok := sftpClient.HasExtension(\"fsync@openssh.com\"); ok {\n\t\tif err := f.Sync(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"sftp: cannot sync temporary ltx file %q: %w\", tmpFilename, err)\n\t\t}\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\treturn nil, err\n\t}\n\tf = nil\n\n\tif err := sftpClient.Chtimes(tmpFilename, timestamp, timestamp); err != nil {\n\t\treturn nil, fmt.Errorf(\"sftp: cannot set file timestamps: %w\", err)\n\t}\n\n\tif err := sftpClient.Rename(tmpFilename, filename); err != nil {\n\t\treturn nil, fmt.Errorf(\"sftp: cannot rename temporary ltx file %q to %q: %w\", tmpFilename, filename, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(n))\n\n\treturn \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: n,\n\t\tCreatedAt: timestamp,\n\t}, nil\n}\n\n// OpenLTXFile returns a reader for an LTX file.\n// Returns os.ErrNotExist if no matching position is found.\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (_ io.ReadCloser, err error) {\n\tdefer func() { c.resetOnConnError(err) }()\n\n\tsftpClient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\tf, err := sftpClient.OpenFile(filename, os.O_RDONLY)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif offset \u003e 0 {\n\t\tif _, err := f.Seek(offset, io.SeekStart); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\n\tif size \u003e 0 {\n\t\treturn internal.LimitReadCloser(f, size), nil\n\t}\n\treturn f, nil\n}\n\n// DeleteLTXFiles deletes LTX files with at the given positions.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) (err error) {\n\tdefer func() { c.resetOnConnError(err) }()\n\n\tsftpClient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, info := range a {\n\t\tfilename := litestream.LTXFilePath(c.Path, info.Level, info.MinTXID, info.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"path\", filename)\n\n\t\tif err := sftpClient.Remove(filename); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"sftp: cannot delete ltx file %q: %w\", filename, err)\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n\n// Cleanup deletes path \u0026 directories after empty.\nfunc (c *ReplicaClient) Cleanup(ctx context.Context) (err error) {\n\tdefer func() { c.resetOnConnError(err) }()\n\n\tsftpClient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := sftpClient.RemoveDirectory(c.Path); err != nil \u0026\u0026 !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"sftp: cannot delete path: %w\", err)\n\t}\n\treturn nil\n}\n\n// resetOnConnError closes \u0026 clears the client if a connection error occurs.\nfunc (c *ReplicaClient) resetOnConnError(err error) {\n\tif !errors.Is(err, sftp.ErrSSHFxConnectionLost) {\n\t\treturn\n\t}\n\n\tif c.sftpClient != nil {\n\t\tc.sftpClient.Close()\n\t\tc.sftpClient = nil\n\t}\n\tif c.sshClient != nil {\n\t\tc.sshClient.Close()\n\t\tc.sshClient = nil\n\t}\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[23,31]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[32,42]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[43,52]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[53,59]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[60,65]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[66,77]}},{"name":"net","path":"net","span":{"start":[10,1],"end":[10,7],"bytes":[78,84]}},{"name":"url","path":"net/url","span":{"start":[11,1],"end":[11,11],"bytes":[85,95]}},{"name":"os","path":"os","span":{"start":[12,1],"end":[12,6],"bytes":[96,101]}},{"name":"path","path":"path","span":{"start":[13,1],"end":[13,8],"bytes":[102,109]}},{"name":"sync","path":"sync","span":{"start":[14,1],"end":[14,8],"bytes":[110,117]}},{"name":"time","path":"time","span":{"start":[15,1],"end":[15,8],"bytes":[118,125]}},{"name":"sftp","path":"github.com/pkg/sftp","span":{"start":[17,1],"end":[17,23],"bytes":[127,149]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[18,1],"end":[18,27],"bytes":[150,176]}},{"name":"ssh","path":"golang.org/x/crypto/ssh","span":{"start":[19,1],"end":[19,27],"bytes":[177,203]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[21,1],"end":[21,37],"bytes":[205,241]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[22,1],"end":[22,46],"bytes":[242,287]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient","kind":"struct","span":{"start":[40,1],"end":[58,2],"bytes":[682,1131]},"callables":{"github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup","kind":"method","span":{"start":[402,1],"end":[414,2],"bytes":[11168,11502]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[402,1],"end":[402,67],"bytes":[11168,11234]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"403:8":{"kind":"call","span":{"start":[403,8],"end":[403,44],"bytes":[11242,11278]},"callee":null},"405:21":{"kind":"call","span":{"start":[405,21],"end":[405,32],"bytes":[11300,11311]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"},"410:12":{"kind":"call","span":{"start":[410,12],"end":[410,46],"bytes":[11357,11391]},"callee":null},"410:63":{"kind":"call","span":{"start":[410,63],"end":[410,81],"bytes":[11408,11426]},"callee":null},"411:10":{"kind":"call","span":{"start":[411,10],"end":[411,57],"bytes":[11438,11485]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup/github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup.closure_1","kind":"function","span":{"start":[403,1],"end":[403,44],"bytes":[11235,11278]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup.closure_1","parameters":[],"body":{"403:17":{"kind":"call","span":{"start":[403,17],"end":[403,40],"bytes":[11251,11274]},"callee":null}}}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll","kind":"method","span":{"start":[184,1],"end":[223,2],"bytes":[4537,5679]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[184,1],"end":[184,69],"bytes":[4537,4605]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"185:8":{"kind":"call","span":{"start":[185,8],"end":[185,44],"bytes":[4613,4649]},"callee":null},"187:21":{"kind":"call","span":{"start":[187,21],"end":[187,32],"bytes":[4671,4682]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"},"193:12":{"kind":"call","span":{"start":[193,12],"end":[193,35],"bytes":[4747,4770]},"callee":null},"194:6":{"kind":"call","span":{"start":[194,6],"end":[194,19],"bytes":[4776,4789]},"callee":null},"195:13":{"kind":"call","span":{"start":[195,13],"end":[195,25],"bytes":[4804,4816]},"callee":null},"195:27":{"kind":"call","span":{"start":[195,27],"end":[195,45],"bytes":[4818,4836]},"callee":null},"198:11":{"kind":"call","span":{"start":[198,11],"end":[198,74],"bytes":[4886,4949]},"callee":null},"198:55":{"kind":"call","span":{"start":[198,55],"end":[198,68],"bytes":[4930,4943]},"callee":null},"200:6":{"kind":"call","span":{"start":[200,6],"end":[200,19],"bytes":[4959,4972]},"callee":null},"201:11":{"kind":"call","span":{"start":[201,11],"end":[201,38],"bytes":[4993,5020]},"callee":null},"201:24":{"kind":"call","span":{"start":[201,24],"end":[201,37],"bytes":[5006,5019]},"callee":null},"205:13":{"kind":"call","span":{"start":[205,13],"end":[205,45],"bytes":[5050,5082]},"callee":null},"205:31":{"kind":"call","span":{"start":[205,31],"end":[205,44],"bytes":[5068,5081]},"callee":null},"205:62":{"kind":"call","span":{"start":[205,62],"end":[205,80],"bytes":[5099,5117]},"callee":null},"206:11":{"kind":"call","span":{"start":[206,11],"end":[206,76],"bytes":[5130,5195]},"callee":null},"206:57":{"kind":"call","span":{"start":[206,57],"end":[206,70],"bytes":[5176,5189]},"callee":null},"209:3":{"kind":"call","span":{"start":[209,3],"end":[209,81],"bytes":[5203,5281]},"callee":null},"213:11":{"kind":"call","span":{"start":[213,11],"end":[213,20],"bytes":[5372,5381]},"callee":null},"215:13":{"kind":"call","span":{"start":[215,13],"end":[215,49],"bytes":[5435,5471]},"callee":null},"215:66":{"kind":"call","span":{"start":[215,66],"end":[215,84],"bytes":[5488,5506]},"callee":null},"216:11":{"kind":"call","span":{"start":[216,11],"end":[216,76],"bytes":[5519,5584]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll.closure_1","kind":"function","span":{"start":[185,1],"end":[185,44],"bytes":[4606,4649]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll.closure_1","parameters":[],"body":{"185:17":{"kind":"call","span":{"start":[185,17],"end":[185,40],"bytes":[4622,4645]},"callee":null}}}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[379,1],"end":[399,2],"bytes":[10411,11115]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[379,1],"end":[379,93],"bytes":[10411,10503]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[379,1],"end":[379,93],"bytes":[10411,10503]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"380:8":{"kind":"call","span":{"start":[380,8],"end":[380,44],"bytes":[10511,10547]},"callee":null},"382:21":{"kind":"call","span":{"start":[382,21],"end":[382,32],"bytes":[10569,10580]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"},"388:15":{"kind":"call","span":{"start":[388,15],"end":[388,85],"bytes":[10655,10725]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"390:3":{"kind":"call","span":{"start":[390,3],"end":[390,127],"bytes":[10729,10853]},"callee":null},"392:13":{"kind":"call","span":{"start":[392,13],"end":[392,40],"bytes":[10867,10894]},"callee":null},"392:57":{"kind":"call","span":{"start":[392,57],"end":[392,75],"bytes":[10911,10929]},"callee":null},"393:11":{"kind":"call","span":{"start":[393,11],"end":[393,75],"bytes":[10942,11006]},"callee":null},"395:3":{"kind":"call","span":{"start":[395,3],"end":[395,81],"bytes":[11013,11091]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles.closure_1","kind":"function","span":{"start":[380,1],"end":[380,44],"bytes":[10504,10547]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles.closure_1","parameters":[],"body":{"380:17":{"kind":"call","span":{"start":[380,17],"end":[380,40],"bytes":[10520,10543]},"callee":null}}}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.Init":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Init","kind":"method","span":{"start":[104,1],"end":[107,2],"bytes":[2484,2578]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[104,1],"end":[104,58],"bytes":[2484,2541]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"105:12":{"kind":"call","span":{"start":[105,12],"end":[105,23],"bytes":[2553,2564]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles","kind":"method","span":{"start":[228,1],"end":[264,2],"bytes":[5950,6935]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[228,1],"end":[228,122],"bytes":[5950,6071]}},{"name":"level","type":"int","span":{"start":[228,1],"end":[228,122],"bytes":[5950,6071]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[228,1],"end":[228,122],"bytes":[5950,6071]}},{"name":"_","type":"bool","span":{"start":[228,1],"end":[228,122],"bytes":[5950,6071]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":7},"body":{"229:8":{"kind":"call","span":{"start":[229,8],"end":[229,44],"bytes":[6079,6115]},"callee":null},"231:21":{"kind":"call","span":{"start":[231,21],"end":[231,32],"bytes":[6137,6148]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"},"236:9":{"kind":"call","span":{"start":[236,9],"end":[236,46],"bytes":[6196,6233]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"237:14":{"kind":"call","span":{"start":[237,14],"end":[237,37],"bytes":[6247,6270]},"callee":null},"238:5":{"kind":"call","span":{"start":[238,5],"end":[238,23],"bytes":[6275,6293]},"callee":null},"239:10":{"kind":"call","span":{"start":[239,10],"end":[239,43],"bytes":[6305,6338]},"callee":null},"245:11":{"kind":"call","span":{"start":[245,11],"end":[245,45],"bytes":[6453,6487]},"callee":null},"245:36":{"kind":"call","span":{"start":[245,36],"end":[245,44],"bytes":[6478,6486]},"callee":null},"247:28":{"kind":"call","span":{"start":[247,28],"end":[247,67],"bytes":[6541,6580]},"callee":null},"247:46":{"kind":"call","span":{"start":[247,46],"end":[247,66],"bytes":[6559,6579]},"callee":null},"247:56":{"kind":"call","span":{"start":[247,56],"end":[247,65],"bytes":[6569,6578]},"callee":null},"254:11":{"kind":"call","span":{"start":[254,11],"end":[260,5],"bytes":[6667,6880]},"callee":null},"258:15":{"kind":"call","span":{"start":[258,15],"end":[258,24],"bytes":[6777,6786]},"callee":null},"259:15":{"kind":"call","span":{"start":[259,15],"end":[259,27],"bytes":[6802,6814]},"callee":null},"263:9":{"kind":"call","span":{"start":[263,9],"end":[263,44],"bytes":[6893,6928]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles/github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles.closure_1","kind":"function","span":{"start":[229,1],"end":[229,44],"bytes":[6072,6115]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles.closure_1","parameters":[],"body":{"229:17":{"kind":"call","span":{"start":[229,17],"end":[229,40],"bytes":[6088,6111]},"callee":null}}}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[350,1],"end":[376,2],"bytes":[9652,10344]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[350,1],"end":[350,146],"bytes":[9652,9797]}},{"name":"level","type":"int","span":{"start":[350,1],"end":[350,146],"bytes":[9652,9797]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[350,1],"end":[350,146],"bytes":[9652,9797]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[350,1],"end":[350,146],"bytes":[9652,9797]}},{"name":"offset","type":"int64","span":{"start":[350,1],"end":[350,146],"bytes":[9652,9797]}},{"name":"size","type":"int64","span":{"start":[350,1],"end":[350,146],"bytes":[9652,9797]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"351:8":{"kind":"call","span":{"start":[351,8],"end":[351,44],"bytes":[9805,9841]},"callee":null},"353:21":{"kind":"call","span":{"start":[353,21],"end":[353,32],"bytes":[9863,9874]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"},"358:14":{"kind":"call","span":{"start":[358,14],"end":[358,69],"bytes":[9927,9982]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"359:12":{"kind":"call","span":{"start":[359,12],"end":[359,54],"bytes":[9994,10036]},"callee":null},"365:16":{"kind":"call","span":{"start":[365,16],"end":[365,44],"bytes":[10108,10136]},"callee":null},"370:2":{"kind":"call","span":{"start":[370,2],"end":[370,77],"bytes":[10179,10254]},"callee":null},"373:10":{"kind":"call","span":{"start":[373,10],"end":[373,43],"bytes":[10286,10319]},"callee":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser"}},"callables":{"closure_1":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile/github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile.closure_1","kind":"function","span":{"start":[351,1],"end":[351,44],"bytes":[9798,9841]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile.closure_1","parameters":[],"body":{"351:17":{"kind":"call","span":{"start":[351,17],"end":[351,40],"bytes":[9814,9837]},"callee":null}}}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.SetLogger":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.SetLogger","kind":"method","span":{"start":[69,1],"end":[71,2],"bytes":[1438,1544]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[69,1],"end":[69,57],"bytes":[1438,1494]}}],"metrics":{"cyclomatic":1},"body":{"70:13":{"kind":"call","span":{"start":[70,13],"end":[70,48],"bytes":[1507,1542]},"callee":null}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.Type":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Type","kind":"method","span":{"start":[99,1],"end":[101,2],"bytes":[2341,2408]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[267,1],"end":[346,2],"bytes":[6999,9541]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[267,1],"end":[267,144],"bytes":[6999,7142]}},{"name":"level","type":"int","span":{"start":[267,1],"end":[267,144],"bytes":[6999,7142]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[267,1],"end":[267,144],"bytes":[6999,7142]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[267,1],"end":[267,144],"bytes":[6999,7142]}},{"name":"rd","type":"io.Reader","span":{"start":[267,1],"end":[267,144],"bytes":[6999,7142]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"268:8":{"kind":"call","span":{"start":[268,8],"end":[268,44],"bytes":[7150,7186]},"callee":null},"270:21":{"kind":"call","span":{"start":[270,21],"end":[270,32],"bytes":[7208,7219]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init"},"275:14":{"kind":"call","span":{"start":[275,14],"end":[275,69],"bytes":[7272,7327]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"279:15":{"kind":"call","span":{"start":[279,15],"end":[279,37],"bytes":[7438,7460]},"callee":null},"282:17":{"kind":"call","span":{"start":[282,17],"end":[282,42],"bytes":[7516,7541]},"callee":null},"284:15":{"kind":"call","span":{"start":[284,15],"end":[284,71],"bytes":[7573,7629]},"callee":null},"286:15":{"kind":"call","span":{"start":[286,15],"end":[286,44],"bytes":[7647,7676]},"callee":null},"289:16":{"kind":"call","span":{"start":[289,16],"end":[289,40],"bytes":[7745,7769]},"callee":null},"291:12":{"kind":"call","span":{"start":[291,12],"end":[291,51],"bytes":[7782,7821]},"callee":null},"291:32":{"kind":"call","span":{"start":[291,32],"end":[291,50],"bytes":[7802,7820]},"callee":null},"292:15":{"kind":"call","span":{"start":[292,15],"end":[292,104],"bytes":[7850,7939]},"callee":null},"292:80":{"kind":"call","span":{"start":[292,80],"end":[292,98],"bytes":[7915,7933]},"callee":null},"295:17":{"kind":"call","span":{"start":[295,17],"end":[295,90],"bytes":[7960,8033]},"callee":null},"295:55":{"kind":"call","span":{"start":[295,55],"end":[295,66],"bytes":[7998,8009]},"callee":null},"295:68":{"kind":"call","span":{"start":[295,68],"end":[295,78],"bytes":[8011,8021]},"callee":null},"296:12":{"kind":"call","span":{"start":[296,12],"end":[296,80],"bytes":[8045,8113]},"callee":null},"298:15":{"kind":"call","span":{"start":[298,15],"end":[298,91],"bytes":[8145,8221]},"callee":null},"300:8":{"kind":"call","span":{"start":[300,8],"end":[307,5],"bytes":[8232,8342]},"callee":null},"309:12":{"kind":"call","span":{"start":[309,12],"end":[309,34],"bytes":[8355,8377]},"callee":null},"317:14":{"kind":"call","span":{"start":[317,14],"end":[317,58],"bytes":[8646,8690]},"callee":null},"318:13":{"kind":"call","span":{"start":[318,13],"end":[318,21],"bytes":[8709,8717]},"callee":null},"319:16":{"kind":"call","span":{"start":[319,16],"end":[319,91],"bytes":[8747,8822]},"callee":null},"323:12":{"kind":"call","span":{"start":[323,12],"end":[323,21],"bytes":[8842,8851]},"callee":null},"328:12":{"kind":"call","span":{"start":[328,12],"end":[328,65],"bytes":[8908,8961]},"callee":null},"329:15":{"kind":"call","span":{"start":[329,15],"end":[329,70],"bytes":[8990,9045]},"callee":null},"332:12":{"kind":"call","span":{"start":[332,12],"end":[332,52],"bytes":[9061,9101]},"callee":null},"333:15":{"kind":"call","span":{"start":[333,15],"end":[333,108],"bytes":[9130,9223]},"callee":null},"336:2":{"kind":"call","span":{"start":[336,2],"end":[336,77],"bytes":[9229,9304]},"callee":null},"337:2":{"kind":"call","span":{"start":[337,2],"end":[337,77],"bytes":[9312,9387]},"callee":null},"337:82":{"kind":"call","span":{"start":[337,82],"end":[337,92],"bytes":[9392,9402]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile.closure_1","kind":"function","span":{"start":[268,1],"end":[268,44],"bytes":[7143,7186]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile.closure_1","parameters":[],"body":{"268:17":{"kind":"call","span":{"start":[268,17],"end":[268,40],"bytes":[7159,7182]},"callee":null}}},"closure_2":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile.closure_2","kind":"function","span":{"start":[300,1],"end":[307,5],"bytes":[8225,8342]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile.closure_2","parameters":[],"body":{"302:8":{"kind":"call","span":{"start":[302,8],"end":[302,17],"bytes":[8264,8273]},"callee":null},"305:8":{"kind":"call","span":{"start":[305,8],"end":[305,38],"bytes":[8303,8333]},"callee":null}}}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.init":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","kind":"method","span":{"start":[110,1],"end":[181,2],"bytes":[2644,4499]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[110,1],"end":[110,80],"bytes":[2644,2723]}}],"return_type":"(*github.com/pkg/sftp.Client, error)","error_channel":["error"],"metrics":{"cyclomatic":13},"body":{"111:2":{"kind":"call","span":{"start":[111,2],"end":[111,13],"bytes":[2725,2736]},"callee":null},"112:8":{"kind":"call","span":{"start":[112,8],"end":[112,21],"bytes":[2744,2757]},"callee":null},"119:15":{"kind":"call","span":{"start":[119,15],"end":[119,47],"bytes":[2849,2881]},"callee":null},"125:30":{"kind":"call","span":{"start":[125,30],"end":[125,71],"bytes":[3013,3054]},"callee":null},"125:53":{"kind":"call","span":{"start":[125,53],"end":[125,70],"bytes":[3036,3053]},"callee":null},"127:16":{"kind":"call","span":{"start":[127,16],"end":[127,65],"bytes":[3088,3137]},"callee":null},"129:13":{"kind":"call","span":{"start":[129,13],"end":[129,37],"bytes":[3154,3178]},"callee":null},"131:3":{"kind":"call","span":{"start":[131,3],"end":[131,58],"bytes":[3191,3246]},"callee":null},"132:13":{"kind":"call","span":{"start":[132,13],"end":[132,40],"bytes":[3259,3286]},"callee":null},"137:20":{"kind":"call","span":{"start":[137,20],"end":[137,45],"bytes":[3394,3419]},"callee":null},"140:17":{"kind":"call","span":{"start":[140,17],"end":[140,62],"bytes":[3463,3508]},"callee":null},"140:37":{"kind":"call","span":{"start":[140,37],"end":[140,61],"bytes":[3483,3507]},"callee":null},"144:15":{"kind":"call","span":{"start":[144,15],"end":[144,37],"bytes":[3549,3571]},"callee":null},"146:16":{"kind":"call","span":{"start":[146,16],"end":[146,70],"bytes":[3605,3659]},"callee":null},"149:18":{"kind":"call","span":{"start":[149,18],"end":[149,42],"bytes":[3682,3706]},"callee":null},"151:16":{"kind":"call","span":{"start":[151,16],"end":[151,71],"bytes":[3740,3795]},"callee":null},"153:17":{"kind":"call","span":{"start":[153,17],"end":[153,60],"bytes":[3816,3859]},"callee":null},"153:37":{"kind":"call","span":{"start":[153,37],"end":[153,59],"bytes":[3836,3858]},"callee":null},"158:18":{"kind":"call","span":{"start":[158,18],"end":[158,43],"bytes":[3937,3962]},"callee":null},"159:10":{"kind":"call","span":{"start":[159,10],"end":[159,40],"bytes":[3986,4016]},"callee":null},"163:24":{"kind":"call","span":{"start":[163,24],"end":[163,53],"bytes":[4065,4094]},"callee":null},"171:10":{"kind":"call","span":{"start":[171,10],"end":[171,54],"bytes":[4284,4328]},"callee":null},"171:23":{"kind":"call","span":{"start":[171,23],"end":[171,53],"bytes":[4297,4327]},"callee":null},"174:25":{"kind":"call","span":{"start":[174,25],"end":[174,61],"bytes":[4357,4393]},"callee":null},"175:3":{"kind":"call","span":{"start":[175,3],"end":[175,22],"bytes":[4410,4429]},"callee":null}}},"github.com/benbjohnson/litestream/sftp.ReplicaClient.resetOnConnError":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.resetOnConnError","kind":"method","span":{"start":[417,1],"end":[430,2],"bytes":[11581,11843]},"signature":"github.com/benbjohnson/litestream/sftp.ReplicaClient.resetOnConnError","parameters":[{"name":"err","type":"error","span":{"start":[417,1],"end":[417,54],"bytes":[11581,11634]}}],"metrics":{"cyclomatic":4},"body":{"418:6":{"kind":"call","span":{"start":[418,6],"end":[418,49],"bytes":[11640,11683]},"callee":null},"423:3":{"kind":"call","span":{"start":[423,3],"end":[423,23],"bytes":[11727,11747]},"callee":null},"427:3":{"kind":"call","span":{"start":[427,3],"end":[427,22],"bytes":[11799,11818]},"callee":null}}}},"fields":{"ConcurrentWrites":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/ConcurrentWrites","kind":"field","type":"bool","span":{"start":[57,1],"end":[57,23],"bytes":[1107,1129]}},"DialTimeout":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/DialTimeout","kind":"field","type":"time.Duration","span":{"start":[53,1],"end":[53,27],"bytes":[953,979]}},"Host":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/Host","kind":"field","type":"string","span":{"start":[47,1],"end":[47,20],"bytes":[833,852]}},"HostKey":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/HostKey","kind":"field","type":"string","span":{"start":[52,1],"end":[52,20],"bytes":[933,952]}},"KeyPath":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/KeyPath","kind":"field","type":"string","span":{"start":[51,1],"end":[51,20],"bytes":[913,932]}},"Password":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/Password","kind":"field","type":"string","span":{"start":[49,1],"end":[49,20],"bytes":[873,892]}},"Path":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[50,1],"end":[50,20],"bytes":[893,912]}},"User":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/User","kind":"field","type":"string","span":{"start":[48,1],"end":[48,20],"bytes":[853,872]}},"logger":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[44,1],"end":[44,25],"bytes":[782,806]}},"mu":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[41,1],"end":[41,23],"bytes":[710,732]}},"sftpClient":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/sftpClient","kind":"field","type":"*github.com/pkg/sftp.Client","span":{"start":[43,1],"end":[43,25],"bytes":[757,781]}},"sshClient":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/sshClient","kind":"field","type":"*golang.org/x/crypto/ssh.Client","span":{"start":[42,1],"end":[42,24],"bytes":[733,756]}}}}},"functions":{"github.com/benbjohnson/litestream/sftp.NewReplicaClient":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient","kind":"function","span":{"start":[61,1],"end":[67,2],"bytes":[1194,1436]},"signature":"github.com/benbjohnson/litestream/sftp.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/sftp.ReplicaClient","metrics":{"cyclomatic":1},"body":{"63:21":{"kind":"call","span":{"start":[63,21],"end":[63,35],"bytes":[1279,1293]},"callee":null}}},"github.com/benbjohnson/litestream/sftp.NewReplicaClientFromURL":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClientFromURL","kind":"function","span":{"start":[76,1],"end":[96,2],"bytes":[1739,2296]},"signature":"github.com/benbjohnson/litestream/sftp.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[76,1],"end":[76,137],"bytes":[1739,1875]}},{"name":"host","type":"string","span":{"start":[76,1],"end":[76,137],"bytes":[1739,1875]}},{"name":"urlPath","type":"string","span":{"start":[76,1],"end":[76,137],"bytes":[1739,1875]}},{"name":"query","type":"net/url.Values","span":{"start":[76,1],"end":[76,137],"bytes":[1739,1875]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[76,1],"end":[76,137],"bytes":[1739,1875]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"77:12":{"kind":"call","span":{"start":[77,12],"end":[77,30],"bytes":[1887,1905]},"callee":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient"},"81:17":{"kind":"call","span":{"start":[81,17],"end":[81,36],"bytes":[1983,2002]},"callee":null},"82:24":{"kind":"call","span":{"start":[82,24],"end":[82,43],"bytes":[2026,2045]},"callee":null},"89:15":{"kind":"call","span":{"start":[89,15],"end":[89,63],"bytes":[2132,2180]},"callee":null},"92:15":{"kind":"call","span":{"start":[92,15],"end":[92,63],"bytes":[2222,2270]},"callee":null}}},"github.com/benbjohnson/litestream/sftp.init":{"id":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.init","kind":"function","span":{"start":[25,1],"end":[27,2],"bytes":[291,380]},"signature":"github.com/benbjohnson/litestream/sftp.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"26:2":{"kind":"call","span":{"start":[26,2],"end":[26,74],"bytes":[306,378]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}}},"content_hash":"1070af36e0ce6ec261d4c0316e2632daf389405efc6ef1bff59537a1436ec4c5"},"store.go":{"id":"can://go/litestream/store.go","kind":"module","span":{"start":[1,1],"end":[959,1],"bytes":[0,26266]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promauto\"\n\t\"github.com/superfly/ltx\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar (\n\tvalidationChecksCounterVec = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"litestream_validation_checks_total\",\n\t\tHelp: \"Number of replica validation checks by result (success, invalid, or error).\",\n\t}, []string{\"db\", \"level\", \"result\"})\n\n\tlastValidationSuccessGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_last_validation_success\",\n\t\tHelp: \"Whether the last replica validation check succeeded (1) or failed (0). Zero before the first check completes.\",\n\t}, []string{\"db\", \"level\"})\n\n\tlastValidationSuccessTimestampGaugeVec = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"litestream_last_validation_success_timestamp_seconds\",\n\t\tHelp: \"Unix time of the last successful replica validation check. Zero until a check succeeds.\",\n\t}, []string{\"db\", \"level\"})\n)\n\nvar (\n\t// ErrNoCompaction is returned when no new files are available from the previous level.\n\tErrNoCompaction = errors.New(\"no compaction\")\n\n\t// ErrCompactionTooEarly is returned when a compaction is attempted too soon\n\t// since the last compaction time. This is used to prevent frequent\n\t// re-compaction when restarting the process.\n\tErrCompactionTooEarly = errors.New(\"compaction too early\")\n\n\t// ErrTxNotAvailable is returned when a transaction does not exist.\n\tErrTxNotAvailable = errors.New(\"transaction not available\")\n\n\t// ErrDBNotReady is a sentinel for errors.Is() compatibility.\n\tErrDBNotReady = \u0026DBNotReadyError{}\n\n\t// ErrShutdownInterrupted is returned when the shutdown sync retry loop\n\t// is interrupted by a done channel signal (e.g., second Ctrl+C).\n\tErrShutdownInterrupted = errors.New(\"shutdown sync interrupted\")\n\n\tErrDatabaseNotFound = errors.New(\"database not found\")\n\tErrDatabaseNotOpen = errors.New(\"database not open\")\n)\n\n// DBNotReadyError is returned when an operation is attempted before the\n// database has been initialized (e.g., page size not yet known).\ntype DBNotReadyError struct {\n\tReason string\n}\n\nfunc (e *DBNotReadyError) Error() string {\n\tif e.Reason != \"\" {\n\t\treturn \"db not ready: \" + e.Reason\n\t}\n\treturn \"db not ready\"\n}\n\nfunc (e *DBNotReadyError) Is(target error) bool {\n\t_, ok := target.(*DBNotReadyError)\n\treturn ok\n}\n\n// Store defaults\nconst (\n\tDefaultSnapshotInterval = 24 * time.Hour\n\tDefaultSnapshotRetention = 24 * time.Hour\n\n\tDefaultRetention = 24 * time.Hour\n\tDefaultRetentionCheckInterval = 1 * time.Hour\n\n\t// DefaultL0Retention is the default time that L0 files are kept around\n\t// after they have been compacted into L1 files.\n\tDefaultL0Retention = 5 * time.Minute\n\t// DefaultL0RetentionCheckInterval controls how frequently L0 retention is\n\t// enforced. This interval should be more frequent than the L1 compaction\n\t// interval so that VFS read replicas have time to observe new files.\n\tDefaultL0RetentionCheckInterval = 15 * time.Second\n\n\t// DefaultHeartbeatCheckInterval controls how frequently the heartbeat\n\t// monitor checks if heartbeat pings should be sent.\n\tDefaultHeartbeatCheckInterval = 15 * time.Second\n\n\t// DefaultDBInitTimeout is the maximum time to wait for a database to be\n\t// initialized (page size known) before logging a warning.\n\tDefaultDBInitTimeout = 30 * time.Second\n)\n\n// Store represents the top-level container for databases.\n//\n// It manages async background tasks like compactions so that the system\n// is not overloaded by too many concurrent tasks.\ntype Store struct {\n\tmu sync.Mutex\n\tdbs []*DB\n\tlevels CompactionLevels\n\n\twg sync.WaitGroup\n\tctx context.Context\n\tcancel func()\n\tdone \u003c-chan struct{}\n\n\t// The frequency of snapshots.\n\tSnapshotInterval time.Duration\n\t// The duration of time that snapshots are kept before being deleted.\n\tSnapshotRetention time.Duration\n\n\t// The duration that L0 files are kept after being compacted into L1.\n\tL0Retention time.Duration\n\t// How often to check for expired L0 files.\n\tL0RetentionCheckInterval time.Duration\n\n\t// If true, compaction is run in the background according to compaction levels.\n\tCompactionMonitorEnabled bool\n\n\t// If true, verify TXID consistency at destination level after each compaction.\n\tVerifyCompaction bool\n\n\t// RetentionEnabled controls whether Litestream actively deletes old files\n\t// during retention enforcement. When false, cloud provider lifecycle\n\t// policies handle retention instead. Local file cleanup still occurs.\n\tRetentionEnabled bool\n\n\t// Shutdown sync retry settings.\n\tShutdownSyncTimeout time.Duration\n\tShutdownSyncInterval time.Duration\n\n\t// How often to check if heartbeat pings should be sent.\n\tHeartbeatCheckInterval time.Duration\n\n\t// Heartbeat client for health check pings. Sends pings only when\n\t// all databases have synced successfully within the heartbeat interval.\n\tHeartbeat *HeartbeatClient\n\n\t// heartbeatMonitorRunning tracks whether the heartbeat monitor goroutine is running.\n\theartbeatMonitorRunning bool\n\n\t// How often to run validation checks. Zero disables periodic validation.\n\tValidationInterval time.Duration\n\n\tLogger *slog.Logger\n}\n\nfunc NewStore(dbs []*DB, levels CompactionLevels) *Store {\n\ts := \u0026Store{\n\t\tdbs: dbs,\n\t\tlevels: levels,\n\n\t\tSnapshotInterval: DefaultSnapshotInterval,\n\t\tSnapshotRetention: DefaultSnapshotRetention,\n\t\tL0Retention: DefaultL0Retention,\n\t\tL0RetentionCheckInterval: DefaultL0RetentionCheckInterval,\n\t\tCompactionMonitorEnabled: true,\n\t\tRetentionEnabled: true,\n\t\tShutdownSyncTimeout: DefaultShutdownSyncTimeout,\n\t\tShutdownSyncInterval: DefaultShutdownSyncInterval,\n\t\tHeartbeatCheckInterval: DefaultHeartbeatCheckInterval,\n\t\tLogger: slog.Default().With(LogKeySystem, LogSystemStore),\n\t}\n\n\tfor _, db := range dbs {\n\t\tdb.SetLogger(s.Logger.With(LogKeyDB, filepath.Base(db.Path())))\n\t\tdb.L0Retention = s.L0Retention\n\t\tdb.ShutdownSyncTimeout = s.ShutdownSyncTimeout\n\t\tdb.ShutdownSyncInterval = s.ShutdownSyncInterval\n\t\tdb.VerifyCompaction = s.VerifyCompaction\n\t\tdb.RetentionEnabled = s.RetentionEnabled\n\t}\n\ts.ctx, s.cancel = context.WithCancel(context.Background())\n\treturn s\n}\n\nfunc (s *Store) Open(ctx context.Context) error {\n\tif err := s.levels.Validate(); err != nil {\n\t\treturn err\n\t}\n\n\tinitGroup, initCtx := errgroup.WithContext(ctx)\n\tinitGroup.SetLimit(50)\n\tfor _, db := range s.dbs {\n\t\tdb := db\n\t\tinitGroup.Go(func() error {\n\t\t\tselect {\n\t\t\tcase \u003c-initCtx.Done():\n\t\t\t\treturn initCtx.Err()\n\t\t\tdefault:\n\t\t\t}\n\n\t\t\tif db.Replica != nil \u0026\u0026 db.Replica.Client != nil {\n\t\t\t\tif err := db.Replica.Client.Init(initCtx); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"initialize replica client for %q: %w\", db.Path(), err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\tif err := initGroup.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\topenGroup, openCtx := errgroup.WithContext(ctx)\n\topenGroup.SetLimit(50)\n\tfor _, db := range s.dbs {\n\t\tdb := db\n\t\topenGroup.Go(func() error {\n\t\t\tselect {\n\t\t\tcase \u003c-openCtx.Done():\n\t\t\t\treturn openCtx.Err()\n\t\t\tdefault:\n\t\t\t}\n\t\t\treturn db.Open()\n\t\t})\n\t}\n\tif err := openGroup.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\t// Start monitors for compactions \u0026 snapshots.\n\tif s.CompactionMonitorEnabled {\n\t\t// Start compaction monitors for all levels except L0.\n\t\tfor _, lvl := range s.levels {\n\t\t\tlvl := lvl\n\t\t\tif lvl.Level == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ts.wg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer s.wg.Done()\n\t\t\t\ts.monitorCompactionLevel(s.ctx, lvl)\n\t\t\t}()\n\t\t}\n\n\t\t// Start snapshot monitor for snapshots.\n\t\ts.wg.Add(1)\n\t\tgo func() {\n\t\t\tdefer s.wg.Done()\n\t\t\ts.monitorCompactionLevel(s.ctx, s.SnapshotLevel())\n\t\t}()\n\t}\n\n\tif s.L0Retention \u003e 0 \u0026\u0026 s.L0RetentionCheckInterval \u003e 0 {\n\t\ts.wg.Add(1)\n\t\tgo func() {\n\t\t\tdefer s.wg.Done()\n\t\t\ts.monitorL0Retention(s.ctx)\n\t\t}()\n\t}\n\n\t// Start heartbeat monitor if any database has heartbeat configured.\n\ts.startHeartbeatMonitorIfNeeded()\n\n\t// Start validation monitor if configured.\n\tif s.ValidationInterval \u003e 0 {\n\t\ts.wg.Add(1)\n\t\tgo func() {\n\t\t\tdefer s.wg.Done()\n\t\t\ts.monitorValidation(s.ctx)\n\t\t}()\n\t}\n\n\treturn nil\n}\n\nfunc (s *Store) Close(ctx context.Context) (err error) {\n\t// Stop the compaction, snapshot \u0026 retention monitors before closing the\n\t// databases so no snapshot is still reading a database's file handle.\n\ts.cancel()\n\ts.wg.Wait()\n\n\ts.mu.Lock()\n\tdbs := slices.Clone(s.dbs)\n\ts.mu.Unlock()\n\n\tfor _, db := range dbs {\n\t\tif e := db.Close(ctx); e != nil {\n\t\t\tif errors.Is(e, ErrShutdownInterrupted) {\n\t\t\t\tif err == nil {\n\t\t\t\t\terr = e\n\t\t\t\t}\n\t\t\t} else if err == nil || errors.Is(err, ErrShutdownInterrupted) {\n\t\t\t\terr = e\n\t\t\t}\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (s *Store) DBs() []*DB {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\treturn slices.Clone(s.dbs)\n}\n\n// RegisterDB registers a new database with the store and starts monitoring it.\nfunc (s *Store) RegisterDB(db *DB) error {\n\tif db == nil {\n\t\treturn fmt.Errorf(\"db required\")\n\t}\n\n\t// First check: see if database already exists\n\ts.mu.Lock()\n\tfor _, existing := range s.dbs {\n\t\tif existing.Path() == db.Path() {\n\t\t\ts.mu.Unlock()\n\t\t\treturn nil\n\t\t}\n\t}\n\ts.mu.Unlock()\n\n\t// Apply store-wide settings before opening the database.\n\tdb.SetLogger(s.Logger.With(LogKeyDB, filepath.Base(db.Path())))\n\tdb.L0Retention = s.L0Retention\n\tdb.ShutdownSyncTimeout = s.ShutdownSyncTimeout\n\tdb.ShutdownSyncInterval = s.ShutdownSyncInterval\n\tdb.VerifyCompaction = s.VerifyCompaction\n\tdb.RetentionEnabled = s.RetentionEnabled\n\tdb.Done = s.done\n\n\t// Open the database without holding the lock to avoid blocking other operations.\n\t// The double-check pattern below handles the race condition.\n\tif err := db.Open(); err != nil {\n\t\treturn fmt.Errorf(\"open db: %w\", err)\n\t}\n\n\t// Second check: verify database wasn't added by another goroutine while we were opening.\n\t// If it was, close our instance and return without error.\n\ts.mu.Lock()\n\n\tfor _, existing := range s.dbs {\n\t\tif existing.Path() == db.Path() {\n\t\t\t// Another goroutine added this database while we were opening.\n\t\t\t// Release lock before closing to avoid potential deadlock.\n\t\t\ts.mu.Unlock()\n\t\t\tif err := db.Close(context.Background()); err != nil {\n\t\t\t\tdb.Logger.Error(\"close duplicate db\", \"path\", db.Path(), \"error\", err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\ts.dbs = append(s.dbs, db)\n\ts.mu.Unlock()\n\n\t// Start heartbeat monitor if heartbeat is configured and monitor isn't running.\n\ts.startHeartbeatMonitorIfNeeded()\n\n\treturn nil\n}\n\n// UnregisterDB stops monitoring the database at the provided path and closes it.\nfunc (s *Store) UnregisterDB(ctx context.Context, path string) error {\n\tif path == \"\" {\n\t\treturn fmt.Errorf(\"db path required\")\n\t}\n\n\ts.mu.Lock()\n\n\tidx := -1\n\tvar db *DB\n\tfor i, existing := range s.dbs {\n\t\tif existing.Path() == path {\n\t\t\tidx = i\n\t\t\tdb = existing\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif db == nil {\n\t\ts.mu.Unlock()\n\t\treturn nil\n\t}\n\n\ts.dbs = slices.Delete(s.dbs, idx, idx+1)\n\ts.mu.Unlock()\n\n\tif err := db.Close(ctx); err != nil {\n\t\treturn fmt.Errorf(\"close db: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// EnableDB starts replication for a registered database.\n// The context is checked for cancellation before opening.\n// Note: db.Open() itself does not support cancellation.\nfunc (s *Store) EnableDB(ctx context.Context, path string) error {\n\tdb := s.FindDB(path)\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database not found: %s\", path)\n\t}\n\n\tif db.IsOpen() {\n\t\treturn fmt.Errorf(\"database already enabled: %s\", path)\n\t}\n\n\t// Check for cancellation before starting open\n\tif err := ctx.Err(); err != nil {\n\t\treturn fmt.Errorf(\"enable database: %w\", err)\n\t}\n\n\tif err := db.Open(); err != nil {\n\t\treturn fmt.Errorf(\"open database: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// DisableDB stops replication for a database.\nfunc (s *Store) DisableDB(ctx context.Context, path string) error {\n\tdb := s.FindDB(path)\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database not found: %s\", path)\n\t}\n\n\tif !db.IsOpen() {\n\t\treturn fmt.Errorf(\"database already disabled: %s\", path)\n\t}\n\n\tif err := db.Close(ctx); err != nil {\n\t\treturn fmt.Errorf(\"close database: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// SyncDBResult holds the result of a sync operation.\ntype SyncDBResult struct {\n\tTXID uint64\n\tReplicatedTXID uint64\n\tChanged bool\n}\n\n// SyncDB forces an immediate sync for a database. If wait is true, blocks\n// until both WAL-to-LTX and LTX-to-remote sync complete. If wait is false,\n// only performs the WAL-to-LTX sync and lets the replica monitor handle upload.\n// Lock waits are context-aware: the timeout is honored while waiting for\n// the database sync executor and the replica sync lock.\nfunc (s *Store) SyncDB(ctx context.Context, path string, wait bool) (SyncDBResult, error) {\n\tdb := s.FindDB(path)\n\tif db == nil {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"%w: %s\", ErrDatabaseNotFound, path)\n\t}\n\n\tif !db.IsOpen() {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"%w: %s\", ErrDatabaseNotOpen, path)\n\t}\n\n\t_, beforeTXID, err := db.MaxLTX()\n\tif err != nil {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"read position before sync: %w\", err)\n\t}\n\n\tif wait {\n\t\tif err := db.SyncAndWait(ctx); err != nil {\n\t\t\treturn SyncDBResult{}, fmt.Errorf(\"sync database: %w\", err)\n\t\t}\n\t} else {\n\t\tif err := db.Sync(ctx); err != nil {\n\t\t\treturn SyncDBResult{}, fmt.Errorf(\"sync database: %w\", err)\n\t\t}\n\t}\n\n\t_, afterTXID, err := db.MaxLTX()\n\tif err != nil {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"read position after sync: %w\", err)\n\t}\n\n\tvar replicatedTXID uint64\n\tif db.Replica != nil {\n\t\treplicatedTXID = uint64(db.Replica.Pos().TXID)\n\t}\n\n\treturn SyncDBResult{\n\t\tTXID: uint64(afterTXID),\n\t\tReplicatedTXID: replicatedTXID,\n\t\tChanged: afterTXID \u003e beforeTXID,\n\t}, nil\n}\n\n// FindDB returns the database with the given path.\nfunc (s *Store) FindDB(path string) *DB {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tfor _, db := range s.dbs {\n\t\tif db.Path() == path {\n\t\t\treturn db\n\t\t}\n\t}\n\treturn nil\n}\n\n// SetL0Retention updates the retention window for L0 files and propagates it to\n// all managed databases.\nfunc (s *Store) SetL0Retention(d time.Duration) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.L0Retention = d\n\tfor _, db := range s.dbs {\n\t\tdb.L0Retention = d\n\t}\n}\n\n// SetDone sets the done channel used for interrupt handling during shutdown\n// and propagates it to all managed databases.\nfunc (s *Store) SetDone(done \u003c-chan struct{}) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.done = done\n\tfor _, db := range s.dbs {\n\t\tdb.Done = done\n\t}\n}\n\n// SetShutdownSyncTimeout updates the shutdown sync timeout and propagates it to\n// all managed databases.\nfunc (s *Store) SetShutdownSyncTimeout(d time.Duration) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.ShutdownSyncTimeout = d\n\tfor _, db := range s.dbs {\n\t\tdb.ShutdownSyncTimeout = d\n\t}\n}\n\n// SetShutdownSyncInterval updates the shutdown sync interval and propagates it to\n// all managed databases.\nfunc (s *Store) SetShutdownSyncInterval(d time.Duration) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.ShutdownSyncInterval = d\n\tfor _, db := range s.dbs {\n\t\tdb.ShutdownSyncInterval = d\n\t}\n}\n\n// SetVerifyCompaction updates the verify compaction flag and propagates it to\n// all managed databases.\nfunc (s *Store) SetVerifyCompaction(v bool) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.VerifyCompaction = v\n\tfor _, db := range s.dbs {\n\t\tdb.VerifyCompaction = v\n\t\tdb.compactor.VerifyCompaction = v\n\t}\n}\n\nfunc (s *Store) SetRetentionEnabled(v bool) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\ts.RetentionEnabled = v\n\tfor _, db := range s.dbs {\n\t\tdb.RetentionEnabled = v\n\t\tdb.compactor.RetentionEnabled = v\n\t}\n}\n\n// SnapshotLevel returns a pseudo compaction level based on snapshot settings.\nfunc (s *Store) SnapshotLevel() *CompactionLevel {\n\treturn \u0026CompactionLevel{\n\t\tLevel: SnapshotLevel,\n\t\tInterval: s.SnapshotInterval,\n\t}\n}\n\nfunc (s *Store) monitorCompactionLevel(ctx context.Context, lvl *CompactionLevel) {\n\ts.Logger.Info(\"starting compaction monitor\", \"level\", lvl.Level, \"interval\", lvl.Interval)\n\n\tretryDeadline := time.Time{}\n\ttimer := time.NewTimer(time.Nanosecond)\n\tdefer timer.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\treturn\n\t\tcase \u003c-timer.C:\n\t\t\t// proceed\n\t\t}\n\n\t\tnow := time.Now()\n\t\tnextDelay := time.Until(lvl.NextCompactionAt(now))\n\n\t\tvar notReadyDBs []string\n\n\t\tfor _, db := range s.DBs() {\n\t\t\tif !db.IsOpen() {\n\t\t\t\tcontinue // skip disabled DBs\n\t\t\t}\n\t\t\t_, err := s.CompactDB(ctx, db, lvl)\n\t\t\tswitch {\n\t\t\tcase errors.Is(err, ErrNoCompaction), errors.Is(err, ErrCompactionTooEarly):\n\t\t\t\tdb.Logger.Debug(\"no compaction\", \"level\", lvl.Level, \"path\", db.Path())\n\t\t\tcase errors.Is(err, ErrDBNotReady):\n\t\t\t\tdb.Logger.Debug(\"db not ready, skipping\", \"level\", lvl.Level, \"path\", db.Path(), \"error\", err)\n\t\t\t\tnotReadyDBs = append(notReadyDBs, db.Path())\n\t\t\tcase err != nil \u0026\u0026 !isShutdownError(ctx, err):\n\t\t\t\tdb.Logger.Error(\"compaction failed\", \"level\", lvl.Level, \"error\", err)\n\t\t\t}\n\n\t\t\tif lvl.Level == SnapshotLevel {\n\t\t\t\tif err := s.EnforceSnapshotRetention(ctx, db); err != nil \u0026\u0026 !isShutdownError(ctx, err) {\n\t\t\t\t\tdb.Logger.Error(\"retention enforcement failed\", \"error\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttimedOut := !retryDeadline.IsZero() \u0026\u0026 now.After(retryDeadline)\n\t\tif len(notReadyDBs) \u003e 0 \u0026\u0026 !timedOut {\n\t\t\tif retryDeadline.IsZero() {\n\t\t\t\tretryDeadline = now.Add(DefaultDBInitTimeout)\n\t\t\t}\n\t\t\tnextDelay = time.Second\n\t\t\ts.Logger.Debug(\"scheduling retry for unready dbs\", \"level\", lvl.Level)\n\t\t} else {\n\t\t\tif timedOut {\n\t\t\t\ts.Logger.Warn(\"timeout waiting for db initialization\",\n\t\t\t\t\t\"level\", lvl.Level,\n\t\t\t\t\t\"dbs\", notReadyDBs,\n\t\t\t\t\t\"timeout\", DefaultDBInitTimeout,\n\t\t\t\t\t\"hint\", \"database may have corrupted local state or blocked transactions; try removing -litestream directory and restarting\")\n\t\t\t}\n\t\t\tretryDeadline = time.Time{}\n\t\t}\n\n\t\tif nextDelay \u003c 0 {\n\t\t\tnextDelay = 0\n\t\t}\n\t\ttimer.Reset(nextDelay)\n\t}\n}\n\nfunc (s *Store) monitorL0Retention(ctx context.Context) {\n\ts.Logger.Info(\"starting L0 retention monitor\", \"interval\", s.L0RetentionCheckInterval, \"retention\", s.L0Retention)\n\n\tticker := time.NewTicker(s.L0RetentionCheckInterval)\n\tdefer ticker.Stop()\n\nLOOP:\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\tbreak LOOP\n\t\tcase \u003c-ticker.C:\n\t\t}\n\n\t\tfor _, db := range s.DBs() {\n\t\t\tif !db.IsOpen() {\n\t\t\t\tcontinue // skip disabled DBs\n\t\t\t}\n\t\t\tif err := db.EnforceL0RetentionByTime(ctx); err != nil {\n\t\t\t\tif isShutdownError(ctx, err) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdb.Logger.Error(\"l0 retention enforcement failed\", \"path\", db.Path(), \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// startHeartbeatMonitorIfNeeded starts the heartbeat monitor goroutine if:\n// - HeartbeatCheckInterval is configured\n// - Heartbeat is configured on the Store\n// - The monitor is not already running\nfunc (s *Store) startHeartbeatMonitorIfNeeded() {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif s.heartbeatMonitorRunning {\n\t\treturn\n\t}\n\tif s.HeartbeatCheckInterval \u003c= 0 {\n\t\treturn\n\t}\n\tif !s.hasHeartbeatConfigLocked() {\n\t\treturn\n\t}\n\n\ts.heartbeatMonitorRunning = true\n\ts.wg.Add(1)\n\tgo func() {\n\t\tdefer s.wg.Done()\n\t\ts.monitorHeartbeats(s.ctx)\n\t}()\n}\n\n// hasHeartbeatConfigLocked returns true if heartbeat is configured on the Store.\n// Must be called with s.mu held.\nfunc (s *Store) hasHeartbeatConfigLocked() bool {\n\treturn s.Heartbeat != nil \u0026\u0026 s.Heartbeat.URL != \"\"\n}\n\n// monitorHeartbeats periodically checks if heartbeat pings should be sent.\n// Heartbeat pings are only sent when ALL databases have synced successfully\n// within the heartbeat interval.\nfunc (s *Store) monitorHeartbeats(ctx context.Context) {\n\ts.Logger.Info(\"starting heartbeat monitor\", \"interval\", s.HeartbeatCheckInterval)\n\n\tticker := time.NewTicker(s.HeartbeatCheckInterval)\n\tdefer ticker.Stop()\n\nLOOP:\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\tbreak LOOP\n\t\tcase \u003c-ticker.C:\n\t\t}\n\n\t\ts.sendHeartbeatIfNeeded(ctx)\n\t}\n}\n\n// sendHeartbeatIfNeeded sends a heartbeat ping if:\n// - Heartbeat is configured on the Store\n// - Enough time has passed since the last ping attempt\n// - ALL databases have synced successfully within the heartbeat interval\nfunc (s *Store) sendHeartbeatIfNeeded(ctx context.Context) {\n\thb := s.Heartbeat\n\tif hb == nil || hb.URL == \"\" {\n\t\treturn\n\t}\n\n\tif !hb.ShouldPing() {\n\t\treturn\n\t}\n\n\t// Check if all databases are healthy (synced within the heartbeat interval).\n\t// A database is healthy if it synced within the heartbeat interval.\n\thealthySince := time.Now().Add(-hb.Interval)\n\tif !s.allDatabasesHealthy(healthySince) {\n\t\treturn\n\t}\n\n\t// Record ping attempt time before making the request to ensure we respect\n\t// the configured interval even if the ping fails. This prevents rapid\n\t// retries that could overwhelm the endpoint.\n\thb.RecordPing()\n\n\tif err := hb.Ping(ctx); err != nil {\n\t\ts.Logger.Error(\"heartbeat ping failed\", \"url\", hb.URL, \"error\", err)\n\t\treturn\n\t}\n\n\ts.Logger.Debug(\"heartbeat ping sent\", \"url\", hb.URL)\n}\n\n// allDatabasesHealthy returns true if all databases have synced successfully\n// since the given time. Returns false if there are no databases or no enabled databases.\nfunc (s *Store) allDatabasesHealthy(since time.Time) bool {\n\tdbs := s.DBs()\n\tif len(dbs) == 0 {\n\t\treturn false\n\t}\n\n\tenabledCount := 0\n\tfor _, db := range dbs {\n\t\tif !db.IsOpen() {\n\t\t\tcontinue // skip disabled DBs\n\t\t}\n\t\tenabledCount++\n\t\tlastSync := db.LastSuccessfulSyncAt()\n\t\tif lastSync.IsZero() || lastSync.Before(since) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn enabledCount \u003e 0\n}\n\n// CompactDB performs a compaction or snapshot for a given database on a single destination level.\n// This function will only proceed if a compaction has not occurred before the last compaction time.\nfunc (s *Store) CompactDB(ctx context.Context, db *DB, lvl *CompactionLevel) (*ltx.FileInfo, error) {\n\t// Skip if database is not yet initialized (page size unknown).\n\tif db.PageSize() == 0 {\n\t\treturn nil, \u0026DBNotReadyError{Reason: \"page size not initialized\"}\n\t}\n\n\tdstLevel := lvl.Level\n\n\t// Ensure we are not re-compacting before the most recent compaction time.\n\tprevCompactionAt := lvl.PrevCompactionAt(time.Now())\n\tdstInfo, err := db.MaxLTXFileInfo(ctx, dstLevel)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetch dst level info: %w\", err)\n\t} else if dstInfo.CreatedAt.After(prevCompactionAt) {\n\t\treturn nil, ErrCompactionTooEarly\n\t}\n\n\t// Shortcut if this is a snapshot since we are not pulling from a previous level.\n\tif dstLevel == SnapshotLevel {\n\t\tpos, err := db.Pos()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"fetch db position: %w\", err)\n\t\t}\n\t\tif dstInfo.MaxTXID != 0 \u0026\u0026 dstInfo.MaxTXID \u003e= pos.TXID {\n\t\t\treturn nil, ErrNoCompaction\n\t\t}\n\n\t\tinfo, err := db.Snapshot(ctx)\n\t\tif err != nil {\n\t\t\treturn info, err\n\t\t}\n\t\tdb.Logger.InfoContext(ctx, \"snapshot complete\", \"txid\", info.MaxTXID.String(), \"size\", info.Size)\n\t\treturn info, nil\n\t}\n\n\t// Fetch latest LTX files for both the source \u0026 destination so we can see if we need to make progress.\n\tsrcLevel := s.levels.PrevLevel(dstLevel)\n\tsrcInfo, err := db.MaxLTXFileInfo(ctx, srcLevel)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetch src level info: %w\", err)\n\t}\n\n\t// Skip if there are no new files to compact.\n\tif srcInfo.MaxTXID \u003c= dstInfo.MaxTXID {\n\t\treturn nil, ErrNoCompaction\n\t}\n\n\tinfo, err := db.Compact(ctx, dstLevel)\n\tif err != nil {\n\t\treturn info, err\n\t}\n\n\tdb.Logger.InfoContext(ctx, \"compaction complete\",\n\t\t\"level\", dstLevel,\n\t\tslog.Group(\"txid\",\n\t\t\t\"min\", info.MinTXID.String(),\n\t\t\t\"max\", info.MaxTXID.String(),\n\t\t),\n\t\t\"size\", info.Size,\n\t)\n\n\treturn info, nil\n}\n\n// EnforceSnapshotRetention removes old snapshots by timestamp and then\n// cleans up all lower levels based on minimum snapshot TXID.\nfunc (s *Store) EnforceSnapshotRetention(ctx context.Context, db *DB) error {\n\t// Enforce retention for the snapshot level.\n\tminSnapshotTXID, err := db.EnforceSnapshotRetention(ctx, time.Now().Add(-s.SnapshotRetention))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"enforce snapshot retention: %w\", err)\n\t}\n\n\t// We should also enforce retention for L0 on the same schedule as L1.\n\tfor _, lvl := range s.levels {\n\t\t// Skip L0 since it is enforced on a more frequent basis.\n\t\tif lvl.Level == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := db.EnforceRetentionByTXID(ctx, lvl.Level, minSnapshotTXID); err != nil {\n\t\t\treturn fmt.Errorf(\"enforce L%d retention: %w\", lvl.Level, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ValidationResult holds the result of validating a replica's LTX files.\ntype ValidationResult struct {\n\tValid bool // true if no errors found\n\tErrors []ValidationError // all errors found\n}\n\n// Validate checks LTX file consistency across all databases and levels.\n// SnapshotLevel (9) is excluded since snapshots are not contiguous.\nfunc (s *Store) Validate(ctx context.Context) (*ValidationResult, error) {\n\tresult := \u0026ValidationResult{Valid: true}\n\n\ts.mu.Lock()\n\tdbs := s.dbs\n\tlevels := s.levels\n\ts.mu.Unlock()\n\n\tfor _, db := range dbs {\n\t\tif db.Replica == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, lvl := range levels {\n\t\t\tlevel := strconv.Itoa(lvl.Level)\n\t\t\tlastSuccess := lastValidationSuccessTimestampGaugeVec.WithLabelValues(db.Path(), level)\n\t\t\tsuccess := lastValidationSuccessGaugeVec.WithLabelValues(db.Path(), level)\n\t\t\terrs, err := db.Replica.ValidateLevel(ctx, lvl.Level)\n\t\t\tif err != nil {\n\t\t\t\tvalidationChecksCounterVec.WithLabelValues(db.Path(), level, \"error\").Inc()\n\t\t\t\tsuccess.Set(0)\n\t\t\t\treturn nil, fmt.Errorf(\"validate level %d for %s: %w\", lvl.Level, db.Path(), err)\n\t\t\t}\n\t\t\tif len(errs) \u003e 0 {\n\t\t\t\tvalidationChecksCounterVec.WithLabelValues(db.Path(), level, \"invalid\").Inc()\n\t\t\t\tsuccess.Set(0)\n\t\t\t\tresult.Valid = false\n\t\t\t\tresult.Errors = append(result.Errors, errs...)\n\t\t\t} else {\n\t\t\t\tvalidationChecksCounterVec.WithLabelValues(db.Path(), level, \"success\").Inc()\n\t\t\t\tsuccess.Set(1)\n\t\t\t\tlastSuccess.SetToCurrentTime()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\n// monitorValidation periodically runs validation checks on all databases.\nfunc (s *Store) monitorValidation(ctx context.Context) {\n\ts.Logger.Info(\"starting validation monitor\", \"interval\", s.ValidationInterval)\n\n\tticker := time.NewTicker(s.ValidationInterval)\n\tdefer ticker.Stop()\n\nLOOP:\n\tfor {\n\t\tselect {\n\t\tcase \u003c-ctx.Done():\n\t\t\tbreak LOOP\n\t\tcase \u003c-ticker.C:\n\t\t}\n\n\t\tresult, err := s.Validate(ctx)\n\t\tif err != nil {\n\t\t\tif isShutdownError(ctx, err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ts.Logger.Error(\"validation check failed\", \"error\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif !result.Valid {\n\t\t\tfor _, verr := range result.Errors {\n\t\t\t\ts.Logger.Warn(\"validation error detected\",\n\t\t\t\t\t\"level\", verr.Level,\n\t\t\t\t\t\"type\", verr.Type,\n\t\t\t\t\t\"message\", verr.Message,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"errors","path":"errors","span":{"start":[5,1],"end":[5,10],"bytes":[40,49]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[50,56]}},{"name":"slog","path":"log/slog","span":{"start":[7,1],"end":[7,12],"bytes":[57,68]}},{"name":"filepath","path":"path/filepath","span":{"start":[8,1],"end":[8,17],"bytes":[69,85]}},{"name":"slices","path":"slices","span":{"start":[9,1],"end":[9,10],"bytes":[86,95]}},{"name":"strconv","path":"strconv","span":{"start":[10,1],"end":[10,11],"bytes":[96,106]}},{"name":"sync","path":"sync","span":{"start":[11,1],"end":[11,8],"bytes":[107,114]}},{"name":"time","path":"time","span":{"start":[12,1],"end":[12,8],"bytes":[115,122]}},{"name":"prometheus","path":"github.com/prometheus/client_golang/prometheus","span":{"start":[14,1],"end":[14,50],"bytes":[124,173]}},{"name":"promauto","path":"github.com/prometheus/client_golang/prometheus/promauto","span":{"start":[15,1],"end":[15,59],"bytes":[174,232]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[16,1],"end":[16,27],"bytes":[233,259]}},{"name":"errgroup","path":"golang.org/x/sync/errgroup","span":{"start":[17,1],"end":[17,30],"bytes":[260,289]}}],"types":{"DBNotReadyError":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.DBNotReadyError","kind":"struct","span":{"start":[62,1],"end":[64,2],"bytes":[2191,2237]},"callables":{"github.com/benbjohnson/litestream.DBNotReadyError.Error":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.DBNotReadyError/github.com/benbjohnson/litestream.DBNotReadyError.Error","kind":"method","span":{"start":[66,1],"end":[71,2],"bytes":[2239,2367]},"signature":"github.com/benbjohnson/litestream.DBNotReadyError.Error","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{}},"github.com/benbjohnson/litestream.DBNotReadyError.Is":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.DBNotReadyError/github.com/benbjohnson/litestream.DBNotReadyError.Is","kind":"method","span":{"start":[73,1],"end":[76,2],"bytes":[2369,2467]},"signature":"github.com/benbjohnson/litestream.DBNotReadyError.Is","parameters":[{"name":"target","type":"error","span":{"start":[73,1],"end":[73,50],"bytes":[2369,2418]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{}}},"fields":{"Reason":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.DBNotReadyError/Reason","kind":"field","type":"string","span":{"start":[63,1],"end":[63,15],"bytes":[2221,2235]}}}},"Store":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store","kind":"struct","span":{"start":[107,1],"end":[156,2],"bytes":[3655,5259]},"callables":{"github.com/benbjohnson/litestream.Store.Close":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Close","kind":"method","span":{"start":[279,1],"end":[302,2],"bytes":[8144,8682]},"signature":"github.com/benbjohnson/litestream.Store.Close","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[279,1],"end":[279,57],"bytes":[8144,8200]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"282:2":{"kind":"call","span":{"start":[282,2],"end":[282,12],"bytes":[8348,8358]},"callee":null},"283:2":{"kind":"call","span":{"start":[283,2],"end":[283,13],"bytes":[8360,8371]},"callee":null},"285:2":{"kind":"call","span":{"start":[285,2],"end":[285,13],"bytes":[8374,8385]},"callee":null},"286:9":{"kind":"call","span":{"start":[286,9],"end":[286,28],"bytes":[8394,8413]},"callee":null},"287:2":{"kind":"call","span":{"start":[287,2],"end":[287,15],"bytes":[8415,8428]},"callee":null},"290:11":{"kind":"call","span":{"start":[290,11],"end":[290,24],"bytes":[8466,8479]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close"},"291:7":{"kind":"call","span":{"start":[291,7],"end":[291,43],"bytes":[8498,8534]},"callee":null},"295:28":{"kind":"call","span":{"start":[295,28],"end":[295,66],"bytes":[8603,8641]},"callee":null}}},"github.com/benbjohnson/litestream.Store.CompactDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","kind":"method","span":{"start":[789,1],"end":[851,2],"bytes":[21386,23217]},"signature":"github.com/benbjohnson/litestream.Store.CompactDB","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[789,1],"end":[789,102],"bytes":[21386,21487]}},{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[789,1],"end":[789,102],"bytes":[21386,21487]}},{"name":"lvl","type":"*github.com/benbjohnson/litestream.CompactionLevel","span":{"start":[789,1],"end":[789,102],"bytes":[21386,21487]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":11},"body":{"791:5":{"kind":"call","span":{"start":[791,5],"end":[791,18],"bytes":[21557,21570]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.PageSize"},"798:22":{"kind":"call","span":{"start":[798,22],"end":[798,54],"bytes":[21771,21803]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/github.com/benbjohnson/litestream.CompactionLevel.PrevCompactionAt"},"798:43":{"kind":"call","span":{"start":[798,43],"end":[798,53],"bytes":[21792,21802]},"callee":null},"799:18":{"kind":"call","span":{"start":[799,18],"end":[799,50],"bytes":[21821,21853]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTXFileInfo"},"801:15":{"kind":"call","span":{"start":[801,15],"end":[801,58],"bytes":[21885,21928]},"callee":null},"802:12":{"kind":"call","span":{"start":[802,12],"end":[802,53],"bytes":[21940,21981]},"callee":null},"808:15":{"kind":"call","span":{"start":[808,15],"end":[808,23],"bytes":[22153,22161]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos"},"810:16":{"kind":"call","span":{"start":[810,16],"end":[810,56],"bytes":[22195,22235]},"callee":null},"816:16":{"kind":"call","span":{"start":[816,16],"end":[816,32],"bytes":[22350,22366]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot"},"820:3":{"kind":"call","span":{"start":[820,3],"end":[820,100],"bytes":[22411,22508]},"callee":null},"820:59":{"kind":"call","span":{"start":[820,59],"end":[820,80],"bytes":[22467,22488]},"callee":null},"825:14":{"kind":"call","span":{"start":[825,14],"end":[825,42],"bytes":[22649,22677]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.PrevLevel"},"826:18":{"kind":"call","span":{"start":[826,18],"end":[826,50],"bytes":[22695,22727]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTXFileInfo"},"828:15":{"kind":"call","span":{"start":[828,15],"end":[828,58],"bytes":[22759,22802]},"callee":null},"836:15":{"kind":"call","span":{"start":[836,15],"end":[836,40],"bytes":[22943,22968]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Compact"},"841:2":{"kind":"call","span":{"start":[841,2],"end":[848,3],"bytes":[23010,23196]},"callee":null},"843:3":{"kind":"call","span":{"start":[843,3],"end":[846,4],"bytes":[23083,23171]},"callee":null},"844:11":{"kind":"call","span":{"start":[844,11],"end":[844,32],"bytes":[23112,23133]},"callee":null},"845:11":{"kind":"call","span":{"start":[845,11],"end":[845,32],"bytes":[23145,23166]},"callee":null}}},"github.com/benbjohnson/litestream.Store.DBs":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","kind":"method","span":{"start":[304,1],"end":[308,2],"bytes":[8684,8777]},"signature":"github.com/benbjohnson/litestream.Store.DBs","parameters":[],"return_type":"[]*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":1},"body":{"305:2":{"kind":"call","span":{"start":[305,2],"end":[305,13],"bytes":[8715,8726]},"callee":null},"306:8":{"kind":"call","span":{"start":[306,8],"end":[306,21],"bytes":[8734,8747]},"callee":null},"307:9":{"kind":"call","span":{"start":[307,9],"end":[307,28],"bytes":[8756,8775]},"callee":null}}},"github.com/benbjohnson/litestream.Store.DisableDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DisableDB","kind":"method","span":{"start":[425,1],"end":[440,2],"bytes":[11706,12053]},"signature":"github.com/benbjohnson/litestream.Store.DisableDB","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[425,1],"end":[425,68],"bytes":[11706,11773]}},{"name":"path","type":"string","span":{"start":[425,1],"end":[425,68],"bytes":[11706,11773]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"426:8":{"kind":"call","span":{"start":[426,8],"end":[426,22],"bytes":[11781,11795]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"428:10":{"kind":"call","span":{"start":[428,10],"end":[428,52],"bytes":[11821,11863]},"callee":null},"431:6":{"kind":"call","span":{"start":[431,6],"end":[431,17],"bytes":[11873,11884]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"432:10":{"kind":"call","span":{"start":[432,10],"end":[432,59],"bytes":[11896,11945]},"callee":null},"435:12":{"kind":"call","span":{"start":[435,12],"end":[435,25],"bytes":[11961,11974]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close"},"436:10":{"kind":"call","span":{"start":[436,10],"end":[436,47],"bytes":[11998,12035]},"callee":null}}},"github.com/benbjohnson/litestream.Store.EnableDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnableDB","kind":"method","span":{"start":[402,1],"end":[422,2],"bytes":[11183,11657]},"signature":"github.com/benbjohnson/litestream.Store.EnableDB","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[402,1],"end":[402,67],"bytes":[11183,11249]}},{"name":"path","type":"string","span":{"start":[402,1],"end":[402,67],"bytes":[11183,11249]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"403:8":{"kind":"call","span":{"start":[403,8],"end":[403,22],"bytes":[11257,11271]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"405:10":{"kind":"call","span":{"start":[405,10],"end":[405,52],"bytes":[11297,11339]},"callee":null},"408:5":{"kind":"call","span":{"start":[408,5],"end":[408,16],"bytes":[11348,11359]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"409:10":{"kind":"call","span":{"start":[409,10],"end":[409,58],"bytes":[11371,11419]},"callee":null},"413:12":{"kind":"call","span":{"start":[413,12],"end":[413,21],"bytes":[11483,11492]},"callee":null},"414:10":{"kind":"call","span":{"start":[414,10],"end":[414,48],"bytes":[11516,11554]},"callee":null},"417:12":{"kind":"call","span":{"start":[417,12],"end":[417,21],"bytes":[11570,11579]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open"},"418:10":{"kind":"call","span":{"start":[418,10],"end":[418,46],"bytes":[11603,11639]},"callee":null}}},"github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention","kind":"method","span":{"start":[855,1],"end":[875,2],"bytes":[23353,24028]},"signature":"github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[855,1],"end":[855,78],"bytes":[23353,23430]}},{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[855,1],"end":[855,78],"bytes":[23353,23430]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"857:26":{"kind":"call","span":{"start":[857,26],"end":[857,96],"bytes":[23502,23572]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention"},"857:59":{"kind":"call","span":{"start":[857,59],"end":[857,69],"bytes":[23535,23545]},"callee":null},"859:10":{"kind":"call","span":{"start":[859,10],"end":[859,59],"bytes":[23599,23648]},"callee":null},"869:13":{"kind":"call","span":{"start":[869,13],"end":[869,71],"bytes":[23868,23926]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceRetentionByTXID"},"870:11":{"kind":"call","span":{"start":[870,11],"end":[870,66],"bytes":[23951,24006]},"callee":null}}},"github.com/benbjohnson/litestream.Store.FindDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","kind":"method","span":{"start":[497,1],"end":[507,2],"bytes":[13675,13838]},"signature":"github.com/benbjohnson/litestream.Store.FindDB","parameters":[{"name":"path","type":"string","span":{"start":[497,1],"end":[497,42],"bytes":[13675,13716]}}],"return_type":"*github.com/benbjohnson/litestream.DB","metrics":{"cyclomatic":3},"body":{"498:2":{"kind":"call","span":{"start":[498,2],"end":[498,13],"bytes":[13718,13729]},"callee":null},"499:8":{"kind":"call","span":{"start":[499,8],"end":[499,21],"bytes":[13737,13750]},"callee":null},"502:6":{"kind":"call","span":{"start":[502,6],"end":[502,15],"bytes":[13785,13794]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"}}},"github.com/benbjohnson/litestream.Store.Open":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open","kind":"method","span":{"start":[187,1],"end":[277,2],"bytes":[6294,8142]},"signature":"github.com/benbjohnson/litestream.Store.Open","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[187,1],"end":[187,50],"bytes":[6294,6343]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":19},"body":{"188:12":{"kind":"call","span":{"start":[188,12],"end":[188,31],"bytes":[6355,6374]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.Validate"},"192:24":{"kind":"call","span":{"start":[192,24],"end":[192,49],"bytes":[6429,6454]},"callee":null},"193:2":{"kind":"call","span":{"start":[193,2],"end":[193,24],"bytes":[6456,6478]},"callee":null},"196:3":{"kind":"call","span":{"start":[196,3],"end":[209,5],"bytes":[6520,6851]},"callee":null},"211:12":{"kind":"call","span":{"start":[211,12],"end":[211,28],"bytes":[6866,6882]},"callee":null},"215:24":{"kind":"call","span":{"start":[215,24],"end":[215,49],"bytes":[6937,6962]},"callee":null},"216:2":{"kind":"call","span":{"start":[216,2],"end":[216,24],"bytes":[6964,6986]},"callee":null},"219:3":{"kind":"call","span":{"start":[219,3],"end":[226,5],"bytes":[7028,7160]},"callee":null},"228:12":{"kind":"call","span":{"start":[228,12],"end":[228,28],"bytes":[7175,7191]},"callee":null},"241:4":{"kind":"call","span":{"start":[241,4],"end":[241,15],"bytes":[7453,7464]},"callee":null},"242:7":{"kind":"call","span":{"start":[242,7],"end":[245,7],"bytes":[7471,7549]},"callee":null,"is_goroutine":true},"249:3":{"kind":"call","span":{"start":[249,3],"end":[249,14],"bytes":[7600,7611]},"callee":null},"250:6":{"kind":"call","span":{"start":[250,6],"end":[253,6],"bytes":[7617,7706]},"callee":null,"is_goroutine":true},"257:3":{"kind":"call","span":{"start":[257,3],"end":[257,14],"bytes":[7771,7782]},"callee":null},"258:6":{"kind":"call","span":{"start":[258,6],"end":[261,6],"bytes":[7788,7854]},"callee":null,"is_goroutine":true},"265:2":{"kind":"call","span":{"start":[265,2],"end":[265,35],"bytes":[7930,7963]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded"},"269:3":{"kind":"call","span":{"start":[269,3],"end":[269,14],"bytes":[8042,8053]},"callee":null},"270:6":{"kind":"call","span":{"start":[270,6],"end":[273,6],"bytes":[8059,8124]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open/github.com/benbjohnson/litestream.Store.Open.closure_1","kind":"function","span":{"start":[196,1],"end":[209,5],"bytes":[6518,6851]},"signature":"github.com/benbjohnson/litestream.Store.Open.closure_1","parameters":[],"return_type":"error","error_channel":["error"],"body":{"198:11":{"kind":"call","span":{"start":[198,11],"end":[198,25],"bytes":[6570,6584]},"callee":null},"199:12":{"kind":"call","span":{"start":[199,12],"end":[199,25],"bytes":[6597,6610]},"callee":null},"204:15":{"kind":"call","span":{"start":[204,15],"end":[204,46],"bytes":[6697,6728]},"callee":null},"205:13":{"kind":"call","span":{"start":[205,13],"end":[205,79],"bytes":[6755,6821]},"callee":null},"205:64":{"kind":"call","span":{"start":[205,64],"end":[205,73],"bytes":[6806,6815]},"callee":null}}},"closure_2":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open/github.com/benbjohnson/litestream.Store.Open.closure_2","kind":"function","span":{"start":[219,1],"end":[226,5],"bytes":[7026,7160]},"signature":"github.com/benbjohnson/litestream.Store.Open.closure_2","parameters":[],"return_type":"error","error_channel":["error"],"body":{"221:11":{"kind":"call","span":{"start":[221,11],"end":[221,25],"bytes":[7078,7092]},"callee":null},"222:12":{"kind":"call","span":{"start":[222,12],"end":[222,25],"bytes":[7105,7118]},"callee":null},"225:11":{"kind":"call","span":{"start":[225,11],"end":[225,20],"bytes":[7146,7155]},"callee":null}}},"closure_3":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open/github.com/benbjohnson/litestream.Store.Open.closure_3","kind":"function","span":{"start":[242,1],"end":[245,7],"bytes":[7465,7549]},"signature":"github.com/benbjohnson/litestream.Store.Open.closure_3","parameters":[],"body":{"243:11":{"kind":"call","span":{"start":[243,11],"end":[243,22],"bytes":[7490,7501]},"callee":null},"244:5":{"kind":"call","span":{"start":[244,5],"end":[244,41],"bytes":[7506,7542]},"callee":null}}},"closure_4":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open/github.com/benbjohnson/litestream.Store.Open.closure_4","kind":"function","span":{"start":[250,1],"end":[253,6],"bytes":[7612,7706]},"signature":"github.com/benbjohnson/litestream.Store.Open.closure_4","parameters":[],"body":{"251:10":{"kind":"call","span":{"start":[251,10],"end":[251,21],"bytes":[7635,7646]},"callee":null},"252:36":{"kind":"call","span":{"start":[252,36],"end":[252,53],"bytes":[7682,7699]},"callee":null},"252:4":{"kind":"call","span":{"start":[252,4],"end":[252,54],"bytes":[7650,7700]},"callee":null}}},"closure_5":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open/github.com/benbjohnson/litestream.Store.Open.closure_5","kind":"function","span":{"start":[258,1],"end":[261,6],"bytes":[7783,7854]},"signature":"github.com/benbjohnson/litestream.Store.Open.closure_5","parameters":[],"body":{"259:10":{"kind":"call","span":{"start":[259,10],"end":[259,21],"bytes":[7806,7817]},"callee":null},"260:4":{"kind":"call","span":{"start":[260,4],"end":[260,31],"bytes":[7821,7848]},"callee":null}}},"closure_6":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open/github.com/benbjohnson/litestream.Store.Open.closure_6","kind":"function","span":{"start":[270,1],"end":[273,6],"bytes":[8054,8124]},"signature":"github.com/benbjohnson/litestream.Store.Open.closure_6","parameters":[],"body":{"271:10":{"kind":"call","span":{"start":[271,10],"end":[271,21],"bytes":[8077,8088]},"callee":null},"272:4":{"kind":"call","span":{"start":[272,4],"end":[272,30],"bytes":[8092,8118]},"callee":null}}}}},"github.com/benbjohnson/litestream.Store.RegisterDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","kind":"method","span":{"start":[311,1],"end":[364,2],"bytes":[8859,10440]},"signature":"github.com/benbjohnson/litestream.Store.RegisterDB","parameters":[{"name":"db","type":"*github.com/benbjohnson/litestream.DB","span":{"start":[311,1],"end":[311,43],"bytes":[8859,8901]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"313:10":{"kind":"call","span":{"start":[313,10],"end":[313,35],"bytes":[8927,8952]},"callee":null},"317:2":{"kind":"call","span":{"start":[317,2],"end":[317,13],"bytes":[9006,9017]},"callee":null},"319:25":{"kind":"call","span":{"start":[319,25],"end":[319,34],"bytes":[9076,9085]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"319:6":{"kind":"call","span":{"start":[319,6],"end":[319,21],"bytes":[9057,9072]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"320:4":{"kind":"call","span":{"start":[320,4],"end":[320,17],"bytes":[9091,9104]},"callee":null},"324:2":{"kind":"call","span":{"start":[324,2],"end":[324,15],"bytes":[9127,9140]},"callee":null},"327:15":{"kind":"call","span":{"start":[327,15],"end":[327,64],"bytes":[9215,9264]},"callee":null},"327:2":{"kind":"call","span":{"start":[327,2],"end":[327,65],"bytes":[9202,9265]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetLogger"},"327:39":{"kind":"call","span":{"start":[327,39],"end":[327,63],"bytes":[9239,9263]},"callee":null},"327:53":{"kind":"call","span":{"start":[327,53],"end":[327,62],"bytes":[9253,9262]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"337:12":{"kind":"call","span":{"start":[337,12],"end":[337,21],"bytes":[9656,9665]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint"},"338:10":{"kind":"call","span":{"start":[338,10],"end":[338,40],"bytes":[9689,9719]},"callee":null},"343:2":{"kind":"call","span":{"start":[343,2],"end":[343,13],"bytes":[9876,9887]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON"},"346:25":{"kind":"call","span":{"start":[346,25],"end":[346,34],"bytes":[9947,9956]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"346:6":{"kind":"call","span":{"start":[346,6],"end":[346,21],"bytes":[9928,9943]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"349:4":{"kind":"call","span":{"start":[349,4],"end":[349,17],"bytes":[10092,10105]},"callee":null},"350:14":{"kind":"call","span":{"start":[350,14],"end":[350,44],"bytes":[10119,10149]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close"},"350:23":{"kind":"call","span":{"start":[350,23],"end":[350,43],"bytes":[10128,10148]},"callee":null},"351:5":{"kind":"call","span":{"start":[351,5],"end":[351,75],"bytes":[10168,10238]},"callee":null},"351:51":{"kind":"call","span":{"start":[351,51],"end":[351,60],"bytes":[10214,10223]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"357:10":{"kind":"call","span":{"start":[357,10],"end":[357,27],"bytes":[10275,10292]},"callee":null},"358:2":{"kind":"call","span":{"start":[358,2],"end":[358,15],"bytes":[10294,10307]},"callee":null},"361:2":{"kind":"call","span":{"start":[361,2],"end":[361,35],"bytes":[10392,10425]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded"}}},"github.com/benbjohnson/litestream.Store.SetDone":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetDone","kind":"method","span":{"start":[522,1],"end":[529,2],"bytes":[14229,14375]},"signature":"github.com/benbjohnson/litestream.Store.SetDone","parameters":[{"name":"done","type":"\u003c-chan struct{}","span":{"start":[522,1],"end":[522,48],"bytes":[14229,14276]}}],"metrics":{"cyclomatic":2},"body":{"523:2":{"kind":"call","span":{"start":[523,2],"end":[523,13],"bytes":[14278,14289]},"callee":null},"524:8":{"kind":"call","span":{"start":[524,8],"end":[524,21],"bytes":[14297,14310]},"callee":null}}},"github.com/benbjohnson/litestream.Store.SetL0Retention":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetL0Retention","kind":"method","span":{"start":[511,1],"end":[518,2],"bytes":[13947,14103]},"signature":"github.com/benbjohnson/litestream.Store.SetL0Retention","parameters":[{"name":"d","type":"time.Duration","span":{"start":[511,1],"end":[511,50],"bytes":[13947,13996]}}],"metrics":{"cyclomatic":2},"body":{"512:2":{"kind":"call","span":{"start":[512,2],"end":[512,13],"bytes":[13998,14009]},"callee":null},"513:8":{"kind":"call","span":{"start":[513,8],"end":[513,21],"bytes":[14017,14030]},"callee":null}}},"github.com/benbjohnson/litestream.Store.SetRetentionEnabled":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetRetentionEnabled","kind":"method","span":{"start":[565,1],"end":[573,2],"bytes":[15265,15463]},"signature":"github.com/benbjohnson/litestream.Store.SetRetentionEnabled","parameters":[{"name":"v","type":"bool","span":{"start":[565,1],"end":[565,46],"bytes":[15265,15310]}}],"metrics":{"cyclomatic":2},"body":{"566:2":{"kind":"call","span":{"start":[566,2],"end":[566,13],"bytes":[15312,15323]},"callee":null},"567:8":{"kind":"call","span":{"start":[567,8],"end":[567,21],"bytes":[15331,15344]},"callee":null}}},"github.com/benbjohnson/litestream.Store.SetShutdownSyncInterval":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetShutdownSyncInterval","kind":"method","span":{"start":[544,1],"end":[551,2],"bytes":[14775,14958]},"signature":"github.com/benbjohnson/litestream.Store.SetShutdownSyncInterval","parameters":[{"name":"d","type":"time.Duration","span":{"start":[544,1],"end":[544,59],"bytes":[14775,14833]}}],"metrics":{"cyclomatic":2},"body":{"545:2":{"kind":"call","span":{"start":[545,2],"end":[545,13],"bytes":[14835,14846]},"callee":null},"546:8":{"kind":"call","span":{"start":[546,8],"end":[546,21],"bytes":[14854,14867]},"callee":null}}},"github.com/benbjohnson/litestream.Store.SetShutdownSyncTimeout":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetShutdownSyncTimeout","kind":"method","span":{"start":[533,1],"end":[540,2],"bytes":[14484,14664]},"signature":"github.com/benbjohnson/litestream.Store.SetShutdownSyncTimeout","parameters":[{"name":"d","type":"time.Duration","span":{"start":[533,1],"end":[533,58],"bytes":[14484,14541]}}],"metrics":{"cyclomatic":2},"body":{"534:2":{"kind":"call","span":{"start":[534,2],"end":[534,13],"bytes":[14543,14554]},"callee":null},"535:8":{"kind":"call","span":{"start":[535,8],"end":[535,21],"bytes":[14562,14575]},"callee":null}}},"github.com/benbjohnson/litestream.Store.SetVerifyCompaction":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetVerifyCompaction","kind":"method","span":{"start":[555,1],"end":[563,2],"bytes":[15065,15263]},"signature":"github.com/benbjohnson/litestream.Store.SetVerifyCompaction","parameters":[{"name":"v","type":"bool","span":{"start":[555,1],"end":[555,46],"bytes":[15065,15110]}}],"metrics":{"cyclomatic":2},"body":{"556:2":{"kind":"call","span":{"start":[556,2],"end":[556,13],"bytes":[15112,15123]},"callee":null},"557:8":{"kind":"call","span":{"start":[557,8],"end":[557,21],"bytes":[15131,15144]},"callee":null}}},"github.com/benbjohnson/litestream.Store.SnapshotLevel":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SnapshotLevel","kind":"method","span":{"start":[576,1],"end":[581,2],"bytes":[15544,15684]},"signature":"github.com/benbjohnson/litestream.Store.SnapshotLevel","parameters":[],"return_type":"*github.com/benbjohnson/litestream.CompactionLevel","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.Store.SyncDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","kind":"method","span":{"start":[454,1],"end":[494,2],"bytes":[12569,13621]},"signature":"github.com/benbjohnson/litestream.Store.SyncDB","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[454,1],"end":[454,92],"bytes":[12569,12660]}},{"name":"path","type":"string","span":{"start":[454,1],"end":[454,92],"bytes":[12569,12660]}},{"name":"wait","type":"bool","span":{"start":[454,1],"end":[454,92],"bytes":[12569,12660]}}],"return_type":"(github.com/benbjohnson/litestream.SyncDBResult, error)","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"455:8":{"kind":"call","span":{"start":[455,8],"end":[455,22],"bytes":[12668,12682]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB"},"457:26":{"kind":"call","span":{"start":[457,26],"end":[457,73],"bytes":[12724,12771]},"callee":null},"460:6":{"kind":"call","span":{"start":[460,6],"end":[460,17],"bytes":[12781,12792]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"461:26":{"kind":"call","span":{"start":[461,26],"end":[461,72],"bytes":[12820,12866]},"callee":null},"464:24":{"kind":"call","span":{"start":[464,24],"end":[464,35],"bytes":[12894,12905]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX"},"466:26":{"kind":"call","span":{"start":[466,26],"end":[466,74],"bytes":[12948,12996]},"callee":null},"470:13":{"kind":"call","span":{"start":[470,13],"end":[470,32],"bytes":[13024,13043]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncAndWait"},"471:27":{"kind":"call","span":{"start":[471,27],"end":[471,63],"bytes":[13084,13120]},"callee":null},"474:13":{"kind":"call","span":{"start":[474,13],"end":[474,25],"bytes":[13147,13159]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync"},"475:27":{"kind":"call","span":{"start":[475,27],"end":[475,63],"bytes":[13200,13236]},"callee":null},"479:23":{"kind":"call","span":{"start":[479,23],"end":[479,34],"bytes":[13267,13278]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX"},"481:26":{"kind":"call","span":{"start":[481,26],"end":[481,73],"bytes":[13321,13368]},"callee":null},"486:20":{"kind":"call","span":{"start":[486,20],"end":[486,49],"bytes":[13443,13472]},"callee":null},"486:27":{"kind":"call","span":{"start":[486,27],"end":[486,43],"bytes":[13450,13466]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos"},"490:19":{"kind":"call","span":{"start":[490,19],"end":[490,36],"bytes":[13517,13534]},"callee":null}}},"github.com/benbjohnson/litestream.Store.UnregisterDB":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB","kind":"method","span":{"start":[367,1],"end":[397,2],"bytes":[10524,11007]},"signature":"github.com/benbjohnson/litestream.Store.UnregisterDB","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[367,1],"end":[367,71],"bytes":[10524,10594]}},{"name":"path","type":"string","span":{"start":[367,1],"end":[367,71],"bytes":[10524,10594]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"369:10":{"kind":"call","span":{"start":[369,10],"end":[369,40],"bytes":[10621,10651]},"callee":null},"372:2":{"kind":"call","span":{"start":[372,2],"end":[372,13],"bytes":[10657,10668]},"callee":null},"377:6":{"kind":"call","span":{"start":[377,6],"end":[377,21],"bytes":[10732,10747]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"385:3":{"kind":"call","span":{"start":[385,3],"end":[385,16],"bytes":[10821,10834]},"callee":null},"389:10":{"kind":"call","span":{"start":[389,10],"end":[389,42],"bytes":[10861,10893]},"callee":null},"390:2":{"kind":"call","span":{"start":[390,2],"end":[390,15],"bytes":[10895,10908]},"callee":null},"392:12":{"kind":"call","span":{"start":[392,12],"end":[392,25],"bytes":[10921,10934]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close"},"393:10":{"kind":"call","span":{"start":[393,10],"end":[393,41],"bytes":[10958,10989]},"callee":null}}},"github.com/benbjohnson/litestream.Store.Validate":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Validate","kind":"method","span":{"start":[885,1],"end":[922,2],"bytes":[24379,25511]},"signature":"github.com/benbjohnson/litestream.Store.Validate","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[885,1],"end":[885,75],"bytes":[24379,24453]}}],"return_type":"(*github.com/benbjohnson/litestream.ValidationResult, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"888:2":{"kind":"call","span":{"start":[888,2],"end":[888,13],"bytes":[24498,24509]},"callee":null},"891:2":{"kind":"call","span":{"start":[891,2],"end":[891,15],"bytes":[24545,24558]},"callee":null},"899:13":{"kind":"call","span":{"start":[899,13],"end":[899,36],"bytes":[24671,24694]},"callee":null},"900:19":{"kind":"call","span":{"start":[900,19],"end":[900,91],"bytes":[24713,24785]},"callee":null},"900:74":{"kind":"call","span":{"start":[900,74],"end":[900,83],"bytes":[24768,24777]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"901:15":{"kind":"call","span":{"start":[901,15],"end":[901,78],"bytes":[24800,24863]},"callee":null},"901:61":{"kind":"call","span":{"start":[901,61],"end":[901,70],"bytes":[24846,24855]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"902:17":{"kind":"call","span":{"start":[902,17],"end":[902,57],"bytes":[24880,24920]},"callee":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.ValidateLevel"},"904:48":{"kind":"call","span":{"start":[904,48],"end":[904,57],"bytes":[24987,24996]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"904:5":{"kind":"call","span":{"start":[904,5],"end":[904,74],"bytes":[24944,25013]},"callee":null},"905:5":{"kind":"call","span":{"start":[905,5],"end":[905,19],"bytes":[25024,25038]},"callee":null},"906:17":{"kind":"call","span":{"start":[906,17],"end":[906,86],"bytes":[25055,25124]},"callee":null},"906:71":{"kind":"call","span":{"start":[906,71],"end":[906,80],"bytes":[25109,25118]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"908:7":{"kind":"call","span":{"start":[908,7],"end":[908,16],"bytes":[25136,25145]},"callee":null},"909:48":{"kind":"call","span":{"start":[909,48],"end":[909,57],"bytes":[25199,25208]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"909:5":{"kind":"call","span":{"start":[909,5],"end":[909,76],"bytes":[25156,25227]},"callee":null},"910:5":{"kind":"call","span":{"start":[910,5],"end":[910,19],"bytes":[25238,25252]},"callee":null},"912:21":{"kind":"call","span":{"start":[912,21],"end":[912,51],"bytes":[25298,25328]},"callee":null},"914:48":{"kind":"call","span":{"start":[914,48],"end":[914,57],"bytes":[25388,25397]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"914:5":{"kind":"call","span":{"start":[914,5],"end":[914,76],"bytes":[25345,25416]},"callee":null},"915:5":{"kind":"call","span":{"start":[915,5],"end":[915,19],"bytes":[25427,25441]},"callee":null},"916:5":{"kind":"call","span":{"start":[916,5],"end":[916,35],"bytes":[25446,25476]},"callee":null}}},"github.com/benbjohnson/litestream.Store.allDatabasesHealthy":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.allDatabasesHealthy","kind":"method","span":{"start":[767,1],"end":[785,2],"bytes":[20810,21184]},"signature":"github.com/benbjohnson/litestream.Store.allDatabasesHealthy","parameters":[{"name":"since","type":"time.Time","span":{"start":[767,1],"end":[767,60],"bytes":[20810,20869]}}],"return_type":"bool","metrics":{"cyclomatic":5},"body":{"768:9":{"kind":"call","span":{"start":[768,9],"end":[768,16],"bytes":[20878,20885]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"769:5":{"kind":"call","span":{"start":[769,5],"end":[769,13],"bytes":[20890,20898]},"callee":null},"775:7":{"kind":"call","span":{"start":[775,7],"end":[775,18],"bytes":[20976,20987]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"779:15":{"kind":"call","span":{"start":[779,15],"end":[779,40],"bytes":[21058,21083]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt"},"780:27":{"kind":"call","span":{"start":[780,27],"end":[780,49],"bytes":[21110,21132]},"callee":null},"780:6":{"kind":"call","span":{"start":[780,6],"end":[780,23],"bytes":[21089,21106]},"callee":null}}},"github.com/benbjohnson/litestream.Store.hasHeartbeatConfigLocked":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.hasHeartbeatConfigLocked","kind":"method","span":{"start":[706,1],"end":[708,2],"bytes":[18987,19090]},"signature":"github.com/benbjohnson/litestream.Store.hasHeartbeatConfigLocked","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.Store.monitorCompactionLevel":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","kind":"method","span":{"start":[583,1],"end":[648,2],"bytes":[15686,17683]},"signature":"github.com/benbjohnson/litestream.Store.monitorCompactionLevel","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[583,1],"end":[583,84],"bytes":[15686,15769]}},{"name":"lvl","type":"*github.com/benbjohnson/litestream.CompactionLevel","span":{"start":[583,1],"end":[583,84],"bytes":[15686,15769]}}],"metrics":{"cyclomatic":17},"body":{"584:2":{"kind":"call","span":{"start":[584,2],"end":[584,92],"bytes":[15771,15861]},"callee":null},"587:11":{"kind":"call","span":{"start":[587,11],"end":[587,41],"bytes":[15903,15933]},"callee":null},"588:8":{"kind":"call","span":{"start":[588,8],"end":[588,20],"bytes":[15941,15953]},"callee":null},"592:10":{"kind":"call","span":{"start":[592,10],"end":[592,20],"bytes":[15982,15992]},"callee":null},"598:10":{"kind":"call","span":{"start":[598,10],"end":[598,20],"bytes":[16050,16060]},"callee":null},"599:16":{"kind":"call","span":{"start":[599,16],"end":[599,53],"bytes":[16076,16113]},"callee":null},"599:27":{"kind":"call","span":{"start":[599,27],"end":[599,52],"bytes":[16087,16112]},"callee":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/github.com/benbjohnson/litestream.CompactionLevel.NextCompactionAt"},"603:22":{"kind":"call","span":{"start":[603,22],"end":[603,29],"bytes":[16164,16171]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"604:8":{"kind":"call","span":{"start":[604,8],"end":[604,19],"bytes":[16181,16192]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen"},"607:14":{"kind":"call","span":{"start":[607,14],"end":[607,39],"bytes":[16247,16272]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB"},"609:42":{"kind":"call","span":{"start":[609,42],"end":[609,79],"bytes":[16326,16363]},"callee":null},"609:9":{"kind":"call","span":{"start":[609,9],"end":[609,40],"bytes":[16293,16324]},"callee":null},"610:5":{"kind":"call","span":{"start":[610,5],"end":[610,76],"bytes":[16369,16440]},"callee":null},"610:66":{"kind":"call","span":{"start":[610,66],"end":[610,75],"bytes":[16430,16439]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"611:9":{"kind":"call","span":{"start":[611,9],"end":[611,38],"bytes":[16449,16478]},"callee":null},"612:5":{"kind":"call","span":{"start":[612,5],"end":[612,99],"bytes":[16484,16578]},"callee":null},"612:75":{"kind":"call","span":{"start":[612,75],"end":[612,84],"bytes":[16554,16563]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"613:19":{"kind":"call","span":{"start":[613,19],"end":[613,49],"bytes":[16597,16627]},"callee":null},"613:39":{"kind":"call","span":{"start":[613,39],"end":[613,48],"bytes":[16617,16626]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"614:24":{"kind":"call","span":{"start":[614,24],"end":[614,49],"bytes":[16651,16676]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"615:5":{"kind":"call","span":{"start":[615,5],"end":[615,75],"bytes":[16682,16752]},"callee":null},"619:15":{"kind":"call","span":{"start":[619,15],"end":[619,50],"bytes":[16808,16843]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention"},"619:67":{"kind":"call","span":{"start":[619,67],"end":[619,92],"bytes":[16860,16885]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"620:6":{"kind":"call","span":{"start":[620,6],"end":[620,67],"bytes":[16893,16954]},"callee":null},"625:16":{"kind":"call","span":{"start":[625,16],"end":[625,38],"bytes":[16986,17008]},"callee":null},"625:42":{"kind":"call","span":{"start":[625,42],"end":[625,66],"bytes":[17012,17036]},"callee":null},"626:6":{"kind":"call","span":{"start":[626,6],"end":[626,22],"bytes":[17042,17058]},"callee":null},"627:7":{"kind":"call","span":{"start":[627,7],"end":[627,29],"bytes":[17084,17106]},"callee":null},"628:21":{"kind":"call","span":{"start":[628,21],"end":[628,50],"bytes":[17129,17158]},"callee":null},"631:4":{"kind":"call","span":{"start":[631,4],"end":[631,74],"bytes":[17194,17264]},"callee":null},"634:5":{"kind":"call","span":{"start":[634,5],"end":[638,131],"bytes":[17297,17570]},"callee":null},"646:3":{"kind":"call","span":{"start":[646,3],"end":[646,25],"bytes":[17656,17678]},"callee":null}}},"github.com/benbjohnson/litestream.Store.monitorHeartbeats":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorHeartbeats","kind":"method","span":{"start":[713,1],"end":[729,2],"bytes":[19279,19612]},"signature":"github.com/benbjohnson/litestream.Store.monitorHeartbeats","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[713,1],"end":[713,57],"bytes":[19279,19335]}}],"metrics":{"cyclomatic":5},"body":{"714:2":{"kind":"call","span":{"start":[714,2],"end":[714,83],"bytes":[19337,19418]},"callee":null},"716:12":{"kind":"call","span":{"start":[716,12],"end":[716,52],"bytes":[19431,19471]},"callee":null},"717:8":{"kind":"call","span":{"start":[717,8],"end":[717,21],"bytes":[19479,19492]},"callee":null},"722:10":{"kind":"call","span":{"start":[722,10],"end":[722,20],"bytes":[19527,19537]},"callee":null},"727:3":{"kind":"call","span":{"start":[727,3],"end":[727,31],"bytes":[19579,19607]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded"}}},"github.com/benbjohnson/litestream.Store.monitorL0Retention":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorL0Retention","kind":"method","span":{"start":[650,1],"end":[676,2],"bytes":[17685,18326]},"signature":"github.com/benbjohnson/litestream.Store.monitorL0Retention","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[650,1],"end":[650,58],"bytes":[17685,17742]}}],"metrics":{"cyclomatic":9},"body":{"651:2":{"kind":"call","span":{"start":[651,2],"end":[651,116],"bytes":[17744,17858]},"callee":null},"653:12":{"kind":"call","span":{"start":[653,12],"end":[653,54],"bytes":[17871,17913]},"callee":null},"654:8":{"kind":"call","span":{"start":[654,8],"end":[654,21],"bytes":[17921,17934]},"callee":null},"659:10":{"kind":"call","span":{"start":[659,10],"end":[659,20],"bytes":[17969,17979]},"callee":null},"664:22":{"kind":"call","span":{"start":[664,22],"end":[664,29],"bytes":[18040,18047]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs"},"665:8":{"kind":"call","span":{"start":[665,8],"end":[665,19],"bytes":[18057,18068]},"callee":null},"668:14":{"kind":"call","span":{"start":[668,14],"end":[668,46],"bytes":[18123,18155]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime"},"669:8":{"kind":"call","span":{"start":[669,8],"end":[669,33],"bytes":[18177,18202]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"672:5":{"kind":"call","span":{"start":[672,5],"end":[672,88],"bytes":[18229,18312]},"callee":null},"672:64":{"kind":"call","span":{"start":[672,64],"end":[672,73],"bytes":[18288,18297]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"}}},"github.com/benbjohnson/litestream.Store.monitorValidation":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorValidation","kind":"method","span":{"start":[925,1],"end":[958,2],"bytes":[25588,26265]},"signature":"github.com/benbjohnson/litestream.Store.monitorValidation","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[925,1],"end":[925,57],"bytes":[25588,25644]}}],"metrics":{"cyclomatic":9},"body":{"926:2":{"kind":"call","span":{"start":[926,2],"end":[926,80],"bytes":[25646,25724]},"callee":null},"928:12":{"kind":"call","span":{"start":[928,12],"end":[928,48],"bytes":[25737,25773]},"callee":null},"929:8":{"kind":"call","span":{"start":[929,8],"end":[929,21],"bytes":[25781,25794]},"callee":null},"934:10":{"kind":"call","span":{"start":[934,10],"end":[934,20],"bytes":[25829,25839]},"callee":null},"939:18":{"kind":"call","span":{"start":[939,18],"end":[939,33],"bytes":[25896,25911]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Validate"},"941:7":{"kind":"call","span":{"start":[941,7],"end":[941,32],"bytes":[25936,25961]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError"},"944:4":{"kind":"call","span":{"start":[944,4],"end":[944,59],"bytes":[25985,26040]},"callee":null},"950:5":{"kind":"call","span":{"start":[950,5],"end":[954,6],"bytes":[26123,26251]},"callee":null}}},"github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","kind":"method","span":{"start":[735,1],"end":[763,2],"bytes":[19838,20640]},"signature":"github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[735,1],"end":[735,61],"bytes":[19838,19898]}}],"metrics":{"cyclomatic":5},"body":{"741:6":{"kind":"call","span":{"start":[741,6],"end":[741,21],"bytes":[19968,19983]},"callee":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.ShouldPing"},"747:18":{"kind":"call","span":{"start":[747,18],"end":[747,28],"bytes":[20165,20175]},"callee":null},"748:6":{"kind":"call","span":{"start":[748,6],"end":[748,41],"bytes":[20199,20234]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.allDatabasesHealthy"},"755:2":{"kind":"call","span":{"start":[755,2],"end":[755,17],"bytes":[20446,20461]},"callee":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.RecordPing"},"757:12":{"kind":"call","span":{"start":[757,12],"end":[757,24],"bytes":[20474,20486]},"callee":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.Ping"},"758:3":{"kind":"call","span":{"start":[758,3],"end":[758,71],"bytes":[20503,20571]},"callee":null},"762:2":{"kind":"call","span":{"start":[762,2],"end":[762,54],"bytes":[20586,20638]},"callee":null}}},"github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded","kind":"method","span":{"start":[682,1],"end":[702,2],"bytes":[18528,18869]},"signature":"github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded","parameters":[],"metrics":{"cyclomatic":4},"body":{"683:2":{"kind":"call","span":{"start":[683,2],"end":[683,13],"bytes":[18579,18590]},"callee":null},"684:8":{"kind":"call","span":{"start":[684,8],"end":[684,21],"bytes":[18598,18611]},"callee":null},"692:6":{"kind":"call","span":{"start":[692,6],"end":[692,34],"bytes":[18710,18738]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.hasHeartbeatConfigLocked"},"697:2":{"kind":"call","span":{"start":[697,2],"end":[697,13],"bytes":[18789,18800]},"callee":null},"698:5":{"kind":"call","span":{"start":[698,5],"end":[701,5],"bytes":[18805,18867]},"callee":null,"is_goroutine":true}},"callables":{"closure_1":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded.closure_1","kind":"function","span":{"start":[698,1],"end":[701,5],"bytes":[18801,18867]},"signature":"github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded.closure_1","parameters":[],"body":{"699:9":{"kind":"call","span":{"start":[699,9],"end":[699,20],"bytes":[18822,18833]},"callee":null},"700:3":{"kind":"call","span":{"start":[700,3],"end":[700,29],"bytes":[18836,18862]},"callee":null}}}}}},"fields":{"CompactionMonitorEnabled":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/CompactionMonitorEnabled","kind":"field","type":"bool","span":{"start":[128,1],"end":[128,31],"bytes":[4255,4285]}},"Heartbeat":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/Heartbeat","kind":"field","type":"*github.com/benbjohnson/litestream.HeartbeatClient","span":{"start":[147,1],"end":[147,28],"bytes":[4980,5007]}},"HeartbeatCheckInterval":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/HeartbeatCheckInterval","kind":"field","type":"time.Duration","span":{"start":[143,1],"end":[143,38],"bytes":[4800,4837]}},"L0Retention":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/L0Retention","kind":"field","type":"time.Duration","span":{"start":[123,1],"end":[123,27],"bytes":[4061,4087]}},"L0RetentionCheckInterval":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/L0RetentionCheckInterval","kind":"field","type":"time.Duration","span":{"start":[125,1],"end":[125,40],"bytes":[4133,4172]}},"Logger":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/Logger","kind":"field","type":"*log/slog.Logger","span":{"start":[155,1],"end":[155,21],"bytes":[5237,5257]}},"RetentionEnabled":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/RetentionEnabled","kind":"field","type":"bool","span":{"start":[136,1],"end":[136,23],"bytes":[4611,4633]}},"ShutdownSyncInterval":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/ShutdownSyncInterval","kind":"field","type":"time.Duration","span":{"start":[140,1],"end":[140,36],"bytes":[4705,4740]}},"ShutdownSyncTimeout":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/ShutdownSyncTimeout","kind":"field","type":"time.Duration","span":{"start":[139,1],"end":[139,36],"bytes":[4669,4704]}},"SnapshotInterval":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/SnapshotInterval","kind":"field","type":"time.Duration","span":{"start":[118,1],"end":[118,32],"bytes":[3853,3884]}},"SnapshotRetention":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/SnapshotRetention","kind":"field","type":"time.Duration","span":{"start":[120,1],"end":[120,33],"bytes":[3956,3988]}},"ValidationInterval":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/ValidationInterval","kind":"field","type":"time.Duration","span":{"start":[153,1],"end":[153,34],"bytes":[5202,5235]}},"VerifyCompaction":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/VerifyCompaction","kind":"field","type":"bool","span":{"start":[131,1],"end":[131,23],"bytes":[4368,4390]}},"cancel":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/cancel","kind":"field","type":"func()","span":{"start":[114,1],"end":[114,15],"bytes":[3781,3795]}},"ctx":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/ctx","kind":"field","type":"context.Context","span":{"start":[113,1],"end":[113,24],"bytes":[3757,3780]}},"dbs":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/dbs","kind":"field","type":"[]*github.com/benbjohnson/litestream.DB","span":{"start":[109,1],"end":[109,14],"bytes":[3694,3707]}},"done":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/done","kind":"field","type":"\u003c-chan struct{}","span":{"start":[115,1],"end":[115,24],"bytes":[3796,3819]}},"heartbeatMonitorRunning":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/heartbeatMonitorRunning","kind":"field","type":"bool","span":{"start":[150,1],"end":[150,30],"bytes":[5096,5125]}},"levels":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/levels","kind":"field","type":"github.com/benbjohnson/litestream.CompactionLevels","span":{"start":[110,1],"end":[110,25],"bytes":[3708,3732]}},"mu":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/mu","kind":"field","type":"sync.Mutex","span":{"start":[108,1],"end":[108,19],"bytes":[3675,3693]}},"wg":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/wg","kind":"field","type":"sync.WaitGroup","span":{"start":[112,1],"end":[112,23],"bytes":[3734,3756]}}}},"SyncDBResult":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.SyncDBResult","kind":"struct","span":{"start":[443,1],"end":[447,2],"bytes":[12109,12204]},"callables":{},"fields":{"Changed":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.SyncDBResult/Changed","kind":"field","type":"bool","span":{"start":[446,1],"end":[446,21],"bytes":[12182,12202]}},"ReplicatedTXID":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.SyncDBResult/ReplicatedTXID","kind":"field","type":"uint64","span":{"start":[445,1],"end":[445,23],"bytes":[12159,12181]}},"TXID":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.SyncDBResult/TXID","kind":"field","type":"uint64","span":{"start":[444,1],"end":[444,23],"bytes":[12136,12158]}}}},"ValidationResult":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.ValidationResult","kind":"struct","span":{"start":[878,1],"end":[881,2],"bytes":[24104,24235]},"callables":{},"fields":{"Errors":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.ValidationResult/Errors","kind":"field","type":"[]github.com/benbjohnson/litestream.ValidationError","span":{"start":[880,1],"end":[880,46],"bytes":[24188,24233]}},"Valid":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.ValidationResult/Valid","kind":"field","type":"bool","span":{"start":[879,1],"end":[879,53],"bytes":[24135,24187]}}}}},"functions":{"github.com/benbjohnson/litestream.NewStore":{"id":"can://go/litestream/store.go/github.com/benbjohnson/litestream.NewStore","kind":"function","span":{"start":[158,1],"end":[185,2],"bytes":[5261,6292]},"signature":"github.com/benbjohnson/litestream.NewStore","parameters":[{"name":"dbs","type":"[]*github.com/benbjohnson/litestream.DB","span":{"start":[158,1],"end":[158,59],"bytes":[5261,5319]}},{"name":"levels","type":"github.com/benbjohnson/litestream.CompactionLevels","span":{"start":[158,1],"end":[158,59],"bytes":[5261,5319]}}],"return_type":"*github.com/benbjohnson/litestream.Store","metrics":{"cyclomatic":2},"body":{"172:29":{"kind":"call","span":{"start":[172,29],"end":[172,43],"bytes":[5852,5866]},"callee":null},"176:16":{"kind":"call","span":{"start":[176,16],"end":[176,65],"bytes":[5948,5997]},"callee":null},"176:3":{"kind":"call","span":{"start":[176,3],"end":[176,66],"bytes":[5935,5998]},"callee":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError"},"176:40":{"kind":"call","span":{"start":[176,40],"end":[176,64],"bytes":[5972,5996]},"callee":null},"176:54":{"kind":"call","span":{"start":[176,54],"end":[176,63],"bytes":[5986,5995]},"callee":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path"},"183:20":{"kind":"call","span":{"start":[183,20],"end":[183,60],"bytes":[6240,6280]},"callee":null},"183:39":{"kind":"call","span":{"start":[183,39],"end":[183,59],"bytes":[6259,6279]},"callee":null}}}},"content_hash":"ebb3c05192c82c74a49e88667d7b47c61959023edf56d51b1d552976bc44341e"},"v3.go":{"id":"can://go/litestream/v3.go","kind":"module","span":{"start":[1,1],"end":[176,1],"bytes":[0,5940]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"path\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// PosV3 represents a position in a v0.3.x backup.\ntype PosV3 struct {\n\tGeneration string // 16-char hex string\n\tIndex int // WAL index\n\tOffset int64 // Offset within WAL segment\n}\n\n// IsZero returns true if the position is the zero value.\nfunc (p PosV3) IsZero() bool {\n\treturn p == (PosV3{})\n}\n\n// String returns a string representation of the position.\nfunc (p PosV3) String() string {\n\tif p.IsZero() {\n\t\treturn \"\"\n\t}\n\treturn fmt.Sprintf(\"%s/%08x:%016x\", p.Generation, p.Index, p.Offset)\n}\n\n// SnapshotInfoV3 contains metadata about a v0.3.x snapshot.\ntype SnapshotInfoV3 struct {\n\tGeneration string\n\tIndex int\n\tSize int64\n\tCreatedAt time.Time\n}\n\n// Pos returns the position of this snapshot.\nfunc (info SnapshotInfoV3) Pos() PosV3 {\n\treturn PosV3{Generation: info.Generation, Index: info.Index, Offset: 0}\n}\n\n// WALSegmentInfoV3 contains metadata about a v0.3.x WAL segment.\ntype WALSegmentInfoV3 struct {\n\tGeneration string\n\tIndex int\n\tOffset int64\n\tSize int64\n\tCreatedAt time.Time\n}\n\n// Pos returns the position of this WAL segment.\nfunc (info WALSegmentInfoV3) Pos() PosV3 {\n\treturn PosV3{Generation: info.Generation, Index: info.Index, Offset: info.Offset}\n}\n\n// v0.3.x path constants.\nconst (\n\tGenerationsDirV3 = \"generations\"\n\tSnapshotsDirV3 = \"snapshots\"\n\tWALDirV3 = \"wal\"\n)\n\n// GenerationsPathV3 returns the path to the generations directory.\nfunc GenerationsPathV3(root string) string {\n\treturn path.Join(root, GenerationsDirV3)\n}\n\n// GenerationPathV3 returns the path to a specific generation.\nfunc GenerationPathV3(root, generation string) string {\n\treturn path.Join(root, GenerationsDirV3, generation)\n}\n\n// SnapshotsPathV3 returns the path to snapshots within a generation.\nfunc SnapshotsPathV3(root, generation string) string {\n\treturn path.Join(root, GenerationsDirV3, generation, SnapshotsDirV3)\n}\n\n// WALPathV3 returns the path to WAL segments within a generation.\nfunc WALPathV3(root, generation string) string {\n\treturn path.Join(root, GenerationsDirV3, generation, WALDirV3)\n}\n\n// SnapshotPathV3 returns the full path to a v0.3.x snapshot file.\nfunc SnapshotPathV3(root, generation string, index int) string {\n\treturn path.Join(SnapshotsPathV3(root, generation), FormatSnapshotFilenameV3(index))\n}\n\n// WALSegmentPathV3 returns the full path to a v0.3.x WAL segment file.\nfunc WALSegmentPathV3(root, generation string, index int, offset int64) string {\n\treturn path.Join(WALPathV3(root, generation), FormatWALSegmentFilenameV3(index, offset))\n}\n\n// FormatSnapshotFilenameV3 returns the filename for a v0.3.x snapshot.\n// Format: {index:08x}.snapshot.lz4\nfunc FormatSnapshotFilenameV3(index int) string {\n\treturn fmt.Sprintf(\"%08x.snapshot.lz4\", index)\n}\n\n// FormatWALSegmentFilenameV3 returns the filename for a v0.3.x WAL segment.\n// Format: {index:08x}_{offset:08x}.wal.lz4\nfunc FormatWALSegmentFilenameV3(index int, offset int64) string {\n\treturn fmt.Sprintf(\"%08x_%08x.wal.lz4\", index, offset)\n}\n\nvar (\n\tsnapshotRegexV3 = regexp.MustCompile(`^([0-9a-f]{8})\\.snapshot\\.lz4$`)\n\twalSegmentRegexV3 = regexp.MustCompile(`^([0-9a-f]{8})_([0-9a-f]{8,16})\\.wal\\.lz4$`)\n\tgenerationRegexV3 = regexp.MustCompile(`^[0-9a-f]{16}$`)\n)\n\n// ParseSnapshotFilenameV3 parses a v0.3.x snapshot filename and returns the index.\n// Returns an error if the filename does not match the expected format.\nfunc ParseSnapshotFilenameV3(filename string) (index int, err error) {\n\tm := snapshotRegexV3.FindStringSubmatch(filename)\n\tif m == nil {\n\t\treturn 0, fmt.Errorf(\"invalid v0.3.x snapshot filename: %q\", filename)\n\t}\n\tidx, err := strconv.ParseInt(m[1], 16, 32)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid snapshot path: %s: %w\", filename, err)\n\t}\n\treturn int(idx), nil\n}\n\n// ParseWALSegmentFilenameV3 parses a v0.3.x WAL segment filename.\n// Returns the WAL index and byte offset, or an error if the filename is invalid.\nfunc ParseWALSegmentFilenameV3(filename string) (index int, offset int64, err error) {\n\tm := walSegmentRegexV3.FindStringSubmatch(filename)\n\tif m == nil {\n\t\treturn 0, 0, fmt.Errorf(\"invalid v0.3.x WAL segment filename: %q\", filename)\n\t}\n\tidx, err := strconv.ParseInt(m[1], 16, 32)\n\tif err != nil {\n\t\treturn 0, 0, fmt.Errorf(\"invalid wal segment path: %s: %w\", filename, err)\n\t}\n\toff, err := strconv.ParseInt(m[2], 16, 64)\n\tif err != nil {\n\t\treturn 0, 0, fmt.Errorf(\"invalid wal segment path: %s: %w\", filename, err)\n\t}\n\treturn int(idx), off, nil\n}\n\n// IsGenerationIDV3 returns true if s is a valid v0.3.x generation ID (16 hex chars).\nfunc IsGenerationIDV3(s string) bool {\n\treturn generationRegexV3.MatchString(s)\n}\n\n// ReplicaClientV3 reads v0.3.x backup data.\n// ReplicaClient implementations that support v0.3.x restore should implement this interface.\ntype ReplicaClientV3 interface {\n\t// GenerationsV3 returns a list of generation IDs in the replica.\n\t// Returns an empty slice if no v0.3.x backups exist.\n\t// Generation IDs are sorted in ascending order.\n\tGenerationsV3(ctx context.Context) ([]string, error)\n\n\t// SnapshotsV3 returns snapshots for a generation, sorted by index.\n\t// Returns an empty slice if no snapshots exist.\n\tSnapshotsV3(ctx context.Context, generation string) ([]SnapshotInfoV3, error)\n\n\t// WALSegmentsV3 returns WAL segments for a generation, sorted by index then offset.\n\t// Returns an empty slice if no WAL segments exist.\n\tWALSegmentsV3(ctx context.Context, generation string) ([]WALSegmentInfoV3, error)\n\n\t// OpenSnapshotV3 opens a v0.3.x snapshot for reading.\n\t// The returned reader provides LZ4-decompressed data.\n\tOpenSnapshotV3(ctx context.Context, generation string, index int) (io.ReadCloser, error)\n\n\t// OpenWALSegmentV3 opens a v0.3.x WAL segment for reading.\n\t// The returned reader provides LZ4-decompressed data.\n\tOpenWALSegmentV3(ctx context.Context, generation string, index int, offset int64) (io.ReadCloser, error)\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"fmt","path":"fmt","span":{"start":[5,1],"end":[5,7],"bytes":[40,46]}},{"name":"io","path":"io","span":{"start":[6,1],"end":[6,6],"bytes":[47,52]}},{"name":"path","path":"path","span":{"start":[7,1],"end":[7,8],"bytes":[53,60]}},{"name":"regexp","path":"regexp","span":{"start":[8,1],"end":[8,10],"bytes":[61,70]}},{"name":"strconv","path":"strconv","span":{"start":[9,1],"end":[9,11],"bytes":[71,81]}},{"name":"time","path":"time","span":{"start":[10,1],"end":[10,8],"bytes":[82,89]}}],"types":{"PosV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3","kind":"struct","span":{"start":[14,1],"end":[18,2],"bytes":[144,286]},"callables":{"github.com/benbjohnson/litestream.PosV3.IsZero":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/github.com/benbjohnson/litestream.PosV3.IsZero","kind":"method","span":{"start":[21,1],"end":[23,2],"bytes":[346,401]},"signature":"github.com/benbjohnson/litestream.PosV3.IsZero","parameters":[],"return_type":"bool","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.PosV3.String":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/github.com/benbjohnson/litestream.PosV3.String","kind":"method","span":{"start":[26,1],"end":[31,2],"bytes":[462,598]},"signature":"github.com/benbjohnson/litestream.PosV3.String","parameters":[],"return_type":"string","metrics":{"cyclomatic":2},"body":{"27:5":{"kind":"call","span":{"start":[27,5],"end":[27,15],"bytes":[499,509]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/github.com/benbjohnson/litestream.PosV3.IsZero"},"30:9":{"kind":"call","span":{"start":[30,9],"end":[30,70],"bytes":[535,596]},"callee":null}}}},"fields":{"Generation":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/Generation","kind":"field","type":"string","span":{"start":[15,1],"end":[15,41],"bytes":[164,204]}},"Index":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/Index","kind":"field","type":"int","span":{"start":[16,1],"end":[16,32],"bytes":[205,236]}},"Offset":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/Offset","kind":"field","type":"int64","span":{"start":[17,1],"end":[17,48],"bytes":[237,284]}}}},"ReplicaClientV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3","kind":"interface","span":{"start":[154,1],"end":[175,2],"bytes":[4830,5939]},"callables":{"github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3","kind":"method","span":{"start":[158,1],"end":[158,54],"bytes":[5035,5088]},"signature":"github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[158,1],"end":[158,54],"bytes":[5035,5088]}}],"return_type":"([]string, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClientV3.OpenSnapshotV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.OpenSnapshotV3","kind":"method","span":{"start":[170,1],"end":[170,90],"bytes":[5624,5713]},"signature":"github.com/benbjohnson/litestream.ReplicaClientV3.OpenSnapshotV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[170,1],"end":[170,90],"bytes":[5624,5713]}},{"name":"generation","type":"string","span":{"start":[170,1],"end":[170,90],"bytes":[5624,5713]}},{"name":"index","type":"int","span":{"start":[170,1],"end":[170,90],"bytes":[5624,5713]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClientV3.OpenWALSegmentV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.OpenWALSegmentV3","kind":"method","span":{"start":[174,1],"end":[174,106],"bytes":[5832,5937]},"signature":"github.com/benbjohnson/litestream.ReplicaClientV3.OpenWALSegmentV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[174,1],"end":[174,106],"bytes":[5832,5937]}},{"name":"generation","type":"string","span":{"start":[174,1],"end":[174,106],"bytes":[5832,5937]}},{"name":"index","type":"int","span":{"start":[174,1],"end":[174,106],"bytes":[5832,5937]}},{"name":"offset","type":"int64","span":{"start":[174,1],"end":[174,106],"bytes":[5832,5937]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3","kind":"method","span":{"start":[162,1],"end":[162,79],"bytes":[5209,5287]},"signature":"github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[162,1],"end":[162,79],"bytes":[5209,5287]}},{"name":"generation","type":"string","span":{"start":[162,1],"end":[162,79],"bytes":[5209,5287]}}],"return_type":"([]github.com/benbjohnson/litestream.SnapshotInfoV3, error)","error_channel":["error"],"body":{}},"github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3","kind":"method","span":{"start":[166,1],"end":[166,83],"bytes":[5428,5510]},"signature":"github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[166,1],"end":[166,83],"bytes":[5428,5510]}},{"name":"generation","type":"string","span":{"start":[166,1],"end":[166,83],"bytes":[5428,5510]}}],"return_type":"([]github.com/benbjohnson/litestream.WALSegmentInfoV3, error)","error_channel":["error"],"body":{}}}},"SnapshotInfoV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotInfoV3","kind":"struct","span":{"start":[34,1],"end":[39,2],"bytes":[661,766]},"callables":{"github.com/benbjohnson/litestream.SnapshotInfoV3.Pos":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotInfoV3/github.com/benbjohnson/litestream.SnapshotInfoV3.Pos","kind":"method","span":{"start":[42,1],"end":[44,2],"bytes":[814,929]},"signature":"github.com/benbjohnson/litestream.SnapshotInfoV3.Pos","parameters":[],"return_type":"github.com/benbjohnson/litestream.PosV3","metrics":{"cyclomatic":1},"body":{}}},"fields":{"CreatedAt":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotInfoV3/CreatedAt","kind":"field","type":"time.Time","span":{"start":[38,1],"end":[38,22],"bytes":[743,764]}},"Generation":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotInfoV3/Generation","kind":"field","type":"string","span":{"start":[35,1],"end":[35,19],"bytes":[690,708]}},"Index":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotInfoV3/Index","kind":"field","type":"int","span":{"start":[36,1],"end":[36,16],"bytes":[709,724]}},"Size":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotInfoV3/Size","kind":"field","type":"int64","span":{"start":[37,1],"end":[37,18],"bytes":[725,742]}}}},"WALSegmentInfoV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3","kind":"struct","span":{"start":[47,1],"end":[53,2],"bytes":[997,1122]},"callables":{"github.com/benbjohnson/litestream.WALSegmentInfoV3.Pos":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3/github.com/benbjohnson/litestream.WALSegmentInfoV3.Pos","kind":"method","span":{"start":[56,1],"end":[58,2],"bytes":[1173,1300]},"signature":"github.com/benbjohnson/litestream.WALSegmentInfoV3.Pos","parameters":[],"return_type":"github.com/benbjohnson/litestream.PosV3","metrics":{"cyclomatic":1},"body":{}}},"fields":{"CreatedAt":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3/CreatedAt","kind":"field","type":"time.Time","span":{"start":[52,1],"end":[52,22],"bytes":[1099,1120]}},"Generation":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3/Generation","kind":"field","type":"string","span":{"start":[48,1],"end":[48,19],"bytes":[1028,1046]}},"Index":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3/Index","kind":"field","type":"int","span":{"start":[49,1],"end":[49,16],"bytes":[1047,1062]}},"Offset":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3/Offset","kind":"field","type":"int64","span":{"start":[50,1],"end":[50,18],"bytes":[1063,1080]}},"Size":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentInfoV3/Size","kind":"field","type":"int64","span":{"start":[51,1],"end":[51,18],"bytes":[1081,1098]}}}}},"functions":{"github.com/benbjohnson/litestream.FormatSnapshotFilenameV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatSnapshotFilenameV3","kind":"function","span":{"start":[99,1],"end":[101,2],"bytes":[2721,2820]},"signature":"github.com/benbjohnson/litestream.FormatSnapshotFilenameV3","parameters":[{"name":"index","type":"int","span":{"start":[99,1],"end":[99,50],"bytes":[2721,2770]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"100:9":{"kind":"call","span":{"start":[100,9],"end":[100,48],"bytes":[2779,2818]},"callee":null}}},"github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3","kind":"function","span":{"start":[105,1],"end":[107,2],"bytes":[2943,3066]},"signature":"github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3","parameters":[{"name":"index","type":"int","span":{"start":[105,1],"end":[105,66],"bytes":[2943,3008]}},{"name":"offset","type":"int64","span":{"start":[105,1],"end":[105,66],"bytes":[2943,3008]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"106:9":{"kind":"call","span":{"start":[106,9],"end":[106,56],"bytes":[3017,3064]},"callee":null}}},"github.com/benbjohnson/litestream.GenerationPathV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.GenerationPathV3","kind":"function","span":{"start":[73,1],"end":[75,2],"bytes":[1652,1763]},"signature":"github.com/benbjohnson/litestream.GenerationPathV3","parameters":[{"name":"root","type":"string","span":{"start":[73,1],"end":[73,56],"bytes":[1652,1707]}},{"name":"generation","type":"string","span":{"start":[73,1],"end":[73,56],"bytes":[1652,1707]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"74:9":{"kind":"call","span":{"start":[74,9],"end":[74,54],"bytes":[1716,1761]},"callee":null}}},"github.com/benbjohnson/litestream.GenerationsPathV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.GenerationsPathV3","kind":"function","span":{"start":[68,1],"end":[70,2],"bytes":[1499,1587]},"signature":"github.com/benbjohnson/litestream.GenerationsPathV3","parameters":[{"name":"root","type":"string","span":{"start":[68,1],"end":[68,45],"bytes":[1499,1543]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"69:9":{"kind":"call","span":{"start":[69,9],"end":[69,42],"bytes":[1552,1585]},"callee":null}}},"github.com/benbjohnson/litestream.IsGenerationIDV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.IsGenerationIDV3","kind":"function","span":{"start":[148,1],"end":[150,2],"bytes":[4608,4689]},"signature":"github.com/benbjohnson/litestream.IsGenerationIDV3","parameters":[{"name":"s","type":"string","span":{"start":[148,1],"end":[148,39],"bytes":[4608,4646]}}],"return_type":"bool","metrics":{"cyclomatic":1},"body":{"149:9":{"kind":"call","span":{"start":[149,9],"end":[149,41],"bytes":[4655,4687]},"callee":null}}},"github.com/benbjohnson/litestream.ParseSnapshotFilenameV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseSnapshotFilenameV3","kind":"function","span":{"start":[117,1],"end":[127,2],"bytes":[3451,3822]},"signature":"github.com/benbjohnson/litestream.ParseSnapshotFilenameV3","parameters":[{"name":"filename","type":"string","span":{"start":[117,1],"end":[117,71],"bytes":[3451,3521]}}],"return_type":"(int, error)","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"118:7":{"kind":"call","span":{"start":[118,7],"end":[118,51],"bytes":[3528,3572]},"callee":null},"120:13":{"kind":"call","span":{"start":[120,13],"end":[120,73],"bytes":[3600,3660]},"callee":null},"122:14":{"kind":"call","span":{"start":[122,14],"end":[122,44],"bytes":[3677,3707]},"callee":null},"124:13":{"kind":"call","span":{"start":[124,13],"end":[124,71],"bytes":[3737,3795]},"callee":null},"126:9":{"kind":"call","span":{"start":[126,9],"end":[126,17],"bytes":[3807,3815]},"callee":null}}},"github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3","kind":"function","span":{"start":[131,1],"end":[145,2],"bytes":[3973,4520]},"signature":"github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3","parameters":[{"name":"filename","type":"string","span":{"start":[131,1],"end":[131,87],"bytes":[3973,4059]}}],"return_type":"(int, int64, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"132:7":{"kind":"call","span":{"start":[132,7],"end":[132,53],"bytes":[4066,4112]},"callee":null},"134:16":{"kind":"call","span":{"start":[134,16],"end":[134,79],"bytes":[4143,4206]},"callee":null},"136:14":{"kind":"call","span":{"start":[136,14],"end":[136,44],"bytes":[4223,4253]},"callee":null},"138:16":{"kind":"call","span":{"start":[138,16],"end":[138,77],"bytes":[4286,4347]},"callee":null},"140:14":{"kind":"call","span":{"start":[140,14],"end":[140,44],"bytes":[4364,4394]},"callee":null},"142:16":{"kind":"call","span":{"start":[142,16],"end":[142,77],"bytes":[4427,4488]},"callee":null},"144:9":{"kind":"call","span":{"start":[144,9],"end":[144,17],"bytes":[4500,4508]},"callee":null}}},"github.com/benbjohnson/litestream.SnapshotPathV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotPathV3","kind":"function","span":{"start":[88,1],"end":[90,2],"bytes":[2213,2365]},"signature":"github.com/benbjohnson/litestream.SnapshotPathV3","parameters":[{"name":"root","type":"string","span":{"start":[88,1],"end":[88,65],"bytes":[2213,2277]}},{"name":"generation","type":"string","span":{"start":[88,1],"end":[88,65],"bytes":[2213,2277]}},{"name":"index","type":"int","span":{"start":[88,1],"end":[88,65],"bytes":[2213,2277]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"89:19":{"kind":"call","span":{"start":[89,19],"end":[89,52],"bytes":[2296,2329]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotsPathV3"},"89:54":{"kind":"call","span":{"start":[89,54],"end":[89,85],"bytes":[2331,2362]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatSnapshotFilenameV3"},"89:9":{"kind":"call","span":{"start":[89,9],"end":[89,86],"bytes":[2286,2363]},"callee":null}}},"github.com/benbjohnson/litestream.SnapshotsPathV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotsPathV3","kind":"function","span":{"start":[78,1],"end":[80,2],"bytes":[1835,1961]},"signature":"github.com/benbjohnson/litestream.SnapshotsPathV3","parameters":[{"name":"root","type":"string","span":{"start":[78,1],"end":[78,55],"bytes":[1835,1889]}},{"name":"generation","type":"string","span":{"start":[78,1],"end":[78,55],"bytes":[1835,1889]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"79:9":{"kind":"call","span":{"start":[79,9],"end":[79,70],"bytes":[1898,1959]},"callee":null}}},"github.com/benbjohnson/litestream.WALPathV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALPathV3","kind":"function","span":{"start":[83,1],"end":[85,2],"bytes":[2030,2144]},"signature":"github.com/benbjohnson/litestream.WALPathV3","parameters":[{"name":"root","type":"string","span":{"start":[83,1],"end":[83,49],"bytes":[2030,2078]}},{"name":"generation","type":"string","span":{"start":[83,1],"end":[83,49],"bytes":[2030,2078]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"84:9":{"kind":"call","span":{"start":[84,9],"end":[84,64],"bytes":[2087,2142]},"callee":null}}},"github.com/benbjohnson/litestream.WALSegmentPathV3":{"id":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentPathV3","kind":"function","span":{"start":[93,1],"end":[95,2],"bytes":[2439,2611]},"signature":"github.com/benbjohnson/litestream.WALSegmentPathV3","parameters":[{"name":"root","type":"string","span":{"start":[93,1],"end":[93,81],"bytes":[2439,2519]}},{"name":"generation","type":"string","span":{"start":[93,1],"end":[93,81],"bytes":[2439,2519]}},{"name":"index","type":"int","span":{"start":[93,1],"end":[93,81],"bytes":[2439,2519]}},{"name":"offset","type":"int64","span":{"start":[93,1],"end":[93,81],"bytes":[2439,2519]}}],"return_type":"string","metrics":{"cyclomatic":1},"body":{"94:19":{"kind":"call","span":{"start":[94,19],"end":[94,46],"bytes":[2538,2565]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALPathV3"},"94:48":{"kind":"call","span":{"start":[94,48],"end":[94,89],"bytes":[2567,2608]},"callee":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3"},"94:9":{"kind":"call","span":{"start":[94,9],"end":[94,90],"bytes":[2528,2609]},"callee":null}}}},"content_hash":"3bf323f796fa9b0118b9e5f58dc008d33d364a47dafdf1657d0333a57bf1e546"},"wal_reader.go":{"id":"can://go/litestream/wal_reader.go","kind":"module","span":{"start":[1,1],"end":[318,1],"bytes":[0,9615]},"package":"litestream","source":"package litestream\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\n// WALReader wraps an io.Reader and parses SQLite WAL frames.\n//\n// This reader verifies the salt \u0026 checksum integrity while it reads. It does\n// not enforce transaction boundaries (i.e. it may return uncommitted frames).\n// It is the responsibility of the caller to handle this.\ntype WALReader struct {\n\tr io.ReaderAt\n\tframeN int\n\n\tbo binary.ByteOrder\n\tpageSize uint32\n\tseq uint32\n\n\tsalt1, salt2 uint32\n\tchksum1, chksum2 uint32\n\n\tlogger *slog.Logger\n}\n\n// NewWALReader returns a new instance of WALReader.\nfunc NewWALReader(rd io.ReaderAt, logger *slog.Logger) (*WALReader, error) {\n\tr := \u0026WALReader{r: rd, logger: logger}\n\tif err := r.readHeader(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn r, nil\n}\n\n// NewWALReaderWithOffset returns a new instance of WALReader at a given offset.\n// Salt must match or else no frames will be returned. Checksum calculated from\n// from previous page.\nfunc NewWALReaderWithOffset(ctx context.Context, rd io.ReaderAt, offset int64, salt1, salt2 uint32, logger *slog.Logger) (*WALReader, error) {\n\t// Ensure we are not starting on the first page since we need to read the previous.\n\tif offset \u003c= WALHeaderSize {\n\t\treturn nil, fmt.Errorf(\"offset (%d) must be greater than the wal header size (%d)\", offset, WALHeaderSize)\n\t}\n\n\tr := \u0026WALReader{r: rd, logger: logger}\n\n\t// Read header to determine page size \u0026 byte order.\n\tif err := r.readHeader(); err != nil {\n\t\treturn nil, fmt.Errorf(\"read header: %w\", err)\n\t}\n\n\t// Load in salt in case the beginning of the file has been overwritten.\n\tr.salt1, r.salt2 = salt1, salt2\n\n\t// Ensure offset is positioned on a frame start.\n\tframeSize := int64(r.pageSize + WALFrameHeaderSize)\n\tif (offset-WALHeaderSize)%frameSize != 0 {\n\t\treturn nil, fmt.Errorf(\"unaligned wal offset %d for page size %d\", offset, r.pageSize)\n\t}\n\tr.frameN = int((offset - WALHeaderSize) / frameSize)\n\n\t// Read previous page to load checksum. Context errors are returned as-is\n\t// so callers don't mistake a cancellation for a frame mismatch.\n\tr.frameN--\n\tif _, _, err := r.readFrame(ctx, make([]byte, r.pageSize), false); err != nil {\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil, context.Cause(ctx)\n\t\t}\n\t\treturn nil, \u0026PrevFrameMismatchError{Err: err}\n\t}\n\n\treturn r, nil\n}\n\n// PageSize returns the page size from the header. Must call ReadHeader() first.\nfunc (r *WALReader) PageSize() uint32 { return r.pageSize }\n\n// Offset returns the file offset of the last read frame.\n// Returns zero if no frames have been read.\nfunc (r *WALReader) Offset() int64 {\n\tif r.frameN == 0 {\n\t\treturn 0\n\t}\n\treturn WALHeaderSize + ((int64(r.frameN) - 1) * (WALFrameHeaderSize + int64(r.pageSize)))\n}\n\n// readHeader reads the WAL header into the reader. Returns io.EOF if WAL is invalid.\nfunc (r *WALReader) readHeader() error {\n\t// If we have a partial WAL, then mark WAL as done.\n\thdr := make([]byte, WALHeaderSize)\n\tif n, err := r.r.ReadAt(hdr, 0); n \u003c len(hdr) {\n\t\treturn io.EOF\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\t// Determine byte order of checksums.\n\tswitch magic := binary.BigEndian.Uint32(hdr[0:]); magic {\n\tcase 0x377f0682:\n\t\tr.bo = binary.LittleEndian\n\tcase 0x377f0683:\n\t\tr.bo = binary.BigEndian\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid wal header magic: %x\", magic)\n\t}\n\n\t// If the header checksum doesn't match then we may have failed with\n\t// a partial WAL header write during checkpointing.\n\tchksum1 := binary.BigEndian.Uint32(hdr[24:])\n\tchksum2 := binary.BigEndian.Uint32(hdr[28:])\n\tif v0, v1 := WALChecksum(r.bo, 0, 0, hdr[:24]); v0 != chksum1 || v1 != chksum2 {\n\t\treturn io.EOF\n\t}\n\n\t// Verify version is correct.\n\tif version := binary.BigEndian.Uint32(hdr[4:]); version != 3007000 {\n\t\treturn fmt.Errorf(\"unsupported wal version: %d\", version)\n\t}\n\n\tr.pageSize = binary.BigEndian.Uint32(hdr[8:])\n\tr.seq = binary.BigEndian.Uint32(hdr[12:])\n\tr.salt1 = binary.BigEndian.Uint32(hdr[16:])\n\tr.salt2 = binary.BigEndian.Uint32(hdr[20:])\n\tr.chksum1, r.chksum2 = chksum1, chksum2\n\n\treturn nil\n}\n\n// ReadFrame reads the next frame from the WAL and returns the page number.\n// Returns io.EOF at the end of the valid WAL.\nfunc (r *WALReader) ReadFrame(ctx context.Context, data []byte) (pgno, commit uint32, err error) {\n\treturn r.readFrame(ctx, data, true)\n}\n\nfunc (r *WALReader) readFrame(ctx context.Context, data []byte, verifyChecksum bool) (pgno, commit uint32, err error) {\n\tselect {\n\tcase \u003c-ctx.Done():\n\t\treturn 0, 0, context.Cause(ctx)\n\tdefault:\n\t}\n\n\tif len(data) != int(r.pageSize) {\n\t\treturn 0, 0, fmt.Errorf(\"WALReader.ReadFrame(): buffer size (%d) must match page size (%d)\", len(data), r.pageSize)\n\t}\n\n\tframeSize := r.pageSize + WALFrameHeaderSize\n\toffset := WALHeaderSize + (int64(r.frameN) * int64(frameSize))\n\n\t// Read WAL frame header.\n\thdr := make([]byte, WALFrameHeaderSize)\n\tif n, err := r.r.ReadAt(hdr, offset); n != len(hdr) {\n\t\treturn 0, 0, io.EOF\n\t} else if err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\t// Read WAL page data.\n\tif n, err := r.r.ReadAt(data, offset+WALFrameHeaderSize); n != len(data) {\n\t\treturn 0, 0, io.EOF\n\t} else if err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\t// Verify salt matches the salt in the header.\n\tsalt1 := binary.BigEndian.Uint32(hdr[8:])\n\tsalt2 := binary.BigEndian.Uint32(hdr[12:])\n\tif r.salt1 != salt1 || r.salt2 != salt2 {\n\t\treturn 0, 0, io.EOF\n\t}\n\n\t// Verify the checksum is valid. If checksum verification is disabled, it\n\t// is because we are jumping to an offset and not checksumming from the beginning.\n\tchksum1 := binary.BigEndian.Uint32(hdr[16:])\n\tchksum2 := binary.BigEndian.Uint32(hdr[20:])\n\tif verifyChecksum {\n\t\tr.chksum1, r.chksum2 = WALChecksum(r.bo, r.chksum1, r.chksum2, hdr[:8]) // frame header\n\t\tr.chksum1, r.chksum2 = WALChecksum(r.bo, r.chksum1, r.chksum2, data) // frame data\n\t\tif r.chksum1 != chksum1 || r.chksum2 != chksum2 {\n\t\t\treturn 0, 0, io.EOF\n\t\t}\n\t} else {\n\t\tr.chksum1, r.chksum2 = chksum1, chksum2\n\t}\n\n\tpgno = binary.BigEndian.Uint32(hdr[0:])\n\tcommit = binary.BigEndian.Uint32(hdr[4:])\n\n\tr.frameN++\n\n\treturn pgno, commit, nil\n}\n\n// PageMap reads all committed frames until the end of the file and returns a\n// map of pgno to offset of the latest version of each page. Also returns the\n// max offset of the wal segment read, and the final database size, in pages.\nfunc (r *WALReader) PageMap(ctx context.Context) (m map[uint32]int64, maxOffset int64, commit uint32, err error) {\n\tm, maxOffset, commit, _, err = r.pageMap(ctx, 0)\n\treturn m, maxOffset, commit, err\n}\n\nfunc (r *WALReader) pageMap(ctx context.Context, maxBytes int64) (m map[uint32]int64, maxOffset int64, commit uint32, limited bool, err error) {\n\tm = make(map[uint32]int64)\n\ttxMap := make(map[uint32]int64)\n\tdata := make([]byte, r.pageSize)\n\tframeSize := int64(WALFrameHeaderSize + r.pageSize)\n\tstartOffset := WALHeaderSize + int64(r.frameN)*frameSize\n\tfor {\n\t\tpgno, fcommit, err := r.ReadFrame(ctx, data)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn nil, 0, 0, false, err\n\t\t}\n\n\t\t// Update latest offset for the page for this transaction.\n\t\t// Pages should not be saved to full map until we know txn is committed.\n\t\toffset := r.Offset()\n\t\ttxMap[pgno] = offset\n\n\t\t// For commit records, transfer offsets to full map and update db size.\n\t\tif fcommit != 0 {\n\t\t\tfor pgno, offset := range txMap {\n\t\t\t\tm[pgno] = offset\n\t\t\t}\n\t\t\ttxMap = make(map[uint32]int64)\n\t\t\tcommit = fcommit\n\n\t\t\tif maxBytes \u003e 0 \u0026\u0026 r.Offset()+frameSize-startOffset \u003e= maxBytes {\n\t\t\t\tlimited = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove pages that exceed the final commit size. This can occur when the\n\t// database shrinks (e.g., via VACUUM) between transactions in the WAL.\n\tfor pgno := range m {\n\t\tif pgno \u003e commit {\n\t\t\tdelete(m, pgno)\n\t\t}\n\t}\n\n\t// If full transactions available, return the original offset.\n\tif len(m) == 0 {\n\t\treturn m, 0, 0, limited, nil\n\t}\n\n\t// Compute the highest page offsets.\n\tvar end int64\n\tfor _, offset := range m {\n\t\tif end == 0 || offset \u003e end {\n\t\t\tend = offset\n\t\t}\n\t}\n\n\t// Extend to the end of the last frame read.\n\tend += WALFrameHeaderSize + int64(r.pageSize)\n\n\tr.logger.Log(ctx, internal.LevelTrace, \"page map complete\", \"n\", len(m), \"end\", end, \"commit\", commit)\n\treturn m, end, commit, limited, nil\n}\n\n// FrameSaltsUntil returns a set of all unique frame salts in the WAL file.\nfunc (r *WALReader) FrameSaltsUntil(ctx context.Context, until [2]uint32) (map[[2]uint32]struct{}, error) {\n\tm := make(map[[2]uint32]struct{})\n\tfor offset := int64(WALHeaderSize); ; offset += int64(WALFrameHeaderSize + r.pageSize) {\n\t\thdr := make([]byte, WALFrameHeaderSize)\n\t\tif n, err := r.r.ReadAt(hdr, offset); n != len(hdr) {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsalt1 := binary.BigEndian.Uint32(hdr[8:])\n\t\tsalt2 := binary.BigEndian.Uint32(hdr[12:])\n\n\t\t// Track unique salts.\n\t\tm[[2]uint32{salt1, salt2}] = struct{}{}\n\n\t\t// Only read salts until the last one we expect.\n\t\tif salt1 == until[0] \u0026\u0026 salt2 == until[1] {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn m, nil\n}\n\n// WALChecksum computes a running SQLite WAL checksum over a byte slice.\nfunc WALChecksum(bo binary.ByteOrder, s0, s1 uint32, b []byte) (uint32, uint32) {\n\tassert(len(b)%8 == 0, \"misaligned checksum byte slice\")\n\n\t// Iterate over 8-byte units and compute checksum.\n\tfor i := 0; i \u003c len(b); i += 8 {\n\t\ts0 += bo.Uint32(b[i:]) + s1\n\t\ts1 += bo.Uint32(b[i+4:]) + s0\n\t}\n\treturn s0, s1\n}\n\ntype PrevFrameMismatchError struct {\n\tErr error\n}\n\nfunc (e *PrevFrameMismatchError) Error() string {\n\treturn fmt.Sprintf(\"prev frame mismatch: %s\", e.Err)\n}\n\nfunc (e *PrevFrameMismatchError) Unwrap() error {\n\treturn e.Err\n}\n","imports":[{"name":"context","path":"context","span":{"start":[4,1],"end":[4,11],"bytes":[29,39]}},{"name":"binary","path":"encoding/binary","span":{"start":[5,1],"end":[5,19],"bytes":[40,58]}},{"name":"errors","path":"errors","span":{"start":[6,1],"end":[6,10],"bytes":[59,68]}},{"name":"fmt","path":"fmt","span":{"start":[7,1],"end":[7,7],"bytes":[69,75]}},{"name":"io","path":"io","span":{"start":[8,1],"end":[8,6],"bytes":[76,81]}},{"name":"slog","path":"log/slog","span":{"start":[9,1],"end":[9,12],"bytes":[82,93]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[11,1],"end":[11,46],"bytes":[95,140]}}],"types":{"PrevFrameMismatchError":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.PrevFrameMismatchError","kind":"struct","span":{"start":[307,1],"end":[309,2],"bytes":[9391,9440]},"callables":{"github.com/benbjohnson/litestream.PrevFrameMismatchError.Error":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.PrevFrameMismatchError/github.com/benbjohnson/litestream.PrevFrameMismatchError.Error","kind":"method","span":{"start":[311,1],"end":[313,2],"bytes":[9442,9547]},"signature":"github.com/benbjohnson/litestream.PrevFrameMismatchError.Error","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{"312:9":{"kind":"call","span":{"start":[312,9],"end":[312,54],"bytes":[9500,9545]},"callee":null}}},"github.com/benbjohnson/litestream.PrevFrameMismatchError.Unwrap":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.PrevFrameMismatchError/github.com/benbjohnson/litestream.PrevFrameMismatchError.Unwrap","kind":"method","span":{"start":[315,1],"end":[317,2],"bytes":[9549,9614]},"signature":"github.com/benbjohnson/litestream.PrevFrameMismatchError.Unwrap","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{}}},"fields":{"Err":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.PrevFrameMismatchError/Err","kind":"field","type":"error","span":{"start":[308,1],"end":[308,11],"bytes":[9428,9438]}}}},"WALReader":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader","kind":"struct","span":{"start":[19,1],"end":[31,2],"bytes":[424,616]},"callables":{"github.com/benbjohnson/litestream.WALReader.FrameSaltsUntil":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.FrameSaltsUntil","kind":"method","span":{"start":[270,1],"end":[293,2],"bytes":[8330,9007]},"signature":"github.com/benbjohnson/litestream.WALReader.FrameSaltsUntil","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[270,1],"end":[270,108],"bytes":[8330,8437]}},{"name":"until","type":"[2]uint32","span":{"start":[270,1],"end":[270,108],"bytes":[8330,8437]}}],"return_type":"(map[[2]uint32]struct{}, error)","error_channel":["error"],"metrics":{"cyclomatic":5},"body":{"271:7":{"kind":"call","span":{"start":[271,7],"end":[271,35],"bytes":[8444,8472]},"callee":null},"272:16":{"kind":"call","span":{"start":[272,16],"end":[272,36],"bytes":[8488,8508]},"callee":null},"272:50":{"kind":"call","span":{"start":[272,50],"end":[272,88],"bytes":[8522,8560]},"callee":null},"273:10":{"kind":"call","span":{"start":[273,10],"end":[273,42],"bytes":[8572,8604]},"callee":null},"274:16":{"kind":"call","span":{"start":[274,16],"end":[274,39],"bytes":[8620,8643]},"callee":null},"274:46":{"kind":"call","span":{"start":[274,46],"end":[274,54],"bytes":[8650,8658]},"callee":null},"280:12":{"kind":"call","span":{"start":[280,12],"end":[280,44],"bytes":[8730,8762]},"callee":null},"281:12":{"kind":"call","span":{"start":[281,12],"end":[281,45],"bytes":[8774,8807]},"callee":null}}},"github.com/benbjohnson/litestream.WALReader.Offset":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.Offset","kind":"method","span":{"start":[86,1],"end":[91,2],"bytes":[2621,2784]},"signature":"github.com/benbjohnson/litestream.WALReader.Offset","parameters":[],"return_type":"int64","metrics":{"cyclomatic":2},"body":{"90:27":{"kind":"call","span":{"start":[90,27],"end":[90,42],"bytes":[2718,2733]},"callee":null},"90:72":{"kind":"call","span":{"start":[90,72],"end":[90,89],"bytes":[2763,2780]},"callee":null}}},"github.com/benbjohnson/litestream.WALReader.PageMap":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.PageMap","kind":"method","span":{"start":[202,1],"end":[205,2],"bytes":[6328,6528]},"signature":"github.com/benbjohnson/litestream.WALReader.PageMap","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[202,1],"end":[202,115],"bytes":[6328,6442]}}],"return_type":"(map[uint32]int64, int64, uint32, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"203:33":{"kind":"call","span":{"start":[203,33],"end":[203,50],"bytes":[6475,6492]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap"}}},"github.com/benbjohnson/litestream.WALReader.PageSize":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.PageSize","kind":"method","span":{"start":[82,1],"end":[82,60],"bytes":[2457,2516]},"signature":"github.com/benbjohnson/litestream.WALReader.PageSize","parameters":[],"return_type":"uint32","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream.WALReader.ReadFrame":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.ReadFrame","kind":"method","span":{"start":[137,1],"end":[139,2],"bytes":[4212,4349]},"signature":"github.com/benbjohnson/litestream.WALReader.ReadFrame","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[137,1],"end":[137,99],"bytes":[4212,4310]}},{"name":"data","type":"[]byte","span":{"start":[137,1],"end":[137,99],"bytes":[4212,4310]}}],"return_type":"(uint32, uint32, error)","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"138:9":{"kind":"call","span":{"start":[138,9],"end":[138,37],"bytes":[4319,4347]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readFrame"}}},"github.com/benbjohnson/litestream.WALReader.pageMap":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap","kind":"method","span":{"start":[207,1],"end":[267,2],"bytes":[6530,8252]},"signature":"github.com/benbjohnson/litestream.WALReader.pageMap","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[207,1],"end":[207,145],"bytes":[6530,6674]}},{"name":"maxBytes","type":"int64","span":{"start":[207,1],"end":[207,145],"bytes":[6530,6674]}}],"return_type":"(map[uint32]int64, int64, uint32, bool, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"208:6":{"kind":"call","span":{"start":[208,6],"end":[208,28],"bytes":[6680,6702]},"callee":null},"209:11":{"kind":"call","span":{"start":[209,11],"end":[209,33],"bytes":[6713,6735]},"callee":null},"210:10":{"kind":"call","span":{"start":[210,10],"end":[210,34],"bytes":[6745,6769]},"callee":null},"211:15":{"kind":"call","span":{"start":[211,15],"end":[211,53],"bytes":[6784,6822]},"callee":null},"212:33":{"kind":"call","span":{"start":[212,33],"end":[212,48],"bytes":[6855,6870]},"callee":null},"214:25":{"kind":"call","span":{"start":[214,25],"end":[214,47],"bytes":[6912,6934]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.ReadFrame"},"215:6":{"kind":"call","span":{"start":[215,6],"end":[215,28],"bytes":[6940,6962]},"callee":null},"223:13":{"kind":"call","span":{"start":[223,13],"end":[223,23],"bytes":[7184,7194]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.Offset"},"231:12":{"kind":"call","span":{"start":[231,12],"end":[231,34],"bytes":[7387,7409]},"callee":null},"234:23":{"kind":"call","span":{"start":[234,23],"end":[234,33],"bytes":[7453,7463]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.Offset"},"245:4":{"kind":"call","span":{"start":[245,4],"end":[245,19],"bytes":[7738,7753]},"callee":null},"250:5":{"kind":"call","span":{"start":[250,5],"end":[250,11],"bytes":[7830,7836]},"callee":null},"263:30":{"kind":"call","span":{"start":[263,30],"end":[263,47],"bytes":[8091,8108]},"callee":null},"265:2":{"kind":"call","span":{"start":[265,2],"end":[265,104],"bytes":[8111,8213]},"callee":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded"},"265:67":{"kind":"call","span":{"start":[265,67],"end":[265,73],"bytes":[8176,8182]},"callee":null}}},"github.com/benbjohnson/litestream.WALReader.readFrame":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readFrame","kind":"method","span":{"start":[141,1],"end":[197,2],"bytes":[4351,6092]},"signature":"github.com/benbjohnson/litestream.WALReader.readFrame","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[141,1],"end":[141,120],"bytes":[4351,4470]}},{"name":"data","type":"[]byte","span":{"start":[141,1],"end":[141,120],"bytes":[4351,4470]}},{"name":"verifyChecksum","type":"bool","span":{"start":[141,1],"end":[141,120],"bytes":[4351,4470]}}],"return_type":"(uint32, uint32, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"143:9":{"kind":"call","span":{"start":[143,9],"end":[143,19],"bytes":[4489,4499]},"callee":null},"144:16":{"kind":"call","span":{"start":[144,16],"end":[144,34],"bytes":[4516,4534]},"callee":null},"148:18":{"kind":"call","span":{"start":[148,18],"end":[148,33],"bytes":[4566,4581]},"callee":null},"148:5":{"kind":"call","span":{"start":[148,5],"end":[148,14],"bytes":[4553,4562]},"callee":null},"149:16":{"kind":"call","span":{"start":[149,16],"end":[149,118],"bytes":[4599,4701]},"callee":null},"149:96":{"kind":"call","span":{"start":[149,96],"end":[149,105],"bytes":[4679,4688]},"callee":null},"153:29":{"kind":"call","span":{"start":[153,29],"end":[153,44],"bytes":[4780,4795]},"callee":null},"153:47":{"kind":"call","span":{"start":[153,47],"end":[153,63],"bytes":[4798,4814]},"callee":null},"156:9":{"kind":"call","span":{"start":[156,9],"end":[156,41],"bytes":[4852,4884]},"callee":null},"157:15":{"kind":"call","span":{"start":[157,15],"end":[157,38],"bytes":[4899,4922]},"callee":null},"157:45":{"kind":"call","span":{"start":[157,45],"end":[157,53],"bytes":[4929,4937]},"callee":null},"164:15":{"kind":"call","span":{"start":[164,15],"end":[164,58],"bytes":[5047,5090]},"callee":null},"164:65":{"kind":"call","span":{"start":[164,65],"end":[164,74],"bytes":[5097,5106]},"callee":null},"171:11":{"kind":"call","span":{"start":[171,11],"end":[171,43],"bytes":[5236,5268]},"callee":null},"172:11":{"kind":"call","span":{"start":[172,11],"end":[172,44],"bytes":[5279,5312]},"callee":null},"179:13":{"kind":"call","span":{"start":[179,13],"end":[179,46],"bytes":[5553,5586]},"callee":null},"180:13":{"kind":"call","span":{"start":[180,13],"end":[180,46],"bytes":[5599,5632]},"callee":null},"182:26":{"kind":"call","span":{"start":[182,26],"end":[182,74],"bytes":[5679,5727]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum"},"183:26":{"kind":"call","span":{"start":[183,26],"end":[183,71],"bytes":[5769,5814]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum"},"191:9":{"kind":"call","span":{"start":[191,9],"end":[191,41],"bytes":[5975,6007]},"callee":null},"192:11":{"kind":"call","span":{"start":[192,11],"end":[192,43],"bytes":[6018,6050]},"callee":null}}},"github.com/benbjohnson/litestream.WALReader.readHeader":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readHeader","kind":"method","span":{"start":[94,1],"end":[133,2],"bytes":[2872,4087]},"signature":"github.com/benbjohnson/litestream.WALReader.readHeader","parameters":[],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"104:18":{"kind":"call","span":{"start":[104,18],"end":[104,50],"bytes":[3164,3196]},"callee":null},"110:10":{"kind":"call","span":{"start":[110,10],"end":[110,59],"bytes":[3316,3365]},"callee":null},"115:13":{"kind":"call","span":{"start":[115,13],"end":[115,46],"bytes":[3505,3538]},"callee":null},"116:13":{"kind":"call","span":{"start":[116,13],"end":[116,46],"bytes":[3551,3584]},"callee":null},"117:15":{"kind":"call","span":{"start":[117,15],"end":[117,48],"bytes":[3599,3632]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum"},"122:16":{"kind":"call","span":{"start":[122,16],"end":[122,48],"bytes":[3733,3765]},"callee":null},"123:10":{"kind":"call","span":{"start":[123,10],"end":[123,60],"bytes":[3797,3847]},"callee":null},"126:15":{"kind":"call","span":{"start":[126,15],"end":[126,47],"bytes":[3866,3898]},"callee":null},"127:10":{"kind":"call","span":{"start":[127,10],"end":[127,43],"bytes":[3908,3941]},"callee":null},"128:12":{"kind":"call","span":{"start":[128,12],"end":[128,45],"bytes":[3953,3986]},"callee":null},"129:12":{"kind":"call","span":{"start":[129,12],"end":[129,45],"bytes":[3998,4031]},"callee":null},"96:9":{"kind":"call","span":{"start":[96,9],"end":[96,36],"bytes":[2974,3001]},"callee":null},"97:15":{"kind":"call","span":{"start":[97,15],"end":[97,33],"bytes":[3016,3034]},"callee":null},"97:39":{"kind":"call","span":{"start":[97,39],"end":[97,47],"bytes":[3040,3048]},"callee":null}}}},"fields":{"bo":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/bo","kind":"field","type":"encoding/binary.ByteOrder","span":{"start":[23,1],"end":[23,27],"bytes":[481,507]}},"chksum1":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/chksum1","kind":"field","type":"uint32","span":{"start":[28,1],"end":[28,25],"bytes":[568,592]}},"chksum2":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/chksum2","kind":"field","type":"uint32","span":{"start":[28,1],"end":[28,25],"bytes":[568,592]}},"frameN":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/frameN","kind":"field","type":"int","span":{"start":[21,1],"end":[21,12],"bytes":[468,479]}},"logger":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[30,1],"end":[30,21],"bytes":[594,614]}},"pageSize":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/pageSize","kind":"field","type":"uint32","span":{"start":[24,1],"end":[24,17],"bytes":[508,524]}},"r":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/r","kind":"field","type":"io.ReaderAt","span":{"start":[20,1],"end":[20,20],"bytes":[448,467]}},"salt1":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/salt1","kind":"field","type":"uint32","span":{"start":[27,1],"end":[27,25],"bytes":[543,567]}},"salt2":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/salt2","kind":"field","type":"uint32","span":{"start":[27,1],"end":[27,25],"bytes":[543,567]}},"seq":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/seq","kind":"field","type":"uint32","span":{"start":[25,1],"end":[25,17],"bytes":[525,541]}}}}},"functions":{"github.com/benbjohnson/litestream.NewWALReader":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader","kind":"function","span":{"start":[34,1],"end":[40,2],"bytes":[671,865]},"signature":"github.com/benbjohnson/litestream.NewWALReader","parameters":[{"name":"rd","type":"io.ReaderAt","span":{"start":[34,1],"end":[34,77],"bytes":[671,747]}},{"name":"logger","type":"*log/slog.Logger","span":{"start":[34,1],"end":[34,77],"bytes":[671,747]}}],"return_type":"(*github.com/benbjohnson/litestream.WALReader, error)","error_channel":["error"],"metrics":{"cyclomatic":2},"body":{"36:12":{"kind":"call","span":{"start":[36,12],"end":[36,26],"bytes":[799,813]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readHeader"}}},"github.com/benbjohnson/litestream.NewWALReaderWithOffset":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReaderWithOffset","kind":"function","span":{"start":[45,1],"end":[79,2],"bytes":[1051,2374]},"signature":"github.com/benbjohnson/litestream.NewWALReaderWithOffset","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[45,1],"end":[45,143],"bytes":[1051,1193]}},{"name":"rd","type":"io.ReaderAt","span":{"start":[45,1],"end":[45,143],"bytes":[1051,1193]}},{"name":"offset","type":"int64","span":{"start":[45,1],"end":[45,143],"bytes":[1051,1193]}},{"name":"salt1","type":"uint32","span":{"start":[45,1],"end":[45,143],"bytes":[1051,1193]}},{"name":"salt2","type":"uint32","span":{"start":[45,1],"end":[45,143],"bytes":[1051,1193]}},{"name":"logger","type":"*log/slog.Logger","span":{"start":[45,1],"end":[45,143],"bytes":[1051,1193]}}],"return_type":"(*github.com/benbjohnson/litestream.WALReader, error)","error_channel":["error"],"metrics":{"cyclomatic":6},"body":{"48:15":{"kind":"call","span":{"start":[48,15],"end":[48,109],"bytes":[1323,1417]},"callee":null},"54:12":{"kind":"call","span":{"start":[54,12],"end":[54,26],"bytes":[1527,1541]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readHeader"},"55:15":{"kind":"call","span":{"start":[55,15],"end":[55,49],"bytes":[1570,1604]},"callee":null},"62:15":{"kind":"call","span":{"start":[62,15],"end":[62,53],"bytes":[1780,1818]},"callee":null},"64:15":{"kind":"call","span":{"start":[64,15],"end":[64,89],"bytes":[1877,1951]},"callee":null},"66:13":{"kind":"call","span":{"start":[66,13],"end":[66,54],"bytes":[1967,2008]},"callee":null},"71:18":{"kind":"call","span":{"start":[71,18],"end":[71,67],"bytes":[2180,2229]},"callee":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readFrame"},"71:35":{"kind":"call","span":{"start":[71,35],"end":[71,59],"bytes":[2197,2221]},"callee":null},"72:6":{"kind":"call","span":{"start":[72,6],"end":[72,15],"bytes":[2249,2258]},"callee":null},"73:16":{"kind":"call","span":{"start":[73,16],"end":[73,34],"bytes":[2283,2301]},"callee":null}}},"github.com/benbjohnson/litestream.WALChecksum":{"id":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum","kind":"function","span":{"start":[296,1],"end":[305,2],"bytes":[9082,9389]},"signature":"github.com/benbjohnson/litestream.WALChecksum","parameters":[{"name":"bo","type":"encoding/binary.ByteOrder","span":{"start":[296,1],"end":[296,82],"bytes":[9082,9163]}},{"name":"s0","type":"uint32","span":{"start":[296,1],"end":[296,82],"bytes":[9082,9163]}},{"name":"s1","type":"uint32","span":{"start":[296,1],"end":[296,82],"bytes":[9082,9163]}},{"name":"b","type":"[]byte","span":{"start":[296,1],"end":[296,82],"bytes":[9082,9163]}}],"return_type":"(uint32, uint32)","metrics":{"cyclomatic":2},"body":{"297:2":{"kind":"call","span":{"start":[297,2],"end":[297,57],"bytes":[9165,9220]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert"},"297:9":{"kind":"call","span":{"start":[297,9],"end":[297,15],"bytes":[9172,9178]},"callee":null},"300:18":{"kind":"call","span":{"start":[300,18],"end":[300,24],"bytes":[9291,9297]},"callee":null},"301:9":{"kind":"call","span":{"start":[301,9],"end":[301,25],"bytes":[9316,9332]},"callee":null},"302:9":{"kind":"call","span":{"start":[302,9],"end":[302,27],"bytes":[9346,9364]},"callee":null}}}},"content_hash":"00f72ee5d55ca0780b8d6725f51cf1c0a5be718454fba7b3cfd642b4749a3297"},"webdav/replica_client.go":{"id":"can://go/litestream/webdav/replica_client.go","kind":"module","span":{"start":[1,1],"end":[372,1],"bytes":[0,10831]},"package":"webdav","source":"package webdav\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/studio-b12/gowebdav\"\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/internal\"\n)\n\nfunc init() {\n\tlitestream.RegisterReplicaClientFactory(\"webdav\", NewReplicaClientFromURL)\n\tlitestream.RegisterReplicaClientFactory(\"webdavs\", NewReplicaClientFromURL)\n}\n\nconst ReplicaClientType = \"webdav\"\n\nconst (\n\tDefaultTimeout = 30 * time.Second\n)\n\nvar _ litestream.ReplicaClient = (*ReplicaClient)(nil)\n\ntype ReplicaClient struct {\n\tmu sync.Mutex\n\tclient *gowebdav.Client\n\tlogger *slog.Logger\n\n\tURL string\n\tUsername string\n\tPassword string\n\tPath string\n\tTimeout time.Duration\n}\n\nfunc NewReplicaClient() *ReplicaClient {\n\treturn \u0026ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t\tTimeout: DefaultTimeout,\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\n// URL format: webdav://[user[:password]@]host[:port]/path or webdavs://... (for HTTPS)\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tclient := NewReplicaClient()\n\n\t// Determine HTTP or HTTPS based on scheme\n\thttpScheme := \"http\"\n\tif scheme == \"webdavs\" {\n\t\thttpScheme = \"https\"\n\t}\n\n\t// Extract credentials from userinfo\n\tif userinfo != nil {\n\t\tclient.Username = userinfo.Username()\n\t\tclient.Password, _ = userinfo.Password()\n\t}\n\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"host required for webdav replica URL\")\n\t}\n\n\tclient.URL = fmt.Sprintf(\"%s://%s\", httpScheme, host)\n\tclient.Path = urlPath\n\n\treturn client, nil\n}\n\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\nfunc (c *ReplicaClient) Init(ctx context.Context) error {\n\t_, err := c.init(ctx)\n\treturn err\n}\n\n// init initializes the connection and returns the WebDAV client.\nfunc (c *ReplicaClient) init(ctx context.Context) (_ *gowebdav.Client, err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.client != nil {\n\t\treturn c.client, nil\n\t}\n\n\tif c.URL == \"\" {\n\t\treturn nil, fmt.Errorf(\"webdav url required\")\n\t}\n\n\tc.client = gowebdav.NewClient(c.URL, c.Username, c.Password)\n\n\tc.client.SetTimeout(c.Timeout)\n\n\tif err := c.client.Connect(); err != nil {\n\t\tc.client = nil\n\t\treturn nil, fmt.Errorf(\"webdav: cannot connect to server: %w\", err)\n\t}\n\n\treturn c.client, nil\n}\n\nfunc (c *ReplicaClient) DeleteAll(ctx context.Context) error {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := client.RemoveAll(c.Path); err != nil \u0026\u0026 !os.IsNotExist(err) \u0026\u0026 !gowebdav.IsErrNotFound(err) {\n\t\treturn fmt.Errorf(\"webdav: cannot delete path %q: %w\", c.Path, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\n\treturn nil\n}\n\nfunc (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, _ bool) (_ ltx.FileIterator, err error) {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdir := litestream.LTXLevelDir(c.Path, level)\n\tfiles, err := client.ReadDir(dir)\n\tif err != nil {\n\t\tif os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {\n\t\t\treturn ltx.NewFileInfoSliceIterator(nil), nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"webdav: cannot read directory %q: %w\", dir, err)\n\t}\n\n\tinfos := make([]*ltx.FileInfo, 0, len(files))\n\tfor _, fi := range files {\n\t\tif fi.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tminTXID, maxTXID, err := ltx.ParseFilename(path.Base(fi.Name()))\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t} else if minTXID \u003c seek {\n\t\t\tcontinue\n\t\t}\n\n\t\tinfos = append(infos, \u0026ltx.FileInfo{\n\t\t\tLevel: level,\n\t\t\tMinTXID: minTXID,\n\t\t\tMaxTXID: maxTXID,\n\t\t\tSize: fi.Size(),\n\t\t\tCreatedAt: fi.ModTime().UTC(),\n\t\t})\n\t}\n\n\tsort.Slice(infos, func(i, j int) bool {\n\t\tif infos[i].MinTXID != infos[j].MinTXID {\n\t\t\treturn infos[i].MinTXID \u003c infos[j].MinTXID\n\t\t}\n\t\treturn infos[i].MaxTXID \u003c infos[j].MaxTXID\n\t})\n\n\treturn ltx.NewFileInfoSliceIterator(infos), nil\n}\n\n// WriteLTXFile writes an LTX file to the WebDAV server.\n//\n// WebDAV Upload Strategy - Temp File Approach:\n//\n// Unlike other replica backends (S3, SFTP, NATS, ABS) which stream directly using\n// internal.NewReadCounter, WebDAV requires a different approach due to library and\n// protocol constraints:\n//\n// 1. gowebdav Library Limitations:\n//\n// - WriteStream() buffers entire payload in memory for non-seekable readers\n//\n// - WriteStreamWithLength() requires both content-length AND seekable reader\n//\n// - No native support for HTTP chunked transfer encoding\n//\n// 2. Server Compatibility Issues:\n// Research shows HTTP chunked transfer encoding with WebDAV is unreliable:\n//\n// - Nginx + FastCGI: Discards request body → 0-byte files (silent data loss)\n//\n// - Lighttpd: Returns HTTP 411 (Length Required), rejects chunked requests\n//\n// - Apache + FastCGI: Request body never arrives at application\n//\n// - Only Apache + mod_php handles chunked encoding reliably (~30-40% of deployments)\n//\n// 3. LTX Header Requirement:\n// - Must peek at LTX header to extract timestamp before upload\n// - Peeking consumes data, making the reader non-seekable\n// - Cannot calculate content-length without fully reading stream\n//\n// Solution: Stage to temporary file\n//\n// To ensure universal compatibility and prevent silent data loss:\n// 1. Extract timestamp from LTX header (required for file metadata)\n// 2. Stream full contents to temporary file on disk\n// 3. Seek back to start of temp file (now seekable + known size)\n// 4. Upload using WriteStreamWithLength() with Content-Length header\n// 5. Clean up temp file\n//\n// Trade-offs:\n// - Universal compatibility with all WebDAV server configurations\n// - No risk of silent data loss or failed uploads\n// - Predictable, reliable behavior\n// - Additional disk I/O overhead\n// - Requires local disk space proportional to LTX file size\n// - Diverges from streaming pattern used by other backends\n//\n// References:\n// - https://github.com/studio-b12/gowebdav/issues/35 (chunked encoding issues)\n// - https://github.com/nextcloud/server/issues/7995 (0-byte file bug)\n// - https://evertpot.com/260/ (WebDAV chunked encoding compatibility)\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(rd, \u0026buf)\n\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Stage to temporary file to get seekable reader with known size.\n\t// This ensures compatibility with all WebDAV servers and avoids the\n\t// unreliable chunked transfer encoding that causes silent data loss\n\t// on common configurations (Nginx+FastCGI, Lighttpd, Apache+FastCGI).\n\ttmpFile, err := os.CreateTemp(\"\", \"litestream-webdav-*.ltx\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot create temp file: %w\", err)\n\t}\n\tdefer func() {\n\t\t_ = tmpFile.Close()\n\t\t_ = os.Remove(tmpFile.Name())\n\t}()\n\n\tfullReader := io.MultiReader(\u0026buf, rd)\n\n\tsize, err := io.Copy(tmpFile, fullReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot copy to temp file: %w\", err)\n\t}\n\n\tif _, err := tmpFile.Seek(0, io.SeekStart); err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot seek temp file: %w\", err)\n\t}\n\n\tif err := client.MkdirAll(path.Dir(filename), 0755); err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot create parent directory %q: %w\", path.Dir(filename), err)\n\t}\n\n\t// Upload with Content-Length header using seekable temp file.\n\t// WriteStreamWithLength requires both a seekable reader and known size,\n\t// which we now have from the temp file. This avoids chunked encoding\n\t// and ensures reliable uploads across all WebDAV server configurations.\n\tif err := client.WriteStreamWithLength(filename, tmpFile, size, 0644); err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot write file %q: %w\", filename, err)\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(size))\n\n\treturn \u0026ltx.FileInfo{\n\t\tLevel: level,\n\t\tMinTXID: minTXID,\n\t\tMaxTXID: maxTXID,\n\t\tSize: size,\n\t\tCreatedAt: timestamp,\n\t}, nil\n}\n\nfunc (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (_ io.ReadCloser, err error) {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\n\tif size \u003e 0 {\n\t\trc, err := client.ReadStreamRange(filename, offset, size)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {\n\t\t\t\treturn nil, os.ErrNotExist\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"webdav: cannot read file %q: %w\", filename, err)\n\t\t}\n\t\treturn internal.LimitReadCloser(rc, size), nil\n\t}\n\n\tif offset \u003e 0 {\n\t\trc, err := client.ReadStream(filename)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {\n\t\t\t\treturn nil, os.ErrNotExist\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"webdav: cannot read file %q: %w\", filename, err)\n\t\t}\n\n\t\tif _, err := io.CopyN(io.Discard, rc, offset); err != nil {\n\t\t\tif err == io.EOF || err == io.ErrUnexpectedEOF {\n\t\t\t\t_ = rc.Close()\n\t\t\t\treturn io.NopCloser(bytes.NewReader(nil)), nil\n\t\t\t}\n\t\t\t_ = rc.Close()\n\t\t\treturn nil, fmt.Errorf(\"webdav: cannot skip offset in file %q: %w\", filename, err)\n\t\t}\n\n\t\treturn rc, nil\n\t}\n\n\trc, err := client.ReadStream(filename)\n\tif err != nil {\n\t\tif os.IsNotExist(err) || gowebdav.IsErrNotFound(err) {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, fmt.Errorf(\"webdav: cannot read file %q: %w\", filename, err)\n\t}\n\treturn rc, nil\n}\n\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, info := range a {\n\t\tfilename := litestream.LTXFilePath(c.Path, info.Level, info.MinTXID, info.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"path\", filename)\n\n\t\tif err := client.Remove(filename); err != nil \u0026\u0026 !os.IsNotExist(err) \u0026\u0026 !gowebdav.IsErrNotFound(err) {\n\t\t\treturn fmt.Errorf(\"webdav: cannot delete ltx file %q: %w\", filename, err)\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n","imports":[{"name":"bytes","path":"bytes","span":{"start":[4,1],"end":[4,9],"bytes":[25,33]}},{"name":"context","path":"context","span":{"start":[5,1],"end":[5,11],"bytes":[34,44]}},{"name":"fmt","path":"fmt","span":{"start":[6,1],"end":[6,7],"bytes":[45,51]}},{"name":"io","path":"io","span":{"start":[7,1],"end":[7,6],"bytes":[52,57]}},{"name":"slog","path":"log/slog","span":{"start":[8,1],"end":[8,12],"bytes":[58,69]}},{"name":"url","path":"net/url","span":{"start":[9,1],"end":[9,11],"bytes":[70,80]}},{"name":"os","path":"os","span":{"start":[10,1],"end":[10,6],"bytes":[81,86]}},{"name":"path","path":"path","span":{"start":[11,1],"end":[11,8],"bytes":[87,94]}},{"name":"sort","path":"sort","span":{"start":[12,1],"end":[12,8],"bytes":[95,102]}},{"name":"sync","path":"sync","span":{"start":[13,1],"end":[13,8],"bytes":[103,110]}},{"name":"time","path":"time","span":{"start":[14,1],"end":[14,8],"bytes":[111,118]}},{"name":"gowebdav","path":"github.com/studio-b12/gowebdav","span":{"start":[16,1],"end":[16,34],"bytes":[120,153]}},{"name":"ltx","path":"github.com/superfly/ltx","span":{"start":[17,1],"end":[17,27],"bytes":[154,180]}},{"name":"litestream","path":"github.com/benbjohnson/litestream","span":{"start":[19,1],"end":[19,37],"bytes":[182,218]}},{"name":"internal","path":"github.com/benbjohnson/litestream/internal","span":{"start":[20,1],"end":[20,46],"bytes":[219,264]}}],"types":{"ReplicaClient":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient","kind":"struct","span":{"start":[36,1],"end":[46,2],"bytes":[576,763]},"callables":{"github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteAll":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteAll","kind":"method","span":{"start":[121,1],"end":[134,2],"bytes":[2598,3002]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteAll","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[121,1],"end":[121,63],"bytes":[2598,2660]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":3},"body":{"122:17":{"kind":"call","span":{"start":[122,17],"end":[122,28],"bytes":[2677,2688]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init"},"127:12":{"kind":"call","span":{"start":[127,12],"end":[127,36],"bytes":[2734,2758]},"callee":null},"127:53":{"kind":"call","span":{"start":[127,53],"end":[127,71],"bytes":[2775,2793]},"callee":null},"127:76":{"kind":"call","span":{"start":[127,76],"end":[127,103],"bytes":[2798,2825]},"callee":null},"128:10":{"kind":"call","span":{"start":[128,10],"end":[128,70],"bytes":[2837,2897]},"callee":null},"131:2":{"kind":"call","span":{"start":[131,2],"end":[131,80],"bytes":[2903,2981]},"callee":null}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteLTXFiles":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteLTXFiles","kind":"method","span":{"start":[353,1],"end":[371,2],"bytes":[10151,10830]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteLTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[353,1],"end":[353,87],"bytes":[10151,10237]}},{"name":"a","type":"[]*github.com/superfly/ltx.FileInfo","span":{"start":[353,1],"end":[353,87],"bytes":[10151,10237]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"354:17":{"kind":"call","span":{"start":[354,17],"end":[354,28],"bytes":[10254,10265]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init"},"360:15":{"kind":"call","span":{"start":[360,15],"end":[360,85],"bytes":[10340,10410]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"362:3":{"kind":"call","span":{"start":[362,3],"end":[362,127],"bytes":[10414,10538]},"callee":null},"364:13":{"kind":"call","span":{"start":[364,13],"end":[364,36],"bytes":[10552,10575]},"callee":null},"364:53":{"kind":"call","span":{"start":[364,53],"end":[364,71],"bytes":[10592,10610]},"callee":null},"364:76":{"kind":"call","span":{"start":[364,76],"end":[364,103],"bytes":[10615,10642]},"callee":null},"365:11":{"kind":"call","span":{"start":[365,11],"end":[365,77],"bytes":[10655,10721]},"callee":null},"367:3":{"kind":"call","span":{"start":[367,3],"end":[367,81],"bytes":[10728,10806]},"callee":null}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.Init":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.Init","kind":"method","span":{"start":[91,1],"end":[94,2],"bytes":[1942,2036]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.Init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[91,1],"end":[91,58],"bytes":[1942,1999]}}],"return_type":"error","error_channel":["error"],"metrics":{"cyclomatic":1},"body":{"92:12":{"kind":"call","span":{"start":[92,12],"end":[92,23],"bytes":[2011,2022]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init"}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles","kind":"method","span":{"start":[136,1],"end":[181,2],"bytes":[3004,4142]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[136,1],"end":[136,122],"bytes":[3004,3125]}},{"name":"level","type":"int","span":{"start":[136,1],"end":[136,122],"bytes":[3004,3125]}},{"name":"seek","type":"github.com/superfly/ltx.TXID","span":{"start":[136,1],"end":[136,122],"bytes":[3004,3125]}},{"name":"_","type":"bool","span":{"start":[136,1],"end":[136,122],"bytes":[3004,3125]}}],"return_type":"(github.com/superfly/ltx.FileIterator, error)","error_channel":["error"],"metrics":{"cyclomatic":9},"body":{"137:17":{"kind":"call","span":{"start":[137,17],"end":[137,28],"bytes":[3142,3153]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init"},"142:9":{"kind":"call","span":{"start":[142,9],"end":[142,46],"bytes":[3201,3238]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir"},"143:16":{"kind":"call","span":{"start":[143,16],"end":[143,35],"bytes":[3254,3273]},"callee":null},"145:28":{"kind":"call","span":{"start":[145,28],"end":[145,55],"bytes":[3318,3345]},"callee":null},"145:6":{"kind":"call","span":{"start":[145,6],"end":[145,24],"bytes":[3296,3314]},"callee":null},"146:11":{"kind":"call","span":{"start":[146,11],"end":[146,44],"bytes":[3358,3391]},"callee":null},"148:15":{"kind":"call","span":{"start":[148,15],"end":[148,75],"bytes":[3415,3475]},"callee":null},"151:11":{"kind":"call","span":{"start":[151,11],"end":[151,47],"bytes":[3490,3526]},"callee":null},"151:36":{"kind":"call","span":{"start":[151,36],"end":[151,46],"bytes":[3515,3525]},"callee":null},"153:6":{"kind":"call","span":{"start":[153,6],"end":[153,16],"bytes":[3560,3570]},"callee":null},"157:28":{"kind":"call","span":{"start":[157,28],"end":[157,67],"bytes":[3617,3656]},"callee":null},"157:46":{"kind":"call","span":{"start":[157,46],"end":[157,66],"bytes":[3635,3655]},"callee":null},"157:56":{"kind":"call","span":{"start":[157,56],"end":[157,65],"bytes":[3645,3654]},"callee":null},"164:11":{"kind":"call","span":{"start":[164,11],"end":[170,5],"bytes":[3743,3902]},"callee":null},"168:15":{"kind":"call","span":{"start":[168,15],"end":[168,24],"bytes":[3853,3862]},"callee":null},"169:15":{"kind":"call","span":{"start":[169,15],"end":[169,27],"bytes":[3878,3890]},"callee":null},"173:2":{"kind":"call","span":{"start":[173,2],"end":[178,4],"bytes":[3908,4090]},"callee":null},"180:9":{"kind":"call","span":{"start":[180,9],"end":[180,44],"bytes":[4100,4135]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles/github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles.closure_1","kind":"function","span":{"start":[173,1],"end":[178,4],"bytes":[3907,4090]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles.closure_1","parameters":[{"name":"i","type":"int","span":{"start":[173,1],"end":[173,41],"bytes":[3907,3947]}},{"name":"j","type":"int","span":{"start":[173,1],"end":[173,41],"bytes":[3907,3947]}}],"return_type":"bool","body":{}}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.OpenLTXFile":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.OpenLTXFile","kind":"method","span":{"start":[301,1],"end":[351,2],"bytes":[8649,10149]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.OpenLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[301,1],"end":[301,146],"bytes":[8649,8794]}},{"name":"level","type":"int","span":{"start":[301,1],"end":[301,146],"bytes":[8649,8794]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[301,1],"end":[301,146],"bytes":[8649,8794]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[301,1],"end":[301,146],"bytes":[8649,8794]}},{"name":"offset","type":"int64","span":{"start":[301,1],"end":[301,146],"bytes":[8649,8794]}},{"name":"size","type":"int64","span":{"start":[301,1],"end":[301,146],"bytes":[8649,8794]}}],"return_type":"(io.ReadCloser, error)","error_channel":["error"],"metrics":{"cyclomatic":12},"body":{"302:17":{"kind":"call","span":{"start":[302,17],"end":[302,28],"bytes":[8811,8822]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init"},"307:14":{"kind":"call","span":{"start":[307,14],"end":[307,69],"bytes":[8875,8930]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"309:2":{"kind":"call","span":{"start":[309,2],"end":[309,77],"bytes":[8933,9008]},"callee":null},"312:14":{"kind":"call","span":{"start":[312,14],"end":[312,60],"bytes":[9044,9090]},"callee":null},"314:29":{"kind":"call","span":{"start":[314,29],"end":[314,56],"bytes":[9137,9164]},"callee":null},"314:7":{"kind":"call","span":{"start":[314,7],"end":[314,25],"bytes":[9115,9133]},"callee":null},"317:16":{"kind":"call","span":{"start":[317,16],"end":[317,76],"bytes":[9218,9278]},"callee":null},"319:10":{"kind":"call","span":{"start":[319,10],"end":[319,44],"bytes":[9292,9326]},"callee":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser"},"323:14":{"kind":"call","span":{"start":[323,14],"end":[323,41],"bytes":[9366,9393]},"callee":null},"325:29":{"kind":"call","span":{"start":[325,29],"end":[325,56],"bytes":[9440,9467]},"callee":null},"325:7":{"kind":"call","span":{"start":[325,7],"end":[325,25],"bytes":[9418,9436]},"callee":null},"328:16":{"kind":"call","span":{"start":[328,16],"end":[328,76],"bytes":[9521,9581]},"callee":null},"331:16":{"kind":"call","span":{"start":[331,16],"end":[331,48],"bytes":[9602,9634]},"callee":null},"333:9":{"kind":"call","span":{"start":[333,9],"end":[333,19],"bytes":[9709,9719]},"callee":null},"334:12":{"kind":"call","span":{"start":[334,12],"end":[334,46],"bytes":[9731,9765]},"callee":null},"334:25":{"kind":"call","span":{"start":[334,25],"end":[334,45],"bytes":[9744,9764]},"callee":null},"336:8":{"kind":"call","span":{"start":[336,8],"end":[336,18],"bytes":[9783,9793]},"callee":null},"337:16":{"kind":"call","span":{"start":[337,16],"end":[337,86],"bytes":[9809,9879]},"callee":null},"343:13":{"kind":"call","span":{"start":[343,13],"end":[343,40],"bytes":[9918,9945]},"callee":null},"345:28":{"kind":"call","span":{"start":[345,28],"end":[345,55],"bytes":[9990,10017]},"callee":null},"345:6":{"kind":"call","span":{"start":[345,6],"end":[345,24],"bytes":[9968,9986]},"callee":null},"348:15":{"kind":"call","span":{"start":[348,15],"end":[348,75],"bytes":[10068,10128]},"callee":null}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.SetLogger":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.SetLogger","kind":"method","span":{"start":[55,1],"end":[57,2],"bytes":[919,1025]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.SetLogger","parameters":[{"name":"logger","type":"*log/slog.Logger","span":{"start":[55,1],"end":[55,57],"bytes":[919,975]}}],"metrics":{"cyclomatic":1},"body":{"56:13":{"kind":"call","span":{"start":[56,13],"end":[56,48],"bytes":[988,1023]},"callee":null}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.Type":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.Type","kind":"method","span":{"start":[87,1],"end":[89,2],"bytes":[1873,1940]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.Type","parameters":[],"return_type":"string","metrics":{"cyclomatic":1},"body":{}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile","kind":"method","span":{"start":[236,1],"end":[299,2],"bytes":[6374,8647]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[236,1],"end":[236,144],"bytes":[6374,6517]}},{"name":"level","type":"int","span":{"start":[236,1],"end":[236,144],"bytes":[6374,6517]}},{"name":"minTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[236,1],"end":[236,144],"bytes":[6374,6517]}},{"name":"maxTXID","type":"github.com/superfly/ltx.TXID","span":{"start":[236,1],"end":[236,144],"bytes":[6374,6517]}},{"name":"rd","type":"io.Reader","span":{"start":[236,1],"end":[236,144],"bytes":[6374,6517]}}],"return_type":"(*github.com/superfly/ltx.FileInfo, error)","error_channel":["error"],"metrics":{"cyclomatic":8},"body":{"237:17":{"kind":"call","span":{"start":[237,17],"end":[237,28],"bytes":[6534,6545]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init"},"242:14":{"kind":"call","span":{"start":[242,14],"end":[242,69],"bytes":[6598,6653]},"callee":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath"},"245:15":{"kind":"call","span":{"start":[245,15],"end":[245,37],"bytes":[6691,6713]},"callee":null},"247:17":{"kind":"call","span":{"start":[247,17],"end":[247,42],"bytes":[6731,6756]},"callee":null},"249:15":{"kind":"call","span":{"start":[249,15],"end":[249,71],"bytes":[6788,6844]},"callee":null},"251:15":{"kind":"call","span":{"start":[251,15],"end":[251,44],"bytes":[6862,6891]},"callee":null},"257:18":{"kind":"call","span":{"start":[257,18],"end":[257,62],"bytes":[7196,7240]},"callee":null},"259:15":{"kind":"call","span":{"start":[259,15],"end":[259,69],"bytes":[7272,7326]},"callee":null},"261:8":{"kind":"call","span":{"start":[261,8],"end":[264,5],"bytes":[7337,7404]},"callee":null},"266:16":{"kind":"call","span":{"start":[266,16],"end":[266,40],"bytes":[7421,7445]},"callee":null},"268:15":{"kind":"call","span":{"start":[268,15],"end":[268,43],"bytes":[7461,7489]},"callee":null},"270:15":{"kind":"call","span":{"start":[270,15],"end":[270,70],"bytes":[7521,7576]},"callee":null},"273:15":{"kind":"call","span":{"start":[273,15],"end":[273,44],"bytes":[7595,7624]},"callee":null},"274:15":{"kind":"call","span":{"start":[274,15],"end":[274,67],"bytes":[7653,7705]},"callee":null},"277:12":{"kind":"call","span":{"start":[277,12],"end":[277,53],"bytes":[7721,7762]},"callee":null},"277:28":{"kind":"call","span":{"start":[277,28],"end":[277,46],"bytes":[7737,7755]},"callee":null},"278:15":{"kind":"call","span":{"start":[278,15],"end":[278,99],"bytes":[7791,7875]},"callee":null},"278:75":{"kind":"call","span":{"start":[278,75],"end":[278,93],"bytes":[7851,7869]},"callee":null},"285:12":{"kind":"call","span":{"start":[285,12],"end":[285,71],"bytes":[8174,8233]},"callee":null},"286:15":{"kind":"call","span":{"start":[286,15],"end":[286,76],"bytes":[8262,8323]},"callee":null},"289:2":{"kind":"call","span":{"start":[289,2],"end":[289,77],"bytes":[8329,8404]},"callee":null},"290:2":{"kind":"call","span":{"start":[290,2],"end":[290,77],"bytes":[8412,8487]},"callee":null},"290:82":{"kind":"call","span":{"start":[290,82],"end":[290,95],"bytes":[8492,8505]},"callee":null}},"callables":{"closure_1":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile/github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile.closure_1","kind":"function","span":{"start":[261,1],"end":[264,5],"bytes":[7330,7404]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile.closure_1","parameters":[],"body":{"262:7":{"kind":"call","span":{"start":[262,7],"end":[262,22],"bytes":[7352,7367]},"callee":null},"263:17":{"kind":"call","span":{"start":[263,17],"end":[263,31],"bytes":[7384,7398]},"callee":null},"263:7":{"kind":"call","span":{"start":[263,7],"end":[263,32],"bytes":[7374,7399]},"callee":null}}}}},"github.com/benbjohnson/litestream/webdav.ReplicaClient.init":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","kind":"method","span":{"start":[97,1],"end":[119,2],"bytes":[2104,2596]},"signature":"github.com/benbjohnson/litestream/webdav.ReplicaClient.init","parameters":[{"name":"ctx","type":"context.Context","span":{"start":[97,1],"end":[97,84],"bytes":[2104,2187]}}],"return_type":"(*github.com/studio-b12/gowebdav.Client, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"106:15":{"kind":"call","span":{"start":[106,15],"end":[106,48],"bytes":[2304,2337]},"callee":null},"109:13":{"kind":"call","span":{"start":[109,13],"end":[109,62],"bytes":[2354,2403]},"callee":null},"111:2":{"kind":"call","span":{"start":[111,2],"end":[111,32],"bytes":[2406,2436]},"callee":null},"113:12":{"kind":"call","span":{"start":[113,12],"end":[113,30],"bytes":[2449,2467]},"callee":null},"115:15":{"kind":"call","span":{"start":[115,15],"end":[115,70],"bytes":[2513,2568]},"callee":null},"98:2":{"kind":"call","span":{"start":[98,2],"end":[98,13],"bytes":[2189,2200]},"callee":null},"99:8":{"kind":"call","span":{"start":[99,8],"end":[99,21],"bytes":[2208,2221]},"callee":null}}}},"fields":{"Password":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/Password","kind":"field","type":"string","span":{"start":[43,1],"end":[43,17],"bytes":[704,720]}},"Path":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/Path","kind":"field","type":"string","span":{"start":[44,1],"end":[44,17],"bytes":[721,737]}},"Timeout":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/Timeout","kind":"field","type":"time.Duration","span":{"start":[45,1],"end":[45,24],"bytes":[738,761]}},"URL":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/URL","kind":"field","type":"string","span":{"start":[41,1],"end":[41,17],"bytes":[670,686]}},"Username":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/Username","kind":"field","type":"string","span":{"start":[42,1],"end":[42,17],"bytes":[687,703]}},"client":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/client","kind":"field","type":"*github.com/studio-b12/gowebdav.Client","span":{"start":[38,1],"end":[38,25],"bytes":[623,647]}},"logger":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/logger","kind":"field","type":"*log/slog.Logger","span":{"start":[39,1],"end":[39,21],"bytes":[648,668]}},"mu":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/mu","kind":"field","type":"sync.Mutex","span":{"start":[37,1],"end":[37,19],"bytes":[604,622]}}}}},"functions":{"github.com/benbjohnson/litestream/webdav.NewReplicaClient":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient","kind":"function","span":{"start":[48,1],"end":[53,2],"bytes":[765,917]},"signature":"github.com/benbjohnson/litestream/webdav.NewReplicaClient","parameters":[],"return_type":"*github.com/benbjohnson/litestream/webdav.ReplicaClient","metrics":{"cyclomatic":1},"body":{"50:12":{"kind":"call","span":{"start":[50,12],"end":[50,26],"bytes":[841,855]},"callee":null}}},"github.com/benbjohnson/litestream/webdav.NewReplicaClientFromURL":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClientFromURL","kind":"function","span":{"start":[62,1],"end":[85,2],"bytes":[1251,1871]},"signature":"github.com/benbjohnson/litestream/webdav.NewReplicaClientFromURL","parameters":[{"name":"scheme","type":"string","span":{"start":[62,1],"end":[62,137],"bytes":[1251,1387]}},{"name":"host","type":"string","span":{"start":[62,1],"end":[62,137],"bytes":[1251,1387]}},{"name":"urlPath","type":"string","span":{"start":[62,1],"end":[62,137],"bytes":[1251,1387]}},{"name":"query","type":"net/url.Values","span":{"start":[62,1],"end":[62,137],"bytes":[1251,1387]}},{"name":"userinfo","type":"*net/url.Userinfo","span":{"start":[62,1],"end":[62,137],"bytes":[1251,1387]}}],"return_type":"(github.com/benbjohnson/litestream.ReplicaClient, error)","error_channel":["error"],"metrics":{"cyclomatic":4},"body":{"63:12":{"kind":"call","span":{"start":[63,12],"end":[63,30],"bytes":[1399,1417]},"callee":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient"},"73:21":{"kind":"call","span":{"start":[73,21],"end":[73,40],"bytes":[1618,1637]},"callee":null},"74:24":{"kind":"call","span":{"start":[74,24],"end":[74,43],"bytes":[1661,1680]},"callee":null},"78:15":{"kind":"call","span":{"start":[78,15],"end":[78,65],"bytes":[1716,1766]},"callee":null},"81:15":{"kind":"call","span":{"start":[81,15],"end":[81,55],"bytes":[1785,1825]},"callee":null}}},"github.com/benbjohnson/litestream/webdav.init":{"id":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.init","kind":"function","span":{"start":[23,1],"end":[26,2],"bytes":[268,436]},"signature":"github.com/benbjohnson/litestream/webdav.init","parameters":[],"metrics":{"cyclomatic":1},"body":{"24:2":{"kind":"call","span":{"start":[24,2],"end":[24,76],"bytes":[283,357]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"},"25:2":{"kind":"call","span":{"start":[25,2],"end":[25,77],"bytes":[359,434]},"callee":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory"}}}},"content_hash":"0663d9d2f104f9e08f85fb1d08c60dd84bbaa0f77b692584f583b18f7f092a35"}},"call_graph":[{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClientFromURL","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteAll","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.LTXFiles","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.LTXFiles","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.newLTXFileIterator","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ReplicaClient/github.com/benbjohnson/litestream/abs.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.Next","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.ltxFileIterator/github.com/benbjohnson/litestream/abs.ltxFileIterator.loadNextPage","prov":["go/types"],"weight":1},{"src":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.newLTXFileIterator","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.Run","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.calculateRate","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.sinApprox","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.ensureTestTable","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.finalReport","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.generateLoad","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.reportStats","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.worker","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.calculateRate","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.worker","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.worker","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performWrite","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","dst":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Usage","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","dst":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream-test.VersionCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","dst":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","dst":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.main","dst":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.Main/github.com/benbjohnson/litestream/cmd/litestream-test.Main.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.main","dst":"can://go/litestream/cmd/litestream-test/main.go/github.com/benbjohnson/litestream/cmd/litestream-test.NewMain","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.parseSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase","dst":"can://go/litestream/cmd/litestream-test/load.go/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand/github.com/benbjohnson/litestream/cmd/litestream-test.LoadCommand.performRead","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase","dst":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateTable","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.PopulateCommand.populateDatabase","dst":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.Run","dst":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","dst":"can://go/litestream/cmd/litestream-test/populate.go/github.com/benbjohnson/litestream/cmd/litestream-test.getDatabaseSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","dst":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.deleteFromTable","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","dst":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.getTableList","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","dst":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runCheckpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.shrinkDatabase","dst":"can://go/litestream/cmd/litestream-test/shrink.go/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ShrinkCommand.runVacuum","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performIntegrityCheck","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performQuickCheck","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performRestore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.reportResults","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.Run","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.validateLTXContinuity","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performChecksumCheck","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.calculateDBChecksum","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getRowCount","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.performDataValidation","dst":"can://go/litestream/cmd/litestream-test/validate.go/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand/github.com/benbjohnson/litestream/cmd/litestream-test.ValidateCommand.getTableList","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Usage","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.flushPendingEvents","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addDirectoryWatch","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDirectoryWatch","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.shouldSkipPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.matchesPattern","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.IsSQLiteDatabase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabase","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.removeDatabasesUnder","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.flushPendingEvents","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handleEvent","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.handlePotentialDatabase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.scanDirectory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Usage","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ByteSize/github.com/benbjohnson/litestream/cmd/litestream.ByteSize.UnmarshalYAML","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseByteSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.Validate","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.isValidHeartbeatURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.propagateGlobalSettings","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings/github.com/benbjohnson/litestream/cmd/litestream.ReplicaSettings.SetDefaults","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.DefaultSocketConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/databases.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand/github.com/benbjohnson/litestream/cmd/litestream.DatabasesCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/info.go/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand/github.com/benbjohnson/litestream/cmd/litestream.InfoCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/list.go/github.com/benbjohnson/litestream/cmd/litestream.ListCommand/github.com/benbjohnson/litestream/cmd/litestream.ListCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/ltx.go/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand/github.com/benbjohnson/litestream/cmd/litestream.LTXCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Usage","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.isWindowsService","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.runWindowsService","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.signalChan","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicateCommand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.SetDone","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/start.go/github.com/benbjohnson/litestream/cmd/litestream.StartCommand/github.com/benbjohnson/litestream/cmd/litestream.StartCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/stop.go/github.com/benbjohnson/litestream/cmd/litestream.StopCommand/github.com/benbjohnson/litestream/cmd/litestream.StopCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","dst":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetMetaPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.FindSQLiteDatabases","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MetaPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newABSReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newSFTPReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newWebDAVReplicaClientFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.SetLogger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.ApplyDefault","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.boolSetting/github.com/benbjohnson/litestream/cmd/litestream.boolSetting.Set","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newBoolSetting","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsBackblazeEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsCloudflareR2Endpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsDigitalOceanEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsFilebaseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsHetznerEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsMinIOEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsScalewayEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsSupabaseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsTigrisEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegionFromS3ARN","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewS3ReplicaClientFromConfig","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.OpenConfigFile","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.Validate","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.applyDBSnapshotConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.propagateGlobalSettings","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.OpenConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ParseConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ReplicaTypeFromURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig/github.com/benbjohnson/litestream/cmd/litestream.ReplicaConfig.ReplicaType","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.appendRelativePathToURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.StripSQLitePrefix","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.init","dst":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.main","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Main/github.com/benbjohnson/litestream/cmd/litestream.Main.Run","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.main","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewMain","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.main","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newABSReplicaClientFromConfig","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.cloneReplicaConfigWithRelativePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.deriveMetaPathForDirectoryEntry","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newDBFromDirectoryEntry","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newFileReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newGSReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newNATSReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newOSSReplicaClientFromConfig","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newSFTPReplicaClientFromConfig","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.newWebDAVReplicaClientFromConfig","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.DatabasesTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.InfoTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.LTXTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.ResetTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.StatusTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.VersionTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Run","dst":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.registerDisplayStatus","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/register.go/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand/github.com/benbjohnson/litestream/cmd/litestream.RegisterCommand.Usage","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.applyLitestreamEnv","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Close","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.ParseFlags","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.NewDirectoryMonitor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.CompactionLevels","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBsFromDirectoryConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.MCPServer/github.com/benbjohnson/litestream/cmd/litestream.MCPServer.Start","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.NewMCP","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.NewHeartbeatClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.NewServer","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.Start","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.NewStore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetDone","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetL0Retention","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetShutdownSyncInterval","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetShutdownSyncTimeout","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Run","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SetVerifyCompaction","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.Usage","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.expand","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.restoreIfNeeded","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/replicate.go/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand/github.com/benbjohnson/litestream/cmd/litestream.ReplicateCommand.runOnce","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.localLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MetaPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/reset.go/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand/github.com/benbjohnson/litestream/cmd/litestream.ResetCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/main_notwindows.go/github.com/benbjohnson/litestream/cmd/litestream.signalChan","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/mcp.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreTool","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.prepareOutputPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Run","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.Usage","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.dryRunPlan","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.Config/github.com/benbjohnson/litestream/cmd/litestream.Config.DBConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromConfig","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewReplicaFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.loadFromURL","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CalcRestoreTarget","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.restoreTXID","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/restore.go/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand/github.com/benbjohnson/litestream/cmd/litestream.RestoreCommand.restoreTXID","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.NewDBFromConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.ReadConfigFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.registerConfigFlag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","dst":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Run","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.Usage","dst":"can://go/litestream/cmd/litestream/main.go/github.com/benbjohnson/litestream/cmd/litestream.DefaultConfigPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/status.go/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand/github.com/benbjohnson/litestream/cmd/litestream.StatusCommand.getDBStatus","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run","dst":"can://go/litestream/cmd/litestream/directory_watcher.go/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor/github.com/benbjohnson/litestream/cmd/litestream.DirectoryMonitor.addInitialWatches","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run","dst":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.printSyncResult","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand/github.com/benbjohnson/litestream/cmd/litestream.SyncCommand.Run","dst":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/sync.go/github.com/benbjohnson/litestream/cmd/litestream.printSyncResult","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run","dst":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.printUnregisterResult","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/unregister.go/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand/github.com/benbjohnson/litestream/cmd/litestream.UnregisterCommand.Run","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.InitLog","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand/github.com/benbjohnson/litestream/cmd/litestream.VersionCommand.Run","dst":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.vcsSettings","prov":["go/types"],"weight":1},{"src":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.resolveVersion","dst":"can://go/litestream/cmd/litestream/version.go/github.com/benbjohnson/litestream/cmd/litestream.vcsSettings","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.Level","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.NextLevel","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.PrevLevel","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.MaxLevel","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.MaxLTXFileInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.VerifyLevelConsistency","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.NewResumableReader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceL0Retention","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceL0Retention","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceSnapshotRetention","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceSnapshotRetention","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.MaxLTXFileInfo","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.VerifyLevelConsistency","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.CRC64","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.CRC64","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.CRC64","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.CRC64","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncDiag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.deleteMetrics","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.releaseReadLock","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncReplicaWithRetry","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Stop","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Compact","dst":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.Compact","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Compact","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Compact","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceRetentionByTXID","dst":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.EnforceRetentionByTXID","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnsureExists","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnsureExists","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewRestoreOptions","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnsureExists","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTXFileInfo","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTXFileInfo","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.initMetrics","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.removeTmpFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetLogger","dst":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.Compactor/github.com/benbjohnson/litestream.Compactor.setLogger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetLogger","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetLogger","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.SetLogger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SnapshotReader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SnapshotReader","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SnapshotReader","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotReader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SnapshotReader","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/github.com/benbjohnson/litestream.snapshotReadPosition.close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncOnce","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncAndWait","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncAndWait","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncStatus","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncStatus","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.calcPos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncDiag","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.effectiveTruncatePageN","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isSQLiteBusyError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.bumpLitestreamSeq","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointWithExecutor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.rollback","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.deleteLocalLTXFile","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.deleteLocalLTXFile","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.detectFullCheckpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.detectFullCheckpoint","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.detectFullCheckpoint","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.FrameSaltsUntil","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.bumpLitestreamSeq","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.effectiveTruncatePageN","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.calcWALSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.acquireReadLock","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.execCheckpoint","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.releaseReadLock","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.acquireReadLock","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkDatabaseBehindReplica","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setPersistWAL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Start","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lastPageMatch","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lastPageMatch","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALFileAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncExecutorWait","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.finishSyncExecutorWait","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.monitor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.monitor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.monitor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.monitor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.removeTmpFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.openLocalLTXFile","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.releaseReadLock","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.rollback","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.PageSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotPosition","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.snapshotWALEndOffset","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.invalidatePosCache","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.writeLTXFromDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.writeLTXFromWAL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.isDiskFullError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile/github.com/benbjohnson/litestream.ltxStagingFile.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.ltxStagingFile/github.com/benbjohnson/litestream.ltxStagingFile.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReaderWithOffset","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.applySyncExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.beginSyncDiag","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.checkpointIfNeeded","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ensureWALExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.exceedsTruncateThreshold","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.newSyncExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.syncExecutor/github.com/benbjohnson/litestream.syncExecutor.applySyncResult","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lockExec","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncLocked","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.syncReplicaWithRetry","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verify","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verify","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSync","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.setSyncDiagPhase","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyAndSyncWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.detectFullCheckpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.lastPageMatch","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.verifyWithExecutor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.readWALHeader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.walFileSize","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.WALPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","dst":"can://go/litestream/compactor.go/github.com/benbjohnson/litestream.NewCompactor","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.initMetrics","prov":["go/types"],"weight":1},{"src":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadCloser/github.com/benbjohnson/litestream.snapshotReadCloser.Close","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.snapshotReadPosition/github.com/benbjohnson/litestream.snapshotReadPosition.close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClientFromURL","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.GenerationsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.IsGenerationIDV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFiles","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXLevelDir","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenSnapshotV3","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenSnapshotV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatSnapshotFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenWALSegmentV3","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.OpenWALSegmentV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.SnapshotsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseSnapshotFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WALSegmentsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.DirInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.FileInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.db","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.CreateFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.ReplicaClient/github.com/benbjohnson/litestream/file.ReplicaClient.db","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClientFromURL","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteAll","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteAll","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.LTXFiles","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.LTXFiles","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.newLTXFileIterator","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.LTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.Hexdump","dst":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.CreateFile","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.Fileinfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.fixRootDirectory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCloser/github.com/benbjohnson/litestream/internal.ReadCloser.Close","dst":"can://go/litestream/internal/hexdump.go/github.com/benbjohnson/litestream/internal.toChar","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.LockFileExclusive","dst":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.UnlockFile","dst":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.setFcntlLock","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.ReplaceAttr","dst":"can://go/litestream/internal/log.go/github.com/benbjohnson/litestream/internal.CleanSourcePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.LTXFileOpener/github.com/benbjohnson/litestream/internal.LTXFileOpener.OpenLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.dropStream","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.readStream","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.retry","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.setStream","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.Read","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.ResumableReader/github.com/benbjohnson/litestream/internal.ResumableReader.stream","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDBs","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseDBs","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustCloseSQLDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustDeleteAll","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.DeleteAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustDeleteAll","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDB","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBAt","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBs","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBs","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenDBs","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.MustOpenSQLDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewABSReplicaClient","dst":"can://go/litestream/abs/replica_client.go/github.com/benbjohnson/litestream/abs.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewB2ReplicaClient","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient","dst":"can://go/litestream/file/replica_client.go/github.com/benbjohnson/litestream/file.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient","dst":"can://go/litestream/gs/replica_client.go/github.com/benbjohnson/litestream/gs.ReplicaClient/github.com/benbjohnson/litestream/gs.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewNATSReplicaClient","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewOSSReplicaClient","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewR2ReplicaClient","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewABSReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewB2ReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewFileReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewGSReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewNATSReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewOSSReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewR2ReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewS3ReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewSFTPReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewTigrisReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewReplicaClient","dst":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewWebDAVReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewS3ReplicaClient","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewSFTPReplicaClient","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewTigrisReplicaClient","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/internal/testingutil/testingutil.go/github.com/benbjohnson/litestream/internal/testingutil.NewWebDAVReplicaClient","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.Checksum","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert","prov":["go/types"],"weight":1},{"src":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClientFromURL","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteAll","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteAll","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.connect","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.initObjectStore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.LTXFiles","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.parseLTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.isNotFoundError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.connect","dst":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.options","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.ReplicaClient/github.com/benbjohnson/litestream/nats.ReplicaClient.ltxPath","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/nats/replica_client.go/github.com/benbjohnson/litestream/nats.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClientFromURL","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.NewReplicaClientFromURL","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseURL","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ParseHost","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteAll","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteAll","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.deleteResultError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.deleteResultError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.LTXFiles","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.LTXFiles","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.newFileIterator","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.Next","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.fetchMetadataBatch","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.Next","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.initPaginator","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.fileIterator/github.com/benbjohnson/litestream/oss.fileIterator.initPaginator","dst":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.ReplicaClient/github.com/benbjohnson/litestream/oss.ReplicaClient.ltxPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/oss/replica_client.go/github.com/benbjohnson/litestream/oss.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreCandidateBetter","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.refresh","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplicaWithClient","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ReadTXIDFile","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.TXIDPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CalcRestoreTarget","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBounds","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CalcRestoreTarget","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.CreatedAt","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.Type","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.MkdirAll","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/internal/resumable_reader.go/github.com/benbjohnson/litestream/internal.NewResumableReader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.CalcRestorePlan","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.ReadTXIDFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Logger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkIntegrity","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Restore","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.DirInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.DB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.checkIntegrity","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.filterWALSegmentsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.findBestSnapshotV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.RestoreV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Start","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Stop","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Sync","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBounds","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.WALSegmentsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.ValidateLevel","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.appendWALSegmentV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.OpenWALSegmentV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile","dst":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.LockFileExclusive","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile","dst":"can://go/litestream/internal/lock_unix.go/github.com/benbjohnson/litestream/internal.UnlockFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyWALSegmentsV3","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.appendWALSegmentV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.calcPos","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.MaxLTXFileInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.downloadSnapshotV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.OpenSnapshotV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.fillFollowGap","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FindLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.findBestSnapshotV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.sortSnapshotsV3ByCreatedAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.GenerationsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ReplicaClientV3/github.com/benbjohnson/litestream.ReplicaClientV3.SnapshotsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.applyNewLTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.follow","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Notify","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.ResetLocalState","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXError/github.com/benbjohnson/litestream.LTXError.IsAutoRecoverable","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.monitor","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBounds","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.TimeBoundsV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestLTXSnapshotForTimestamp","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.shouldUseV3Restore","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.findBestV3SnapshotForTimestamp","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.sync","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.RecordSuccessfulSync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.SetPos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.calcPos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.lockSync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.syncOnce","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LTXPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.NewLTXError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.uploadLTXFile","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.WriteLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile","dst":"can://go/litestream/internal/internal_unix.go/github.com/benbjohnson/litestream/internal.FsyncDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.WriteTXIDFile","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.TXIDPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.refresh","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreCandidateBetter","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.refresh","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.restoreLevelCursor/github.com/benbjohnson/litestream.restoreLevelCursor.ensureCurrent","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FetchLTXHeader","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FetchPageIndex","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.fetchPageIndexData","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.FindLTXFiles","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.LTXFiles","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.fetchPageIndexData","dst":"can://go/litestream/replica_client.go/github.com/benbjohnson/litestream.ReplicaClient/github.com/benbjohnson/litestream.ReplicaClient.OpenLTXFile","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsLocalEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsBackblazeEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsCloudflareR2Endpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsDigitalOceanEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsFilebaseEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsGoogleCloudStorageEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsHetznerEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsLocalEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsMinIOEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsScalewayEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsSupabaseEndpoint","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURLWithQuery","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ReplicaTypeFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ReplicaTypeFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.ParseReplicaURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.CleanReplicaURLPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseS3AccessPointURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.splitS3AccessPointARN","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.AcquireLease","dst":"can://go/litestream/leaser.go/github.com/benbjohnson/litestream.Lease/github.com/benbjohnson/litestream.Lease.IsExpired","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.AcquireLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.AcquireLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.DeleteObject","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isNotFoundError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isPreconditionFailed","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.ReleaseLease","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.RenewLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.GetObject","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isNotFoundError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.readLease","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.lockKey","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.S3API/github.com/benbjohnson/litestream/s3.S3API.PutObject","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.Leaser/github.com/benbjohnson/litestream/s3.Leaser.writeLease","dst":"can://go/litestream/s3/leaser.go/github.com/benbjohnson/litestream/s3.isPreconditionFailed","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.BoolQueryValue","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.EnsureEndpointScheme","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IntQueryValue","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsBackblazeEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsCloudflareR2Endpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsDigitalOceanEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsFilebaseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsHetznerEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsMinIOEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsScalewayEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsSupabaseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.IsTigrisEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegionFromS3ARN","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.NewReplicaClientFromURL","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseURL","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ParseHost","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteAll","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteAll","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.deleteOutputError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.deleteOutputError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.GenerationsV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.GenerationsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.GenerationsPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.GenerationsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.IsGenerationIDV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.findBucketRegion","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.validateSSEConfig","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.parseS3DebugEnv","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.LTXFiles","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.LTXFiles","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.newFileIterator","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenSnapshotV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewLZ4Reader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.isNotExists","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.OpenWALSegmentV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseSnapshotFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.SnapshotsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotsPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.ParseWALSegmentFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WALSegmentsV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.Init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.findBucketRegion","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.configureEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadSizedLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadLTX","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadSizedLTX","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.NewReadCounter","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX","dst":"can://go/litestream/internal/internal.go/github.com/benbjohnson/litestream/internal.ReadCounter/github.com/benbjohnson/litestream/internal.ReadCounter.N","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.uploadStreamedLTX","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.ReplicaClient/github.com/benbjohnson/litestream/s3.ReplicaClient.putObjectInput","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.computeDeleteObjectsContentMD5","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.marshalDeleteObjects","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeDeleteDocument","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifierList","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifierList","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeObjectIdentifier","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.Next","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.fileIterator/github.com/benbjohnson/litestream/s3.fileIterator.fetchMetadataBatch","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.marshalDeleteObjects","dst":"can://go/litestream/s3/replica_client.go/github.com/benbjohnson/litestream/s3.encodeDeleteDocument","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleInfo","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleList","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleList","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleList","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleList","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleList","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.NewDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.NewReplica","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.NewReplicaClientFromURL","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleRegister","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnableDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStart","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleStop","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DisableDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSync","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSync","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSync","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSync","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncDiagnostic","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleSyncStatus","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleTXID","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleTXID","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.expandPath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleTXID","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleTXID","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleUnregister","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleUnregister","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleUnregister","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleUnregister","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.handleUnregister","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/server.go/github.com/benbjohnson/litestream.Server/github.com/benbjohnson/litestream.Server.storeTXID","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClientFromURL","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Cleanup","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteAll","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.Init","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.LTXFiles","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.ReplicaClient/github.com/benbjohnson/litestream/sftp.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/sftp/replica_client.go/github.com/benbjohnson/litestream/sftp.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.NewStore","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.NewStore","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSONError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Close","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/github.com/benbjohnson/litestream.CompactionLevel.PrevCompactionAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.PrevLevel","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Compact","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTXFileInfo","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.PageSize","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Snapshot","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DisableDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DisableDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DisableDB","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnableDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnableDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Open","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnableDB","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceRetentionByTXID","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceSnapshotRetention","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevels/github.com/benbjohnson/litestream.CompactionLevels.Validate","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Open","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SetLogger","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.parseEndpoint","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","dst":"can://go/litestream/server.go/github.com/benbjohnson/litestream.writeJSON","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.RegisterDB","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.MaxLTX","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Sync","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.SyncAndWait","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.Pos","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.SyncDB","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.FindDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Close","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.UnregisterDB","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Validate","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Validate","dst":"can://go/litestream/replica.go/github.com/benbjohnson/litestream.Replica/github.com/benbjohnson/litestream.Replica.ValidateLevel","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.allDatabasesHealthy","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.allDatabasesHealthy","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.LastSuccessfulSyncAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.allDatabasesHealthy","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/compaction_level.go/github.com/benbjohnson/litestream.CompactionLevel/github.com/benbjohnson/litestream.CompactionLevel.NextCompactionAt","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.IsOpen","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.CompactDB","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorCompactionLevel","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.EnforceSnapshotRetention","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorHeartbeats","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorL0Retention","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.EnforceL0RetentionByTime","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorL0Retention","dst":"can://go/litestream/db.go/github.com/benbjohnson/litestream.DB/github.com/benbjohnson/litestream.DB.Path","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorL0Retention","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorL0Retention","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.DBs","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorValidation","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.isShutdownError","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.monitorValidation","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.Validate","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","dst":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.Ping","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","dst":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.RecordPing","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","dst":"can://go/litestream/heartbeat.go/github.com/benbjohnson/litestream.HeartbeatClient/github.com/benbjohnson/litestream.HeartbeatClient.ShouldPing","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.sendHeartbeatIfNeeded","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.allDatabasesHealthy","prov":["go/types"],"weight":1},{"src":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.hasHeartbeatConfigLocked","prov":["go/types"],"weight":1},{"src":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/github.com/benbjohnson/litestream.PosV3.String","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.PosV3/github.com/benbjohnson/litestream.PosV3.IsZero","prov":["go/types"],"weight":1},{"src":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotPathV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatSnapshotFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotPathV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.SnapshotsPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentPathV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.FormatWALSegmentFilenameV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALSegmentPathV3","dst":"can://go/litestream/v3.go/github.com/benbjohnson/litestream.WALPathV3","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReader","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readHeader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReaderWithOffset","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readFrame","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.NewWALReaderWithOffset","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readHeader","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.assert","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.PageMap","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.ReadFrame","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readFrame","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap","dst":"can://go/litestream/store.go/github.com/benbjohnson/litestream.Store/github.com/benbjohnson/litestream.Store.startHeartbeatMonitorIfNeeded","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.Offset","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.pageMap","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.ReadFrame","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readFrame","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum","prov":["go/types"],"weight":1},{"src":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALReader/github.com/benbjohnson/litestream.WALReader.readHeader","dst":"can://go/litestream/wal_reader.go/github.com/benbjohnson/litestream.WALChecksum","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClientFromURL","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.NewReplicaClient","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteAll","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.DeleteLTXFiles","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.Init","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXLevelDir","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.LTXFiles","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/internal/limit_read_closer.go/github.com/benbjohnson/litestream/internal.LimitReadCloser","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.OpenLTXFile","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/litestream.go/github.com/benbjohnson/litestream.LTXFilePath","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.WriteLTXFile","dst":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.ReplicaClient/github.com/benbjohnson/litestream/webdav.ReplicaClient.init","prov":["go/types"],"weight":1},{"src":"can://go/litestream/webdav/replica_client.go/github.com/benbjohnson/litestream/webdav.init","dst":"can://go/litestream/replica_url.go/github.com/benbjohnson/litestream.RegisterReplicaClientFactory","prov":["go/types"],"weight":1}]}} \ No newline at end of file diff --git a/tests/resources/go/analysis_json/v2/README.md b/tests/resources/go/analysis_json/v2/README.md new file mode 100644 index 00000000..1ca31787 --- /dev/null +++ b/tests/resources/go/analysis_json/v2/README.md @@ -0,0 +1,30 @@ +The Go analysis fixture is a single schema-v2 output committed one directory up, at +`tests/resources/go/analysis.json`, generated by **codeanalyzer-go** (`cango`) on the application in +`tests/resources/go/litestream.zip` (the [litestream](https://github.com/benbjohnson/litestream) Go +project). It is the real `can://go/litestream` corpus: 55 modules, 959 call-graph edges, +`max_level: 2`. + + # unzip litestream.zip -> litestream/ (carries go.mod at its root) + cango -i litestream --app-name litestream -a 2 -o --cache-dir --skip-tests + +That is the command `GoCodeanalyzer._argv` builds (`cldk/analysis/go/codeanalyzer/codeanalyzer.py`), +with `--app-name` defaulting to the input directory's basename (`litestream`). `-a` is capped at +**2**: cango has no L3/L4, so this corpus is a single level-2 file — there is no `a1`/`a2`/`a3`/`a4` +per-level split, unlike the TypeScript and Java resource trees, because cango's L1 output is a strict +subset of its L2 output and nothing in the suite needs the shallower generation. + +**No version pin yet.** codeanalyzer-go has not cut a release, so there is no wheel to pin and no +`analyzer.version` to assert against (the committed JSON stamps `"version": "0.1.0"`, a dev build). +The binary is resolved at run time from `$CODEANALYZER_GO_BIN` (a locally built `cango`) or, once it +ships, the `codeanalyzer-go` PyPI wheel. `tests/analysis/go/test_go_e2e.py` unzips `litestream.zip` +and runs whichever binary it finds against it, skipping cleanly when neither is present. When the +release lands and the pin is added, regenerate `analysis.json` with the pinned binary and record its +version here. + +**Schema v2, no `--analysis-schema` flag.** Earlier generations passed `--analysis-schema 2` to stop +cango emitting legacy v1; that flag was removed (`_argv` no longer sends it — the binary defaults to +v2). The committed JSON carries `"schema_version": "2.0.0"`; if a regeneration emits v1, the models +(an `extra="forbid"` mirror of canonical schema v2) refuse it rather than parse it. + +**The application is committed as `tests/resources/go/litestream.zip`** so a regeneration runs on +exactly the source `analysis.json` describes. Regenerate the JSON whenever the zip changes. diff --git a/tests/resources/go/litestream.zip b/tests/resources/go/litestream.zip new file mode 100644 index 00000000..0089680e Binary files /dev/null and b/tests/resources/go/litestream.zip differ diff --git a/uv.lock b/uv.lock index 51f54cfc..e0e728a9 100644 --- a/uv.lock +++ b/uv.lock @@ -303,6 +303,7 @@ name = "cldk" version = "2.0.0" source = { editable = "." } dependencies = [ + { name = "codeanalyzer-go" }, { name = "codeanalyzer-python" }, { name = "codeanalyzer-typescript" }, { name = "networkx" }, @@ -350,6 +351,7 @@ test = [ requires-dist = [ { name = "cldk", extras = ["java"], marker = "extra == 'all'" }, { name = "cldk", extras = ["neo4j"], marker = "extra == 'all'" }, + { name = "codeanalyzer-go", specifier = "==0.1.0rc1" }, { name = "codeanalyzer-java", marker = "extra == 'java'", specifier = "==3.3.3" }, { name = "codeanalyzer-python", specifier = "==1.5.3" }, { name = "codeanalyzer-typescript", specifier = "==1.6.0" }, @@ -396,6 +398,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, ] +[[package]] +name = "codeanalyzer-go" +version = "0.1.0rc1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/65/0af9fd9e48ffab66493d2c3d62f5d00109cf3212d8a64071bf2d1481f551/codeanalyzer_go-0.1.0rc1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7ab13b1cf86814e4f3035966f714b05a020992e71f199ad798ff5e87c2273b34", size = 4145429, upload-time = "2026-10-11T22:44:23.35Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5f/065bc30b54e4b2f4b57b9a5c06563018ee38ccb60dda3dde3378dc700557/codeanalyzer_go-0.1.0rc1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4a03c3474b62f06bfa4dac0fd059ccb1738dec24550fdd148729fd9637ad8eb", size = 3797856, upload-time = "2026-10-11T22:44:25.54Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a4/576ff56a8c37ff69d783142b574db07a34da74a32fa17fd6ab14d8e72a01/codeanalyzer_go-0.1.0rc1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:afaf08ba252f098ed9a80f7e6f3dfa06bb4da911c57e5ebeccae99fd708857ec", size = 3666158, upload-time = "2026-10-11T22:44:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/f9/77/c8f970bc71aa7fa74b1531c4819fe7e3ff902793dfb3d7593c175c15a13f/codeanalyzer_go-0.1.0rc1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:680d97d2aa18c320dbf530b166d9a377a79a43fc250ee1490926a8b2d7a85de6", size = 4090421, upload-time = "2026-10-11T22:44:29.665Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/7f5d00ff849f520f1956719ec03f8d809d05e4c8dffd9b8442fb09bc893f/codeanalyzer_go-0.1.0rc1-py3-none-win_amd64.whl", hash = "sha256:1150232c252f1eb179b1fb1dee1d08f9bb44d92423b169b0e4420e3c0b10828c", size = 4201789, upload-time = "2026-10-11T22:44:31.688Z" }, +] + [[package]] name = "codeanalyzer-java" version = "3.3.3"