shareddata 6.85.8__tar.gz → 6.85.10__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.8/src/shareddata.egg-info → shareddata-6.85.10}/PKG-INFO +1 -1
  2. {shareddata-6.85.8 → shareddata-6.85.10}/setup.py +1 -1
  3. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Table.py +61 -3
  4. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableProtocol.py +9 -3
  5. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableRewrite.py +27 -3
  6. {shareddata-6.85.8 → shareddata-6.85.10/src/shareddata.egg-info}/PKG-INFO +1 -1
  7. {shareddata-6.85.8 → shareddata-6.85.10}/LICENSE +0 -0
  8. {shareddata-6.85.8 → shareddata-6.85.10}/MANIFEST.in +0 -0
  9. {shareddata-6.85.8 → shareddata-6.85.10}/README.md +0 -0
  10. {shareddata-6.85.8 → shareddata-6.85.10}/pyproject.toml +0 -0
  11. {shareddata-6.85.8 → shareddata-6.85.10}/setup.cfg +0 -0
  12. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/ServerGunicorn.py +0 -0
  13. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/ServerWaitress.py +0 -0
  14. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/__init__.py +0 -0
  15. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/apidocs-dark.css +0 -0
  16. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/apidocs.yml +0 -0
  17. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/auth.py +0 -0
  18. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/constants.py +0 -0
  19. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/download_paths.py +0 -0
  20. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/download_slots.py +0 -0
  21. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/install/worker.ps1 +0 -0
  22. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/install/worker.sh +0 -0
  23. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/listcache.py +0 -0
  24. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/__init__.py +0 -0
  25. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/cache.py +0 -0
  26. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/collections.py +0 -0
  27. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/data.py +0 -0
  28. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/downloads.py +0 -0
  29. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/metadata.py +0 -0
  30. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/operations.py +0 -0
  31. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/schedules.py +0 -0
  32. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/sqlite.py +0 -0
  33. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/system.py +0 -0
  34. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/tables.py +0 -0
  35. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/timeseries.py +0 -0
  36. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/web_routes.py +0 -0
  37. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/routes/workers.py +0 -0
  38. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/API/utils.py +0 -0
  39. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Auth/__init__.py +0 -0
  40. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Auth/bootstrap.py +0 -0
  41. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Auth/migrate.py +0 -0
  42. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Auth/store.py +0 -0
  43. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Auth/workers.py +0 -0
  44. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/CacheRedis.py +0 -0
  45. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/CollectionMongoDB.py +0 -0
  46. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/__init__.py +0 -0
  47. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/catalog.py +0 -0
  48. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/config.py +0 -0
  49. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/errors.py +0 -0
  50. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/indexes.py +0 -0
  51. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/readers.py +0 -0
  52. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/resolve.py +0 -0
  53. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/DataAPI/service.py +0 -0
  54. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Database.py +0 -0
  55. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Defaults.py +0 -0
  56. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/AWSEC2.py +0 -0
  57. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/AWSS3.py +0 -0
  58. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/AutoDocstrings.py +0 -0
  59. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/ClientAPI.py +0 -0
  60. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/ClientSocket.py +0 -0
  61. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/ClientWebSocket.py +0 -0
  62. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/LogHandlerAPI.py +0 -0
  63. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/LogReader.py +0 -0
  64. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/MongoDBClient.py +0 -0
  65. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/SaveTables.py +0 -0
  66. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/ServerSocket.py +0 -0
  67. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/ServerWebSocket.py +0 -0
  68. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/StreamsCache.py +0 -0
  69. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/StreamsPersist.py +0 -0
  70. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/SyncTable.py +0 -0
  71. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/TunnelWebSocket.py +0 -0
  72. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/IO/__init__.py +0 -0
  73. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Listing.py +0 -0
  74. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/LockWaiters.py +0 -0
  75. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Logger.py +0 -0
  76. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/MCP/__init__.py +0 -0
  77. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/MCP/__main__.py +0 -0
  78. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/MCP/client.py +0 -0
  79. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/MCP/server.py +0 -0
  80. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Metadata.py +0 -0
  81. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/MultiProc.py +0 -0
  82. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/OpenFIGI.py +0 -0
  83. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Redaction.py +0 -0
  84. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
  85. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/BatchJob.py +0 -0
  86. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/ContainerHealth.py +0 -0
  87. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/JobStore.py +0 -0
  88. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/RoutineCommand.py +0 -0
  89. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/RoutineOrigin.py +0 -0
  90. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/Schedule.py +0 -0
  91. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/ScheduleMirror.py +0 -0
  92. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
  93. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/ScheduleStore.py +0 -0
  94. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/Scheduler.py +0 -0
  95. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
  96. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/Worker.py +0 -0
  97. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
  98. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/WorkerLib.py +0 -0
  99. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/WorkerPool.py +0 -0
  100. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Routines/__init__.py +0 -0
  101. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/SQLite.py +0 -0
  102. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/SharedData.py +0 -0
  103. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/SharedNumpy.py +0 -0
  104. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/StreamKafka.py +0 -0
  105. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Symbol.py +0 -0
  106. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableDisk.py +0 -0
  107. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableIndex.py +0 -0
  108. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableIndexJitFunctions.py +0 -0
  109. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
  110. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableIndexJitGenerate.py +0 -0
  111. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableIndexJitHash.py +0 -0
  112. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableIndexJitLoc.py +0 -0
  113. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TableRead.py +0 -0
  114. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TimeSeriesDisk.py +0 -0
  115. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/TimeseriesContainer.py +0 -0
  116. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Users.py +0 -0
  117. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Utils.py +0 -0
  118. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
  119. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/__init__.py +0 -0
  120. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/activity_logger.py +0 -0
  121. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/app.py +0 -0
  122. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
  123. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
  124. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/google_logo.png +0 -0
  125. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/kafka_keys.js +0 -0
  126. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/logs.css +0 -0
  127. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/sched.css +0 -0
  128. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/assets/styles.css +0 -0
  129. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/auth.py +0 -0
  130. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/backends.py +0 -0
  131. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/config.py +0 -0
  132. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/containers.py +0 -0
  133. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/identity.py +0 -0
  134. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/kafka_view.py +0 -0
  135. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/mongo_view.py +0 -0
  136. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/proc.py +0 -0
  137. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/redis_view.py +0 -0
  138. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/route_health.py +0 -0
  139. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/route_registry.py +0 -0
  140. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/sched.py +0 -0
  141. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/schedule_editor.py +0 -0
  142. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/server.py +0 -0
  143. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/services.py +0 -0
  144. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/shell.py +0 -0
  145. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/standalone.py +0 -0
  146. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/account.js +0 -0
  147. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/custom.css +0 -0
  148. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/favicon.ico +0 -0
  149. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/favicon.png +0 -0
  150. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/logo.png +0 -0
  151. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/logo_96x96.png +0 -0
  152. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
  153. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/shell.css +0 -0
  154. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/shell.js +0 -0
  155. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/standalone.css +0 -0
  156. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/static/standalone.js +0 -0
  157. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/suggest.py +0 -0
  158. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/account.html +0 -0
  159. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/base.html +0 -0
  160. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/claim.html +0 -0
  161. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/claim_done.html +0 -0
  162. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/dash.html +0 -0
  163. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/index.html +0 -0
  164. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/login.html +0 -0
  165. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/login_2fa.html +0 -0
  166. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/partials/navbar.html +0 -0
  167. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/partials/scripts.html +0 -0
  168. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/recovery_codes.html +0 -0
  169. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/shell.html +0 -0
  170. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/templates/standalone.html +0 -0
  171. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/twofactor.py +0 -0
  172. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/utils.py +0 -0
  173. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/Web/workerpool.py +0 -0
  174. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/__init__.py +0 -0
  175. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/build_info.py +0 -0
  176. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/fleet.py +0 -0
  177. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/preflight.py +0 -0
  178. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/private_files.py +0 -0
  179. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/release_backup.py +0 -0
  180. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/server.py +0 -0
  181. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/services.py +0 -0
  182. {shareddata-6.85.8 → shareddata-6.85.10}/src/SharedData/sharedmutexwin.pyd +0 -0
  183. {shareddata-6.85.8 → shareddata-6.85.10}/src/shareddata.egg-info/SOURCES.txt +0 -0
  184. {shareddata-6.85.8 → shareddata-6.85.10}/src/shareddata.egg-info/dependency_links.txt +0 -0
  185. {shareddata-6.85.8 → shareddata-6.85.10}/src/shareddata.egg-info/entry_points.txt +0 -0
  186. {shareddata-6.85.8 → shareddata-6.85.10}/src/shareddata.egg-info/requires.txt +0 -0
  187. {shareddata-6.85.8 → shareddata-6.85.10}/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.8
