shareddata 6.85.11__tar.gz → 6.85.14__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.11/src/shareddata.egg-info → shareddata-6.85.14}/PKG-INFO +1 -1
- {shareddata-6.85.11 → shareddata-6.85.14}/setup.py +1 -1
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/WorkerLib.py +2 -2
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/SharedData.py +10 -9
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/SharedNumpy.py +3 -2
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Table.py +81 -23
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableDisk.py +169 -3
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndex.py +51 -15
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableRead.py +8 -1
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableRewrite.py +126 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TimeSeriesDisk.py +83 -1
- {shareddata-6.85.11 → shareddata-6.85.14/src/shareddata.egg-info}/PKG-INFO +1 -1
- {shareddata-6.85.11 → shareddata-6.85.14}/LICENSE +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/MANIFEST.in +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/README.md +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/pyproject.toml +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/setup.cfg +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/ServerGunicorn.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/ServerWaitress.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/apidocs-dark.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/apidocs.yml +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/auth.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/constants.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/download_paths.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/download_slots.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/install/worker.ps1 +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/install/worker.sh +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/listcache.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/cache.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/collections.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/data.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/downloads.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/metadata.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/operations.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/schedules.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/sqlite.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/system.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/tables.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/timeseries.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/web_routes.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/workers.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/utils.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/bootstrap.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/migrate.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/store.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/workers.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/CacheRedis.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/CollectionMongoDB.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/catalog.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/config.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/errors.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/indexes.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/readers.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/resolve.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/service.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Database.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Defaults.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/AWSEC2.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/AWSS3.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/AutoDocstrings.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ClientAPI.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ClientSocket.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ClientWebSocket.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/LogHandlerAPI.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/LogReader.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/MongoDBClient.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/SaveTables.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ServerSocket.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ServerWebSocket.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/StreamsCache.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/StreamsPersist.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/SyncTable.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/TunnelWebSocket.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Listing.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/LockWaiters.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Logger.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/__main__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/client.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/server.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Metadata.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MultiProc.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/OpenFIGI.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Redaction.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/BatchJob.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ContainerHealth.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/JobStore.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/RoutineCommand.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/RoutineOrigin.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/Schedule.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ScheduleMirror.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ScheduleStore.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/Scheduler.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/Worker.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/WorkerPool.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/SQLite.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/StreamKafka.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Symbol.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitFunctions.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitGenerate.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitHash.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitLoc.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableProtocol.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TimeseriesContainer.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Users.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Utils.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/activity_logger.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/app.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/google_logo.png +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/kafka_keys.js +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/logs.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/sched.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/styles.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/auth.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/backends.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/config.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/containers.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/identity.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/kafka_view.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/mongo_view.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/proc.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/redis_view.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/route_health.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/route_registry.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/sched.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/schedule_editor.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/server.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/services.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/shell.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/standalone.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/account.js +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/custom.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/favicon.ico +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/favicon.png +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/logo.png +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/logo_96x96.png +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/shell.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/shell.js +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/standalone.css +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/standalone.js +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/suggest.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/account.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/base.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/claim.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/claim_done.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/dash.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/index.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/login.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/login_2fa.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/partials/navbar.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/partials/scripts.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/recovery_codes.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/shell.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/standalone.html +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/twofactor.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/utils.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/workerpool.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/__init__.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/build_info.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/fleet.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/preflight.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/private_files.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/release_backup.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/server.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/services.py +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/sharedmutexwin.pyd +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/SOURCES.txt +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/dependency_links.txt +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/entry_points.txt +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/requires.txt +0 -0
- {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/top_level.txt +0 -0
|
@@ -915,7 +915,7 @@ def _install_repo(command, quiet=False):
|
|
|
915
915
|
if not send_command(checkout_cmd):
|
|
916
916
|
Logger.log.error(f'Checking out branch {command["branch"]} FAILED!')
|
|
917
917
|
runroutine = False
|
|
918
|
-
|
|
918
|
+
elif not quiet:
|
|
919
919
|
Logger.log.info(f'Checked out branch {command["branch"]}')
|
|
920
920
|
else:
|
|
921
921
|
# Checkout main branch if no branch specified
|
|
@@ -923,7 +923,7 @@ def _install_repo(command, quiet=False):
|
|
|
923
923
|
if not send_command(checkout_cmd):
|
|
924
924
|
Logger.log.error(f'Checking out branch main FAILED!')
|
|
925
925
|
runroutine = False
|
|
926
|
-
|
|
926
|
+
elif not quiet:
|
|
927
927
|
Logger.log.info(f'Checked out branch main')
|
|
928
928
|
|
|
929
929
|
# pull existing repo
|
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import warnings
|
|
7
7
|
import time
|
|
8
8
|
import shutil
|
|
9
|
+
from SharedData.TableRewrite import remove_with_retry, rmtree_with_retry
|
|
9
10
|
from datetime import datetime, timezone
|
|
10
11
|
|
|
11
12
|
from multiprocessing import shared_memory
|
|
@@ -989,7 +990,7 @@ class SharedData:
|
|
|
989
990
|
fpath = Path(os.environ['DATABASE_FOLDER'])
|
|
990
991
|
fpath = fpath/('shm/'+shm_name.replace('\\', '/')+'.csv')
|
|
991
992
|
if fpath.is_file():
|
|
992
|
-
|
|
993
|
+
remove_with_retry(fpath)
|
|
993
994
|
except:
|
|
994
995
|
pass
|
|
995
996
|
|
|
@@ -1685,7 +1686,7 @@ class SharedData:
|
|
|
1685
1686
|
except:
|
|
1686
1687
|
try:
|
|
1687
1688
|
if fpath.is_file():
|
|
1688
|
-
|
|
1689
|
+
remove_with_retry(fpath)
|
|
1689
1690
|
except:
|
|
1690
1691
|
pass
|
|
1691
1692
|
shm_names = shm_names.sort_index()
|
|
@@ -1845,11 +1846,11 @@ class SharedData:
|
|
|
1845
1846
|
for file in delfiles:
|
|
1846
1847
|
delpath = Path(localpath/file)
|
|
1847
1848
|
if delpath.exists():
|
|
1848
|
-
|
|
1849
|
+
remove_with_retry(delpath)
|
|
1849
1850
|
# if folder is empty remove it
|
|
1850
1851
|
if localpath.exists():
|
|
1851
1852
|
if not any(localpath.iterdir()):
|
|
1852
|
-
|
|
1853
|
+
rmtree_with_retry(localpath)
|
|
1853
1854
|
else:
|
|
1854
1855
|
database_folder = os.environ['DATABASE_FOLDER']
|
|
1855
1856
|
# select the first disk with less percent_used
|
|
@@ -1865,10 +1866,10 @@ class SharedData:
|
|
|
1865
1866
|
for file in delfiles:
|
|
1866
1867
|
delpath = Path(localpath/file)
|
|
1867
1868
|
if delpath.exists():
|
|
1868
|
-
|
|
1869
|
+
remove_with_retry(delpath)
|
|
1869
1870
|
# if folder is empty remove it
|
|
1870
1871
|
if not any(localpath.iterdir()):
|
|
1871
|
-
|
|
1872
|
+
rmtree_with_retry(localpath)
|
|
1872
1873
|
|
|
1873
1874
|
from SharedData.services import s3_enabled
|
|
1874
1875
|
if not localonly and s3_enabled():
|
|
@@ -1911,12 +1912,12 @@ class SharedData:
|
|
|
1911
1912
|
del self.data[path]
|
|
1912
1913
|
localpath = Path(os.environ['DATABASE_FOLDER'])/Path(path)
|
|
1913
1914
|
if localpath.exists():
|
|
1914
|
-
|
|
1915
|
+
rmtree_with_retry(localpath)
|
|
1915
1916
|
files_del = ['timeseries_head.bin','timeseries_tail.bin']
|
|
1916
1917
|
for file in files_del:
|
|
1917
1918
|
delpath = Path(localpath.parent/file)
|
|
1918
1919
|
if delpath.exists():
|
|
1919
|
-
|
|
1920
|
+
remove_with_retry(delpath)
|
|
1920
1921
|
from SharedData.services import s3_enabled
|
|
1921
1922
|
if not localonly and s3_enabled():
|
|
1922
1923
|
S3DeleteTimeseries(path.replace('/timeseries',''))
|
|
@@ -1928,7 +1929,7 @@ class SharedData:
|
|
|
1928
1929
|
fpath, shm_name = tstag.get_path()
|
|
1929
1930
|
del self.data[path].tags[tag]
|
|
1930
1931
|
del ts
|
|
1931
|
-
|
|
1932
|
+
remove_with_retry(fpath)
|
|
1932
1933
|
return True
|
|
1933
1934
|
|
|
1934
1935
|
except Exception as e:
|
|
@@ -297,8 +297,9 @@ class SharedNumpy(np.ndarray):
|
|
|
297
297
|
force_write (bool): If True, forces the write operation even if not necessary. Defaults to False.
|
|
298
298
|
"""
|
|
299
299
|
# Saving remains available without unsupported record replacement.
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
from SharedData.TableRewrite import rewrite_supported
|
|
301
|
+
if (self.table.type != 1 or sys.platform == 'linux' or not rewrite_supported()) and (
|
|
302
|
+
self.table.hdr['hasindex'] == 1 and self.table.hdr['isidxsorted'] == 0): # Windows sorts in place
|
|
302
303
|
self.sort_index()
|
|
303
304
|
self.table.write(force_write, strict)
|
|
304
305
|
|
|
@@ -689,14 +689,25 @@ class Table:
|
|
|
689
689
|
from SharedData.TableRewrite import (RewriteError, fsync_directory,
|
|
690
690
|
rewrite_supported)
|
|
691
691
|
if not rewrite_supported():
|
|
692
|
-
#
|
|
693
|
-
# mode, so
|
|
694
|
-
#
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
692
|
+
# Windows cannot rename over or unlink a file any process maps. The shared mutex is held in STRUCT
|
|
693
|
+
# mode, so the image is re-initialised in place: the old committed rows and the header are zeroed
|
|
694
|
+
# through a mapped view (coherent with every other process's view), the file is only ever extended,
|
|
695
|
+
# and the index / BSON files stay as they are (the index is rebuilt by the caller, and the new
|
|
696
|
+
# header resets next_bson_ptr, so no old document is reachable).
|
|
697
|
+
from SharedData.TableRewrite import RewriteError, extend_file, read_layout
|
|
698
|
+
final = self.filepath
|
|
699
|
+
try:
|
|
700
|
+
with open(final, 'rb') as stream:
|
|
701
|
+
old_header, old_records = read_layout(stream)
|
|
702
|
+
clear = old_header.dtype.itemsize + int(old_header['count']) * old_records.itemsize
|
|
703
|
+
except RewriteError:
|
|
704
|
+
clear = os.path.getsize(final) # not a coherent image: clear everything it holds
|
|
705
|
+
extend_file(final, int(self.hdrdtype.itemsize + self.recdtype.itemsize * int(self.size)))
|
|
706
|
+
clear = max(clear, int(self.hdrdtype.itemsize))
|
|
707
|
+
view = np.memmap(final, np.uint8, 'r+', 0, (clear,))
|
|
708
|
+
view[:] = 0
|
|
709
|
+
view.flush()
|
|
710
|
+
del view
|
|
700
711
|
self.malloc(create=True)
|
|
701
712
|
return
|
|
702
713
|
final = self.filepath
|
|
@@ -1395,7 +1406,7 @@ class Table:
|
|
|
1395
1406
|
return h1[:l] == h2[:l]
|
|
1396
1407
|
|
|
1397
1408
|
############### BSON MANAGEMENT ###############
|
|
1398
|
-
def _reset_bson_mapping(self):
|
|
1409
|
+
def _reset_bson_mapping(self, reopen=True):
|
|
1399
1410
|
"""Drop the BSON mapping and reopen the file by path: after a paired publication the old
|
|
1400
1411
|
mapping belongs to an unlinked inode and its offsets no longer match the records."""
|
|
1401
1412
|
if self.bson_mmap is not None:
|
|
@@ -1409,7 +1420,7 @@ class Table:
|
|
|
1409
1420
|
self.bson_file_handle = None
|
|
1410
1421
|
self.curr_bson_size = 0
|
|
1411
1422
|
self.prev_bson_ptr = 0
|
|
1412
|
-
if self.is_schemaless and self.bsonpath.exists() and self.bsonpath.stat().st_size > 0:
|
|
1423
|
+
if reopen and self.is_schemaless and self.bsonpath.exists() and self.bsonpath.stat().st_size > 0:
|
|
1413
1424
|
self.init_bson_mmap()
|
|
1414
1425
|
|
|
1415
1426
|
def init_bson_mmap(self, from_download=False):
|
|
@@ -1669,10 +1680,6 @@ class Table:
|
|
|
1669
1680
|
|
|
1670
1681
|
def compact_bson_if_worthwhile(self):
|
|
1671
1682
|
"""Compact the BSON file when enough of it is orphaned; None when it is not worth a rewrite."""
|
|
1672
|
-
if self.type == 1:
|
|
1673
|
-
from SharedData.TableRewrite import rewrite_supported
|
|
1674
|
-
if not rewrite_supported():
|
|
1675
|
-
return None # the pair publication needs POSIX file APIs; compaction only saves space
|
|
1676
1683
|
usage = self.bson_usage()
|
|
1677
1684
|
if usage['orphaned'] < BSON_DEFRAG_MIN_BYTES \
|
|
1678
1685
|
or usage['orphaned'] < BSON_DEFRAG_MIN_FRACTION * max(usage['used'], 1):
|
|
@@ -1682,6 +1689,50 @@ class Table:
|
|
|
1682
1689
|
return None
|
|
1683
1690
|
return self.defragment_bson(acquire=False)
|
|
1684
1691
|
|
|
1692
|
+
def _defrag_in_place(self, stage, mapping, payload, new_size):
|
|
1693
|
+
"""Windows: the compacted BSON `stage` is copied over the start of the live file and every moved
|
|
1694
|
+
bson_ptr is rewritten in place, as one STRUCT section under the shared mutex (nothing else touches the
|
|
1695
|
+
table), then the file is shrunk when no process maps it. Not crash-safe (a crash between the BSON
|
|
1696
|
+
copy and the pointer update leaves STRUCT/DIRTY and pointers into a rewritten file); the staged
|
|
1697
|
+
publication that covers that needs Linux."""
|
|
1698
|
+
from SharedData.TableProtocol import structure
|
|
1699
|
+
from SharedData.TableRewrite import shrink_file, sync_header
|
|
1700
|
+
count = int(self.hdr['count'])
|
|
1701
|
+
with structure(self):
|
|
1702
|
+
if self.bson_mmap is not None:
|
|
1703
|
+
self.bson_mmap.flush()
|
|
1704
|
+
data = np.fromfile(stage, dtype=np.uint8) # the stage is the compacted payload (+1 pad byte)
|
|
1705
|
+
view = np.memmap(self.bsonpath, np.uint8, 'r+', 0, (len(data),))
|
|
1706
|
+
view[:] = data
|
|
1707
|
+
view.flush()
|
|
1708
|
+
del view
|
|
1709
|
+
rows = self.shf_data[:count] # the file's own mapping, never a caller-visible copy
|
|
1710
|
+
selected = mapping >= 0
|
|
1711
|
+
if selected.any():
|
|
1712
|
+
column = rows['bson_ptr']
|
|
1713
|
+
if column.dtype.kind == 'V' and column.dtype.itemsize == 8:
|
|
1714
|
+
column = column.view(np.int64)
|
|
1715
|
+
column[selected] = mapping[selected]
|
|
1716
|
+
self.shf_data.flush()
|
|
1717
|
+
self.hdr['next_bson_ptr'] = payload
|
|
1718
|
+
self.hdr['mtime'] = max(datetime.now().timestamp(), float(self.hdr['mtime']) + 0.001)
|
|
1719
|
+
sync_header(self)
|
|
1720
|
+
self.attach() # records/header of this object: the mapping of the file, as after a publication
|
|
1721
|
+
self._reset_bson_mapping(reopen=False) # drop this process's map, then try to give the space back
|
|
1722
|
+
physical = self.bsonpath.stat().st_size
|
|
1723
|
+
if not shrink_file(self.bsonpath, new_size) and physical > new_size:
|
|
1724
|
+
# another process still maps the file: it stays as large, but the stale documents behind the
|
|
1725
|
+
# new end are zeroed so the upload (which compresses the file to its end) stays small
|
|
1726
|
+
tail = np.memmap(self.bsonpath, np.uint8, 'r+', new_size, (physical - new_size,))
|
|
1727
|
+
tail[:] = 0
|
|
1728
|
+
tail.flush()
|
|
1729
|
+
del tail
|
|
1730
|
+
self._reset_bson_mapping()
|
|
1731
|
+
try:
|
|
1732
|
+
Path(stage).unlink()
|
|
1733
|
+
except OSError:
|
|
1734
|
+
pass
|
|
1735
|
+
|
|
1685
1736
|
def _defragment_bson_disk(self, chunk_size, acquire):
|
|
1686
1737
|
"""Compact bson.bin of a disk table and move every bson_ptr, as ONE crash-safe publication.
|
|
1687
1738
|
|
|
@@ -1695,7 +1746,8 @@ class Table:
|
|
|
1695
1746
|
from SharedData.TableRewrite import (RewriteError, copy_metadata, file_identity, fsync_directory,
|
|
1696
1747
|
pending_path, publish_rewrite, require_rewrite_support, write_all)
|
|
1697
1748
|
from SharedData.TableProtocol import structure
|
|
1698
|
-
|
|
1749
|
+
from SharedData.TableRewrite import rewrite_supported
|
|
1750
|
+
in_place = not rewrite_supported()
|
|
1699
1751
|
if not self.is_schemaless:
|
|
1700
1752
|
raise Exception(f'Cannot defragment non-schemaless table {self.relpath}')
|
|
1701
1753
|
if acquire:
|
|
@@ -1709,8 +1761,8 @@ class Table:
|
|
|
1709
1761
|
if self.bson_mmap is not None:
|
|
1710
1762
|
self.bson_mmap.flush()
|
|
1711
1763
|
if self.bsonpath.exists():
|
|
1712
|
-
|
|
1713
|
-
|
|
1764
|
+
from SharedData.TableRewrite import sync_file
|
|
1765
|
+
sync_file(self.bsonpath) # r+b on Windows: fsync needs a writable handle
|
|
1714
1766
|
count = int(self.hdr['count'])
|
|
1715
1767
|
pointers, sizes = self._bson_columns(count) if count else (np.zeros(0, np.int64), np.zeros(0, np.int64))
|
|
1716
1768
|
live_idx = np.flatnonzero(sizes > 0)
|
|
@@ -1736,9 +1788,10 @@ class Table:
|
|
|
1736
1788
|
token = uuid.uuid4().hex
|
|
1737
1789
|
stage = self.bsonpath.parent / ('bson.bin.stage.' + token)
|
|
1738
1790
|
digest = hashlib.sha256()
|
|
1739
|
-
fd = os.open(stage, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o600)
|
|
1791
|
+
fd = os.open(stage, os.O_RDWR | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0), 0o600)
|
|
1740
1792
|
try:
|
|
1741
|
-
|
|
1793
|
+
if not in_place:
|
|
1794
|
+
copy_metadata(self.bsonpath, fd)
|
|
1742
1795
|
if len(live_idx):
|
|
1743
1796
|
with open(self.bsonpath, 'rb') as source:
|
|
1744
1797
|
view = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ)
|
|
@@ -1765,6 +1818,15 @@ class Table:
|
|
|
1765
1818
|
new_size = max(payload, 1)
|
|
1766
1819
|
mapping = np.full(count, -1, dtype=np.int64)
|
|
1767
1820
|
mapping[live_idx] = new_offsets
|
|
1821
|
+
if in_place:
|
|
1822
|
+
self._defrag_in_place(stage, mapping, payload, new_size)
|
|
1823
|
+
stats = {'original_size': original_size, 'new_size': self.bsonpath.stat().st_size,
|
|
1824
|
+
'reclaimed': original_size - self.bsonpath.stat().st_size, 'live_bytes': payload,
|
|
1825
|
+
'live_rows': int(len(live_idx)), 'logical_size': new_size}
|
|
1826
|
+
Logger.log.info(
|
|
1827
|
+
f'Defragmented {self.relpath} BSON file in place: {original_size/1024/1024:.2f}MB -> '
|
|
1828
|
+
f'{new_size/1024/1024:.2f}MB logical (file {stats["new_size"]/1024/1024:.2f}MB)')
|
|
1829
|
+
return stats
|
|
1768
1830
|
|
|
1769
1831
|
def move_pointers(chunk, first):
|
|
1770
1832
|
moved = mapping[first:first + len(chunk)]
|
|
@@ -1834,10 +1896,6 @@ class Table:
|
|
|
1834
1896
|
Exception: If the table is not schemaless or if defragmentation fails.
|
|
1835
1897
|
"""
|
|
1836
1898
|
if self.type == 1:
|
|
1837
|
-
from SharedData.TableRewrite import rewrite_supported
|
|
1838
|
-
if not rewrite_supported():
|
|
1839
|
-
Logger.log.warning(f'{self.relpath} BSON not compacted: the pair publication needs POSIX file APIs')
|
|
1840
|
-
return {'original_size': 0, 'new_size': 0, 'reclaimed': 0}
|
|
1841
1899
|
return self._defragment_bson_disk(chunk_size, acquire)
|
|
1842
1900
|
if not self.is_schemaless:
|
|
1843
1901
|
raise Exception(f'Cannot defragment non-schemaless table {self.relpath}')
|
|
@@ -13,6 +13,7 @@ from tqdm import tqdm
|
|
|
13
13
|
import threading
|
|
14
14
|
import psutil
|
|
15
15
|
import shutil
|
|
16
|
+
from SharedData.TableRewrite import remove_with_retry, rmtree_with_retry
|
|
16
17
|
|
|
17
18
|
from SharedData.Logger import Logger
|
|
18
19
|
from SharedData.IO.AWSS3 import S3DeleteFolder
|
|
@@ -178,6 +179,9 @@ class TableDisk(Table):
|
|
|
178
179
|
# Header, size check and data come from one open file: trim replaces
|
|
179
180
|
# data.bin, and np.memmap by path would map (and extend) the new file.
|
|
180
181
|
with open(self.filepath, 'r+b') as stream:
|
|
182
|
+
from SharedData.TableRewrite import rewrite_supported
|
|
183
|
+
if not rewrite_supported():
|
|
184
|
+
self._adopt_layout(stream) # Windows: layout changes are published in place
|
|
181
185
|
# memory map header — read the live header, never write it
|
|
182
186
|
self.shf_hdr = np.memmap(stream, self.hdrdtype, 'r+', 0, (1,))
|
|
183
187
|
self.hdr = self.shf_hdr[0]
|
|
@@ -199,6 +203,24 @@ class TableDisk(Table):
|
|
|
199
203
|
self.records = SharedNumpy('DISK', self.shf_data)
|
|
200
204
|
self.records.table = self
|
|
201
205
|
|
|
206
|
+
def _adopt_layout(self, stream):
|
|
207
|
+
"""Follow a layout change published under the same inode (overwrite or reload with another schema,
|
|
208
|
+
in place): the header describes itself, so a process that still holds the old column list would map
|
|
209
|
+
the new bytes with the old record stride. Reparsed from the file, never from this object's copy."""
|
|
210
|
+
from SharedData.TableRewrite import RewriteError, read_layout
|
|
211
|
+
try:
|
|
212
|
+
header, records = read_layout(stream)
|
|
213
|
+
except RewriteError:
|
|
214
|
+
return # not a coherent image (yet): the caller's own checks report it
|
|
215
|
+
if header.dtype == self.hdrdtype and records == self.recdtype:
|
|
216
|
+
return
|
|
217
|
+
self.hdrdtype, self.recdtype = header.dtype, records
|
|
218
|
+
self.recnames, self.recformats = list(records.names), [str(records[n]) for n in records.names]
|
|
219
|
+
self.is_schemaless = 'bson_size' in self.recnames and 'bson_ptr' in self.recnames
|
|
220
|
+
self._record_bundle = None
|
|
221
|
+
self._index_bundle = None
|
|
222
|
+
self._reset_bson_mapping()
|
|
223
|
+
|
|
202
224
|
############### FREE ###############
|
|
203
225
|
def free(self, acquire=True):
|
|
204
226
|
"""
|
|
@@ -347,6 +369,9 @@ class TableDisk(Table):
|
|
|
347
369
|
return self.records
|
|
348
370
|
if change == 'tail' and not self.is_schemaless:
|
|
349
371
|
return self._reload_tail()
|
|
372
|
+
from SharedData.TableRewrite import rewrite_supported
|
|
373
|
+
if not rewrite_supported():
|
|
374
|
+
return self._reload_image_in_place()
|
|
350
375
|
require_rewrite_support()
|
|
351
376
|
final = self.filepath
|
|
352
377
|
old = os.stat(final)
|
|
@@ -401,6 +426,144 @@ class TableDisk(Table):
|
|
|
401
426
|
raise Exception(errmsg)
|
|
402
427
|
return self.records
|
|
403
428
|
|
|
429
|
+
def _apply_tail_in_place(self, header, tail_io, tailhdr, tail_at, capacity):
|
|
430
|
+
"""Windows: the remote tail (already parsed up to its rows) is downloaded and checked against its
|
|
431
|
+
header and checksum into a private temporary file first; only then, in a STRUCT section under the
|
|
432
|
+
shared mutex, is the live file extended if the capacity grew, the tail rows are written through a
|
|
433
|
+
mapped view, rows beyond a shorter tail are zeroed, the header fields are published and the indexes
|
|
434
|
+
rebuilt. A failure before the STRUCT section leaves the live table untouched."""
|
|
435
|
+
import hashlib
|
|
436
|
+
import uuid
|
|
437
|
+
from SharedData.TableRewrite import RewriteError, extend_file, same_digest, sync_header, zero_mapped, copy_mapped
|
|
438
|
+
itemsize, tailsize = int(header['itemsize']), int(tailhdr['tailsize'])
|
|
439
|
+
final = self.filepath
|
|
440
|
+
temp = final.parent / ('data.bin.tail.' + uuid.uuid4().hex)
|
|
441
|
+
try:
|
|
442
|
+
tail_io.seek(tailhdr.dtype.itemsize)
|
|
443
|
+
digest, written = hashlib.md5(), 0
|
|
444
|
+
with open(temp, 'wb') as out:
|
|
445
|
+
while True:
|
|
446
|
+
chunk = tail_io.read(CHUNK)
|
|
447
|
+
if not chunk:
|
|
448
|
+
break
|
|
449
|
+
if written + len(chunk) > tailsize * itemsize:
|
|
450
|
+
raise RewriteError('the remote tail of %s is longer than its header says' % self.relpath)
|
|
451
|
+
digest.update(chunk)
|
|
452
|
+
out.write(chunk)
|
|
453
|
+
written += len(chunk)
|
|
454
|
+
out.flush()
|
|
455
|
+
os.fsync(out.fileno())
|
|
456
|
+
if written != tailsize * itemsize:
|
|
457
|
+
raise RewriteError('the remote tail of %s holds %d bytes, its header says %d' % (self.relpath, written, tailsize * itemsize))
|
|
458
|
+
if not same_digest(digest.digest(), tailhdr['md5hash']):
|
|
459
|
+
raise RewriteError('the remote tail of %s does not match its checksum' % self.relpath)
|
|
460
|
+
with structure(self):
|
|
461
|
+
hdrsize = int(self.hdr.dtype.itemsize)
|
|
462
|
+
old_end = hdrsize + int(self.hdr['count']) * itemsize
|
|
463
|
+
new_end = tail_at + written
|
|
464
|
+
extend_file(final, hdrsize + capacity * itemsize)
|
|
465
|
+
self.shf_data.flush()
|
|
466
|
+
copy_mapped(final, temp, written, dst_offset=tail_at)
|
|
467
|
+
zero_mapped(final, new_end, old_end) # a shorter tail than the local one leaves no old rows behind
|
|
468
|
+
for field in ('md5hashtail', 'tailsize', 'count', 'mtimetail', 'recordssize', 'isidxcreated',
|
|
469
|
+
'isloaded', 'isidxsorted', 'minchgid'):
|
|
470
|
+
self.hdr[field] = header[field]
|
|
471
|
+
sync_header(self)
|
|
472
|
+
self.size = capacity
|
|
473
|
+
self._attach_replacement(final, rebuild_files=False)
|
|
474
|
+
return self.records
|
|
475
|
+
finally:
|
|
476
|
+
try:
|
|
477
|
+
temp.unlink()
|
|
478
|
+
except OSError:
|
|
479
|
+
pass
|
|
480
|
+
|
|
481
|
+
def _reload_image_in_place(self):
|
|
482
|
+
"""Windows: the whole remote image is downloaded beside the live files (the live BSON path is
|
|
483
|
+
redirected too, so nothing live is written while preparing), then, inside the STRUCT section the
|
|
484
|
+
caller holds, copied over the live files through mapped views: the file is extended when the remote
|
|
485
|
+
capacity is larger, committed rows left over from a longer local image are zeroed, the BSON file is
|
|
486
|
+
copied and shrunk when no other process maps it. Index files are kept (rebuilt, oversized is fine)."""
|
|
487
|
+
import gc
|
|
488
|
+
import uuid
|
|
489
|
+
from SharedData.TableRewrite import (RewriteError, copy_mapped, extend_file, read_layout, shrink_file,
|
|
490
|
+
zero_mapped)
|
|
491
|
+
final, bson_final = self.filepath, self.bsonpath
|
|
492
|
+
for leftover in list(final.parent.glob('data.bin.reload.*')) + list(final.parent.glob('bson.bin.reload.*')):
|
|
493
|
+
try:
|
|
494
|
+
leftover.unlink()
|
|
495
|
+
except OSError:
|
|
496
|
+
pass
|
|
497
|
+
token = uuid.uuid4().hex
|
|
498
|
+
stage = final.parent / ('data.bin.reload.' + token)
|
|
499
|
+
bson_stage = final.parent / ('bson.bin.reload.' + token)
|
|
500
|
+
old_end = int(self.hdr.dtype.itemsize) + int(self.hdr['count']) * int(self.recdtype.itemsize)
|
|
501
|
+
old_bson = bson_final.stat().st_size if bson_final.exists() else 0
|
|
502
|
+
old_layout = (self.hdrdtype, self.recdtype, getattr(self, 'recnames', None), getattr(self, 'recformats', None),
|
|
503
|
+
self.is_schemaless)
|
|
504
|
+
saved = (self.hdr, self.size, getattr(self, 'orig_hdr', None))
|
|
505
|
+
self.filepath, self.bsonpath, self.hdr = stage, bson_stage, None
|
|
506
|
+
self._bootstrap_record_download = True
|
|
507
|
+
try:
|
|
508
|
+
self._download_empty_image()
|
|
509
|
+
if self.hdr is None or not stage.is_file():
|
|
510
|
+
raise Exception('the remote copy of %s disappeared during the reload' % self.relpath)
|
|
511
|
+
self.free(acquire=False) # the maps of the stage files
|
|
512
|
+
gc.collect()
|
|
513
|
+
with open(stage, 'rb') as stream:
|
|
514
|
+
downloaded, records = read_layout(stream)
|
|
515
|
+
hdrsize = int(downloaded.dtype.itemsize)
|
|
516
|
+
commit = hdrsize + int(downloaded['count']) * int(records.itemsize)
|
|
517
|
+
capacity_bytes = hdrsize + int(downloaded['recordssize']) * int(records.itemsize)
|
|
518
|
+
new_hdrdtype, new_recdtype, new_size = self.hdrdtype, self.recdtype, self.size
|
|
519
|
+
new_schemaless = self.is_schemaless
|
|
520
|
+
have_bson = bson_stage.exists()
|
|
521
|
+
self.filepath, self.bsonpath = final, bson_final
|
|
522
|
+
self._reset_bson_mapping(reopen=False)
|
|
523
|
+
extend_file(final, capacity_bytes)
|
|
524
|
+
copy_mapped(final, stage, commit)
|
|
525
|
+
zero_mapped(final, commit, old_end)
|
|
526
|
+
if have_bson:
|
|
527
|
+
bson_size = bson_stage.stat().st_size
|
|
528
|
+
if not bson_final.exists():
|
|
529
|
+
bson_final.touch()
|
|
530
|
+
extend_file(bson_final, bson_size)
|
|
531
|
+
copy_mapped(bson_final, bson_stage, bson_size)
|
|
532
|
+
physical = bson_final.stat().st_size
|
|
533
|
+
if not shrink_file(bson_final, bson_size) and physical > bson_size:
|
|
534
|
+
zero_mapped(bson_final, bson_size, physical)
|
|
535
|
+
except BaseException:
|
|
536
|
+
self.filepath, self.bsonpath = final, bson_final
|
|
537
|
+
self.hdr, self.size, self.orig_hdr = saved
|
|
538
|
+
self.hdrdtype, self.recdtype = old_layout[0], old_layout[1]
|
|
539
|
+
if old_layout[2] is not None:
|
|
540
|
+
self.recnames, self.recformats = old_layout[2], old_layout[3]
|
|
541
|
+
self.is_schemaless = old_layout[4]
|
|
542
|
+
raise
|
|
543
|
+
finally:
|
|
544
|
+
self._bootstrap_record_download = False
|
|
545
|
+
for leftover in (stage, bson_stage):
|
|
546
|
+
try:
|
|
547
|
+
leftover.unlink()
|
|
548
|
+
except OSError:
|
|
549
|
+
pass
|
|
550
|
+
try:
|
|
551
|
+
self.free(acquire=False)
|
|
552
|
+
self.malloc()
|
|
553
|
+
if self.is_schemaless:
|
|
554
|
+
self.init_bson_mmap(from_download=have_bson)
|
|
555
|
+
if self.hasindex:
|
|
556
|
+
self.hdr['isidxcreated'] = 0
|
|
557
|
+
self.index.initialize()
|
|
558
|
+
self.shareddata.data[self.relpath] = self
|
|
559
|
+
self.hdr['isloaded'] = 1
|
|
560
|
+
self.mutex['isloaded'] = 1
|
|
561
|
+
except Exception as e:
|
|
562
|
+
errmsg = f"TableDisk.reload() {self.relpath}: {e}"
|
|
563
|
+
Logger.log.error(errmsg)
|
|
564
|
+
raise Exception(errmsg)
|
|
565
|
+
return self.records
|
|
566
|
+
|
|
404
567
|
def _attach_replacement(self, final, rebuild_files):
|
|
405
568
|
"""After os.replace: make the directory entry durable, then map the new image and rebuild its
|
|
406
569
|
indexes. Whatever fails, this object ends mapped to the image that is now installed (flagged
|
|
@@ -454,7 +617,8 @@ class TableDisk(Table):
|
|
|
454
617
|
from SharedData.TableRewrite import (RewriteError, copy_metadata, pwrite_all, require_rewrite_support,
|
|
455
618
|
same_digest, sync_header)
|
|
456
619
|
import hashlib
|
|
457
|
-
|
|
620
|
+
from SharedData.TableRewrite import rewrite_supported
|
|
621
|
+
in_place = not rewrite_supported()
|
|
458
622
|
acquired = not self._owns_lock()
|
|
459
623
|
if acquired:
|
|
460
624
|
self.acquire()
|
|
@@ -489,6 +653,8 @@ class TableDisk(Table):
|
|
|
489
653
|
header['isidxsorted'] = 1 # the uploader sorts before it writes (SharedNumpy.write)
|
|
490
654
|
pending = int(header['minchgid']) # a head row edited here and not uploaded yet stays marked for upload;
|
|
491
655
|
header['minchgid'] = pending if pending <= int(header['headsize']) else int(header['count']) + 1 # otherwise as a write leaves it
|
|
656
|
+
if in_place:
|
|
657
|
+
return self._apply_tail_in_place(header, tail_io, tailhdr, tail_at, capacity)
|
|
492
658
|
for leftover in final.parent.glob('data.bin.reload.*'): # the lock is held: a stage left by a dead reload
|
|
493
659
|
leftover.unlink()
|
|
494
660
|
stage = final.parent / ('data.bin.reload.' + uuid.uuid4().hex)
|
|
@@ -574,10 +740,10 @@ class TableDisk(Table):
|
|
|
574
740
|
for file in delfiles:
|
|
575
741
|
delpath = Path(localpath/file)
|
|
576
742
|
if delpath.exists():
|
|
577
|
-
|
|
743
|
+
remove_with_retry(delpath)
|
|
578
744
|
# if folder is empty remove it
|
|
579
745
|
if not any(localpath.iterdir()):
|
|
580
|
-
|
|
746
|
+
rmtree_with_retry(localpath)
|
|
581
747
|
|
|
582
748
|
success = True
|
|
583
749
|
|