shareddata 6.85.2__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.2/src/shareddata.egg-info → shareddata-6.85.4}/PKG-INFO +1 -1
- {shareddata-6.85.2 → shareddata-6.85.4}/setup.py +1 -1
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Table.py +24 -7
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableDisk.py +211 -8
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableRewrite.py +26 -0
- {shareddata-6.85.2 → shareddata-6.85.4/src/shareddata.egg-info}/PKG-INFO +1 -1
- {shareddata-6.85.2 → shareddata-6.85.4}/LICENSE +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/MANIFEST.in +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/README.md +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/pyproject.toml +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/setup.cfg +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/ServerGunicorn.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/ServerWaitress.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/apidocs-dark.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/apidocs.yml +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/auth.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/constants.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/download_paths.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/download_slots.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/install/worker.ps1 +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/install/worker.sh +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/listcache.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/cache.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/collections.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/data.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/downloads.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/metadata.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/operations.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/schedules.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/sqlite.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/system.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/tables.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/timeseries.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/web_routes.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/routes/workers.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/API/utils.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Auth/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Auth/bootstrap.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Auth/migrate.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Auth/store.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Auth/workers.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/CacheRedis.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/CollectionMongoDB.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/catalog.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/config.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/errors.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/indexes.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/readers.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/resolve.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/DataAPI/service.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Database.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Defaults.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/AWSEC2.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/AWSS3.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/AutoDocstrings.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/ClientAPI.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/ClientSocket.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/ClientWebSocket.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/LogHandlerAPI.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/LogReader.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/MongoDBClient.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/SaveTables.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/ServerSocket.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/ServerWebSocket.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/StreamsCache.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/StreamsPersist.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/SyncTable.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/TunnelWebSocket.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/IO/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Listing.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/LockWaiters.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Logger.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/MCP/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/MCP/__main__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/MCP/client.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/MCP/server.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Metadata.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/MultiProc.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/OpenFIGI.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Redaction.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/BatchJob.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/ContainerHealth.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/JobStore.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/RoutineCommand.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/RoutineOrigin.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/Schedule.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/ScheduleMirror.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/ScheduleStore.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/Scheduler.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/Worker.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/WorkerLib.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/WorkerPool.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Routines/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/SQLite.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/SharedData.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/SharedNumpy.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/StreamKafka.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Symbol.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableIndex.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableIndexJitFunctions.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableIndexJitGenerate.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableIndexJitHash.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableIndexJitLoc.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableProtocol.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TableRead.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TimeSeriesDisk.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/TimeseriesContainer.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Users.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Utils.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/activity_logger.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/app.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/google_logo.png +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/kafka_keys.js +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/logs.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/sched.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/assets/styles.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/auth.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/backends.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/config.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/containers.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/identity.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/kafka_view.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/mongo_view.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/proc.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/redis_view.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/route_health.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/route_registry.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/sched.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/schedule_editor.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/server.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/services.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/shell.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/standalone.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/account.js +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/custom.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/favicon.ico +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/favicon.png +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/logo.png +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/logo_96x96.png +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/shell.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/shell.js +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/standalone.css +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/static/standalone.js +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/suggest.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/account.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/base.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/claim.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/claim_done.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/dash.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/index.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/login.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/login_2fa.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/partials/navbar.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/partials/scripts.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/recovery_codes.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/shell.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/templates/standalone.html +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/twofactor.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/utils.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/Web/workerpool.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/__init__.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/build_info.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/fleet.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/preflight.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/private_files.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/release_backup.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/server.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/services.py +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/SharedData/sharedmutexwin.pyd +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/shareddata.egg-info/SOURCES.txt +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/shareddata.egg-info/dependency_links.txt +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/shareddata.egg-info/entry_points.txt +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/shareddata.egg-info/requires.txt +0 -0
- {shareddata-6.85.2 → shareddata-6.85.4}/src/shareddata.egg-info/top_level.txt +0 -0
|
@@ -865,20 +865,26 @@ class Table:
|
|
|
865
865
|
raise RewriteError('automatic disk header migration requires staged publication')
|
|
866
866
|
|
|
867
867
|
############### DOWNLOAD ###############
|
|
868
|
-
def
|
|
869
|
-
"""
|
|
870
|
-
|
|
868
|
+
def _remote_change(self):
|
|
869
|
+
"""What a reload would have to fetch: 'head' when the remote head is newer than this table's
|
|
870
|
+
(the whole image changed), 'tail' when only the remote tail is (the usual case: the rows of
|
|
871
|
+
the current year), None when nothing is newer, there is no remote copy, or S3 is off."""
|
|
871
872
|
from SharedData.services import s3_enabled
|
|
872
873
|
if not s3_enabled() or self.hdr is None:
|
|
873
|
-
return
|
|
874
|
+
return None
|
|
874
875
|
head = S3GetMtime(str(self.headpath) + '.gzip', self.database_folder)
|
|
875
876
|
if head is not None and head > float(self.hdr['mtimehead']):
|
|
876
|
-
return
|
|
877
|
+
return 'head'
|
|
877
878
|
if int(self.hdr['hastail']) == 1:
|
|
878
879
|
tail = S3GetMtime(str(self.tailpath) + '.gzip', self.database_folder)
|
|
879
880
|
if tail is not None and tail > float(self.hdr['mtimetail']):
|
|
880
|
-
return
|
|
881
|
-
return
|
|
881
|
+
return 'tail'
|
|
882
|
+
return None
|
|
883
|
+
|
|
884
|
+
def _download_remote_tail(self):
|
|
885
|
+
"""[io, local mtime, remote mtime] of the remote tail; io is None unless it is newer than this table's."""
|
|
886
|
+
return S3Download(str(self.tailpath) + '.gzip', local_mtime=float(self.hdr['mtimetail']),
|
|
887
|
+
database_folder=self.database_folder)
|
|
882
888
|
|
|
883
889
|
@structural
|
|
884
890
|
def download(self):
|
|
@@ -1223,6 +1229,17 @@ class Table:
|
|
|
1223
1229
|
io_obj.close()
|
|
1224
1230
|
self.copy_file()
|
|
1225
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
|
+
|
|
1226
1243
|
def read_tailheader(self, tail_io):
|
|
1227
1244
|
"""
|
|
1228
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,26 +304,44 @@ 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.
|
|
267
310
|
|
|
268
311
|
Nothing newer remotely (or no remote copy, or S3 off) leaves the local table as it is, so
|
|
269
|
-
rows not uploaded yet are never discarded.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
files describe the old rows, so they are removed and rebuilt.
|
|
312
|
+
rows not uploaded yet are never discarded. When only the remote tail is newer (the usual
|
|
313
|
+
case: the head holds the years before this one and the tail this year's rows) only the tail
|
|
314
|
+
is fetched and applied, see _reload_tail. When the remote head is newer, the whole remote
|
|
315
|
+
image is fetched, see _reload_image.
|
|
274
316
|
|
|
275
317
|
Returns:
|
|
276
318
|
The records of the reloaded table.
|
|
277
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
|
+
"""
|
|
278
337
|
import stat
|
|
279
338
|
import uuid
|
|
280
339
|
from SharedData.TableRewrite import fsync_directory, read_layout, require_rewrite_support, sync_file
|
|
281
|
-
|
|
340
|
+
change = self._remote_change()
|
|
341
|
+
if change is None:
|
|
282
342
|
return self.records
|
|
343
|
+
if change == 'tail' and not self.is_schemaless:
|
|
344
|
+
return self._reload_tail()
|
|
283
345
|
require_rewrite_support()
|
|
284
346
|
final = self.filepath
|
|
285
347
|
old = os.stat(final)
|
|
@@ -334,6 +396,147 @@ class TableDisk(Table):
|
|
|
334
396
|
raise Exception(errmsg)
|
|
335
397
|
return self.records
|
|
336
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
|
+
|
|
431
|
+
def _reload_tail(self):
|
|
432
|
+
"""
|
|
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).
|
|
435
|
+
|
|
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).
|
|
447
|
+
"""
|
|
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
|
|
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
|
|
468
|
+
tail_gz.seek(0) # S3Download leaves the buffer at its end
|
|
469
|
+
tail_io = gzip.GzipFile(fileobj=tail_gz, mode='rb')
|
|
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
|
|
529
|
+
except BaseException:
|
|
530
|
+
if stage is not None:
|
|
531
|
+
try:
|
|
532
|
+
stage.unlink()
|
|
533
|
+
except FileNotFoundError:
|
|
534
|
+
pass
|
|
535
|
+
raise
|
|
536
|
+
finally:
|
|
537
|
+
if acquired:
|
|
538
|
+
self.release()
|
|
539
|
+
|
|
337
540
|
############### DELETE ###############
|
|
338
541
|
def delete_local(self):
|
|
339
542
|
"""
|
|
@@ -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.2 → 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
|