DefStream 0.1.19__tar.gz → 0.1.20__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 (250) hide show
  1. {defstream-0.1.19 → defstream-0.1.20}/DefStream.egg-info/PKG-INFO +1 -1
  2. {defstream-0.1.19 → defstream-0.1.20}/DefStream.egg-info/SOURCES.txt +14 -0
  3. {defstream-0.1.19 → defstream-0.1.20}/PKG-INFO +1 -1
  4. {defstream-0.1.19 → defstream-0.1.20}/docs/RabbitMQ.en.md +44 -2
  5. {defstream-0.1.19 → defstream-0.1.20}/docs/RabbitMQ.md +42 -1
  6. defstream-0.1.20/issues/feature/47/CHANGELOG.md +25 -0
  7. defstream-0.1.20/issues/feature/47/IMPLEMENTS.md +22 -0
  8. defstream-0.1.20/issues/feature/47/PLAN.md +27 -0
  9. defstream-0.1.20/issues/feature/47/README.md +72 -0
  10. defstream-0.1.20/issues/feature/47/REQUIREMENTS.md +101 -0
  11. defstream-0.1.20/issues/feature/47/TASKS.md +17 -0
  12. defstream-0.1.20/issues/feature/47/implements/1793271568985.md +10 -0
  13. defstream-0.1.20/issues/feature/47/implements/1793272077065.md +10 -0
  14. defstream-0.1.20/issues/feature/47/tasks/1793271236067.md +9 -0
  15. defstream-0.1.20/issues/feature/47/tasks/1793271384432.md +8 -0
  16. defstream-0.1.20/issues/feature/47/tasks/1793271506089.md +8 -0
  17. defstream-0.1.20/issues/feature/47/tasks/1793271819682.md +35 -0
  18. defstream-0.1.20/issues/feature/47/tasks/1793272077066.md +14 -0
  19. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Connection.py +3 -1
  20. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Subscription.py +17 -1
  21. defstream-0.1.20/tests/test_rabbitmq_timeouts.py +278 -0
  22. {defstream-0.1.19 → defstream-0.1.20}/.github/ISSUE_TEMPLATE/01.feature.yml +0 -0
  23. {defstream-0.1.19 → defstream-0.1.20}/.github/ISSUE_TEMPLATE/02.bug.yml +0 -0
  24. {defstream-0.1.19 → defstream-0.1.20}/.github/ISSUE_TEMPLATE/03.fix.yml +0 -0
  25. {defstream-0.1.19 → defstream-0.1.20}/.github/ISSUE_TEMPLATE/04.task.yml +0 -0
  26. {defstream-0.1.19 → defstream-0.1.20}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  27. {defstream-0.1.19 → defstream-0.1.20}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  28. {defstream-0.1.19 → defstream-0.1.20}/.github/workflows/ci.yml +0 -0
  29. {defstream-0.1.19 → defstream-0.1.20}/.gitignore +0 -0
  30. {defstream-0.1.19 → defstream-0.1.20}/DefStream.egg-info/dependency_links.txt +0 -0
  31. {defstream-0.1.19 → defstream-0.1.20}/DefStream.egg-info/requires.txt +0 -0
  32. {defstream-0.1.19 → defstream-0.1.20}/DefStream.egg-info/top_level.txt +0 -0
  33. {defstream-0.1.19 → defstream-0.1.20}/LICENSE +0 -0
  34. {defstream-0.1.19 → defstream-0.1.20}/README.en.md +0 -0
  35. {defstream-0.1.19 → defstream-0.1.20}/README.md +0 -0
  36. {defstream-0.1.19 → defstream-0.1.20}/README.setup.md +0 -0
  37. {defstream-0.1.19 → defstream-0.1.20}/docs/Contracts.en.md +0 -0
  38. {defstream-0.1.19 → defstream-0.1.20}/docs/Contracts.md +0 -0
  39. {defstream-0.1.19 → defstream-0.1.20}/docs/GettingStarted.en.md +0 -0
  40. {defstream-0.1.19 → defstream-0.1.20}/docs/GettingStarted.md +0 -0
  41. {defstream-0.1.19 → defstream-0.1.20}/docs/LifecycleHooks.en.md +0 -0
  42. {defstream-0.1.19 → defstream-0.1.20}/docs/LifecycleHooks.md +0 -0
  43. {defstream-0.1.19 → defstream-0.1.20}/docs/Operations.en.md +0 -0
  44. {defstream-0.1.19 → defstream-0.1.20}/docs/Operations.md +0 -0
  45. {defstream-0.1.19 → defstream-0.1.20}/docs/README.en.md +0 -0
  46. {defstream-0.1.19 → defstream-0.1.20}/docs/README.md +0 -0
  47. {defstream-0.1.19 → defstream-0.1.20}/docs/Redis.en.md +0 -0
  48. {defstream-0.1.19 → defstream-0.1.20}/docs/Redis.md +0 -0
  49. {defstream-0.1.19 → defstream-0.1.20}/docs/Tasks.en.md +0 -0
  50. {defstream-0.1.19 → defstream-0.1.20}/docs/Tasks.md +0 -0
  51. {defstream-0.1.19 → defstream-0.1.20}/instructions/CODE.instructions.md +0 -0
  52. {defstream-0.1.19 → defstream-0.1.20}/instructions/DOCS.instructions.md +0 -0
  53. {defstream-0.1.19 → defstream-0.1.20}/instructions/README.instructions.md +0 -0
  54. {defstream-0.1.19 → defstream-0.1.20}/issues/bug/16/IMPLEMENTS.md +0 -0
  55. {defstream-0.1.19 → defstream-0.1.20}/issues/bug/16/ISSUE.md +0 -0
  56. {defstream-0.1.19 → defstream-0.1.20}/issues/bug/16/README.md +0 -0
  57. {defstream-0.1.19 → defstream-0.1.20}/issues/bug/16/REQUIREMENTS.md +0 -0
  58. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/1/IMPLEMENTS.md +0 -0
  59. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/1/README.md +0 -0
  60. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/1/REQUIREMENTS.md +0 -0
  61. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/1/TASK.md +0 -0
  62. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/1/ci.refs.json +0 -0
  63. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/8/IMPLEMENTS.md +0 -0
  64. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/8/README.md +0 -0
  65. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/8/REQUIREMENTS.md +0 -0
  66. {defstream-0.1.19 → defstream-0.1.20}/issues/feature/8/ci.refs.json +0 -0
  67. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/10/IMPLEMENTS.md +0 -0
  68. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/10/README.md +0 -0
  69. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/10/REQUIREMENTS.md +0 -0
  70. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/13/README.md +0 -0
  71. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/21/IMPLEMENTS.md +0 -0
  72. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/21/ISSUE.md +0 -0
  73. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/21/README.md +0 -0
  74. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/21/REQUIREMENTS.md +0 -0
  75. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/24/IMPLEMENTS.md +0 -0
  76. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/24/ISSUE.md +0 -0
  77. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/24/README.md +0 -0
  78. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/24/REQUIREMENTS.md +0 -0
  79. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/25/IMPLEMENTS.md +0 -0
  80. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/25/ISSUE.md +0 -0
  81. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/25/README.md +0 -0
  82. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/25/REQUIREMENTS.md +0 -0
  83. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/25/TASK.md +0 -0
  84. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/26/IMPLEMENTS.md +0 -0
  85. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/26/ISSUE.md +0 -0
  86. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/26/README.md +0 -0
  87. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/26/REQUIREMENTS.md +0 -0
  88. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/29/ISSUE.md +0 -0
  89. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/29/README.md +0 -0
  90. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/29/REQUIREMENTS.md +0 -0
  91. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/29/TASK.md +0 -0
  92. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/3/IMPLEMENTS.md +0 -0
  93. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/3/README.md +0 -0
  94. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/3/REQUIREMENTS.md +0 -0
  95. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/3/ci.refs.json +0 -0
  96. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/CHANGELOG.md +0 -0
  97. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/IMPLEMENTS.md +0 -0
  98. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/PLAN.md +0 -0
  99. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/README.md +0 -0
  100. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/REQUIREMENTS.md +0 -0
  101. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/TASKS.md +0 -0
  102. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/implements/1793257519898.md +0 -0
  103. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/implements/1793257826069.md +0 -0
  104. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/tasks/1793256929057.md +0 -0
  105. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/tasks/1793256990072.md +0 -0
  106. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/tasks/1793257216492.md +0 -0
  107. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/tasks/1793257519899.md +0 -0
  108. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/41/tasks/1793257826068.md +0 -0
  109. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/CHANGELOG.md +0 -0
  110. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/IMPLEMENTS.md +0 -0
  111. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/PLAN.md +0 -0
  112. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/README.md +0 -0
  113. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/REQUIREMENTS.md +0 -0
  114. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/TASKS.md +0 -0
  115. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/implements/1793260818660.md +0 -0
  116. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/implements/1793261447018.md +0 -0
  117. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/tasks/1793260818661.md +0 -0
  118. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/tasks/1793261447019.md +0 -0
  119. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/43/tasks/1793262314955.md +0 -0
  120. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/6/IMPLEMENTS.md +0 -0
  121. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/6/README.md +0 -0
  122. {defstream-0.1.19 → defstream-0.1.20}/issues/fix/6/REQUIREMENTS.md +0 -0
  123. {defstream-0.1.19 → defstream-0.1.20}/issues/task/18/IMPLEMENTS.md +0 -0
  124. {defstream-0.1.19 → defstream-0.1.20}/issues/task/18/README.md +0 -0
  125. {defstream-0.1.19 → defstream-0.1.20}/issues/task/18/REQUIREMENTS.md +0 -0
  126. {defstream-0.1.19 → defstream-0.1.20}/issues/task/32/IMPLEMENTS.md +0 -0
  127. {defstream-0.1.19 → defstream-0.1.20}/issues/task/32/ISSUE.md +0 -0
  128. {defstream-0.1.19 → defstream-0.1.20}/issues/task/32/PLAN.md +0 -0
  129. {defstream-0.1.19 → defstream-0.1.20}/issues/task/32/README.md +0 -0
  130. {defstream-0.1.19 → defstream-0.1.20}/issues/task/32/REQUIREMENTS.md +0 -0
  131. {defstream-0.1.19 → defstream-0.1.20}/issues/task/32/TASK.md +0 -0
  132. {defstream-0.1.19 → defstream-0.1.20}/issues/task/34/CHANGELOG.md +0 -0
  133. {defstream-0.1.19 → defstream-0.1.20}/issues/task/34/IMPLEMENTS.md +0 -0
  134. {defstream-0.1.19 → defstream-0.1.20}/issues/task/34/PLAN.md +0 -0
  135. {defstream-0.1.19 → defstream-0.1.20}/issues/task/34/README.md +0 -0
  136. {defstream-0.1.19 → defstream-0.1.20}/issues/task/34/REQUIREMENTS.md +0 -0
  137. {defstream-0.1.19 → defstream-0.1.20}/issues/task/36/CHANGELOG.md +0 -0
  138. {defstream-0.1.19 → defstream-0.1.20}/issues/task/36/IMPLEMENTS.md +0 -0
  139. {defstream-0.1.19 → defstream-0.1.20}/issues/task/36/PLAN.md +0 -0
  140. {defstream-0.1.19 → defstream-0.1.20}/issues/task/36/README.md +0 -0
  141. {defstream-0.1.19 → defstream-0.1.20}/issues/task/36/REQUIREMENTS.md +0 -0
  142. {defstream-0.1.19 → defstream-0.1.20}/samples/Broker/RabbitMQ/Sample.RPC.py +0 -0
  143. {defstream-0.1.19 → defstream-0.1.20}/samples/Broker/RabbitMQ/Sample.Stream.py +0 -0
  144. {defstream-0.1.19 → defstream-0.1.20}/samples/Broker/RabbitMQ/Sample.py +0 -0
  145. {defstream-0.1.19 → defstream-0.1.20}/samples/Broker/Redis/Sample.Stream.py +0 -0
  146. {defstream-0.1.19 → defstream-0.1.20}/samples/Broker/Redis/Sample.py +0 -0
  147. {defstream-0.1.19 → defstream-0.1.20}/samples/Broker/Sample.py +0 -0
  148. {defstream-0.1.19 → defstream-0.1.20}/samples/Worker/RabbitMQ/Client.py +0 -0
  149. {defstream-0.1.19 → defstream-0.1.20}/samples/Worker/RabbitMQ/Tasks.py +0 -0
  150. {defstream-0.1.19 → defstream-0.1.20}/samples/Worker/RabbitMQ/Worker.py +0 -0
  151. {defstream-0.1.19 → defstream-0.1.20}/samples/Worker/Redis/Sample.py +0 -0
  152. {defstream-0.1.19 → defstream-0.1.20}/samples/Worker/Sample.py +0 -0
  153. {defstream-0.1.19 → defstream-0.1.20}/setup.cfg +0 -0
  154. {defstream-0.1.19 → defstream-0.1.20}/setup.py +0 -0
  155. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Configuration.py +0 -0
  156. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Connection.py +0 -0
  157. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Consumer.py +0 -0
  158. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Errors.py +0 -0
  159. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Event.py +0 -0
  160. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Configuration.py +0 -0
  161. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Consumer.py +0 -0
  162. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Event.py +0 -0
  163. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Exchange.py +0 -0
  164. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Queue.py +0 -0
  165. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Serializer.py +0 -0
  166. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/Stream.py +0 -0
  167. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/RabbitMQ/__init__.py +0 -0
  168. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Configuration.py +0 -0
  169. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Connection.py +0 -0
  170. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Consumer.py +0 -0
  171. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Event.py +0 -0
  172. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Queue.py +0 -0
  173. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Serializer.py +0 -0
  174. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/Subscription.py +0 -0
  175. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/Redis/__init__.py +0 -0
  176. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Implements/__init__.py +0 -0
  177. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Priority.py +0 -0
  178. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/Protocol.py +0 -0
  179. {defstream-0.1.19 → defstream-0.1.20}/src/Broker/__init__.py +0 -0
  180. {defstream-0.1.19 → defstream-0.1.20}/src/Descriptor/DataObject.py +0 -0
  181. {defstream-0.1.19 → defstream-0.1.20}/src/Descriptor/Types.py +0 -0
  182. {defstream-0.1.19 → defstream-0.1.20}/src/Descriptor/Value.py +0 -0
  183. {defstream-0.1.19 → defstream-0.1.20}/src/Descriptor/__init__.py +0 -0
  184. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extension.py +0 -0
  185. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Metrics.py +0 -0
  186. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Ping.py +0 -0
  187. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Play.html +0 -0
  188. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Play.py +0 -0
  189. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Specifications.py +0 -0
  190. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Status.html +0 -0
  191. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/Status.py +0 -0
  192. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Extensions/__init__.py +0 -0
  193. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Inspector.py +0 -0
  194. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/Response.py +0 -0
  195. {defstream-0.1.19 → defstream-0.1.20}/src/Inspector/__init__.py +0 -0
  196. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Pattern.py +0 -0
  197. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Array.py +0 -0
  198. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Common.py +0 -0
  199. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Compare.py +0 -0
  200. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Condition.py +0 -0
  201. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/DataObject.py +0 -0
  202. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/DateTime.py +0 -0
  203. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Number.py +0 -0
  204. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Object.py +0 -0
  205. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Size.py +0 -0
  206. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/String.py +0 -0
  207. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/Type.py +0 -0
  208. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Patterns/__init__.py +0 -0
  209. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Validate.py +0 -0
  210. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/Validator.py +0 -0
  211. {defstream-0.1.19 → defstream-0.1.20}/src/Validator/__init__.py +0 -0
  212. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Executor.py +0 -0
  213. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Message.py +0 -0
  214. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Pool.py +0 -0
  215. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Run.py +0 -0
  216. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Specification.py +0 -0
  217. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Startup.py +0 -0
  218. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/Worker.py +0 -0
  219. {defstream-0.1.19 → defstream-0.1.20}/src/Worker/__init__.py +0 -0
  220. {defstream-0.1.19 → defstream-0.1.20}/src/__init__.py +0 -0
  221. {defstream-0.1.19 → defstream-0.1.20}/tests/_support.py +0 -0
  222. {defstream-0.1.19 → defstream-0.1.20}/tests/test_broker_packaging.py +0 -0
  223. {defstream-0.1.19 → defstream-0.1.20}/tests/test_descriptor.py +0 -0
  224. {defstream-0.1.19 → defstream-0.1.20}/tests/test_inspector.py +0 -0
  225. {defstream-0.1.19 → defstream-0.1.20}/tests/test_inspector_logging.py +0 -0
  226. {defstream-0.1.19 → defstream-0.1.20}/tests/test_inspector_play.py +0 -0
  227. {defstream-0.1.19 → defstream-0.1.20}/tests/test_lifecycle_hooks.py +0 -0
  228. {defstream-0.1.19 → defstream-0.1.20}/tests/test_logging.py +0 -0
  229. {defstream-0.1.19 → defstream-0.1.20}/tests/test_pool_callbacks.py +0 -0
  230. {defstream-0.1.19 → defstream-0.1.20}/tests/test_process_signals.py +0 -0
  231. {defstream-0.1.19 → defstream-0.1.20}/tests/test_protocol_contracts.py +0 -0
  232. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_broker_methods.py +0 -0
  233. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_integration.py +0 -0
  234. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_logging.py +0 -0
  235. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_priorities.py +0 -0
  236. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_rpc.py +0 -0
  237. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_rpc_integration.py +0 -0
  238. {defstream-0.1.19 → defstream-0.1.20}/tests/test_rabbitmq_worker.py +0 -0
  239. {defstream-0.1.19 → defstream-0.1.20}/tests/test_redis.py +0 -0
  240. {defstream-0.1.19 → defstream-0.1.20}/tests/test_redis_integration.py +0 -0
  241. {defstream-0.1.19 → defstream-0.1.20}/tests/test_redis_logging.py +0 -0
  242. {defstream-0.1.19 → defstream-0.1.20}/tests/test_runner_hooks.py +0 -0
  243. {defstream-0.1.19 → defstream-0.1.20}/tests/test_specifications.py +0 -0
  244. {defstream-0.1.19 → defstream-0.1.20}/tests/test_status.py +0 -0
  245. {defstream-0.1.19 → defstream-0.1.20}/tests/test_status_ui.cjs +0 -0
  246. {defstream-0.1.19 → defstream-0.1.20}/tests/test_task_messages.py +0 -0
  247. {defstream-0.1.19 → defstream-0.1.20}/tests/test_task_priorities.py +0 -0
  248. {defstream-0.1.19 → defstream-0.1.20}/tests/test_validator_patterns.py +0 -0
  249. {defstream-0.1.19 → defstream-0.1.20}/tests/test_worker.py +0 -0
  250. {defstream-0.1.19 → defstream-0.1.20}/tests/test_worker_startup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DefStream
3
- Version: 0.1.19
3
+ Version: 0.1.20
4
4
  Summary: Synchronous message processing with pluggable broker adapters
5
5
  Home-page: https://github.com/yong5eon/DefStream
6
6
  Author: Heo Yongseon
@@ -44,6 +44,19 @@ issues/feature/1/README.md
44
44
  issues/feature/1/REQUIREMENTS.md
45
45
  issues/feature/1/TASK.md
46
46
  issues/feature/1/ci.refs.json
47
+ issues/feature/47/CHANGELOG.md
48
+ issues/feature/47/IMPLEMENTS.md
49
+ issues/feature/47/PLAN.md
50
+ issues/feature/47/README.md
51
+ issues/feature/47/REQUIREMENTS.md
52
+ issues/feature/47/TASKS.md
53
+ issues/feature/47/implements/1793271568985.md
54
+ issues/feature/47/implements/1793272077065.md
55
+ issues/feature/47/tasks/1793271236067.md
56
+ issues/feature/47/tasks/1793271384432.md
57
+ issues/feature/47/tasks/1793271506089.md
58
+ issues/feature/47/tasks/1793271819682.md
59
+ issues/feature/47/tasks/1793272077066.md
47
60
  issues/feature/8/IMPLEMENTS.md
48
61
  issues/feature/8/README.md
