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.
Files changed (187) hide show
  1. {shareddata-6.85.11/src/shareddata.egg-info → shareddata-6.85.14}/PKG-INFO +1 -1
  2. {shareddata-6.85.11 → shareddata-6.85.14}/setup.py +1 -1
  3. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/WorkerLib.py +2 -2
  4. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/SharedData.py +10 -9
  5. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/SharedNumpy.py +3 -2
  6. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Table.py +81 -23
  7. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableDisk.py +169 -3
  8. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndex.py +51 -15
  9. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableRead.py +8 -1
  10. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableRewrite.py +126 -0
  11. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TimeSeriesDisk.py +83 -1
  12. {shareddata-6.85.11 → shareddata-6.85.14/src/shareddata.egg-info}/PKG-INFO +1 -1
  13. {shareddata-6.85.11 → shareddata-6.85.14}/LICENSE +0 -0
  14. {shareddata-6.85.11 → shareddata-6.85.14}/MANIFEST.in +0 -0
  15. {shareddata-6.85.11 → shareddata-6.85.14}/README.md +0 -0
  16. {shareddata-6.85.11 → shareddata-6.85.14}/pyproject.toml +0 -0
  17. {shareddata-6.85.11 → shareddata-6.85.14}/setup.cfg +0 -0
  18. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/ServerGunicorn.py +0 -0
  19. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/ServerWaitress.py +0 -0
  20. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/__init__.py +0 -0
  21. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/apidocs-dark.css +0 -0
  22. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/apidocs.yml +0 -0
  23. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/auth.py +0 -0
  24. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/constants.py +0 -0
  25. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/download_paths.py +0 -0
  26. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/download_slots.py +0 -0
  27. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/install/worker.ps1 +0 -0
  28. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/install/worker.sh +0 -0
  29. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/listcache.py +0 -0
  30. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/__init__.py +0 -0
  31. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/cache.py +0 -0
  32. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/collections.py +0 -0
  33. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/data.py +0 -0
  34. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/downloads.py +0 -0
  35. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/metadata.py +0 -0
  36. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/operations.py +0 -0
  37. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/schedules.py +0 -0
  38. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/sqlite.py +0 -0
  39. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/system.py +0 -0
  40. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/tables.py +0 -0
  41. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/timeseries.py +0 -0
  42. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/web_routes.py +0 -0
  43. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/routes/workers.py +0 -0
  44. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/API/utils.py +0 -0
  45. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/__init__.py +0 -0
  46. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/bootstrap.py +0 -0
  47. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/migrate.py +0 -0
  48. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/store.py +0 -0
  49. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Auth/workers.py +0 -0
  50. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/CacheRedis.py +0 -0
  51. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/CollectionMongoDB.py +0 -0
  52. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/__init__.py +0 -0
  53. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/catalog.py +0 -0
  54. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/config.py +0 -0
  55. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/errors.py +0 -0
  56. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/indexes.py +0 -0
  57. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/readers.py +0 -0
  58. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/resolve.py +0 -0
  59. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/DataAPI/service.py +0 -0
  60. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Database.py +0 -0
  61. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Defaults.py +0 -0
  62. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/AWSEC2.py +0 -0
  63. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/AWSS3.py +0 -0
  64. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/AutoDocstrings.py +0 -0
  65. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ClientAPI.py +0 -0
  66. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ClientSocket.py +0 -0
  67. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ClientWebSocket.py +0 -0
  68. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/LogHandlerAPI.py +0 -0
  69. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/LogReader.py +0 -0
  70. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/MongoDBClient.py +0 -0
  71. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/SaveTables.py +0 -0
  72. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ServerSocket.py +0 -0
  73. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/ServerWebSocket.py +0 -0
  74. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/StreamsCache.py +0 -0
  75. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/StreamsPersist.py +0 -0
  76. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/SyncTable.py +0 -0
  77. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/TunnelWebSocket.py +0 -0
  78. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/IO/__init__.py +0 -0
  79. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Listing.py +0 -0
  80. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/LockWaiters.py +0 -0
  81. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Logger.py +0 -0
  82. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/__init__.py +0 -0
  83. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/__main__.py +0 -0
  84. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/client.py +0 -0
  85. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MCP/server.py +0 -0
  86. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Metadata.py +0 -0
  87. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/MultiProc.py +0 -0
  88. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/OpenFIGI.py +0 -0
  89. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Redaction.py +0 -0
  90. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
  91. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/BatchJob.py +0 -0
  92. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ContainerHealth.py +0 -0
  93. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/JobStore.py +0 -0
  94. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/RoutineCommand.py +0 -0
  95. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/RoutineOrigin.py +0 -0
  96. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/Schedule.py +0 -0
  97. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ScheduleMirror.py +0 -0
  98. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
  99. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/ScheduleStore.py +0 -0
  100. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/Scheduler.py +0 -0
  101. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
  102. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/Worker.py +0 -0
  103. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
  104. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/WorkerPool.py +0 -0
  105. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Routines/__init__.py +0 -0
  106. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/SQLite.py +0 -0
  107. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/StreamKafka.py +0 -0
  108. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Symbol.py +0 -0
  109. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitFunctions.py +0 -0
  110. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
  111. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitGenerate.py +0 -0
  112. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitHash.py +0 -0
  113. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableIndexJitLoc.py +0 -0
  114. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TableProtocol.py +0 -0
  115. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/TimeseriesContainer.py +0 -0
  116. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Users.py +0 -0
  117. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Utils.py +0 -0
  118. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
  119. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/__init__.py +0 -0
  120. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/activity_logger.py +0 -0
  121. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/app.py +0 -0
  122. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
  123. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
  124. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/google_logo.png +0 -0
  125. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/kafka_keys.js +0 -0
  126. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/logs.css +0 -0
  127. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/sched.css +0 -0
  128. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/assets/styles.css +0 -0
  129. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/auth.py +0 -0
  130. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/backends.py +0 -0
  131. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/config.py +0 -0
  132. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/containers.py +0 -0
  133. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/identity.py +0 -0
  134. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/kafka_view.py +0 -0
  135. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/mongo_view.py +0 -0
  136. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/proc.py +0 -0
  137. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/redis_view.py +0 -0
  138. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/route_health.py +0 -0
  139. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/route_registry.py +0 -0
  140. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/sched.py +0 -0
  141. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/schedule_editor.py +0 -0
  142. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/server.py +0 -0
  143. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/services.py +0 -0
  144. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/shell.py +0 -0
  145. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/standalone.py +0 -0
  146. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/account.js +0 -0
  147. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/custom.css +0 -0
  148. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/favicon.ico +0 -0
  149. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/favicon.png +0 -0
  150. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/logo.png +0 -0
  151. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/logo_96x96.png +0 -0
  152. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
  153. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/shell.css +0 -0
  154. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/shell.js +0 -0
  155. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/standalone.css +0 -0
  156. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/static/standalone.js +0 -0
  157. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/suggest.py +0 -0
  158. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/account.html +0 -0
  159. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/base.html +0 -0
  160. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/claim.html +0 -0
  161. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/claim_done.html +0 -0
  162. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/dash.html +0 -0
  163. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/index.html +0 -0
  164. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/login.html +0 -0
  165. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/login_2fa.html +0 -0
  166. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/partials/navbar.html +0 -0
  167. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/partials/scripts.html +0 -0
  168. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/recovery_codes.html +0 -0
  169. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/shell.html +0 -0
  170. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/templates/standalone.html +0 -0
  171. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/twofactor.py +0 -0
  172. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/utils.py +0 -0
  173. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/Web/workerpool.py +0 -0
  174. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/__init__.py +0 -0
  175. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/build_info.py +0 -0
  176. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/fleet.py +0 -0
  177. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/preflight.py +0 -0
  178. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/private_files.py +0 -0
  179. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/release_backup.py +0 -0
  180. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/server.py +0 -0
  181. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/services.py +0 -0
  182. {shareddata-6.85.11 → shareddata-6.85.14}/src/SharedData/sharedmutexwin.pyd +0 -0
  183. {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/SOURCES.txt +0 -0
  184. {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/dependency_links.txt +0 -0
  185. {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/entry_points.txt +0 -0
  186. {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/requires.txt +0 -0
  187. {shareddata-6.85.11 → shareddata-6.85.14}/src/shareddata.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shareddata
3
- Version: 6.85.11
3
+ Version: 6.85.14
4
4
  Summary: Memory Mapped / Shared Memory Database with S3 repository
5
5
  Home-page: https://shareddata.net
6
6
  Author: shareddata
@@ -66,7 +66,7 @@ test_requires = [
66
66
 
67
67
  setup(
68
68
  name='shareddata',
69
- version='6.85.11',
69
+ version='6.85.14',
70
70
  description='Memory Mapped / Shared Memory Database with S3 repository',
71
71
  author='shareddata',
72
72
  author_email='admin@shareddata.net',
@@ -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
- else:
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
- else:
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
- os.remove(fpath)
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
- os.remove(fpath)
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
- os.remove(delpath)
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
- shutil.rmtree(localpath)
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
- os.remove(delpath)
1869
+ remove_with_retry(delpath)
1869
1870
  # if folder is empty remove it
1870
1871
  if not any(localpath.iterdir()):
1871
- shutil.rmtree(localpath)
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
- shutil.rmtree(localpath)
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
- os.remove(delpath)
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
- os.remove(fpath)
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
- if (self.table.type != 1 or sys.platform == 'linux') and (
301
- self.table.hdr['hasindex'] == 1 and self.table.hdr['isidxsorted'] == 0):
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
- # No POSIX rename-over-a-mapped-file: remove the old files (the shared mutex is held in STRUCT
693
- # mode, so this process is their only user; another process that still maps one makes the
694
- # unlink fail loudly) and create the empty image in place.
695
- for name in ('data.bin', 'bson.bin', 'dateidx.bin', 'pkey.bin', 'symbolidx.bin', 'portidx.bin'):
696
- try:
697
- (self.filepath.parent / name).unlink()
698
- except FileNotFoundError:
699
- pass
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
- require_rewrite_support()
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
- with open(self.bsonpath, 'rb') as baseline:
1713
- os.fsync(baseline.fileno())
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
- copy_metadata(self.bsonpath, fd)
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
- require_rewrite_support()
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
- os.remove(delpath)
743
+ remove_with_retry(delpath)
578
744
  # if folder is empty remove it
579
745
  if not any(localpath.iterdir()):
580
- shutil.rmtree(localpath)
746
+ rmtree_with_retry(localpath)
581
747
 
582
748
  success = True
583
749