Coverage for python/lsst/dax/apdb/apdbReplica.py: 83%
58 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-09 03:35 -0700
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-09 03:35 -0700
1# This file is part of dax_apdb.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = ["ApdbReplica", "ApdbTableData", "ReplicaChunk"]
26import uuid
27from abc import ABC, abstractmethod
28from collections.abc import Collection, Iterable, Sequence
29from dataclasses import dataclass
30from typing import TYPE_CHECKING
32import astropy.time
33import felis.datamodel
35from lsst.resources import ResourcePathExpression
37from .apdb import ApdbConfig, ApdbTables
38from .factory import make_apdb_replica
40if TYPE_CHECKING:
41 from .versionTuple import VersionTuple
44class ApdbTableData(ABC):
45 """Abstract class for representing table data."""
47 @abstractmethod
48 def column_names(self) -> Sequence[str]:
49 """Return ordered sequence of column names in the table.
51 Returns
52 -------
53 names : `~collections.abc.Sequence` [`str`]
54 Column names.
55 """
56 raise NotImplementedError()
58 @abstractmethod
59 def column_defs(self) -> Sequence[tuple[str, felis.datamodel.DataType]]:
60 """Return ordered sequence of column names and their types.
62 Returns
63 -------
64 columns : `~collections.abc.Sequence` \
65 [`tuple`[`str`, `felis.datamodel.DataType`]]
66 Sequence of 2-tuples, each tuple consists of column name and its
67 type.
68 """
69 raise NotImplementedError()
71 @abstractmethod
72 def rows(self) -> Collection[tuple]:
73 """Return table rows, each row is a tuple of values.
75 Returns
76 -------
77 rows : `~collections.abc.Collection` [`tuple`]
78 Collection of tuples.
79 """
80 raise NotImplementedError()
83@dataclass(frozen=True)
84class ReplicaChunk:
85 """Class used for identification of replication chunks.
87 Instances of this class are used to identify the units of transfer from
88 APDB to PPDB. Usually single `ReplicaChunk` corresponds to multiple
89 consecutive calls to `Apdb.store` method.
91 Every ``store`` with the same ``id`` value will update ``unique_id`` with
92 some unique value so that it can be verified on PPDB side.
93 """
95 id: int
96 """A number identifying replication chunk (`int`)."""
98 last_update_time: astropy.time.Time
99 """Time of last insert for this chunk, usually corresponds to visit time
100 (`astropy.time.Time`).
101 """
103 unique_id: uuid.UUID
104 """Unique value updated on each new store (`uuid.UUID`)."""
106 @classmethod
107 def make_replica_chunk(
108 cls, last_update_time: astropy.time.Time, chunk_window_seconds: int
109 ) -> ReplicaChunk:
110 """Generate new unique insert identifier."""
111 seconds = int(last_update_time.unix_tai)
112 seconds = (seconds // chunk_window_seconds) * chunk_window_seconds
113 unique_id = uuid.uuid4()
114 return ReplicaChunk(id=seconds, last_update_time=last_update_time, unique_id=unique_id)
116 def __str__(self) -> str:
117 class_name = self.__class__.__name__
118 time_str = str(self.last_update_time.tai.isot)
119 return f"{class_name}(id={self.id:10d}, last_update_time={time_str}/tai, unique_id={self.unique_id})"
122class ApdbReplica(ABC):
123 """Abstract interface for APDB replication methods."""
125 @classmethod
126 def from_config(cls, config: ApdbConfig) -> ApdbReplica:
127 """Create ApdbReplica instance from configuration object.
129 Parameters
130 ----------
131 config : `ApdbConfig`
132 Configuration object, type of this object determines type of the
133 ApdbReplica implementation.
135 Returns
136 -------
137 replica : `ApdbReplica`
138 Instance of `ApdbReplica` class.
139 """
140 return make_apdb_replica(config)
142 @classmethod
143 def from_uri(cls, uri: ResourcePathExpression) -> ApdbReplica:
144 """Make ApdbReplica instance from a serialized configuration.
146 Parameters
147 ----------
148 uri : `~lsst.resources.ResourcePathExpression`
149 URI or local file path pointing to a file with serialized
150 configuration, or a string with a "label:" prefix. In the latter
151 case, the configuration will be looked up from an APDB index file
152 using the label name that follows the prefix. The APDB index file's
153 location is determined by the ``DAX_APDB_INDEX_URI`` environment
154 variable.
156 Returns
157 -------
158 replica : `ApdbReplica`
159 Instance of `ApdbReplica` class, the type of the returned instance
160 is determined by configuration.
161 """
162 config = ApdbConfig.from_uri(uri)
163 return make_apdb_replica(config)
165 @classmethod
166 @abstractmethod
167 def apdbReplicaImplementationVersion(cls) -> VersionTuple:
168 """Return version number for current ApdbReplica implementation.
170 Returns
171 -------
172 version : `VersionTuple`
173 Version of the code defined in implementation class.
174 """
175 raise NotImplementedError()
177 @abstractmethod
178 def schemaVersion(self) -> VersionTuple:
179 """Return version number of the database schema.
181 Returns
182 -------
183 version : `VersionTuple`
184 Version of the database schema.
185 """
186 raise NotImplementedError()
188 @abstractmethod
189 def getReplicaChunks(self) -> list[ReplicaChunk] | None:
190 """Return collection of replication chunks known to the database.
192 Returns
193 -------
194 chunks : `list` [`ReplicaChunk`] or `None`
195 List of chunks, they may be time-ordered if database supports
196 ordering. `None` is returned if database is not configured for
197 replication.
198 """
199 raise NotImplementedError()
201 @abstractmethod
202 def deleteReplicaChunks(self, chunks: Iterable[int]) -> None:
203 """Remove replication chunks from the database.
205 Parameters
206 ----------
207 chunks : `~collections.abc.Iterable` [`int`]
208 Chunk identifiers to remove.
210 Notes
211 -----
212 This method causes Apdb to forget about specified chunks. If there
213 are any auxiliary data associated with the identifiers, it is also
214 removed from database (but data in regular tables is not removed).
215 This method should be called after successful transfer of data from
216 APDB to PPDB to free space used by replicas.
217 """
218 raise NotImplementedError()
220 @abstractmethod
221 def getTableDataChunks(self, table: ApdbTables, chunks: Iterable[int]) -> ApdbTableData:
222 """Return catalog of new records for a table from given replica chunks.
224 Parameters
225 ----------
226 table : `ApdbTables`
227 Table for which to return the data. Acceptable tables are
228 `ApdbTables.DiaObject`, `ApdbTables.DiaSource`, and
229 `ApdbTables.DiaForcedSource`.
230 chunks : `~collections.abc.Iterable` [`int`]
231 Chunk identifiers to return.
233 Returns
234 -------
235 data : `ApdbTableData`
236 Catalog containing table records. In addition to all regular
237 columns it will contain ``apdb_replica_chunk`` column.
239 Notes
240 -----
241 This method returns new records that have been added to the table by
242 `Apdb.store()` method. Updates to the records that happen at later time
243 are available from `getTableUpdateChunks` method.
245 This part of API may not be very stable and can change before the
246 implementation finalizes.
247 """
248 raise NotImplementedError()
250 @abstractmethod
251 def getTableUpdateChunks(self, table: ApdbTables, chunks: Iterable[int]) -> ApdbTableData:
252 """Return the list of record updates for a table from given replica
253 chunks.
255 Parameters
256 ----------
257 table : `ApdbTables`
258 Table for which to return the updates. Acceptable tables are
259 `ApdbTables.DiaObject`, `ApdbTables.DiaSource`, and
260 `ApdbTables.DiaForcedSource`.
261 chunks : `~collections.abc.Iterable` [`int`]
262 Chunk identifiers to return.
264 Returns
265 -------
266 data : `ApdbTableData`
267 Catalog containing table updates.
269 Notes
270 -----
271 The returned catalog will include all primary key columns,
272 ``timestamp`` column, ``apdb_replica_chunk`` column, and
273 ``update_action`` column containing a string with JSON-serialized
274 description of the update.
276 This part of API may not be very stable and can change before the
277 implementation finalizes.
278 """
279 raise NotImplementedError()