3
+ Version: 6.85.10
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.8',
69
+ version='6.85.10',
70
70
  description='Memory Mapped / Shared Memory Database with S3 repository',
71
71
  author='shareddata',
72
72
  author_email='admin@shareddata.net',
@@ -22,6 +22,7 @@ import uuid
22
22
 
23
23
  from SharedData.Logger import Logger
24
24
  from SharedData.LockWaiters import TableBusy
25
+ from SharedData.TableRewrite import BsonIntegrityError
25
26
  from SharedData.IO.AWSS3 import S3Upload, S3Download, S3ListFolder, S3GetMtime, S3DeleteFile, UpdateModTime
26
27
  from SharedData.TableIndex import TableIndex
27
28
  from SharedData.SharedNumpy import SharedNumpy
@@ -38,6 +39,11 @@ from SharedData.TableProtocol import (IDLE, WRITE, STRUCT, DIRTY, mode, epoch,
38
39
  # orphaned (append-only) has nothing to defragment, and rewriting it would only copy identical bytes.
39
40
  BSON_DEFRAG_MIN_BYTES = 64 * 1024
40
41
  BSON_DEFRAG_MIN_FRACTION = 0.0
42
+ # The rewrite holds the table lock and copies every live byte: for a large BSON file that must buy
43
+ # something. Above BSON_DEFRAG_LARGE_BYTES of allocated BSON, compact once at least this fraction is orphaned
44
+ # (a 43 GB file with 23 MB orphaned is 0.05%: not worth minutes of blocked ingestion).
45
+ BSON_DEFRAG_LARGE_BYTES = 1024 * 1024 * 1024
46
+ BSON_DEFRAG_LARGE_MIN_FRACTION = 0.01
41
47
 
42
48
 
43
49
  class TableNotFound(FileNotFoundError):
@@ -1623,12 +1629,43 @@ class Table:
1623
1629
  usage['orphaned'] = max(usage['used'] - usage['live'], 0)
1624
1630
  return usage
1625
1631
 
1632
+ def _bson_mismatches(self, pointers, sizes, live_idx):
1633
+ """Rows whose pointer does not lead to a BSON document of exactly the recorded size: the int32 at
1634
+ the pointer is the document length, and a document ends with a zero byte."""
1635
+ import mmap
1636
+ bad = []
1637
+ if not len(live_idx):
1638
+ return np.zeros(0, dtype=np.int64)
1639
+ with open(self.bsonpath, 'rb') as source:
1640
+ view = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ)
1641
+ data = None
1642
+ try:
1643
+ data = np.frombuffer(view, dtype=np.uint8)
1644
+ span = np.arange(4)
1645
+ for first in range(0, len(live_idx), 1 << 20):
1646
+ rows = live_idx[first:first + (1 << 20)]
1647
+ short = sizes[rows] < 5 # the smallest document, {}, is 5 bytes
1648
+ bad.append(rows[short])
1649
+ rows = rows[~short]
1650
+ start, length = pointers[rows], sizes[rows]
1651
+ head = data[start[:, None] + span].astype(np.int64)
1652
+ declared = head[:, 0] | (head[:, 1] << 8) | (head[:, 2] << 16) | (head[:, 3] << 24)
1653
+ ends_with_zero = data[start + length - 1] == 0
1654
+ bad.append(rows[(declared != length) | ~ends_with_zero])
1655
+ finally:
1656
+ data = None # release the buffer export before closing the map
1657
+ view.close()
1658
+ return np.concatenate(bad) if bad else np.zeros(0, dtype=np.int64)
1659
+
1626
1660
  def compact_bson_if_worthwhile(self):
1627
1661
  """Compact the BSON file when enough of it is orphaned; None when it is not worth a rewrite."""
1628
1662
  usage = self.bson_usage()
1629
1663
  if usage['orphaned'] < BSON_DEFRAG_MIN_BYTES \
1630
1664
  or usage['orphaned'] < BSON_DEFRAG_MIN_FRACTION * max(usage['used'], 1):
1631
1665
  return None
1666
+ if usage['used'] > BSON_DEFRAG_LARGE_BYTES \
1667
+ and usage['orphaned'] < BSON_DEFRAG_LARGE_MIN_FRACTION * usage['used']:
1668
+ return None
1632
1669
  return self.defragment_bson(acquire=False)
1633
1670
 
1634
1671
  def _defragment_bson_disk(self, chunk_size, acquire):
@@ -1671,6 +1708,13 @@ class Table:
1671
1708
  original_size = self.bsonpath.stat().st_size
1672
1709
  if len(live_idx) and (np.any(pointers[live_idx] < 0) or np.any(pointers[live_idx] + sizes[live_idx] > original_size)):
1673
1710
  raise RewriteError(f'{self.relpath}: a record points outside its BSON file; nothing was changed')
1711
+ mismatched = self._bson_mismatches(pointers, sizes, live_idx)
1712
+ if len(mismatched):
1713
+ from SharedData.TableRewrite import BsonIntegrityError
1714
+ raise BsonIntegrityError(
1715
+ f'{self.relpath}: {len(mismatched)} of {len(live_idx)} records do not point at a BSON document '
1716
+ f'of their recorded size (first rows {mismatched[:5].tolist()}); not compacted, '
1717
+ 'the unreferenced bytes may be the documents those records lost')
1674
1718
  new_offsets = np.zeros(len(live_idx), dtype=np.int64)
