shareddata 6.85.3__tar.gz → 6.85.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shareddata-6.85.3/src/shareddata.egg-info → shareddata-6.85.4}/PKG-INFO +1 -1
- {shareddata-6.85.3 → shareddata-6.85.4}/setup.py +1 -1
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Table.py +11 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableDisk.py +193 -52
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableRewrite.py +26 -0
- {shareddata-6.85.3 → shareddata-6.85.4/src/shareddata.egg-info}/PKG-INFO +1 -1
- {shareddata-6.85.3 → shareddata-6.85.4}/LICENSE +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/MANIFEST.in +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/README.md +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/pyproject.toml +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/setup.cfg +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/ServerGunicorn.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/ServerWaitress.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/apidocs-dark.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/apidocs.yml +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/auth.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/constants.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/download_paths.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/download_slots.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/install/worker.ps1 +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/install/worker.sh +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/listcache.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/cache.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/collections.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/data.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/downloads.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/metadata.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/operations.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/schedules.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/sqlite.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/system.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/tables.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/timeseries.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/web_routes.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/routes/workers.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/API/utils.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Auth/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Auth/bootstrap.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Auth/migrate.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Auth/store.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Auth/workers.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/CacheRedis.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/CollectionMongoDB.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/catalog.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/config.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/errors.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/indexes.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/readers.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/resolve.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/DataAPI/service.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Database.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Defaults.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/AWSEC2.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/AWSS3.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/AutoDocstrings.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/ClientAPI.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/ClientSocket.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/ClientWebSocket.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/LogHandlerAPI.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/LogReader.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/MongoDBClient.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/SaveTables.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/ServerSocket.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/ServerWebSocket.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/StreamsCache.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/StreamsPersist.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/SyncTable.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/TunnelWebSocket.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/IO/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Listing.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/LockWaiters.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Logger.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/MCP/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/MCP/__main__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/MCP/client.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/MCP/server.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Metadata.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/MultiProc.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/OpenFIGI.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Redaction.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/BatchJob.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/ContainerHealth.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/JobStore.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/RoutineCommand.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/RoutineOrigin.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/Schedule.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/ScheduleMirror.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/ScheduleStore.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/Scheduler.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/Worker.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/WorkerLib.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/WorkerPool.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Routines/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/SQLite.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/SharedData.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/SharedNumpy.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/StreamKafka.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Symbol.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableIndex.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableIndexJitFunctions.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableIndexJitGenerate.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableIndexJitHash.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableIndexJitLoc.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableProtocol.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TableRead.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TimeSeriesDisk.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/TimeseriesContainer.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Users.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Utils.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/activity_logger.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/app.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/google_logo.png +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/kafka_keys.js +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/logs.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/sched.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/styles.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/auth.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/backends.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/config.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/containers.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/identity.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/kafka_view.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/mongo_view.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/proc.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/redis_view.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/route_health.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/route_registry.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/sched.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/schedule_editor.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/server.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/services.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/shell.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/standalone.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/account.js +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/custom.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/favicon.ico +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/favicon.png +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/logo.png +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/logo_96x96.png +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/shell.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/shell.js +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/standalone.css +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/static/standalone.js +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/suggest.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/account.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/base.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/claim.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/claim_done.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/dash.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/index.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/login.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/login_2fa.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/partials/navbar.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/partials/scripts.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/recovery_codes.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/shell.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/templates/standalone.html +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/twofactor.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/utils.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/workerpool.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/__init__.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/build_info.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/fleet.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/preflight.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/private_files.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/release_backup.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/server.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/services.py +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/sharedmutexwin.pyd +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/shareddata.egg-info/SOURCES.txt +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/shareddata.egg-info/dependency_links.txt +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/shareddata.egg-info/entry_points.txt +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/shareddata.egg-info/requires.txt +0 -0
- {shareddata-6.85.3 → shareddata-6.85.4}/src/shareddata.egg-info/top_level.txt +0 -0
|
@@ -1229,6 +1229,17 @@ class Table:
|
|
|
1229
1229
|
io_obj.close()
|
|
1230
1230
|
self.copy_file()
|
|
1231
1231
|
|
|
1232
|
+
def _parse_tail_header(self, tail_io):
|
|
1233
|
+
"""The tail header record at the start of a tail stream (its first row starts right after it).
|
|
1234
|
+
Unlike read_tailheader, nothing of this table is changed."""
|
|
1235
|
+
tail_io.seek(0)
|
|
1236
|
+
size = int.from_bytes(tail_io.read(8), byteorder='little')
|
|
1237
|
+
description = tail_io.read(size).decode(encoding='UTF-8', errors='ignore').replace('\x00', '')
|
|
1238
|
+
names, formats = description.split(';')[0].split(','), description.split(';')[1].split(',')
|
|
1239
|
+
dtype = np.dtype({'names': names, 'formats': formats})
|
|
1240
|
+
tail_io.seek(0)
|
|
1241
|
+
return np.ndarray((1,), dtype=dtype, buffer=tail_io.read(dtype.itemsize))[0].copy()
|
|
1242
|
+
|
|
1232
1243
|
def read_tailheader(self, tail_io):
|
|
1233
1244
|
"""
|
|
1234
1245
|
Reads and parses the tail header from a binary stream.
|
|
@@ -23,7 +23,51 @@ from SharedData.Table import Table
|
|
|
23
23
|
from SharedData.LockWaiters import TableBusy
|
|
24
24
|
|
|
25
25
|
from SharedData.Utils import cpp
|
|
26
|
-
from SharedData.TableProtocol import structural
|
|
26
|
+
from SharedData.TableProtocol import structural, structure
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
FICLONE = 0x40049409 # linux/fs.h: make the file a reflink clone of another
|
|
30
|
+
CHUNK = 64 << 20
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _clone_prefix(source, stage, prefix):
|
|
34
|
+
"""Create *stage* as a copy of the first *prefix* bytes of *source*, which is only read.
|
|
35
|
+
|
|
36
|
+
On a filesystem with reflinks (XFS, btrfs) the whole file is cloned: no data is read or
|
|
37
|
+
written and no space is used until one of them changes. Anywhere else just the prefix (the
|
|
38
|
+
header and the head) is copied by the kernel. Returns 'reflink' or 'copy'.
|
|
39
|
+
"""
|
|
40
|
+
import errno
|
|
41
|
+
import fcntl
|
|
42
|
+
from SharedData.TableRewrite import RewriteError
|
|
43
|
+
unsupported = (errno.EOPNOTSUPP, errno.ENOTTY, errno.EXDEV, errno.EINVAL, errno.ENOSYS)
|
|
44
|
+
src, dst = os.open(source, os.O_RDONLY), None
|
|
45
|
+
try:
|
|
46
|
+
dst = os.open(stage, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o600)
|
|
47
|
+
try:
|
|
48
|
+
fcntl.ioctl(dst, FICLONE, src)
|
|
49
|
+
return 'reflink'
|
|
50
|
+
except OSError as error:
|
|
51
|
+
if error.errno not in unsupported:
|
|
52
|
+
raise
|
|
53
|
+
copied = 0
|
|
54
|
+
while copied < prefix:
|
|
55
|
+
want = min(1 << 30, prefix - copied)
|
|
56
|
+
try:
|
|
57
|
+
done = os.copy_file_range(src, dst, want, copied, copied)
|
|
58
|
+
except OSError as error:
|
|
59
|
+
if error.errno not in unsupported:
|
|
60
|
+
raise
|
|
61
|
+
chunk = os.pread(src, min(want, CHUNK), copied)
|
|
62
|
+
done = os.pwrite(dst, chunk, copied) if chunk else 0
|
|
63
|
+
if done <= 0:
|
|
64
|
+
raise RewriteError('the live image ended before its head was copied')
|
|
65
|
+
copied += done
|
|
66
|
+
return 'copy'
|
|
67
|
+
finally:
|
|
68
|
+
os.close(src)
|
|
69
|
+
if dst is not None:
|
|
70
|
+
os.close(dst)
|
|
27
71
|
|
|
28
72
|
|
|
29
73
|
class TableDisk(Table):
|
|
@@ -260,7 +304,6 @@ class TableDisk(Table):
|
|
|
260
304
|
return publish_rewrite(self, None, 'trim', capacity=max(2, int(self.hdr['count'])))
|
|
261
305
|
|
|
262
306
|
############### RELOAD ###############
|
|
263
|
-
@structural
|
|
264
307
|
def reload(self):
|
|
265
308
|
"""
|
|
266
309
|
Replace the local image with the remote copy when that is newer.
|
|
@@ -269,14 +312,28 @@ class TableDisk(Table):
|
|
|
269
312
|
rows not uploaded yet are never discarded. When only the remote tail is newer (the usual
|
|
270
313
|
case: the head holds the years before this one and the tail this year's rows) only the tail
|
|
271
314
|
is fetched and applied, see _reload_tail. When the remote head is newer, the whole remote
|
|
272
|
-
image is
|
|
273
|
-
the caller's STRUCT section: a failure before the replace leaves the old image untouched, and the epoch
|
|
274
|
-
published on leaving the section makes other processes reopen the new file. The old index
|
|
275
|
-
files describe the old rows, so they are removed and rebuilt.
|
|
315
|
+
image is fetched, see _reload_image.
|
|
276
316
|
|
|
277
317
|
Returns:
|
|
278
318
|
The records of the reloaded table.
|
|
279
319
|
"""
|
|
320
|
+
change = self._remote_change()
|
|
321
|
+
if change is None:
|
|
322
|
+
return self.records
|
|
323
|
+
if change == 'tail' and not self.is_schemaless:
|
|
324
|
+
done = self._reload_tail()
|
|
325
|
+
if done is not None:
|
|
326
|
+
return done
|
|
327
|
+
return self._reload_image()
|
|
328
|
+
|
|
329
|
+
@structural
|
|
330
|
+
def _reload_image(self):
|
|
331
|
+
"""
|
|
332
|
+
The whole remote image is built beside data.bin under a stage name and installed by one
|
|
333
|
+
os.replace, inside a STRUCT section: a failure before the replace leaves the old image
|
|
334
|
+
untouched, and the epoch published on leaving the section makes other processes reopen the
|
|
335
|
+
new file. The old index files describe the old rows, so they are removed and rebuilt.
|
|
336
|
+
"""
|
|
280
337
|
import stat
|
|
281
338
|
import uuid
|
|
282
339
|
from SharedData.TableRewrite import fsync_directory, read_layout, require_rewrite_support, sync_file
|
|
@@ -339,62 +396,146 @@ class TableDisk(Table):
|
|
|
339
396
|
raise Exception(errmsg)
|
|
340
397
|
return self.records
|
|
341
398
|
|
|
399
|
+
def _attach_replacement(self, final, rebuild_files):
|
|
400
|
+
"""After os.replace: make the directory entry durable, then map the new image and rebuild its
|
|
401
|
+
indexes. Whatever fails, this object ends mapped to the image that is now installed (flagged
|
|
402
|
+
for index repair by the replacement's own header), never to two images."""
|
|
403
|
+
from SharedData.TableRewrite import fsync_directory
|
|
404
|
+
try:
|
|
405
|
+
fsync_directory(final.parent) # the new name is durable before any old index file is touched
|
|
406
|
+
self.free(acquire=False) # the old mappings and index arrays, flushed first
|
|
407
|
+
if rebuild_files:
|
|
408
|
+
for name in ('dateidx.bin', 'pkey.bin', 'symbolidx.bin', 'portidx.bin'):
|
|
409
|
+
try:
|
|
410
|
+
(final.parent / name).unlink()
|
|
411
|
+
except FileNotFoundError:
|
|
412
|
+
pass
|
|
413
|
+
self.malloc()
|
|
414
|
+
if self.hasindex:
|
|
415
|
+
self.hdr['isidxcreated'] = 0
|
|
416
|
+
self.index.initialize()
|
|
417
|
+
self.shareddata.data[self.relpath] = self
|
|
418
|
+
self.hdr['isloaded'] = 1
|
|
419
|
+
self.mutex['isloaded'] = 1
|
|
420
|
+
except BaseException as error:
|
|
421
|
+
self._mapping_epoch = -1 # the next owner re-attaches by path
|
|
422
|
+
try:
|
|
423
|
+
self.attach()
|
|
424
|
+
except Exception:
|
|
425
|
+
pass
|
|
426
|
+
if isinstance(error, Exception) and not isinstance(error, OSError):
|
|
427
|
+
Logger.log.error(f"TableDisk.reload() {self.relpath}: {error}")
|
|
428
|
+
raise
|
|
429
|
+
return self.records
|
|
430
|
+
|
|
342
431
|
def _reload_tail(self):
|
|
343
432
|
"""
|
|
344
|
-
Apply the remote tail alone:
|
|
345
|
-
split is for)
|
|
433
|
+
Apply the remote tail alone: only the tail object is downloaded (that is what the head/tail
|
|
434
|
+
split is for) and no live file is written (see TableRewrite).
|
|
346
435
|
|
|
347
|
-
|
|
348
|
-
the
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
436
|
+
Under the writer lock only (readers go on reading the live image meanwhile) a stage is built
|
|
437
|
+
as a clone of the live image, see _clone_prefix: the head, which did not change, is shared or
|
|
438
|
+
copied by the kernel. Its unused capacity is allocated again, since a clone shares only what
|
|
439
|
+
was written. The tail is streamed into the stage with a bounded buffer, its length and checksum
|
|
440
|
+
checked against its header, then the new header (indexes marked not created) is written, the
|
|
441
|
+
stage synced. A failure up to here leaves the live image and header untouched and the table
|
|
442
|
+
not flagged: nothing was changed. Then, in a STRUCT section, one os.replace installs the stage,
|
|
443
|
+
the directory is synced before any index file is touched, the indexes are rebuilt and the epoch
|
|
444
|
+
published. A process that dies at any point leaves the complete old image or the complete new
|
|
445
|
+
one, and nothing is needed from S3 to recover. A remote head that changed meanwhile means the
|
|
446
|
+
tail belongs to another head: the whole image is replaced instead (returns None).
|
|
353
447
|
"""
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
448
|
+
import uuid
|
|
449
|
+
from SharedData.TableRewrite import (RewriteError, copy_metadata, pwrite_all, require_rewrite_support,
|
|
450
|
+
same_digest, sync_header)
|
|
451
|
+
import hashlib
|
|
452
|
+
require_rewrite_support()
|
|
453
|
+
acquired = not self._owns_lock()
|
|
454
|
+
if acquired:
|
|
455
|
+
self.acquire()
|
|
456
|
+
stage = None
|
|
361
457
|
try:
|
|
458
|
+
self._sync_held_mapping()
|
|
459
|
+
tail_gz, _, remote_mtime = self._download_remote_tail()
|
|
460
|
+
if tail_gz is None: # nothing newer after all
|
|
461
|
+
return self.records
|
|
462
|
+
# The tail belongs to the head it was uploaded with, and the uploader sends the head first:
|
|
463
|
+
# a head that changed since the check in reload() means this tail is not for the head held here.
|
|
464
|
+
if self._remote_change() == 'head':
|
|
465
|
+
return None
|
|
466
|
+
final = self.filepath
|
|
467
|
+
header = self.hdr.copy() # detached: this object's header is not touched
|
|
362
468
|
tail_gz.seek(0) # S3Download leaves the buffer at its end
|
|
363
469
|
tail_io = gzip.GzipFile(fileobj=tail_gz, mode='rb')
|
|
364
|
-
self.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
470
|
+
tailhdr = self._parse_tail_header(tail_io)
|
|
471
|
+
itemsize, tailsize = int(header['itemsize']), int(tailhdr['tailsize'])
|
|
472
|
+
header['md5hashtail'] = tailhdr['md5hash']
|
|
473
|
+
header['tailsize'] = tailsize
|
|
474
|
+
header['count'] = int(header['headsize']) + tailsize
|
|
475
|
+
header['mtimetail'] = remote_mtime
|
|
476
|
+
if int(header['count']) > int(header['recordssize']):
|
|
477
|
+
header['recordssize'] = header['count']
|
|
478
|
+
capacity = int(header['recordssize'])
|
|
479
|
+
hdrsize = header.dtype.itemsize
|
|
480
|
+
tail_at = hdrsize + int(header['headsize']) * itemsize
|
|
481
|
+
total = hdrsize + capacity * itemsize
|
|
482
|
+
header['isidxcreated'] = 0 # durable in the stage: no client may trust the old indexes
|
|
483
|
+
header['isloaded'] = 0
|
|
484
|
+
header['isidxsorted'] = 1 # the uploader sorts before it writes (SharedNumpy.write)
|
|
485
|
+
pending = int(header['minchgid']) # a head row edited here and not uploaded yet stays marked for upload;
|
|
486
|
+
header['minchgid'] = pending if pending <= int(header['headsize']) else int(header['count']) + 1 # otherwise as a write leaves it
|
|
487
|
+
for leftover in final.parent.glob('data.bin.reload.*'): # the lock is held: a stage left by a dead reload
|
|
488
|
+
leftover.unlink()
|
|
489
|
+
stage = final.parent / ('data.bin.reload.' + uuid.uuid4().hex)
|
|
490
|
+
self.shf_data.flush()
|
|
491
|
+
sync_header(self) # the old image is complete on disk before it is cloned
|
|
492
|
+
_clone_prefix(final, stage, tail_at)
|
|
493
|
+
fd = os.open(stage, os.O_RDWR)
|
|
494
|
+
try:
|
|
495
|
+
copy_metadata(final, fd)
|
|
496
|
+
os.posix_fallocate(fd, 0, total) # a clone shares written extents only: the capacity is reserved again
|
|
497
|
+
tail_io.seek(tailhdr.dtype.itemsize)
|
|
498
|
+
digest, written = hashlib.md5(), 0
|
|
499
|
+
while True:
|
|
500
|
+
chunk = tail_io.read(CHUNK)
|
|
501
|
+
if not chunk:
|
|
502
|
+
break
|
|
503
|
+
if written + len(chunk) > tailsize * itemsize:
|
|
504
|
+
raise RewriteError('the remote tail of %s is longer than its header says' % self.relpath)
|
|
505
|
+
digest.update(chunk)
|
|
506
|
+
pwrite_all(fd, chunk, tail_at + written)
|
|
507
|
+
written += len(chunk)
|
|
508
|
+
if written != tailsize * itemsize:
|
|
509
|
+
raise RewriteError('the remote tail of %s holds %d bytes, its header says %d' % (self.relpath, written, tailsize * itemsize))
|
|
510
|
+
if not same_digest(digest.digest(), tailhdr['md5hash']):
|
|
511
|
+
raise RewriteError('the remote tail of %s does not match its checksum' % self.relpath)
|
|
512
|
+
pwrite_all(fd, header.tobytes(), 0)
|
|
513
|
+
os.fsync(fd)
|
|
514
|
+
finally:
|
|
515
|
+
os.close(fd)
|
|
516
|
+
refused = None
|
|
517
|
+
with structure(self): # only the swap and the index rebuild block readers
|
|
518
|
+
try:
|
|
519
|
+
os.replace(stage, final)
|
|
520
|
+
except OSError as error: # nothing changed: leave STRUCT without flagging the live header
|
|
521
|
+
refused = error
|
|
522
|
+
else:
|
|
523
|
+
stage = None
|
|
524
|
+
self.size = capacity
|
|
525
|
+
self._attach_replacement(final, rebuild_files=True)
|
|
526
|
+
if refused is not None:
|
|
527
|
+
raise refused
|
|
528
|
+
return self.records
|
|
376
529
|
except BaseException:
|
|
377
|
-
|
|
530
|
+
if stage is not None:
|
|
531
|
+
try:
|
|
532
|
+
stage.unlink()
|
|
533
|
+
except FileNotFoundError:
|
|
534
|
+
pass
|
|
378
535
|
raise
|
|
379
536
|
finally:
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
self.shf_hdr.flush()
|
|
383
|
-
sync_file(self.filepath)
|
|
384
|
-
try:
|
|
385
|
-
self.free(acquire=False)
|
|
386
|
-
self.malloc()
|
|
387
|
-
if self.hasindex:
|
|
388
|
-
self.hdr['isidxcreated'] = 0
|
|
389
|
-
self.index.initialize()
|
|
390
|
-
self.shareddata.data[self.relpath] = self
|
|
391
|
-
self.hdr['isloaded'] = 1
|
|
392
|
-
self.mutex['isloaded'] = 1
|
|
393
|
-
except Exception as e:
|
|
394
|
-
errmsg = f"TableDisk.reload() {self.relpath}: {e}"
|
|
395
|
-
Logger.log.error(errmsg)
|
|
396
|
-
raise Exception(errmsg)
|
|
397
|
-
return self.records
|
|
537
|
+
if acquired:
|
|
538
|
+
self.release()
|
|
398
539
|
|
|
399
540
|
############### DELETE ###############
|
|
400
541
|
def delete_local(self):
|
|
@@ -61,6 +61,32 @@ def write_all(fd, data):
|
|
|
61
61
|
data = data[written:]
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def pwrite_all(fd, data, offset):
|
|
65
|
+
"""os.pwrite until every byte is written; a write that makes no progress is an error."""
|
|
66
|
+
view = memoryview(data).cast('B')
|
|
67
|
+
done = 0
|
|
68
|
+
while done < len(view):
|
|
69
|
+
written = os.pwrite(fd, view[done:], offset + done)
|
|
70
|
+
if written <= 0:
|
|
71
|
+
raise RewriteError('no progress writing the replacement image')
|
|
72
|
+
done += written
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def copy_metadata(source, fd):
|
|
76
|
+
"""Mode, owner, ACLs and extended attributes of *source* onto the open replacement *fd*
|
|
77
|
+
(fchmod is not filtered by the umask). A failure aborts the replacement, as in publish_rewrite."""
|
|
78
|
+
info = os.stat(source)
|
|
79
|
+
os.fchown(fd, info.st_uid, info.st_gid)
|
|
80
|
+
os.fchmod(fd, stat.S_IMODE(info.st_mode))
|
|
81
|
+
for attribute in os.listxattr(source):
|
|
82
|
+
os.setxattr(fd, attribute, os.getxattr(source, attribute))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def same_digest(digest, stored):
|
|
86
|
+
"""A header keeps an MD5 in a bytes field, which NumPy returns without its trailing NULs."""
|
|
87
|
+
return bytes(stored) == digest.rstrip(b'\0')
|
|
88
|
+
|
|
89
|
+
|
|
64
90
|
def read_layout(stream):
|
|
65
91
|
"""Strict, read-only self-described header parser, also used before open."""
|
|
66
92
|
stream.seek(0)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shareddata-6.85.3 → shareddata-6.85.4}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|