abstract-block-dumper 0.0.1__tar.gz → 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.gitignore +2 -1
  2. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/CHANGELOG.md +15 -0
  3. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/PKG-INFO +127 -52
  4. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/README.md +126 -51
  5. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/README.md +1 -1
  6. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/tasks.py +1 -1
  7. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/docker-compose.yml +1 -1
  8. abstract_block_dumper-0.0.4/example_project/example_project/celery.py +26 -0
  9. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/example_project/settings.py +10 -0
  10. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/dal/django_dal.py +4 -3
  11. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/dal/memory_registry.py +12 -20
  12. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/discovery.py +1 -1
  13. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/exceptions.py +1 -1
  14. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/services/block_processor.py +15 -14
  15. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/services/executor.py +2 -2
  16. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/services/scheduler.py +9 -7
  17. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/services/utils.py +3 -7
  18. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/_version.py +2 -2
  19. abstract_block_dumper-0.0.1/src/abstract_block_dumper/management/commands/block_tasks.py → abstract_block_dumper-0.0.4/src/abstract_block_dumper/management/commands/block_tasks_v1.py +6 -3
  20. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/models.py +1 -1
  21. abstract_block_dumper-0.0.4/src/abstract_block_dumper/v1/celery.py +53 -0
  22. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/v1}/decorators.py +24 -29
  23. {abstract_block_dumper-0.0.1/src/abstract_block_dumper → abstract_block_dumper-0.0.4/src/abstract_block_dumper/v1}/tasks.py +3 -3
  24. abstract_block_dumper-0.0.4/tests/__init__.py +0 -0
  25. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/conftest.py +2 -2
  26. abstract_block_dumper-0.0.4/tests/integration/__init__.py +0 -0
  27. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/integration/test_block_processor.py +3 -3
  28. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/integration/test_concurrent_processing.py +6 -6
  29. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/integration/test_multi_arguments_tasks.py +4 -4
  30. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/integration/test_registered_celery_tasks.py +5 -5
  31. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/integration/test_scheduler.py +5 -5
  32. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/integration/test_task_registration.py +4 -4
  33. abstract_block_dumper-0.0.4/tests/unit/test_celery_integration.py +17 -0
  34. abstract_block_dumper-0.0.4/tests/unit/test_decorator.py +0 -0
  35. abstract_block_dumper-0.0.1/example_project/example_project/celery.py +0 -12
  36. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.cruft.json +0 -0
  37. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.github/dependabot.yml +0 -0
  38. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.github/workflows/ci.yml +0 -0
  39. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.github/workflows/publish.yml +0 -0
  40. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.pre-commit-config.yaml +0 -0
  41. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/.shellcheckrc +0 -0
  42. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/SECURITY.md +0 -0
  43. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/docs/3rd_party/cookiecutter-rt-pkg/CHANGELOG.md +0 -0
  44. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/.dockerignore +0 -0
  45. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/.gitignore +0 -0
  46. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/Dockerfile +0 -0
  47. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/__init__.py +0 -0
  48. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/admin.py +0 -0
  49. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/apps.py +0 -0
  50. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/management/__init__.py +0 -0
  51. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/management/commands/__init__.py +0 -0
  52. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/management/commands/create_admin.py +0 -0
  53. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/migrations/__init__.py +0 -0
  54. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/models.py +0 -0
  55. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/tests.py +0 -0
  56. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/block_explorer/views.py +0 -0
  57. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/example_project/__init__.py +0 -0
  58. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/example_project/asgi.py +0 -0
  59. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/example_project/urls.py +0 -0
  60. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/example_project/wsgi.py +0 -0
  61. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/main.py +0 -0
  62. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/manage.py +0 -0
  63. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/pyproject.toml +0 -0
  64. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/pytest.ini +0 -0
  65. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/example_project/uv.lock +0 -0
  66. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/noxfile.py +0 -0
  67. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/pyproject.toml +0 -0
  68. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/__init__.py +0 -0
  69. {abstract_block_dumper-0.0.1/src/abstract_block_dumper/dal → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal}/__init__.py +0 -0
  70. {abstract_block_dumper-0.0.1/src/abstract_block_dumper/management → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal/dal}/__init__.py +0 -0
  71. {abstract_block_dumper-0.0.1/src/abstract_block_dumper/migrations → abstract_block_dumper-0.0.4/src/abstract_block_dumper/_internal/services}/__init__.py +0 -0
  72. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/admin.py +0 -0
  73. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/apps.py +0 -0
  74. {abstract_block_dumper-0.0.1/src/abstract_block_dumper/services → abstract_block_dumper-0.0.4/src/abstract_block_dumper/management}/__init__.py +0 -0
  75. {abstract_block_dumper-0.0.1/tests → abstract_block_dumper-0.0.4/src/abstract_block_dumper/management/commands}/__init__.py +0 -0
  76. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/migrations/0001_initial.py +0 -0
  77. {abstract_block_dumper-0.0.1/tests/integration → abstract_block_dumper-0.0.4/src/abstract_block_dumper/migrations}/__init__.py +0 -0
  78. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/src/abstract_block_dumper/py.typed +0 -0
  79. /abstract_block_dumper-0.0.1/tests/unit/test_decorator.py → /abstract_block_dumper-0.0.4/src/abstract_block_dumper/v1/__init__.py +0 -0
  80. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/django_fixtures.py +0 -0
  81. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/fatories.py +0 -0
  82. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/tests/settings.py +0 -0
  83. {abstract_block_dumper-0.0.1 → abstract_block_dumper-0.0.4}/uv.lock +0 -0