1675
1719
  if len(live_idx):
1676
1720
  new_offsets[1:] = np.cumsum(sizes[live_idx])[:-1]
@@ -1720,7 +1764,8 @@ class Table:
1720
1764
 
1721
1765
  with structure(self):
1722
1766
  publish_rewrite(self, None, 'defrag', start=0, transform=move_pointers,
1723
- header_updates={'next_bson_ptr': payload, 'mtime': max(datetime.now().timestamp(), float(self.hdr['mtime']) + 0.001)},
1767
+ header_updates={'next_bson_ptr': payload, 'mtime': max(datetime.now().timestamp(), float(self.hdr['mtime']) + 0.001),
1768
+ 'isidxcreated': int(self.hdr['isidxcreated'])}, # pointers only: the index stays valid
1724
1769
  bson=dict(token=token, stage=stage, sha256=digest.hexdigest(), size=new_size))
1725
1770
  stats = {'original_size': original_size, 'new_size': new_size,
1726
1771
  'reclaimed': original_size - new_size, 'live_bytes': payload, 'live_rows': int(len(live_idx))}
@@ -2007,7 +2052,13 @@ class Table:
2007
2052
  if remote and self.is_schemaless and self.type == 1 and self.bsonpath.exists():
2008
2053
  remote_bson_mtime = S3GetMtime(str(self.bsonpath) + '.gzip', self.database_folder)
2009
2054
  if force_write or remote_bson_mtime is None or remote_bson_mtime < mtime:
2010
- if self.compact_bson_if_worthwhile() is not None:
2055
+ try:
2056
+ stats = self.compact_bson_if_worthwhile()
2057
+ except BsonIntegrityError as error:
2058
+ # Loud, every night, but the upload goes on as it was: nothing is rewritten.
2059
+ Logger.log.error('BSON compaction skipped: %s' % error)
2060
+ stats = None
2061
+ if stats is not None:
2011
2062
  compacted = True
2012
2063
  # New pointers: head, tail and BSON are all rewritten and all uploaded, under a
2013
2064
  # publication time later than anything the remote holds, so consumers see the change.
@@ -2967,11 +3018,18 @@ class Table:
2967
3018
  self._set_mode(DIRTY)
2968
3019
  # A nested alias may follow a failed publication by this thread;
2969
3020
  # only an in-progress structural operation of this thread is STRUCT.
3021
+ self._rewrite_kept_index = False
2970
3022
  if self.type == 1 and mode(self.mutex['type']) != STRUCT:
2971
3023
  from SharedData.TableRewrite import recover_rewrite
2972
3024
  recover_rewrite(self)
3025
+ # A pending defrag descriptor means the dead holder was only in the defrag (one holder at a time, a
3026
+ # crashed upsert would have been repaired before it could start): keys and rows never moved, so the
3027
+ # index it left is valid and the dirty repair must not rebuild it (a rebuild can be refused).
3028
+ kept_index = getattr(self, '_rewrite_kept_index', False)
2973
3029
  self._refresh_mapping(dirty=dirty)
2974
- if dirty and self.hdr is not None:
3030
+ if kept_index and self.hasindex:
3031
+ self.index.rememmap() # keep the index, but map its files as they are now (the dirty refresh skips it)
3032
+ if dirty and self.hdr is not None and not kept_index:
2975
3033
  self.hdr['isidxcreated'] = 0
2976
3034
  if self.hasindex:
2977
3035
  self.index.initialize()
@@ -154,9 +154,15 @@ def structure(table):
154
154
  try:
155
155
  yield
156
156
  except BaseException:
157
- table._set_mode(DIRTY)
158
- if table.hdr is not None:
159
- table.hdr['isidxcreated'] = 0
157
+ if getattr(table, '_rewrite_live_untouched', False):
158
+ # A defrag that failed before it replaced any live file: the table is exactly as it was, so it is
159
+ # neither dirty nor does its index need a rebuild (which a legacy table can refuse).
160
+ table._rewrite_live_untouched = False
161
+ table._set_mode(previous)
162
+ else:
163
+ table._set_mode(DIRTY)
164
+ if table.hdr is not None:
165
+ table.hdr['isidxcreated'] = 0
160
166
  raise
161
167
  else:
162
168
  word = int(table.mutex['type'])
@@ -18,6 +18,11 @@ class RewriteError(RuntimeError):
18
18
  pass
19
19
 
20
20
 
21
+ class BsonIntegrityError(RewriteError):
22
+ """Records whose bson_ptr/bson_size do not describe a BSON document. Compacting such a table would
23
+ keep only the byte ranges the records point at and discard the documents they miss."""
24
+
25
+
21
26
  def checkpoint(name):
22
27
  """Test seam, deliberately not controlled by production environment."""
23
28
 
@@ -185,6 +190,8 @@ def _load(table):
185
190
  raise ValueError('invalid BSON digest')
186
191
  elif 'bson' in descriptor:
187
192
  raise ValueError('BSON section requires version 2')
193
+ if 'index_valid' in descriptor and descriptor['index_valid'] not in (0, 1):
194
+ raise ValueError('invalid index validity')
188
195
  return descriptor