49
62
  issues/feature/8/REQUIREMENTS.md
@@ -219,6 +232,7 @@ tests/test_rabbitmq_logging.py
219
232
  tests/test_rabbitmq_priorities.py
220
233
  tests/test_rabbitmq_rpc.py
221
234
  tests/test_rabbitmq_rpc_integration.py
235
+ tests/test_rabbitmq_timeouts.py
222
236
  tests/test_rabbitmq_worker.py
223
237
  tests/test_redis.py
224
238
  tests/test_redis_integration.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DefStream
3
- Version: 0.1.19
3
+ Version: 0.1.20
4
4
  Summary: Synchronous message processing with pluggable broker adapters
5
5
  Home-page: https://github.com/yong5eon/DefStream
6
6
  Author: Heo Yongseon
@@ -170,11 +170,53 @@ the selected failure policy on error. Omitting `on_error` defaults to `Requeue()
170
170
  - `max_priority`: maximum priority for a classic task queue; an integer in `1..255` or `None`.
171
171
  - `priority`: default message priority; an integer in `0..max_priority` or `None`.
172
172
  Setting only this value sets `max_priority` to `255`.
173
+ - `timeout`: per-queue ACK waiting limit; a positive `int` in milliseconds,
174
+ a positive `datetime.timedelta` with whole-millisecond precision, or `None`.
175
+ Omitting it leaves the broker's existing settings in effect.
173
176
 
174
177
  Constructing a `Subscription` or calling `consumer.subs()` does not declare queues.
175
178
  Task `declare()` provisions ordinary durable queues and an optional DLQ,
176
- not stream queues or Exchange bindings. Incompatible existing queue options
177
- fail explicitly; queues are not automatically deleted and recreated.
179
+ not stream queues or Exchange bindings. Declaration errors caused by incompatible
180
+ queue options propagate; queues are not automatically deleted and recreated.
181
+
182
+ ### ACK waiting limits for long-running tasks
183
+
184
+ On RabbitMQ 3.12 or later, setting `Subscription.timeout` makes task `declare()`
185
+ pass `x-consumer-timeout` to the task queue as integer milliseconds, not to the error queue.
186
+ In the preceding Worker example, define this task and replace `worker.add(Add)`
187
+ with `worker.add(LongRunningAdd)`. The existing `Prepare` declares the queue.
188
+
189
+ ```python
190
+ from datetime import timedelta
191
+
192
+
193
+ class LongRunningAdd(
194
+ Add,
195
+ parameters=Subscription(
196
+ queue='defstream.example.rabbitmq.long.add',
197
+ error='defstream.example.rabbitmq.long.add.error',
198
+ timeout=timedelta(hours=24),
199
+ ),
200
+ ):
201
+ pass
202
+ ```
203
+
204
+ This setting passes `x-consumer-timeout=86400000`.
205
+ Passing integer milliseconds as `timeout=86400000` is equivalent.
206
+ `Subscription.timeout` is normalized to integer milliseconds during construction.
207
+ A Mapping such as `Subscription.parse({'queue': 'tasks', 'timeout': timedelta(hours=24)})`
208
+ is also supported. Omitting the value or passing `None` omits this queue argument.
209
+
210
+ - `bool` and types other than `int` or `timedelta` raise `TypeError`. Zero, negative durations, or
211
+ sub-millisecond precision raise `ValueError`. Values are not silently rounded or truncated.
212
+ - Constructing or subscribing does not apply the setting. Explicit task queue declaration is required.
213
+ - This is not a task execution deadline. ACK after completion, failure policies, and prefetch
214
+ remain unchanged. The RPC response waiting timeout is separate.
215
+ - Changing or recreating existing queues and applying policies are the operator's responsibility.
216
+
217
+ According to the [RabbitMQ ACK timeout guide](https://www.rabbitmq.com/docs/3.13/consumers#acknowledgement-timeout),
218
+ values below one minute are unsupported and values below five minutes are not recommended.
219
+ Checks run approximately once per minute, so do not use this as a precise execution deadline.
178
220
 
179
221
  ### Worker task priorities
180
222
 
@@ -165,11 +165,52 @@ Worker는 실행과 완료 훅이 끝난 뒤 ACK하고, 실패하면 지정한
165
165
  - `max_priority`: classic 작업 큐의 최대 우선순위. 정수 `1..255` 또는 `None`.
166
166
  - `priority`: 기본 메시지 우선순위. 정수 `0..max_priority` 또는 `None`.
167
167
  이 값만 지정하면 `max_priority`는 `255`로 설정됩니다.
168
+ - `timeout`: 큐별 ACK 대기 한도. 밀리초 단위의 양수 `int`, 양수의 정수 밀리초
169
+ 정밀도 `datetime.timedelta` 또는 `None`. 생략하면 브로커의 기존 설정을 따릅니다.
168
170
 
169
171
  `Subscription` 객체 생성이나 `consumer.subs()`는 큐를 선언하지 않습니다.
170
172
  작업용 `declare()`는 일반 durable 큐와 선택적 DLQ를 준비하며,
171
173
  스트림 큐나 Exchange 바인딩을 자동 생성하지 않습니다.
172
- 기존 큐의 선언 옵션이 다르면 실패하며 자동 삭제·재생성하지 않습니다.
174
+ 선언 옵션이 호환되지 않아 발생한 오류는 전파하며 자동 삭제·재생성하지 않습니다.
175
+
176
+ ### 장시간 작업의 ACK 대기 한도
177
+
178
+ RabbitMQ 3.12 이상에서 `Subscription.timeout`을 지정하면 작업용 `declare()`가
179
+ 작업 큐에 `x-consumer-timeout`을 정수 밀리초로 전달합니다. 오류 큐에는 적용하지 않습니다.
180
+ 앞의 Worker 예제에서 다음 작업을 정의하고 `worker.add(Add)` 대신
181
+ `worker.add(LongRunningAdd)`로 등록하면 기존 `Prepare`가 큐를 선언합니다.
182
+
183
+ ```python
184
+ from datetime import timedelta
185
+
186
+
187
+ class LongRunningAdd(
188
+ Add,
189
+ parameters=Subscription(
190
+ queue='defstream.example.rabbitmq.long.add',
191
+ error='defstream.example.rabbitmq.long.add.error',
192
+ timeout=timedelta(hours=24),
193
+ ),
194
+ ):
195
+ pass
196
+ ```
197
+
198
+ 위 설정은 `x-consumer-timeout=86400000`을 전달합니다.
199
+ `timeout=86400000`처럼 정수 밀리초로 지정해도 같습니다.
200
+ `Subscription.timeout`은 생성 시 정수 밀리초로 정규화됩니다.
201
+ `Subscription.parse({'queue': 'tasks', 'timeout': timedelta(hours=24)})`처럼
202
+ Mapping으로도 지정할 수 있습니다. 생략 또는 `None`이면 해당 큐 인자를 보내지 않습니다.
203
+
204
+ - `bool` 및 `int`·`timedelta` 이외의 타입은 `TypeError`, 0·음수 또는 밀리초 미만 정밀도는
205
+ `ValueError`로 거부합니다. 시간을 조용히 반올림하거나 절삭하지 않습니다.
206
+ - 객체 생성이나 구독만으로 설정을 적용하지 않습니다. 명시적인 작업 큐 선언이 필요합니다.
207
+ - 작업 실행을 강제 중단하는 기능이 아니며 완료 후 ACK·실패 정책·prefetch를 유지합니다.
208
+ RPC 응답 대기시간은 별도입니다.
209
+ - 기존 큐의 설정 변경·재생성·policy 적용은 운영 측 책임입니다.
210
+
211
+ [RabbitMQ의 ACK timeout 안내](https://www.rabbitmq.com/docs/3.13/consumers#acknowledgement-timeout)에
212
+ 따르면 1분 미만은 지원하지 않으며 5분 미만은 권장하지 않습니다.
213
+ 이 검사는 약 1분 간격이므로 정확한 실행 제한 시간으로 사용하지 마세요.
173
214
 
174
215
  ### 워커 작업 우선순위
175
216
 
@@ -0,0 +1,25 @@
1
+ # RabbitMQ 작업 큐별 ACK 대기 한도 설정
2
+
3
+ 장시간 작업에 맞춰 RabbitMQ의 ACK 대기 한도를 큐별로 지정할 수 있습니다. 전역 설정이나 완료 후 ACK 동작을 바꾸지 않고 `Subscription.timeout`으로 설정합니다.
4
+
5
+ ```python
6
+ from datetime import timedelta
7
+
8
+ from DefStream.Broker.Implements.RabbitMQ import Subscription
9
+
10
+ subscription = Subscription(
11
+ queue='etl.split',
12
+ error='etl.split.error',
13
+ timeout=timedelta(hours=24),
14
+ )
15
+ ```
16
+
17
+ 작업에 지정한 구독 설정으로 `declare()`를 호출하면 작업 큐에 `x-consumer-timeout=86400000`이 전달됩니다. 생략하거나 `None`을 지정하면 기존 동작을 유지합니다.
18
+
19
+ - `timeout=86400000`처럼 밀리초 단위의 양수 정수도 사용할 수 있습니다. `timedelta`는 생성 시 정수 밀리초로 정규화됩니다.
20
+ - `bool`·잘못된 타입·0·음수·밀리초 미만 정밀도는 오류로 처리합니다.
21
+ - 큐별 ACK timeout을 지원하는 RabbitMQ 3.12 이상이 필요합니다.
22
+ - 구독만으로 설정을 적용하지 않으며 기존 큐 변경·재생성·policy 적용은 운영 측 책임입니다.
23
+ - 작업 실행 제한 시간이나 RPC 응답 대기시간은 변경하지 않습니다.
24
+
25
+ 구체적인 선언 예시는 [RabbitMQ 매뉴얼](../../../docs/RabbitMQ.md#장시간-작업의-ack-대기-한도)을 참고하세요.
@@ -0,0 +1,22 @@
1
+ # Subscription timeout 구현 결과
2
+
3
+ [요구사항](./REQUIREMENTS.md)과 [계획](./PLAN.md)에 따라 RabbitMQ 구독 설정의 timeout을 작업 큐 선언에 전달하도록 구현했습니다.
4
+
5
+ ## 주요 변경과 유지한 계약
6
+
7
+ - [Subscription](../../../src/Broker/Implements/RabbitMQ/Subscription.py)에 마지막 선택 필드 `timeout: Optional[Union[int, timedelta]] = None`을 추가했습니다. 기존 위치 인자는 유지하며 객체와 Mapping으로 지정할 수 있습니다.
8
+ - 밀리초 단위의 양수 `int`와 정수 밀리초 정밀도의 양수 `timedelta`를 허용합니다. `bool`·잘못된 타입은 `TypeError`, 0·음수·밀리초 미만 정밀도는 `ValueError`로 처리합니다.
9
+ - 구독 생성 시 부동소수점 없이 정수 밀리초로 정규화합니다. [Connection.declare](../../../src/Broker/Implements/RabbitMQ/Connection.py)는 해당 값을 작업 큐의 `x-consumer-timeout`에만 전달하며 생략·`None`이면 기존 인자 구성을 유지합니다.
10
+ - DLQ·최대 우선순위·durable 선언은 함께 유지합니다. 오류 큐에 timeout을 전파하지 않으며 Consumer·Worker·Redis 및 ACK·실패 정책은 변경하지 않았습니다.
11
+ - 한국어·영어 RabbitMQ 매뉴얼을 갱신했습니다. 기존 큐 변경이나 운영 조치를 자동 수행하지 않습니다.
12
+
13
+ ## 검증과 제한
14
+
15
+ 새 timeout 테스트 9개를 포함해 관련 테스트 92개 중 88개가 통과하고 4개가 생략됐습니다. 변경한 Python 파일의 Pylance 진단은 없고, 양쪽 매뉴얼 예제의 설정 생성도 확인했습니다. 정수 입력·정규화 변경 후 재검증 결과는 [최신 검증 기록](./tasks/1793272077066.md), 최초 검증의 환경 제한은 [기존 검증 기록](./tasks/1793271819682.md)을 참고하세요.
16
+
17
+ 전체 테스트 스위트와 실제 RabbitMQ 운영 환경·기존 큐 변경은 검증하지 않았습니다. 요청 범위의 미구현 사항은 없습니다. 커밋·푸시·PR 생성은 수행하지 않았습니다.
18
+
19
+ ## 구현 기록
20
+
21
+ - [timeout의 큐 선언 전달](./implements/1793271568985.md)
22
+ - [정수 밀리초 입력과 정규화](./implements/1793272077065.md)
@@ -0,0 +1,27 @@
1
+ # Subscription timeout 구현 계획
2
+
3
+ 작업 대상과 병합 대상은 [이슈 기록](./README.md), 범위와 인수 기준은 [요구사항 명세](./REQUIREMENTS.md)를 따릅니다.
4
+
5
+ ## 구현 기준
6
+
7
+ - `Subscription`의 마지막 선택 필드에 `timeout: Optional[Union[int, timedelta]] = None`을 추가하여 기존 위치 인자를 유지합니다.
8
+ - `None`, 밀리초 단위의 양수 `int` 또는 정수 밀리초로 정확히 표현 가능한 양수 `timedelta`를 허용합니다. `bool` 및 다른 타입은 `TypeError`, 0·음수·밀리초 미만 정밀도는 `ValueError`로 처리합니다.
9
+ - 생성 시 `timedelta`의 정수 나눗셈으로 값을 정수 밀리초로 통일하고 `declare()`는 해당 값을 전달합니다. 사용자 확인에 따른 정수 입력 지원을 반영한 계획이며 브로커의 운영 권장 최소시간은 추가하지 않습니다.
10
+ - `Connection.declare()`에서 작업 큐에만 `x-consumer-timeout`을 전달합니다. 오류 큐·소비 등록·ACK·RPC·운영 환경은 변경하지 않습니다.
11
+
12
+ ## 수행 순서와 진행 상태
13
+
14
+ 1. [완료] 현재 구독·큐 선언 및 기존 테스트의 기준 동작 확인
15
+ 2. [완료] timeout 테스트 추가와 구독 설정·선언 구현 (R-01~R-09)
16
+ 3. [완료] 한국어·영어 RabbitMQ 매뉴얼에 설정 예시·선언 방법·검증 계약 반영 (R-10)
17
+ 4. [완료] 대상 회귀 테스트·타입 진단·문서 링크 검증, 실제 결과에 따른 종합 기록과 변경 로그 작성
18
+
19
+ 실제 검증 결과와 생략 범위는 [최신 검증 기록](./tasks/1793272077066.md), 구현 결과는 [구현 종합](./IMPLEMENTS.md)에 정리했습니다.
20
+
21
+ ## 검증 계획
22
+
23
+ - AC-01~AC-04: 객체·Mapping·Worker 선언 경로, 24시간 및 정밀도 경계값, 생략·`None`, 기존 위치 인자, DLQ·우선순위 병합, I/O 전 오류를 Mock 기반 테스트로 검증합니다.
24
+ - AC-05~AC-07: 소비 등록에서 선언·timeout 소비 인자가 없음을 검증하고, 기존 구독·ACK·실패 정책·prefetch·RPC 및 선언 오류 전파 테스트를 함께 실행합니다.
25
+ - AC-08: 한국어·영어 예시와 입력 계약의 일치 및 상대 링크를 확인합니다.
26
+ - 기존 `unittest`와 Pylance 파일 진단을 사용합니다. 실제 브로커 ACK 만료 대기·기존 큐 변경·운영 설정 확인은 실행하지 않습니다.
27
+ - 브로커 데이터나 스키마를 변경하지 않으므로 운영 마이그레이션·복구 절차는 필요하지 않습니다.
@@ -0,0 +1,72 @@
1
+ # Subscription 작업별 consumer timeout 지원
2
+
3
+ ## 이슈와 작업 상태
4
+
5
+ - 원본: GitHub Issues [#47](https://github.com/yong5eon/DefStream/issues/47)
6
+ - 제목: Subscription 작업별 consumer timeout 지원
7
+ - 상태: Open
8
+ - 유형·레이블: 기능 (`feature`), `rabbitmq` (원본에 적용됨)
9
+ - 담당자: `yong5eon`
10
+ - 작업 대상: `yong5eon/DefStream`, 작업 브랜치 `feature/47`
11
+ - 분기 기준: 깨끗한 로컬 `master`의 `15ffbdc`
12
+ - 병합 대상: `master`
13
+ - 로컬 진행 상태: 구현·매뉴얼·관련 검증 완료, 커밋·푸시·PR 생성 미수행
14
+ - 댓글: 없음
15
+
16
+ ## 작업 문서
17
+
18
+ - [요구사항 명세](./REQUIREMENTS.md)
19
+ - [작업 계획](./PLAN.md)
20
+ - [구현 종합](./IMPLEMENTS.md)
21
+ - [작업 종합](./TASKS.md)
22
+ - [변경 로그](./CHANGELOG.md)
23
+ - [최신 구현 기록](./implements/1793272077065.md)
24
+ - [최신 작업 기록](./tasks/1793272077066.md)
25
+
26
+ ## 이슈 원문
27
+
28
+ ### 해결하려는 문제
29
+
30
+ - RabbitMQ는 소비자가 전달받은 메시지를 `consumer_timeout`(기본 30분) 안에 ACK하지 않으면 채널을 `PRECONDITION_FAILED`로 닫고 미확인 메시지를 재큐잉함
31
+ - 작업 완료 후 ACK하는 구조에서는 수 시간 이상 걸리는 작업이 이 한도를 넘기면 정상 실행 중인데도 채널이 닫힘
32
+ - 브로커 전역 `consumer_timeout`을 늘리면 짧은 작업의 멈춤 감지까지 함께 늦어지므로, 장시간 작업의 큐에만 한도를 늘릴 방법이 필요함
33
+ - 현재 `Subscription`에는 큐별 consumer timeout을 지정할 수단이 없음
34
+
35
+ ### 제안하는 해결 방법
36
+
37
+ - `Subscription`에 작업별 timeout을 지정할 수 있도록 지원
38
+ - RabbitMQ 구현에서는 큐 선언 시 이 값을 `x-consumer-timeout`(ms) 큐 인자로 전달
39
+ - `Subscription.error` → `x-dead-letter-*`, `max_priority` → `x-max-priority`를 구성하는 `queue_declare` 인자 처리와 같은 위치
40
+ - 지정하지 않으면 기존과 동일하게 브로커 전역 설정을 따름
41
+
42
+ ```python
43
+ Subscription(queue='etl.split', error='etl.split.error', timeout=timedelta(hours=24))
44
+ ```
45
+
46
+ > 위 예시의 `timeout` 이름과 타입은 제안이며 구현 시 확정
47
+
48
+ ### 고려한 대안
49
+
50
+ - 메시지를 받자마자 ACK하는 정책(이전의 `AckPolicy.BEFORE_RUN`)
51
+ - at-most-once가 되어 작업 실패나 프로세스 종료 시 메시지가 유실됨
52
+ - 오류 시 오류 큐로 보내는 정책(`Reject`)·dead-letter와 prefetch에 의한 동시 처리 제한 계약이 깨짐
53
+ - 브로커 전역 `consumer_timeout` 상향: 모든 큐의 멈춤 감지가 늦어짐
54
+ - 소비 시점(`basic.consume`)에 지정하는 timeout: RabbitMQ가 지원하지 않음
55
+ - 지원 방식은 전역 설정, 큐 인자 `x-consumer-timeout`, policy `consumer-timeout`뿐임
56
+ - AMQP 0-9-1에는 처리 중인 메시지의 ACK 기한을 연장하는 기능이 없음
57
+
58
+ ### 추가 정보
59
+
60
+ RabbitMQ 3.13.7 실측 결과(전역 `consumer_timeout=60000`, ACK하지 않고 메시지 보유)
61
+
62
+ | 큐 설정 | 채널 종료까지 | 브로커 로그의 `Timeout value used` |
63
+ |---|---|---|
64
+ | 없음 | 120초 | 60000 ms |
65
+ | 큐 인자 `x-consumer-timeout=240000` | 240초 | 240000 ms |
66
+ | policy `consumer-timeout=240000` | 240초 | 240000 ms |
67
+
68
+ - 전역보다 큰 큐 인자·policy 값도 적용됨
69
+ - 만료 검사는 약 1분 간격이므로 실제 종료는 설정값보다 최대 약 1분 늦을 수 있음
70
+ - 이미 존재하는 큐를 다른 `x-consumer-timeout` 값으로 재선언하면 오류 없이 성공하지만 값은 변경되지 않음
71
+ - 기존 큐는 삭제 후 재생성하거나 policy로 적용해야 함
72
+ - 참고: [RabbitMQ Consumers - Delivery Acknowledgement Timeout](https://www.rabbitmq.com/docs/consumers#acknowledgement-timeout)
@@ -0,0 +1,101 @@
1
+ # Subscription consumer timeout 지원 요구사항
2
+
3
+ ## 목적
4
+
5
+ 장시간 작업이 정상 실행 중인데도 RabbitMQ의 ACK 대기 한도를 초과하여 채널이 종료되는 문제를 줄이기 위해, 작업 구독 설정에서 해당 큐의 consumer timeout을 지정할 수 있도록 합니다. 브로커 전역 설정이나 작업 완료 후 ACK하는 계약은 변경하지 않습니다.
6
+
7
+ 이슈 원문과 작업 브랜치·병합 대상은 [이슈 기록](./README.md)을 기준으로 합니다. 이 문서는 목표 동작과 인수 기준을 정의하며, 실제 수행 결과는 [구현 종합](./IMPLEMENTS.md)과 [검증 기록](./tasks/1793271819682.md)으로 구분합니다.
8
+
9
+ ## 분석과 근거
10
+
11
+ ### 이슈에서 요청한 결과
12
+
13
+ - RabbitMQ `Subscription`에서 ACK 대기 한도를 선택적으로 지정합니다.
14
+ - 큐 선언 시 `x-consumer-timeout` 큐 인자로 밀리초 값을 전달합니다.
15
+ - 미지정 시 기존처럼 애플리케이션이 한도를 재정의하지 않습니다.
16
+ - 수신 직후 ACK하는 우회 방법 대신, 완료 후 ACK·실패 정책·prefetch 계약을 유지합니다.
17
+
18
+ 이슈 예시의 `timeout` 이름과 `timedelta` 타입은 원문에서는 제안입니다. 사용자의 후속 확인에 따라 이번 범위는 `Subscription`의 timeout 설정을 `declare()`에 전달하는 기능으로 한정합니다. API의 세부 구현은 별도의 사용자 결정 사항이나 작업 차단 사유로 두지 않습니다.
19
+
20
+ ### 분석 당시 코드와 차이
21
+
22
+ - [RabbitMQ Subscription](../../../src/Broker/Implements/RabbitMQ/Subscription.py)은 큐, offset, QoS, 오류 큐 및 우선순위를 표현하고 객체와 Mapping 입력을 지원하지만, consumer timeout 필드는 없습니다.
23
+ - [RabbitMQ Connection](../../../src/Broker/Implements/RabbitMQ/Connection.py)의 `declare()`가 작업 큐의 dead-letter 및 최대 우선순위 인자를 구성합니다. 이 경로에서 새 큐 인자를 함께 전달하면 요청을 충족할 수 있습니다.
24
+ - [Worker declare](../../../src/Worker/Message.py)는 작업의 구독 설정을 브로커에 전달합니다. 공통 Worker API에 새 timeout 매개변수를 추가할 필요는 없습니다.
25
+ - [RabbitMQ Consumer](../../../src/Broker/Implements/RabbitMQ/Consumer.py)의 `subs()`는 큐를 선언하지 않고 구독하며, `complete()`는 성공 처리 후 ACK합니다. 두 계약은 유지해야 합니다.
26
+ - [Worker](../../../src/Worker/Worker.py)는 한 Consumer로 여러 작업을 구독하므로 RabbitMQ 소비 채널을 공유합니다. 큐별 timeout을 설정해도 다른 구독의 timeout으로 같은 채널이 종료될 가능성까지 제거하지는 않습니다.
27
+
28
+ ### 외부 제약과 근거의 범위
29
+
30
+ - [RabbitMQ 3.13 공식 문서](https://www.rabbitmq.com/docs/3.13/consumers#acknowledgement-timeout)에 따르면 큐별 설정은 RabbitMQ 3.12부터 지원하며, 큐 인자 단위는 밀리초입니다. 만료 여부는 약 1분 간격으로 검사합니다.
31
+ - 같은 문서는 1분 미만을 지원하지 않고 5분 미만은 권장하지 않는다고 설명합니다. 이는 브로커의 운영 제약이며, 이를 이유로 운영 설정 확인이나 별도의 사용자 승인을 이번 작업의 선행 조건으로 추가하지 않습니다.
32
+ - 기존 큐를 다른 값으로 재선언해도 값이 바뀌지 않았다는 내용은 이슈 작성자의 RabbitMQ 3.13.7 실측 보고입니다. 이번 분석에서 직접 재현한 결과나 모든 버전·큐 유형에 대한 보장은 아닙니다.
33
+ - 미지정 시 브로커가 적용하는 전역 설정이나 기존 policy를 따릅니다. 라이브러리가 브로커 기본값을 중복 설정하지 않습니다.
34
+
35
+ ## 확정 요구사항
36
+
37
+ ### 포함 범위
38
+
39
+ | ID | 요구사항 |
40
+ | --- | --- |
41
+ | R-01 | RabbitMQ `Subscription`에서 큐별 consumer timeout을 선택적으로 지정할 수 있어야 합니다. 기존 객체 입력과 Mapping 입력 양쪽에서 사용할 수 있어야 합니다. |
42
+ | R-02 | 명시된 값은 기존 작업 큐 선언 경로에서 정수 밀리초의 `x-consumer-timeout` 인자로 전달해야 합니다. |
43
+ | R-03 | 생략 또는 `None`이면 `x-consumer-timeout` 인자를 보내지 않아야 합니다. 브로커 기본값을 라이브러리에서 고정하거나 timeout 비활성화로 해석하지 않아야 합니다. |
44
+ | R-04 | 오류 큐와 우선순위를 함께 지정해도 기존 `x-dead-letter-*`, `x-max-priority` 및 durable 선언을 유지해야 합니다. 작업 구독에 설정한 timeout을 부수적으로 선언하는 오류 큐에 전파하지 않아야 합니다. |
45
+ | R-05 | 잘못된 타입이나 정수 밀리초로 표현할 수 없는 값을 브로커 I/O 전에 명시적인 오류로 거부해야 합니다. 기본값 대체, 조용한 무시 또는 브로커 전역 설정으로의 폴백을 하지 않아야 합니다. 필요한 입력 검증과 변환 기준은 기존 관례에 맞춰 구현하고 테스트로 명시하며, 별도의 운영 요구사항을 추가하지 않습니다. |
46
+ | R-06 | `Subscription` 생성과 `Consumer.subs()`는 큐를 자동 선언하지 않아야 합니다. timeout 적용도 기존 명시적 `declare()` 경로를 사용해야 합니다. |
47
+ | R-07 | 완료 후 ACK, 오류 시 `Requeue`·`Reject`, dead-letter, QoS·prefetch 및 RPC의 기존 처리 계약을 유지해야 합니다. 이 값으로 작업을 강제 중단하거나 RPC 응답 대기시간을 변경하지 않아야 합니다. |
48
+ | R-08 | 기존 `Subscription` 호출의 위치 인자 순서와 미지정 시 동작을 유지해야 합니다. 공통 Worker 및 Redis API에 timeout 지원을 추가하지 않아야 합니다. |
49
+ | R-09 | 기존 큐를 자동 삭제·재생성하거나 policy·브로커 설정을 변경하지 않아야 합니다. 큐 선언에서 발생한 오류는 기존 로깅·예외 전파 계약에 따라 드러내야 합니다. |
50
+ | R-10 | 구현 시 한국어·영어 RabbitMQ 매뉴얼에 설정 방법, 명시적 선언 필요성 및 미지정 동작을 반영해야 합니다. 기존 큐 변경은 운영 측 책임임을 간결히 알리고 운영 절차를 이번 작업에서 작성하거나 수행하지 않습니다. |
51
+
52
+ ### 운영상 의미와 제한
53
+
54
+ - 설정 대상은 개별 메시지나 작업 실행 인스턴스가 아니라 작업이 소비하는 큐입니다. 같은 큐의 소비자마다 서로 다른 한도를 제공하는 기능은 아닙니다.
55
+ - consumer timeout은 브로커가 메시지를 전달한 뒤 ACK를 기다리는 한도입니다. 작업 코드의 실행 시간 제한, 메시지 TTL, 연결 timeout, RPC 응답 대기시간과 구분해야 합니다.
56
+ - 실제 채널 종료 시점이 설정값과 정확히 일치한다고 보장하지 않습니다.
57
+ - 다른 큐의 미확인 메시지로 공유 채널이 종료될 수 있습니다. 큐별 설정만으로 작업 간 장애 격리를 보장하지 않습니다.
58
+ - 기존 큐의 설정 변경·반영 여부 확인·재생성·policy 적용은 운영 측 책임이며, 이번 구현이나 완료 검증의 대상이 아닙니다.
59
+ - 새 설정을 사용하는 환경에는 큐별 timeout을 지원하는 RabbitMQ가 필요합니다. 구버전에서 동일한 효과를 보장하거나 전역 설정 변경으로 대신 처리하지 않습니다.
60
+
61
+ ### 제외 범위
62
+
63
+ - 조기 ACK 및 `AckPolicy.BEFORE_RUN` 재도입
64
+ - 실행 중 메시지의 ACK 기한 연장, 재시도·중복 제거 기능 추가
65
+ - Worker의 채널·연결 분리 및 실행 풀·동시성 구조 변경
66
+ - Redis용 timeout, 공통 timeout 추상화, 별도 capability 인터페이스 추가
67
+ - 저수준 큐 생성·Stream·Exchange API로 설정 범위 확대
68
+ - RabbitMQ policy 관리, 기존 큐 마이그레이션 자동화 및 운영 데이터 변경
69
+
70
+ ## 구현 판단과 사용자 확인 사항
71
+
72
+ - 추가로 사용자에게 확정을 요청할 사항은 없습니다.
73
+ - 공개 필드는 `timeout`, 타입은 `Optional[Union[int, timedelta]]`, 기본값은 `None`입니다. 사용자 확인에 따라 정수 밀리초와 `timedelta`를 함께 지원하고, 생성 시 값을 정수 밀리초로 정규화합니다. 앞선 분석의 `consumer_timeout`은 권장안이었으며 필수 요구가 아닙니다.
74
+ - 정수 밀리초 변환과 필요한 입력 검증은 구현 세부사항으로 처리하고 실제 구현·테스트와 문서에 동일하게 반영합니다. 별도의 설정 옵션이나 호환성 계층을 추가하지 않습니다.
75
+ - 구현 기준은 `None`, 밀리초 단위의 양수 `int` 또는 양수의 정수 밀리초 정밀도 `timedelta`입니다. `bool` 및 다른 타입은 `TypeError`, 0·음수·밀리초 미만 정밀도는 `ValueError`로 거부하며, 브로커 운영 권장 최소시간을 라이브러리의 추가 제한으로 도입하지 않습니다.
76
+ - 기존 큐의 변경이나 실제 운영 환경의 timeout 적용 상태를 조사하지 않습니다.
77
+
78
+ ## 인수 기준
79
+
80
+ 아래 기준은 구현 후 검증할 목표이며, 현재 통과했다는 의미가 아닙니다.
81
+
82
+ | ID | 관련 요구사항 | 검증 기준 |
83
+ | --- | --- | --- |
84
+ | AC-01 | R-01, R-02 | 객체·Mapping의 정수 `86400000`과 `timedelta(hours=24)` 입력 모두 생성 시 정수 밀리초로 정규화되어 작업 큐 선언에 동일하게 전달됩니다. |
85
+ | AC-02 | R-03, R-08 | 생략 및 명시적 `None`일 때 `x-consumer-timeout`이 없고, 기존 위치 인자와 선언 옵션이 유지됩니다. |
86
+ | AC-03 | R-04 | timeout·오류 큐·최대 우선순위를 함께 설정하면 작업 큐 인자에 모두 반영되고, 오류 큐에는 해당 timeout을 전달하지 않습니다. |
87
+ | AC-04 | R-05 | 확정한 타입·범위·정밀도 기준의 정상값과 경계값을 검증하고, 잘못된 값에서는 브로커 I/O 없이 오류가 발생합니다. |
88
+ | AC-05 | R-06 | 구독 객체 생성과 소비자 등록에서 큐 선언이 발생하지 않고, `basic.consume`에 timeout 큐 인자를 잘못 전달하지 않습니다. |
89
+ | AC-06 | R-07 | 기존 성공 후 ACK, 실패 정책, prefetch 및 RPC 관련 회귀 테스트가 유지됩니다. RPC 호출 timeout은 별도 값으로 계속 동작합니다. |
90
+ | AC-07 | R-09 | 큐 선언 오류가 전파되며 큐 자동 삭제·재생성 및 policy 변경을 하지 않습니다. |
91
+ | AC-08 | R-10 | 한국어·영어 매뉴얼이 구현된 API와 동일한 예시·단위·선언 방법을 설명하고, 기존 큐 변경이 운영 측 책임임을 명시합니다. |
92
+
93
+ 이번 기능의 완료 검증은 기존 Mock 기반 선언 테스트를 이용해 `x-consumer-timeout`의 정확한 전달·생략과 기존 선언 인자·구독 계약의 유지를 확인하는 데 집중합니다. 실제 ACK 만료 대기, 기존 큐의 재선언·변경 실측 및 운영 환경 검증은 수행 범위에 포함하지 않습니다. Mock 검증이나 원문의 실측 보고를 이번 변경의 실제 브로커 검증 결과로 표현하지 않습니다.
94
+
95
+ ## 관련 검증·문서
96
+
97
+ - 구독 설정·소비 계약: [test_rabbitmq_worker.py](../../../tests/test_rabbitmq_worker.py)
98
+ - 큐 선언 인자·오류 전파: [test_rabbitmq_priorities.py](../../../tests/test_rabbitmq_priorities.py)
99
+ - 작업 선언 경로: [test_task_messages.py](../../../tests/test_task_messages.py)
100
+ - 실제 브로커 연동: [test_rabbitmq_integration.py](../../../tests/test_rabbitmq_integration.py)
101
+ - 사용자 안내: [RabbitMQ.md](../../../docs/RabbitMQ.md), [RabbitMQ.en.md](../../../docs/RabbitMQ.en.md)
@@ -0,0 +1,17 @@
1
+ # consumer timeout 요구사항 분석·명세 결과
2
+
3
+ [이슈 원문](./README.md)과 현재 코드의 계약을 바탕으로 [요구사항 명세](./REQUIREMENTS.md)를 작성했습니다. 큐 선언에서만 ACK 대기 한도를 설정하고, 미지정 동작·완료 후 ACK·실패 정책·prefetch를 유지하는 범위를 정리했습니다.
4
+
5
+ 사용자의 후속 확인에 따라 범위를 `Subscription`에 지정한 timeout을 `declare()`에서 큐 인자로 전달하는 기능으로 한정했습니다. 기존 큐 변경·반영 확인과 운영 조치는 범위 밖이며, 추가 사용자 결정 사항은 없습니다. 정수 입력 지원 요청을 반영하여 `timeout`은 `Optional[Union[int, timedelta]]`로 받고 생성 시 정수 밀리초로 정규화합니다.
6
+
7
+ [작업 계획](./PLAN.md)을 작성하고 구현 후 관련 테스트를 검증했습니다. 92개 중 88개 통과·4개 생략이며, 변경 Python 파일 3개의 Pylance 진단은 없습니다. 한국어·영어 매뉴얼의 Worker·timeout 예제는 브로커 I/O 없이 실행하여 확인했습니다. 구현 설명은 [구현 종합](./IMPLEMENTS.md), 상세 결과와 실행 제한은 [검증 기록](./tasks/1793271819682.md)을 참고하세요.
8
+
9
+ 실제 RabbitMQ의 ACK 만료 대기·기존 큐 변경·운영 환경 검증은 수행하지 않았습니다. 커밋·푸시·PR 생성은 하지 않았습니다.
10
+
11
+ ## 작업 기록
12
+
13
+ - [요구사항 명세 작성](./tasks/1793271236067.md)
14
+ - [사용자 확인에 따른 범위 정리](./tasks/1793271384432.md)
15
+ - [계획과 기존 동작 확인](./tasks/1793271506089.md)
16
+ - [구현 검증 결과](./tasks/1793271819682.md)
17
+ - [정수 입력 지원 재검증](./tasks/1793272077066.md)
@@ -0,0 +1,10 @@
1
+ # Subscription timeout의 큐 선언 전달
2
+
3
+ - [Subscription](../../../../src/Broker/Implements/RabbitMQ/Subscription.py)의 마지막 필드에 `timeout: Optional[timedelta] = None`을 추가하여 기존 위치 인자 순서를 유지했습니다.
4
+ - 구독 생성 시 타입·양수·정수 밀리초 정밀도를 검증합니다. 부적합한 입력은 오류로 드러내며 변환을 위한 자동 절삭이나 폴백을 하지 않습니다.
5
+ - [Connection.declare](../../../../src/Broker/Implements/RabbitMQ/Connection.py)는 timeout이 있을 때만 `timedelta` 정수 나눗셈으로 밀리초를 계산하여 작업 큐의 `x-consumer-timeout`에 전달합니다. 기존 DLQ·우선순위 인자와 함께 구성하며 오류 큐에는 전달하지 않습니다.
6
+ - [timeout 테스트](../../../../tests/test_rabbitmq_timeouts.py)에 객체·Mapping·Worker 선언, 생략·`None`, 위치 인자 호환, DLQ·우선순위 병합, 경계값·큰 값 정밀도, I/O 전 오류, 소비 등록·RPC 대기시간 독립성과 선언 오류 전파를 추가했습니다.
7
+ - 한국어·영어 [RabbitMQ 매뉴얼](../../../../docs/RabbitMQ.md)에 최종 API와 선언 방법을 반영했습니다. 선택 설정의 상세 설명이므로 최소 빠른 시작을 담은 루트 README는 유지하며 별도 다이어그램은 추가하지 않았습니다.
8
+ - Consumer·Worker·Redis 및 기존 큐의 운영 설정은 변경하지 않았습니다.
9
+
10
+ 새 테스트의 구현 전 실패와 구현 후 통과를 확인했으며, 관련 회귀 검증은 진행 중입니다. 상세 결과는 후속 작업 기록에 정리합니다.
@@ -0,0 +1,10 @@
1
+ # 정수 밀리초 입력과 생성 시 정규화
2
+
3
+ 사용자가 `timeout`에 정수 밀리초와 `timedelta`를 함께 받는 방안에 동의하여 기존 구현을 확장했습니다.
4
+
5
+ - [Subscription](../../../../src/Broker/Implements/RabbitMQ/Subscription.py)은 `Optional[Union[int, timedelta]]`를 받고 `timedelta` 입력을 생성 시 정수 밀리초로 정규화합니다. `None`은 유지하고 `bool`은 명시적으로 거부합니다.
6
+ - [Connection](../../../../src/Broker/Implements/RabbitMQ/Connection.py)의 `declare()`에서는 추가 변환 없이 정규화된 값을 큐 인자로 전달합니다.
7
+ - 정수·기간 입력의 정규화·선언 결과, Mapping 원본 보존 및 잘못된 정수 입력 테스트를 보완했습니다.
8
+ - 요구사항·계획·종합 결과·변경 로그와 한국어·영어 매뉴얼에 정수 단위 및 생성 후 속성 계약을 반영했습니다.
9
+
10
+ 기존 큐 변경이나 ACK 계약 변경은 없습니다. [이전 구현 기록](./1793271568985.md)의 `timedelta` 전용 입력·선언 시 변환 설명은 이 기록으로 대체하며, 검증 결과는 [재검증 기록](../tasks/1793272077066.md)에 정리합니다.
@@ -0,0 +1,9 @@
1
+ # consumer timeout 요구사항 명세
2
+
3
+ - 원본 이슈를 다시 조회하여 본문·댓글·유형 레이블을 확인했습니다. 댓글은 없으며 `feature`, `rabbitmq` 레이블이 유지되어 있습니다.
4
+ - 이전 분석에서 확인한 구독 설정, 큐 선언, 소비 등록 및 완료 후 ACK 계약을 바탕으로 [요구사항 명세](../REQUIREMENTS.md)를 작성했습니다.
5
+ - 확정된 동작과 API 제안을 분리하고, 변경 범위·호환성·운영 제약·인수 기준을 기록했습니다. 필드 이름·타입, 최소값, 밀리초 미만 정밀도 처리 기준은 미확정으로 남겼습니다.
6
+ - 병합 대상은 사용자가 지정한 `master`를 유지했습니다. 작성 전 실제 브랜치가 `feature/47`이고 기존 변경은 해당 이슈 디렉터리에만 있음을 확인했습니다.
7
+ - 제품 코드와 매뉴얼은 변경하지 않았고 테스트도 실행하지 않았습니다. 요구사항 문서는 구현 완료나 브로커 실측 결과를 나타내지 않습니다.
8
+
9
+ 다음 단계는 미확정 API·검증 기준을 해결한 뒤 작업 계획을 작성하는 것입니다. 적용 지침은 공통 Markdown·이슈 작업 지침이며, 프로젝트의 `instructions/README.instructions.md`와 `instructions/DOCS.instructions.md`는 각각 루트 README·상세 매뉴얼에 한정됨을 확인했습니다.
@@ -0,0 +1,8 @@
1
+ # timeout 설정 전달로 작업 범위 한정
2
+
3
+ 사용자가 이번 작업은 `Subscription`의 timeout을 `declare()`에서 적용하는 기능이며 기존 큐 변경 등은 운영 측 책임이라고 명확히 했습니다.
4
+
5
+ - [요구사항 명세](../REQUIREMENTS.md)에서 추가 사용자 확정을 요구하던 내용을 제거했습니다.
6
+ - 이슈 예시의 `timeout`·`Optional[timedelta]`를 구현 기준으로 정리하고, 변환·입력 검증은 구현 세부사항으로 구분했습니다.
7
+ - 기존 큐의 변경·반영 확인, 운영 절차 및 실제 만료 실측을 이번 작업의 구현·완료 검증 범위에서 제외했습니다.
8
+ - 앞선 [명세 작성 기록](./1793271236067.md)의 미확정 상태는 이 기록과 현재 명세로 대체합니다. 제품 코드나 운영 환경은 변경하지 않았습니다.
@@ -0,0 +1,8 @@
1
+ # 구현 계획과 기존 동작 확인
2
+
3
+ - 실제 브랜치 `feature/47`와 깨끗한 작업 트리를 확인했습니다. 기존 문서는 커밋된 상태이며 [최신 범위 정리](./1793271384432.md)를 기준으로 재개했습니다.
4
+ - [작업 계획](../PLAN.md)을 작성했습니다. `timeout`은 양수의 정수 밀리초 정밀도 `timedelta`로 검증하고, 부동소수점 없이 큐 선언 인자로 변환하기로 했습니다.
5
+ - 저장소 루트에서 기존 환경의 `python -m unittest discover -s tests -p test_rabbitmq_worker.py`를 실행하여 10개 통과했습니다.
6
+ - 같은 환경에서 `python -m unittest discover -s tests -p test_rabbitmq_priorities.py`를 실행하여 9개 중 8개 통과, 브로커 연동 1개 생략했습니다.
7
+ - Pylance 참조 조회와 소스를 통해 RabbitMQ 내부의 구독 파싱·선언 경로를 확인했습니다. 공통 Worker와 Redis는 변경하지 않습니다.
8
+ - 적용 지침은 `instructions/CODE.instructions.md`의 영어 진단 문구 정책, `instructions/DOCS.instructions.md`의 한국어·영어 매뉴얼 동기화 및 공통 Python·Markdown 지침입니다.
@@ -0,0 +1,35 @@
1
+ # timeout 구현 검증 결과
2
+
3
+ ## 실행 환경과 명령
4
+
5
+ Python 3.12.10의 기존 작업공간 환경을 사용했습니다. 설치된 `DefStream` 경로가 현재 저장소의 `src`로 연결된 junction임을 확인하여 변경 소스를 검증했습니다. 의존성 설치나 환경 수정은 하지 않았습니다.
6
+
7
+ 저장소 루트에서 `tests`를 Python import 경로에 추가하고 `PYTHONIOENCODING=utf-8`로 다음 명령을 실행했습니다.
8
+
9
+ ```shell
10
+ python -m unittest test_rabbitmq_timeouts test_rabbitmq_worker test_rabbitmq_priorities test_rabbitmq_rpc test_task_messages test_worker test_rabbitmq_logging
11
+ ```
12
+
13
+ 최종 결과는 92개 중 88개 통과, 4개 생략, 실패·오류 없음이며 종료 코드는 0입니다. 전체 테스트 스위트나 실제 브로커 검증을 수행한 결과는 아닙니다.
14
+
15
+ ## 인수 기준 확인
16
+
17
+ | 기준 | 실제 확인 결과 |
18
+ | --- | --- |
19
+ | AC-01 | 객체·Mapping 및 Worker `declare()` 경로에서 24시간이 정수 `86400000`으로 전달됨 |
20
+ | AC-02 | 생략·`None`에서 인자 미전달, 기존 위치 인자 순서 유지 |
21
+ | AC-03 | DLQ·우선순위와 timeout 인자 병합, 오류 큐에는 timeout 미전달 |
22
+ | AC-04 | 1밀리초·큰 기간의 정확한 변환, 잘못된 타입·0·음수·밀리초 미만 정밀도는 I/O 전 오류 |
23
+ | AC-05 | 소비 등록 시 큐 미선언, timeout 소비 인자 미전달 및 수동 ACK 유지 |
24
+ | AC-06 | 기존 Worker·소비·실패 정책·prefetch·RPC 회귀 테스트 통과, RPC 응답 timeout 독립성 확인 |
25
+ | AC-07 | 선언 오류 전파 및 큐 자동 삭제 없음 확인 |
26
+ | AC-08 | 한국어·영어 매뉴얼의 Worker 예제와 timeout 예제를 브로커 I/O 없이 실행하고 24시간 설정 확인 |
27
+
28
+ Pylance `textDocument/diagnostic`에서 변경한 `Subscription.py`, `Connection.py`, `test_rabbitmq_timeouts.py` 모두 진단이 없었습니다. 문서 상대 링크·절대 경로 노출 및 `git diff --check`도 확인했습니다.
29
+
30
+ ## 진행 중 확인한 제한
31
+
32
+ - 구현 전 새 테스트는 기존 API에 `timeout`이 없어 실패했고, 구현 후 새 테스트 9개가 모두 통과했습니다.
33
+ - 첫 회귀 실행은 기존 시작 배너의 Windows cp949 출력 인코딩 오류로 실패했습니다. 제품 코드는 변경하지 않고 UTF-8 출력으로 동일 테스트를 다시 실행하여 해결했습니다.
34
+ - 테스트 출력을 요약하려던 표준입력 기반 실행은 Windows ProcessPool의 spawn이 표준입력 진입점을 재실행할 수 없어 중단했습니다. 해당 실행 프로세스를 종료하고 표준 `python -m unittest` 진입점으로 최종 검증했습니다.
35
+ - 기존 큐 변경·ACK 만료 대기·브로커 운영 환경 검증은 사용자 지정 범위 밖이므로 수행하지 않았습니다.
@@ -0,0 +1,14 @@
1
+ # 정수 timeout 입력 지원 재검증
2
+
3
+ 기존 `feature/47` 브랜치의 구현을 유지하며 정수 밀리초 입력을 추가했습니다.
4
+
5
+ - 변경 전 보완한 테스트에서 정수 입력 거부와 `timedelta` 속성 미정규화 실패를 확인했습니다.
6
+ - 기존 작업공간 환경에서 `tests`를 import 경로에 추가하고 `PYTHONIOENCODING=utf-8`로 저장소 루트에서 다음 명령을 실행했습니다.
7
+
8
+ ```shell
9
+ python -m unittest test_rabbitmq_timeouts test_rabbitmq_worker test_rabbitmq_priorities test_rabbitmq_rpc test_task_messages test_worker test_rabbitmq_logging
10
+ ```
11
+
12
+ 결과는 92개 중 88개 통과·4개 생략, 실패·오류 없음, 종료 코드 0입니다. timeout 테스트 9개 안에서 정수·기간 입력의 객체·Mapping 경로와 정규화, 큰 기간의 정밀도, `bool`·0·음수 거부 및 기존 선언·RPC 계약을 확인했습니다.
13
+
14
+ Pylance 파일 진단은 `Subscription.py`, `Connection.py`, `test_rabbitmq_timeouts.py` 모두 없었습니다. 전체 테스트나 실제 브로커·기존 큐 변경 검증은 수행하지 않았습니다.
@@ -172,7 +172,7 @@ class Connection(
172
172
  self,
173
173
  parameters: AnyValue,
174
174
  ) -> None:
175
- subscription = Subscription.parse(parameters)
175
+ subscription: Subscription = Subscription.parse(parameters)
176
176
  self.logger.debug('Task queue declaration: queue=%s error_queue=%s max_priority=%s durable=True', subscription.queue, subscription.error, subscription.max_priority)
177
177
  connection = self.connected()
178
178
  with self.observe('Task queue declaration'):
@@ -190,6 +190,8 @@ class Connection(
190
190
  }
191
191
  if subscription.max_priority is not None:
192
192
  arguments['x-max-priority'] = subscription.max_priority
193
+ if subscription.timeout is not None:
194
+ arguments['x-consumer-timeout'] = subscription.timeout
193
195
  channel.queue_declare(
194
196
  subscription.queue,
195
197
  durable=True,
@@ -1,8 +1,10 @@
1
1
  from collections.abc import Mapping
2
2
  from dataclasses import dataclass
3
+ from datetime import timedelta
3
4
  from typing import (
4
5
  Any,
5
6
  Optional,
7
+ Union,
6
8
  )
7
9
  from DefStream.Broker import validate_priority
8
10
 
@@ -19,8 +21,22 @@ class Subscription:
19
21
  error: Optional[str] = None
20
22
  max_priority: Optional[int] = None
21
23
  priority: Optional[int] = None
24
+ timeout: Optional[Union[int, timedelta]] = None
22
25
 
23
- def __post_init__(self):
26
+ def __post_init__(self) -> None:
27
+ if self.timeout is not None:
28
+ if isinstance(self.timeout, timedelta):
29
+ if self.timeout <= timedelta(0) or self.timeout.microseconds % 1000:
30
+ raise ValueError('Subscription timeout must be positive with whole-millisecond precision')
31
+ object.__setattr__(
32
+ self,
33
+ 'timeout',
34
+ self.timeout // timedelta(milliseconds=1),
35
+ )
36
+ elif isinstance(self.timeout, bool) or not isinstance(self.timeout, int):
37
+ raise TypeError('Subscription timeout must be integer milliseconds, a timedelta or None')
38
+ elif self.timeout <= 0:
39
+ raise ValueError('Subscription timeout must be positive')
24
40
  if self.priority is not None and self.max_priority is None:
25
41
  object.__setattr__(
26
42
  self,