@@ -14,4 +14,5 @@ media/
14
14
  .terraform/
15
15
  .nox/
16
16
  __pycache__
17
- src/abstract_block_dumper/_version.py
17
+ src/abstract_block_dumper/_version.py
18
+ .gitdo/
@@ -9,6 +9,21 @@ upcoming release can be found in [changelog.d](changelog.d).
9
9
 
10
10
  <!-- towncrier release notes start -->
11
11
 
12
+ ## [0.0.4](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.4) - 2025-11-17
13
+
14
+ No significant changes.
15
+
16
+
17
+ ## [0.0.5](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.5) - 2025-11-17
18
+
19
+ No significant changes.
20
+
21
+
22
+ ## [0.0.2](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.2) - 2025-10-24
23
+
24
+ No significant changes.
25
+
26
+
12
27
  ## [0.0.1](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.1) - 2025-10-21
13
28
 
14
29
  No significant changes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstract-block-dumper
3
- Version: 0.0.1
3
+ Version: 0.0.4
4
4
  Project-URL: Source, https://github.com/bactensor/abstract-block-dumper
5
5
  Project-URL: Issue Tracker, https://github.com/bactensor/abstract-block-dumper/issues
6
6
  Author-email: Reef Technologies <opensource@reef.pl>
@@ -26,6 +26,22 @@ Description-Content-Type: text/markdown
26
26
  This package provides a simplified framework for creating block processing tasks in Django applications.
27
27
  Define tasks with lambda conditions using the @block_task decorator and run them asynchronously with Celery.
28
28
 