189
196
  except Exception as error:
190
197
  raise RewriteError('invalid pending record rewrite; files preserved') from error
@@ -203,10 +210,21 @@ def _finish(table, descriptor, recovery=False):
203
210
  if 'bson' in descriptor:
204
211
  table._reset_bson_mapping() # the BSON file was replaced together with the records
205
212
  checkpoint(prefix + 'data_attached')
206
- table.hdr['isidxcreated'] = 0
207
- sync_header(table)
213
+ # A defrag moves BSON pointers only: keys, row positions and so every index file stay valid and mapped.
214
+ # initialize() always resets and rebuilds the index, and the rebuild refuses legacy tables with an
215
+ # empty-key row ("explicit cleanup required"), so a defrag neither invalidates nor rebuilds it.
216
+ keeps_index = descriptor.get('operation') == 'defrag'
217
+ table._rewrite_kept_index = keeps_index # acquire(): a dirty owner that just finished such a recovery must not rebuild
218
+ if not keeps_index:
219
+ table.hdr['isidxcreated'] = 0
220
+ sync_header(table)
221
+ elif descriptor.get('index_valid') is not None and int(table.hdr['isidxcreated']) != descriptor['index_valid']:
222
+ # structure() clears the bit when a failure follows the attach: the index files never changed, so the
223
+ # validity they had before the defrag is restored (descriptors of older releases do not carry it).
224
+ table.hdr['isidxcreated'] = descriptor['index_valid']
225
+ sync_header(table)
208
226
  checkpoint(prefix + 'index_invalid_synced')
209
- if table.hasindex:
227
+ if table.hasindex and not keeps_index:
210
228
  table.index.initialize()
211
229
  sync_header(table)
212
230
  checkpoint(prefix + 'final_header_synced')
@@ -312,6 +330,9 @@ def publish_rewrite(table, order, operation, *, capacity=None, start=0, sorted_f
312
330
  raise RewriteError('disk record publication requires writer ownership')
313
331
  if pending_path(table).exists():
314
332
  raise RewriteError('unresolved record rewrite')
333
+ # Until the first live file is replaced a failing defrag leaves the table untouched (structure() then
334
+ # restores the mode instead of marking it dirty).
335
+ table._rewrite_live_untouched = operation == 'defrag'
315
336
  checkpoint('struct_enter')
316
337
  table.shf_data.flush()
317
338
  sync_header(table)
@@ -388,6 +409,8 @@ def publish_rewrite(table, order, operation, *, capacity=None, start=0, sorted_f
388
409
  transaction=token, table=str(table.relpath).replace('\\', '/'),
389
410
  operation=operation, start=int(start), stage=stage.name,
390
411
  old=old, new=new)
412
+ if operation == 'defrag':
413
+ descriptor['index_valid'] = int(table.hdr['isidxcreated'])
391
414
  if bson:
392
415
  bson_stage = Path(table.bsonpath).parent / ('bson.bin.stage.' + token)
393
416
  if not bson_stage.is_file() or bson_stage.stat().st_size != bson['size']:
@@ -407,6 +430,7 @@ def publish_rewrite(table, order, operation, *, capacity=None, start=0, sorted_f
407
430
  checkpoint('prepare_installed')
408
431
  fsync_directory(stage.parent)
409
432
  checkpoint('prepare_dir_synced')
433
+ table._rewrite_live_untouched = False # from the first replacement on, recovery and DIRTY apply
410
434
  if bson:
411
435
  os.replace(bson_stage, table.bsonpath)
412
436
  checkpoint('bson_replaced')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shareddata
3
- Version: 6.85.8
3
+ Version: 6.85.10
4
4
  Summary: Memory Mapped / Shared Memory Database with S3 repository
5
5
  Home-page: https://shareddata.net
6
6
  Author: shareddata
File without changes
File without changes
File without changes
File without changes
File without changes