29
+ ## Usage
30
+
31
+ > [!IMPORTANT]
32
+ > This package uses [ApiVer](#versioning), make sure to import `abstract_block_dumper.v1`.
33
+
34
+
35
+ ## Versioning
36
+
37
+ This package uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
38
+ TL;DR you are safe to use [compatible release version specifier](https://packaging.python.org/en/latest/specifications/version-specifiers/#compatible-release) `~=MAJOR.MINOR` in your `pyproject.toml` or `requirements.txt`.
39
+
40
+ Additionally, this package uses [ApiVer](https://www.youtube.com/watch?v=FgcoAKchPjk) to further reduce the risk of breaking changes.
41
+ This means, the public API of this package is explicitly versioned, e.g. `abstract_block_dumper.v1`, and will not change in a backwards-incompatible way even when `abstract_block_dumper.v2` is released.
42
+
43
+ Internal packages, i.e. prefixed by `abstract_block_dumper._` do not share these guarantees and may change in a backwards-incompatible way at any time even in patch releases.
44
+
29
45
  ## Implementation Details
30
46
 
31
47
  ### General Workflow:
@@ -82,6 +98,28 @@ INSTALLED_APPS = [
82
98
  python manage.py migrate
83
99
  ```
84
100
 
101
+ 4. **Configure Celery to discover block tasks:**
102
+
103
+ In your project's `celery.py` file, add the following to ensure Celery workers can discover your `@block_task` decorated functions:
104
+
105
+ ```python
106
+ from celery import Celery
107
+ from celery.signals import worker_ready
108
+ from django.conf import settings
109
+
110
+ app = Celery('your_project')
111
+ app.config_from_object('django.conf:settings', namespace='CELERY')
112
+ app.autodiscover_tasks()
113
+
114
+ @worker_ready.connect
115
+ def on_worker_ready(**kwargs):
116
+ """Load block tasks when worker starts."""
117
+ from abstract_block_dumper.v1.celery import setup_celery_tasks
118
+ setup_celery_tasks()
119
+ ```
120
+
121
+ > **Important:** Without this step, Celery workers will not recognize your `@block_task` decorated functions, and you'll see "Received unregistered task" errors.
122
+
85
123
  ## Usage
86
124
 
87
125
  ### 1. Define Block Processing Tasks
@@ -93,7 +131,7 @@ Create block processing tasks in `tasks.py` or `block_tasks.py` file inside any
93
131
  ### 3. Start the Block Scheduler
94
132
  Run the scheduler to start processing blocks:
95
133
  ```bash
96
- $ python manage.py block_tasks
134
+ $ python manage.py block_tasks_v1
97
135
  ```
98
136
 
99
137
  This command will:
@@ -112,7 +150,7 @@ See examples below:
112
150
  Use the `@block_task` decorator with lambda conditions to create block processing tasks:
113
151
 
114
152
  ```python
115
- from abstract_block_dumper.decorators import block_task
153
+ from abstract_block_dumper.v1.decorators import block_task
116
154
 
117
155
 
118
156
  # Process every block
@@ -144,7 +182,7 @@ def process_multi_netuid_task(block_number: int, netuid: int):
144
182
  The framework provides a maintenance task to clean up old task records and maintain database performance:
145
183
 
146
184
  ```python
147
- from abstract_block_dumper.tasks import cleanup_old_tasks
185
+ from abstract_block_dumper.v1.tasks import cleanup_old_tasks
148
186
 
149
187
  # Delete tasks older than 7 days (default)
150
188
  cleanup_old_tasks.delay()
@@ -160,13 +198,13 @@ This task deletes all succeeded or unrecoverable failed tasks older than the spe
160
198
  **Option 1: Manual Execution**
161
199
  ```bash
162
200
  # Using Django shell
163
- python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
201
+ python manage.py shell -c "from abstract_block_dumper.v1.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
164
202
  ```
165
203
 
166
204
  **Option 2: Cron Job (Recommended - once per day)**
167
205
  ```bash
168
206
  # Add to crontab (daily at 2 AM)
169
- 0 2 * * * cd /path/to/your/project && python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
207
+ 0 2 * * * cd /path/to/your/project && python manage.py shell -c "from abstract_block_dumper.v1.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
170
208
  ```
171
209
 
172
210
  **Option 3: Celery Beat (Automated Scheduling)**
@@ -210,55 +248,92 @@ BLOCK_DUMPER_MAX_ATTEMPTS = 3 # maximum retry attempts
210
248
  BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 1440 # maximum retry delay (24 hours)
211
249
  ```
212
250
 
213
- ### Configuration Options Reference
214
-
215
- #### Core Settings
216
-
217
- **BITTENSOR_NETWORK** (str, default: `'finney'`) Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
218
-
219
- **BLOCK_DUMPER_START_FROM_BLOCK** (str|int|None, default: `None`)
220
- - **Purpose**: Determines the starting block for processing when the scheduler first runs
221
- - **Valid Values**:
222
- - `None`: Resume from the last processed block stored in database
223
- - `'current'`: Start from the current blockchain block (skips historical blocks)
224
- - `int`: Start from a specific block number (e.g., `1000000`)
225
- - **Example**: `BLOCK_DUMPER_START_FROM_BLOCK = 'current'`
226
- - **Performance Impact**: Starting from historical blocks may require significant processing time
227
-
228
- #### Scheduler Settings
229
-
230
- **BLOCK_DUMPER_POLL_INTERVAL** (int, default: `1`)
231
- - **Purpose**: Seconds to wait between checking for new blocks
232
- - **Valid Range**: `1` to `3600` (1 second to 1 hour)
233
- - **Example**: `BLOCK_DUMPER_POLL_INTERVAL = 5`
234
- - **Performance Impact**:
235
- - Lower values (1-2s): Near real-time processing, higher CPU/network usage
236
- - Higher values (10-60s): Reduced load but delayed processing
237
- - Very low values (<1s) may cause rate limiting
238
-
239
- #### Retry and Error Handling Settings
240
-
241
- **BLOCK_DUMPER_MAX_ATTEMPTS** (int, default: `3`)
242
- - **Purpose**: Maximum number of attempts to retry a failed task before giving up
243
- - **Valid Range**: `1` to `10`
244
- - **Example**: `BLOCK_DUMPER_MAX_ATTEMPTS = 5`
245
- - **Performance Impact**: Higher values increase resilience but may delay failure detection
246
-
247
- **BLOCK_TASK_RETRY_BACKOFF** (int, default: `1`)
248
- - **Purpose**: Base number of minutes for exponential backoff retry delays
249
- - **Valid Range**: `1` to `60`
250
- - **Example**: `BLOCK_TASK_RETRY_BACKOFF = 2`
251
- - **Calculation**: Actual delay = `backoff ** attempt_count` minutes
251
+ ## Configuration Options Reference
252
+
253
+ ### `BITTENSOR_NETWORK`
254
+ - **Type:** `str`
255
+ - **Default:** `'finney'`
256
+ - **Description:** Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
257
+
258
+ ---
259
+
260
+ ### `BLOCK_DUMPER_START_FROM_BLOCK`
261
+ - **Type:** `str | int | None`
262
+ - **Default:** `None`
263
+ - **Valid Range:** `None`, `'current'`, or any positive integer
264
+ - **Description:** Determines the starting block for processing when the scheduler first runs
265
+ - `None` → Resume from the last processed block stored in database
266
+ - `'current'` → Start from the current blockchain block (skips historical blocks)
267
+ - Integer → Start from a specific block number (e.g., `1000000`)
268
+
269
+ ```python
270
+ BLOCK_DUMPER_START_FROM_BLOCK = 'current'
271
+ ```
272
+
273
+ > **Performance Impact:** Starting from historical blocks may require significant processing time
274
+
275
+ ---
276
+
277
+ ### `BLOCK_DUMPER_POLL_INTERVAL`
278
+ - **Type:** `int`
279
+ - **Default:** `1`
280
+ - **Valid Range:** `1` to `3600` (seconds)
281
+ - **Description:** Seconds to wait between checking for new blocks
282
+
283
+ ```python
284
+ BLOCK_DUMPER_POLL_INTERVAL = 5
285
+ ```
286
+
287
+ > **Performance Impact:**
288
+ > - Lower values (1-2s): Near real-time processing, higher CPU/network usage
289
+ > - Higher values (10-60s): Reduced load but delayed processing
290
+ > - Very low values (<1s): May cause rate limiting
291
+
292
+ ---
293
+
294
+ ### `BLOCK_DUMPER_MAX_ATTEMPTS`
295
+ - **Type:** `int`
296
+ - **Default:** `3`
297
+ - **Valid Range:** `1` to `10`
298
+ - **Description:** Maximum number of attempts to retry a failed task before giving up
299
+
300
+ ```python
301
+ BLOCK_DUMPER_MAX_ATTEMPTS = 5
302
+ ```
303
+
304
+ > **Performance Impact:** Higher values increase resilience but may delay failure detection
305
+
306
+ ---
307
+
308
+ ### `BLOCK_TASK_RETRY_BACKOFF`
309
+ - **Type:** `int`
310
+ - **Default:** `1`
311
+ - **Valid Range:** `1` to `60` (minutes)
312
+ - **Description:** Base number of minutes for exponential backoff retry delays
313
+ - **Calculation:** Actual delay = `backoff ** attempt_count` minutes
252
314
  - Attempt 1: 2¹ = 2 minutes
253
- - Attempt 2: 2² = 4 minutes
315
+ - Attempt 2: 2² = 4 minutes
254
316
  - Attempt 3: 2³ = 8 minutes
255
- - **Performance Impact**: Lower values retry faster but may overwhelm failing services
256
317
 
257
- **BLOCK_TASK_MAX_RETRY_DELAY_MINUTES** (int, default: `1440`)
258
- - **Purpose**: Maximum delay (in minutes) between retry attempts, caps exponential backoff
259
- - **Valid Range**: `1` to `10080` (1 minute to 1 week)
260
- - **Example**: `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720` # 12 hours max
261
- - **Performance Impact**: Prevents extremely long delays while maintaining backoff benefits
318
+ ```python
319
+ BLOCK_TASK_RETRY_BACKOFF = 2
320
+ ```
321
+
322
+ > **Performance Impact:** Lower values retry faster but may overwhelm failing services
323
+
324
+ ---
325
+
326
+ ### `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES`
327
+ - **Type:** `int`
328
+ - **Default:** `1440` (24 hours)
329
+ - **Valid Range:** `1` to `10080` (1 minute to 1 week)
330
+ - **Description:** Maximum delay (in minutes) between retry attempts, caps exponential backoff
331
+
332
+ ```python
333
+ BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720 # 12 hours max
334
+ ```
335
+
336
+ > **Performance Impact:** Prevents extremely long delays while maintaining backoff benefits
262
337
 
263
338
 
264
339
  ## Example Project
@@ -4,6 +4,22 @@
4
4
  This package provides a simplified framework for creating block processing tasks in Django applications.
5
5
  Define tasks with lambda conditions using the @block_task decorator and run them asynchronously with Celery.
6
6
 
7
+ ## Usage
8
+
9
+ > [!IMPORTANT]
10
+ > This package uses [ApiVer](#versioning), make sure to import `abstract_block_dumper.v1`.
11
+
12
+
13
+ ## Versioning
14
+
15
+ This package uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
16
+ TL;DR you are safe to use [compatible release version specifier](https://packaging.python.org/en/latest/specifications/version-specifiers/#compatible-release) `~=MAJOR.MINOR` in your `pyproject.toml` or `requirements.txt`.
17
+
18
+ Additionally, this package uses [ApiVer](https://www.youtube.com/watch?v=FgcoAKchPjk) to further reduce the risk of breaking changes.
19
+ This means, the public API of this package is explicitly versioned, e.g. `abstract_block_dumper.v1`, and will not change in a backwards-incompatible way even when `abstract_block_dumper.v2` is released.
20
+
21
+ Internal packages, i.e. prefixed by `abstract_block_dumper._` do not share these guarantees and may change in a backwards-incompatible way at any time even in patch releases.
22
+
7
23
  ## Implementation Details
8
24
 
9
25
  ### General Workflow:
@@ -60,6 +76,28 @@ INSTALLED_APPS = [
60
76
  python manage.py migrate
61
77
  ```
62
78
 
79
+ 4. **Configure Celery to discover block tasks:**
80
+
81
+ In your project's `celery.py` file, add the following to ensure Celery workers can discover your `@block_task` decorated functions:
82
+
83
+ ```python
84
+ from celery import Celery
85
+ from celery.signals import worker_ready
86
+ from django.conf import settings
87
+
88
+ app = Celery('your_project')
89
+ app.config_from_object('django.conf:settings', namespace='CELERY')
90
+ app.autodiscover_tasks()
91
+
92
+ @worker_ready.connect
93
+ def on_worker_ready(**kwargs):
94
+ """Load block tasks when worker starts."""
95
+ from abstract_block_dumper.v1.celery import setup_celery_tasks
96
+ setup_celery_tasks()
97
+ ```
98
+
99
+ > **Important:** Without this step, Celery workers will not recognize your `@block_task` decorated functions, and you'll see "Received unregistered task" errors.
100
+
63
101
  ## Usage
64
102
 
65
103
  ### 1. Define Block Processing Tasks
@@ -71,7 +109,7 @@ Create block processing tasks in `tasks.py` or `block_tasks.py` file inside any
71
109
  ### 3. Start the Block Scheduler
72
110
  Run the scheduler to start processing blocks:
73
111
  ```bash
74
- $ python manage.py block_tasks
112
+ $ python manage.py block_tasks_v1
75
113
  ```
76
114
 
77
115
  This command will:
@@ -90,7 +128,7 @@ See examples below:
90
128
  Use the `@block_task` decorator with lambda conditions to create block processing tasks:
91
129
 
92
130
  ```python
93
- from abstract_block_dumper.decorators import block_task
131
+ from abstract_block_dumper.v1.decorators import block_task
94
132
 
95
133
 
96
134
  # Process every block
@@ -122,7 +160,7 @@ def process_multi_netuid_task(block_number: int, netuid: int):
122
160
  The framework provides a maintenance task to clean up old task records and maintain database performance:
123
161
 
124
162
  ```python
125
- from abstract_block_dumper.tasks import cleanup_old_tasks
163
+ from abstract_block_dumper.v1.tasks import cleanup_old_tasks
126
164
 
127
165
  # Delete tasks older than 7 days (default)
128
166
  cleanup_old_tasks.delay()
@@ -138,13 +176,13 @@ This task deletes all succeeded or unrecoverable failed tasks older than the spe
138
176
  **Option 1: Manual Execution**
139
177
  ```bash
140
178
  # Using Django shell
141
- python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
179
+ python manage.py shell -c "from abstract_block_dumper.v1.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
142
180
  ```
143
181
 
144
182
  **Option 2: Cron Job (Recommended - once per day)**
145
183
  ```bash
146
184
  # Add to crontab (daily at 2 AM)
147
- 0 2 * * * cd /path/to/your/project && python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
185
+ 0 2 * * * cd /path/to/your/project && python manage.py shell -c "from abstract_block_dumper.v1.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
148
186
  ```
149
187
 
150
188
  **Option 3: Celery Beat (Automated Scheduling)**
@@ -188,55 +226,92 @@ BLOCK_DUMPER_MAX_ATTEMPTS = 3 # maximum retry attempts
188
226
  BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 1440 # maximum retry delay (24 hours)
189
227
  ```
190
228
 
191
- ### Configuration Options Reference
192
-
193
- #### Core Settings
194
-
195
- **BITTENSOR_NETWORK** (str, default: `'finney'`) Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
196
-
197
- **BLOCK_DUMPER_START_FROM_BLOCK** (str|int|None, default: `None`)
198
- - **Purpose**: Determines the starting block for processing when the scheduler first runs
199
- - **Valid Values**:
200
- - `None`: Resume from the last processed block stored in database
201
- - `'current'`: Start from the current blockchain block (skips historical blocks)
202
- - `int`: Start from a specific block number (e.g., `1000000`)
203
- - **Example**: `BLOCK_DUMPER_START_FROM_BLOCK = 'current'`
204
- - **Performance Impact**: Starting from historical blocks may require significant processing time
205
-
206
- #### Scheduler Settings
207
-
208
- **BLOCK_DUMPER_POLL_INTERVAL** (int, default: `1`)
209
- - **Purpose**: Seconds to wait between checking for new blocks
210
- - **Valid Range**: `1` to `3600` (1 second to 1 hour)
211
- - **Example**: `BLOCK_DUMPER_POLL_INTERVAL = 5`
212
- - **Performance Impact**:
213
- - Lower values (1-2s): Near real-time processing, higher CPU/network usage
214
- - Higher values (10-60s): Reduced load but delayed processing
215
- - Very low values (<1s) may cause rate limiting
216
-
217
- #### Retry and Error Handling Settings
218
-
219
- **BLOCK_DUMPER_MAX_ATTEMPTS** (int, default: `3`)
220
- - **Purpose**: Maximum number of attempts to retry a failed task before giving up
221
- - **Valid Range**: `1` to `10`
222
- - **Example**: `BLOCK_DUMPER_MAX_ATTEMPTS = 5`
223
- - **Performance Impact**: Higher values increase resilience but may delay failure detection
224
-
225
- **BLOCK_TASK_RETRY_BACKOFF** (int, default: `1`)
226
- - **Purpose**: Base number of minutes for exponential backoff retry delays
227
- - **Valid Range**: `1` to `60`
228
- - **Example**: `BLOCK_TASK_RETRY_BACKOFF = 2`
229
- - **Calculation**: Actual delay = `backoff ** attempt_count` minutes
229
+ ## Configuration Options Reference
230
+
231
+ ### `BITTENSOR_NETWORK`
232
+ - **Type:** `str`
233
+ - **Default:** `'finney'`
234
+ - **Description:** Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
235
+
236
+ ---
237
+
238
+ ### `BLOCK_DUMPER_START_FROM_BLOCK`
239
+ - **Type:** `str | int | None`
240
+ - **Default:** `None`
241
+ - **Valid Range:** `None`, `'current'`, or any positive integer
242
+ - **Description:** Determines the starting block for processing when the scheduler first runs
243
+ - `None` → Resume from the last processed block stored in database
244
+ - `'current'` → Start from the current blockchain block (skips historical blocks)
245
+ - Integer → Start from a specific block number (e.g., `1000000`)
246
+
247
+ ```python
248
+ BLOCK_DUMPER_START_FROM_BLOCK = 'current'
249
+ ```
250
+
251
+ > **Performance Impact:** Starting from historical blocks may require significant processing time
252
+
253
+ ---
254
+
255
+ ### `BLOCK_DUMPER_POLL_INTERVAL`
256
+ - **Type:** `int`
257
+ - **Default:** `1`
258
+ - **Valid Range:** `1` to `3600` (seconds)
259
+ - **Description:** Seconds to wait between checking for new blocks
260
+
261
+ ```python
262
+ BLOCK_DUMPER_POLL_INTERVAL = 5
263
+ ```
264
+
265
+ > **Performance Impact:**
266
+ > - Lower values (1-2s): Near real-time processing, higher CPU/network usage
267
+ > - Higher values (10-60s): Reduced load but delayed processing
268
+ > - Very low values (<1s): May cause rate limiting
269
+
270
+ ---
271
+
272
+ ### `BLOCK_DUMPER_MAX_ATTEMPTS`
273
+ - **Type:** `int`
274
+ - **Default:** `3`
275
+ - **Valid Range:** `1` to `10`
276
+ - **Description:** Maximum number of attempts to retry a failed task before giving up
277
+
278
+ ```python
279
+ BLOCK_DUMPER_MAX_ATTEMPTS = 5
280
+ ```
281
+
282
+ > **Performance Impact:** Higher values increase resilience but may delay failure detection
283
+
284
+ ---
285
+
286
+ ### `BLOCK_TASK_RETRY_BACKOFF`
287
+ - **Type:** `int`
288
+ - **Default:** `1`
289
+ - **Valid Range:** `1` to `60` (minutes)
290
+ - **Description:** Base number of minutes for exponential backoff retry delays
291
+ - **Calculation:** Actual delay = `backoff ** attempt_count` minutes
230
292
  - Attempt 1: 2¹ = 2 minutes
231
- - Attempt 2: 2² = 4 minutes
293
+ - Attempt 2: 2² = 4 minutes
232
294
  - Attempt 3: 2³ = 8 minutes
233
- - **Performance Impact**: Lower values retry faster but may overwhelm failing services
234
295
 
235
- **BLOCK_TASK_MAX_RETRY_DELAY_MINUTES** (int, default: `1440`)
236
- - **Purpose**: Maximum delay (in minutes) between retry attempts, caps exponential backoff
237
- - **Valid Range**: `1` to `10080` (1 minute to 1 week)
238
- - **Example**: `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720` # 12 hours max
239
- - **Performance Impact**: Prevents extremely long delays while maintaining backoff benefits
296
+ ```python
297
+ BLOCK_TASK_RETRY_BACKOFF = 2
298
+ ```
299
+
300
+ > **Performance Impact:** Lower values retry faster but may overwhelm failing services
301
+
302
+ ---
303
+
304
+ ### `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES`
305
+ - **Type:** `int`
306
+ - **Default:** `1440` (24 hours)
307
+ - **Valid Range:** `1` to `10080` (1 minute to 1 week)
308
+ - **Description:** Maximum delay (in minutes) between retry attempts, caps exponential backoff
309
+
310
+ ```python
311
+ BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720 # 12 hours max
312
+ ```
313
+
314
+ > **Performance Impact:** Prevents extremely long delays while maintaining backoff benefits
240
315
 
241
316
 
242
317
  ## Example Project
@@ -15,5 +15,5 @@ docker-compose up --build
15
15
  2. Access the Django admin interface at `http://localhost:8000/admin` with username `admin` and password `admin` (automatically created).
16
16
  3. Start the block dumper scheduler:
17
17
  ```bash
18
- docker-compose exec web python manage.py block_tasks
18
+ docker-compose exec web python manage.py block_tasks_v1
19
19
  ```
@@ -1,4 +1,4 @@
1
- from abstract_block_dumper.decorators import block_task
1
+ from abstract_block_dumper.v1.decorators import block_task
2
2
 
3
3
 
4
4
  @block_task(
@@ -69,7 +69,7 @@ services:
69
69
  build:
70
70
  context: ..
71
71
  dockerfile: example_project/Dockerfile
72
- command: uv run python manage.py block_tasks
72
+ command: uv run python manage.py block_tasks_v1
73
73
  volumes:
74
74
  - .:/app/example_project
75
75
  - ../src:/app/src
@@ -0,0 +1,26 @@
1
+ import os
2
+
3
+ from celery import Celery
4
+ from celery.signals import worker_ready
5
+ from django.conf import settings
6
+
7
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_project.settings")
8
+
9
+ app = Celery("example_project")
10
+
11
+ app.config_from_object(settings, namespace="CELERY")
12
+
13
+ app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
14
+
15
+
16
+ @worker_ready.connect
17
+ def on_worker_ready(**kwargs):
18
+ """
19
+ Load block tasks when Celery worker starts.
20
+
21
+ This is required for abstract-block-dumper to register @block_task
22
+ decorated functions so they can receive messages from the broker.
23
+ """
24
+ from abstract_block_dumper.v1.celery import setup_celery_tasks
25
+
26
+ setup_celery_tasks()
@@ -3,6 +3,7 @@ import sys
3
3
  from pathlib import Path
4
4
 
5
5
  import dj_database_url # type: ignore
6
+ from celery.schedules import crontab
6
7
 
7
8
  # Build paths inside the project like this: BASE_DIR / 'subdir'.
8
9
  BASE_DIR = Path(__file__).resolve().parent.parent
@@ -133,3 +134,12 @@ BLOCK_DUMPER_POLL_INTERVAL = 1 # seconds - ultra-fast polling for real-time pro
133
134
  BLOCK_DUMPER_START_FROM_BLOCK = "current" # None = resume from DB, 'current' = current block, or block number
134
135
  BLOCK_TASK_RETRY_BACKOFF = 2
135
136
  BLOCK_DUMPER_MAX_ATTEMPTS = 3
137
+
138
+
139
+ CELERY_BEAT_SCHEDULE = {
140
+ "cleanup-old-tasks": {
141
+ "task": "abstract_block_dumper.cleanup_old_tasks",
142
+ "schedule": crontab(hour=2, minute=0), # Daily at 2 AM
143
+ "kwargs": {"days": 7}, # Customize retention period
144
+ },
145
+ }
@@ -6,8 +6,8 @@ from django.db import transaction
6
6
  from django.db.models.query import QuerySet
7
7
  from django.utils import timezone
8
8
 
9
+ import abstract_block_dumper._internal.services.utils as abd_utils
9
10
  import abstract_block_dumper.models as abd_models
10
- import abstract_block_dumper.services.utils as abd_utils
11
11
 
12
12
 
13
13
  def get_ready_to_retry_attempts() -> QuerySet[abd_models.TaskAttempt]:
@@ -76,7 +76,7 @@ def task_mark_as_success(task: abd_models.TaskAttempt, result_data: dict) -> Non
76
76
  task.save()
77
77
 
78
78
 
79
- def task_mark_as_failed(task) -> None:
79
+ def task_mark_as_failed(task: abd_models.TaskAttempt) -> None:
80
80
  DEFAULT_BLOCK_TASK_RETRY_BACKOFF = 1
81
81
  MAX_RETRY_DELAY_MINUTES = 1440 # 24 hours max delay
82
82
 
@@ -98,7 +98,7 @@ def task_mark_as_failed(task) -> None:
98
98
  task.save()
99
99
 
100
100
 
101
- def task_schedule_to_retry(task):
101
+ def task_schedule_to_retry(task: abd_models.TaskAttempt) -> None:
102
102
  task.status = abd_models.TaskAttempt.Status.PENDING
103
103
  task.save()
104
104
 
@@ -110,6 +110,7 @@ def task_create_or_get_pending(
110
110
  ) -> tuple[abd_models.TaskAttempt, bool]:
111
111
  """
112
112
  Create or get a pending task attempt.
113
+
113
114
  Returns (task, created) where created indicates if a new task was created.
114
115
 
115
116
  For failed tasks that can retry: