abstract-block-dumper 0.0.5__tar.gz → 0.0.7__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 (85) hide show
  1. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.gitignore +2 -1
  2. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/CHANGELOG.md +9 -4
  3. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/PKG-INFO +132 -54
  4. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/README.md +130 -52
  5. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/README.md +1 -1
  6. abstract_block_dumper-0.0.7/example_project/block_explorer/tasks.py +46 -0
  7. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/docker-compose.yml +1 -1
  8. abstract_block_dumper-0.0.7/example_project/example_project/celery.py +26 -0
  9. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/example_project/settings.py +10 -0
  10. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/uv.lock +1 -1
  11. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/noxfile.py +4 -1
  12. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/pyproject.toml +2 -2
  13. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/dal/django_dal.py +4 -3
  14. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/dal/memory_registry.py +12 -20
  15. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/discovery.py +1 -1
  16. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/exceptions.py +1 -1
  17. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/services/block_processor.py +15 -14
  18. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/services/executor.py +2 -2
  19. abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal/services/scheduler.py +161 -0
  20. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/services/utils.py +4 -12
  21. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/_version.py +2 -2
  22. abstract_block_dumper-0.0.5/src/abstract_block_dumper/management/commands/block_tasks.py → abstract_block_dumper-0.0.7/src/abstract_block_dumper/management/commands/block_tasks_v1.py +6 -3
  23. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/models.py +1 -1
  24. abstract_block_dumper-0.0.7/src/abstract_block_dumper/v1/celery.py +53 -0
  25. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/v1}/decorators.py +56 -39
  26. {abstract_block_dumper-0.0.5/src/abstract_block_dumper → abstract_block_dumper-0.0.7/src/abstract_block_dumper/v1}/tasks.py +3 -3
  27. abstract_block_dumper-0.0.7/tests/__init__.py +0 -0
  28. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/conftest.py +2 -2
  29. abstract_block_dumper-0.0.7/tests/integration/__init__.py +0 -0
  30. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/integration/test_block_processor.py +3 -3
  31. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/integration/test_concurrent_processing.py +6 -6
  32. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/integration/test_multi_arguments_tasks.py +4 -4
  33. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/integration/test_registered_celery_tasks.py +6 -7
  34. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/integration/test_scheduler.py +9 -9
  35. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/integration/test_task_registration.py +4 -4
  36. abstract_block_dumper-0.0.7/tests/unit/test_celery_integration.py +17 -0
  37. abstract_block_dumper-0.0.7/tests/unit/test_decorator.py +0 -0
  38. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/uv.lock +1 -1
  39. abstract_block_dumper-0.0.5/example_project/block_explorer/tasks.py +0 -27
  40. abstract_block_dumper-0.0.5/example_project/example_project/celery.py +0 -12
  41. abstract_block_dumper-0.0.5/src/abstract_block_dumper/services/scheduler.py +0 -92
  42. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.cruft.json +0 -0
  43. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.github/dependabot.yml +0 -0
  44. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.github/workflows/ci.yml +0 -0
  45. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.github/workflows/publish.yml +0 -0
  46. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.pre-commit-config.yaml +0 -0
  47. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/.shellcheckrc +0 -0
  48. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/SECURITY.md +0 -0
  49. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/docs/3rd_party/cookiecutter-rt-pkg/CHANGELOG.md +0 -0
  50. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/.dockerignore +0 -0
  51. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/.gitignore +0 -0
  52. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/Dockerfile +0 -0
  53. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/__init__.py +0 -0
  54. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/admin.py +0 -0
  55. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/apps.py +0 -0
  56. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/management/__init__.py +0 -0
  57. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/management/commands/__init__.py +0 -0
  58. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/management/commands/create_admin.py +0 -0
  59. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/migrations/__init__.py +0 -0
  60. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/models.py +0 -0
  61. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/tests.py +0 -0
  62. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/block_explorer/views.py +0 -0
  63. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/example_project/__init__.py +0 -0
  64. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/example_project/asgi.py +0 -0
  65. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/example_project/urls.py +0 -0
  66. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/example_project/wsgi.py +0 -0
  67. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/main.py +0 -0
  68. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/manage.py +0 -0
  69. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/pyproject.toml +0 -0
  70. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/example_project/pytest.ini +0 -0
  71. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/__init__.py +0 -0
  72. {abstract_block_dumper-0.0.5/src/abstract_block_dumper/dal → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal}/__init__.py +0 -0
  73. {abstract_block_dumper-0.0.5/src/abstract_block_dumper/management → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal/dal}/__init__.py +0 -0
  74. {abstract_block_dumper-0.0.5/src/abstract_block_dumper/migrations → abstract_block_dumper-0.0.7/src/abstract_block_dumper/_internal/services}/__init__.py +0 -0
  75. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/admin.py +0 -0
  76. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/apps.py +0 -0
  77. {abstract_block_dumper-0.0.5/src/abstract_block_dumper/services → abstract_block_dumper-0.0.7/src/abstract_block_dumper/management}/__init__.py +0 -0
  78. {abstract_block_dumper-0.0.5/tests → abstract_block_dumper-0.0.7/src/abstract_block_dumper/management/commands}/__init__.py +0 -0
  79. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/migrations/0001_initial.py +0 -0
  80. {abstract_block_dumper-0.0.5/tests/integration → abstract_block_dumper-0.0.7/src/abstract_block_dumper/migrations}/__init__.py +0 -0
  81. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/src/abstract_block_dumper/py.typed +0 -0
  82. /abstract_block_dumper-0.0.5/tests/unit/test_decorator.py → /abstract_block_dumper-0.0.7/src/abstract_block_dumper/v1/__init__.py +0 -0
  83. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/django_fixtures.py +0 -0
  84. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/fatories.py +0 -0
  85. {abstract_block_dumper-0.0.5 → abstract_block_dumper-0.0.7}/tests/settings.py +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,22 +9,27 @@ upcoming release can be found in [changelog.d](changelog.d).
9
9
 
10
10
  <!-- towncrier release notes start -->
11
11
 
12
- ## [0.0.5](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.5) - 2025-10-21
12
+ ## [0.0.7](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.7) - 2025-12-01
13
13
 
14
14
  No significant changes.
15
15
 
16
16
 
17
- ## [0.0.4](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.4) - 2025-10-21
17
+ ## [0.0.6](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.6) - 2025-11-26
18
18
 
19
19
  No significant changes.
20
20
 
21
21
 
22
- ## [0.0.3](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.3) - 2025-10-21
22
+ ## [0.0.4](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.4) - 2025-11-17
23
23
 
24
24
  No significant changes.
25
25
 
26
26
 
27
- ## [0.0.2](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.2) - 2025-10-21
27
+ ## [0.0.5](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.5) - 2025-11-17
28
+
29
+ No significant changes.
30
+
31
+
32
+ ## [0.0.2](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.2) - 2025-10-24
28
33
 
29
34
  No significant changes.
30
35
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstract-block-dumper
3
- Version: 0.0.5
3
+ Version: 0.0.7
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>
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Topic :: Software Development :: Libraries
16
16
  Requires-Python: >=3.11
17
17
  Requires-Dist: bittensor>=9.10.1
18
- Requires-Dist: celery>=5.5.3
18
+ Requires-Dist: celery>=5.3
19
19
  Requires-Dist: django<6.0,>=3.2
20
20
  Requires-Dist: structlog>=25.4.0
21
21
  Description-Content-Type: text/markdown
@@ -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,31 @@ 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 celeryd_init
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
+
115
+
116
+ @celeryd_init.connect
117
+ def on_worker_init(**kwargs) -> None:
118
+ """Load block tasks when worker initializes."""
119
+ from abstract_block_dumper.v1.celery import setup_celery_tasks
120
+ setup_celery_tasks()
121
+
122
+ ```
123
+
124
+ > **Important:** Without this step, Celery workers will not recognize your `@block_task` decorated functions, and you'll see "Received unregistered task" errors.
125
+
85
126
  ## Usage
86
127
 
87
128
  ### 1. Define Block Processing Tasks
@@ -93,7 +134,7 @@ Create block processing tasks in `tasks.py` or `block_tasks.py` file inside any
93
134
  ### 3. Start the Block Scheduler
94
135
  Run the scheduler to start processing blocks:
95
136
  ```bash
96
- $ python manage.py block_tasks
137
+ $ python manage.py block_tasks_v1
97
138
  ```
98
139
 
99
140
  This command will:
@@ -112,11 +153,11 @@ See examples below:
112
153
  Use the `@block_task` decorator with lambda conditions to create block processing tasks:
113
154
 
114
155
  ```python
115
- from abstract_block_dumper.decorators import block_task
156
+ from abstract_block_dumper.v1.decorators import block_task
116
157
 
117
158
 
118
159
  # Process every block
119
- @block_task(condition=lambda bn: True)
160
+ @block_task
120
161
  def process_every_block(block_number: int):
121
162
  print(f"Processing every block: {block_number}")
122
163
 
@@ -144,7 +185,7 @@ def process_multi_netuid_task(block_number: int, netuid: int):
144
185
  The framework provides a maintenance task to clean up old task records and maintain database performance:
145
186
 
146
187
  ```python
147
- from abstract_block_dumper.tasks import cleanup_old_tasks
188
+ from abstract_block_dumper.v1.tasks import cleanup_old_tasks
148
189
 
149
190
  # Delete tasks older than 7 days (default)
150
191
  cleanup_old_tasks.delay()
@@ -160,13 +201,13 @@ This task deletes all succeeded or unrecoverable failed tasks older than the spe
160
201
  **Option 1: Manual Execution**
161
202
  ```bash
162
203
  # Using Django shell
163
- python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
204
+ python manage.py shell -c "from abstract_block_dumper.v1.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
164
205
  ```
165
206
 
166
207
  **Option 2: Cron Job (Recommended - once per day)**
167
208
  ```bash
168
209
  # 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()"
210
+ 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
211
  ```
171
212
 
172
213
  **Option 3: Celery Beat (Automated Scheduling)**
@@ -210,55 +251,92 @@ BLOCK_DUMPER_MAX_ATTEMPTS = 3 # maximum retry attempts
210
251
  BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 1440 # maximum retry delay (24 hours)
211
252
  ```
212
253
 
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
254
+ ## Configuration Options Reference
255
+
256
+ ### `BITTENSOR_NETWORK`
257
+ - **Type:** `str`
258
+ - **Default:** `'finney'`
259
+ - **Description:** Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
260
+
261
+ ---
262
+
263
+ ### `BLOCK_DUMPER_START_FROM_BLOCK`
264
+ - **Type:** `str | int | None`
265
+ - **Default:** `None`
266
+ - **Valid Range:** `None`, `'current'`, or any positive integer
267
+ - **Description:** Determines the starting block for processing when the scheduler first runs
268
+ - `None` → Resume from the last processed block stored in database
269
+ - `'current'` → Start from the current blockchain block (skips historical blocks)
270
+ - Integer → Start from a specific block number (e.g., `1000000`)
271
+
272
+ ```python
273
+ BLOCK_DUMPER_START_FROM_BLOCK = 'current'
274
+ ```
275
+
276
+ > **Performance Impact:** Starting from historical blocks may require significant processing time
277
+
278
+ ---
279
+
280
+ ### `BLOCK_DUMPER_POLL_INTERVAL`
281
+ - **Type:** `int`
282
+ - **Default:** `1`
283
+ - **Valid Range:** `1` to `3600` (seconds)
284
+ - **Description:** Seconds to wait between checking for new blocks
285
+
286
+ ```python
287
+ BLOCK_DUMPER_POLL_INTERVAL = 5
288
+ ```
289
+
290
+ > **Performance Impact:**
291
+ > - Lower values (1-2s): Near real-time processing, higher CPU/network usage
292
+ > - Higher values (10-60s): Reduced load but delayed processing
293
+ > - Very low values (<1s): May cause rate limiting
294
+
295
+ ---
296
+
297
+ ### `BLOCK_DUMPER_MAX_ATTEMPTS`
298
+ - **Type:** `int`
299
+ - **Default:** `3`
300
+ - **Valid Range:** `1` to `10`
301
+ - **Description:** Maximum number of attempts to retry a failed task before giving up
302
+
303
+ ```python
304
+ BLOCK_DUMPER_MAX_ATTEMPTS = 5
305
+ ```
306
+
307
+ > **Performance Impact:** Higher values increase resilience but may delay failure detection
308
+
309
+ ---
310
+
311
+ ### `BLOCK_TASK_RETRY_BACKOFF`
312
+ - **Type:** `int`
313
+ - **Default:** `1`
314
+ - **Valid Range:** `1` to `60` (minutes)
315
+ - **Description:** Base number of minutes for exponential backoff retry delays
316
+ - **Calculation:** Actual delay = `backoff ** attempt_count` minutes
252
317
  - Attempt 1: 2¹ = 2 minutes
253
- - Attempt 2: 2² = 4 minutes
318
+ - Attempt 2: 2² = 4 minutes
254
319
  - Attempt 3: 2³ = 8 minutes
255
- - **Performance Impact**: Lower values retry faster but may overwhelm failing services
256
320
 
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
321
+ ```python
322
+ BLOCK_TASK_RETRY_BACKOFF = 2
323
+ ```
324
+
325
+ > **Performance Impact:** Lower values retry faster but may overwhelm failing services
326
+
327
+ ---
328
+
329
+ ### `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES`
330
+ - **Type:** `int`
331
+ - **Default:** `1440` (24 hours)
332
+ - **Valid Range:** `1` to `10080` (1 minute to 1 week)
333
+ - **Description:** Maximum delay (in minutes) between retry attempts, caps exponential backoff
334
+
335
+ ```python
336
+ BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720 # 12 hours max
337
+ ```
338
+
339
+ > **Performance Impact:** Prevents extremely long delays while maintaining backoff benefits
262
340
 
263
341
 
264
342
  ## 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,31 @@ 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 celeryd_init
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
+
93
+
94
+ @celeryd_init.connect
95
+ def on_worker_init(**kwargs) -> None:
96
+ """Load block tasks when worker initializes."""
97
+ from abstract_block_dumper.v1.celery import setup_celery_tasks
98
+ setup_celery_tasks()
99
+
100
+ ```
101
+
102
+ > **Important:** Without this step, Celery workers will not recognize your `@block_task` decorated functions, and you'll see "Received unregistered task" errors.
103
+
63
104
  ## Usage
64
105
 
65
106
  ### 1. Define Block Processing Tasks
@@ -71,7 +112,7 @@ Create block processing tasks in `tasks.py` or `block_tasks.py` file inside any
71
112
  ### 3. Start the Block Scheduler
72
113
  Run the scheduler to start processing blocks:
73
114
  ```bash
74
- $ python manage.py block_tasks
115
+ $ python manage.py block_tasks_v1
75
116
  ```
76
117
 
77
118
  This command will:
@@ -90,11 +131,11 @@ See examples below:
90
131
  Use the `@block_task` decorator with lambda conditions to create block processing tasks:
91
132
 
92
133
  ```python
93
- from abstract_block_dumper.decorators import block_task
134
+ from abstract_block_dumper.v1.decorators import block_task
94
135
 
95
136
 
96
137
  # Process every block
97
- @block_task(condition=lambda bn: True)
138
+ @block_task
98
139
  def process_every_block(block_number: int):
99
140
  print(f"Processing every block: {block_number}")
100
141
 
@@ -122,7 +163,7 @@ def process_multi_netuid_task(block_number: int, netuid: int):
122
163
  The framework provides a maintenance task to clean up old task records and maintain database performance:
123
164
 
124
165
  ```python
125
- from abstract_block_dumper.tasks import cleanup_old_tasks
166
+ from abstract_block_dumper.v1.tasks import cleanup_old_tasks
126
167
 
127
168
  # Delete tasks older than 7 days (default)
128
169
  cleanup_old_tasks.delay()
@@ -138,13 +179,13 @@ This task deletes all succeeded or unrecoverable failed tasks older than the spe
138
179
  **Option 1: Manual Execution**
139
180
  ```bash
140
181
  # Using Django shell
141
- python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
182
+ python manage.py shell -c "from abstract_block_dumper.v1.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
142
183
  ```
143
184
 
144
185
  **Option 2: Cron Job (Recommended - once per day)**
145
186
  ```bash
146
187
  # 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()"
188
+ 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
189
  ```
149
190
 
150
191
  **Option 3: Celery Beat (Automated Scheduling)**
@@ -188,55 +229,92 @@ BLOCK_DUMPER_MAX_ATTEMPTS = 3 # maximum retry attempts
188
229
  BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 1440 # maximum retry delay (24 hours)
189
230
  ```
190
231
 
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
232
+ ## Configuration Options Reference
233
+
234
+ ### `BITTENSOR_NETWORK`
235
+ - **Type:** `str`
236
+ - **Default:** `'finney'`
237
+ - **Description:** Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
238
+
239
+ ---
240
+
241
+ ### `BLOCK_DUMPER_START_FROM_BLOCK`
242
+ - **Type:** `str | int | None`
243
+ - **Default:** `None`
244
+ - **Valid Range:** `None`, `'current'`, or any positive integer
245
+ - **Description:** Determines the starting block for processing when the scheduler first runs
246
+ - `None` → Resume from the last processed block stored in database
247
+ - `'current'` → Start from the current blockchain block (skips historical blocks)
248
+ - Integer → Start from a specific block number (e.g., `1000000`)
249
+
250
+ ```python
251
+ BLOCK_DUMPER_START_FROM_BLOCK = 'current'
252
+ ```
253
+
254
+ > **Performance Impact:** Starting from historical blocks may require significant processing time
255
+
256
+ ---
257
+
258
+ ### `BLOCK_DUMPER_POLL_INTERVAL`
259
+ - **Type:** `int`
260
+ - **Default:** `1`
261
+ - **Valid Range:** `1` to `3600` (seconds)
262
+ - **Description:** Seconds to wait between checking for new blocks
263
+
264
+ ```python
265
+ BLOCK_DUMPER_POLL_INTERVAL = 5
266
+ ```
267
+
268
+ > **Performance Impact:**
269
+ > - Lower values (1-2s): Near real-time processing, higher CPU/network usage
270
+ > - Higher values (10-60s): Reduced load but delayed processing
271
+ > - Very low values (<1s): May cause rate limiting
272
+
273
+ ---
274
+
275
+ ### `BLOCK_DUMPER_MAX_ATTEMPTS`
276
+ - **Type:** `int`
277
+ - **Default:** `3`
278
+ - **Valid Range:** `1` to `10`
279
+ - **Description:** Maximum number of attempts to retry a failed task before giving up
280
+
281
+ ```python
282
+ BLOCK_DUMPER_MAX_ATTEMPTS = 5
283
+ ```
284
+
285
+ > **Performance Impact:** Higher values increase resilience but may delay failure detection
286
+
287
+ ---
288
+
289
+ ### `BLOCK_TASK_RETRY_BACKOFF`
290
+ - **Type:** `int`
291
+ - **Default:** `1`
292
+ - **Valid Range:** `1` to `60` (minutes)
293
+ - **Description:** Base number of minutes for exponential backoff retry delays
294
+ - **Calculation:** Actual delay = `backoff ** attempt_count` minutes
230
295
  - Attempt 1: 2¹ = 2 minutes
231
- - Attempt 2: 2² = 4 minutes
296
+ - Attempt 2: 2² = 4 minutes
232
297
  - Attempt 3: 2³ = 8 minutes
233
- - **Performance Impact**: Lower values retry faster but may overwhelm failing services
234
298
 
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
299
+ ```python
300
+ BLOCK_TASK_RETRY_BACKOFF = 2
301
+ ```
302
+
303
+ > **Performance Impact:** Lower values retry faster but may overwhelm failing services
304
+
305
+ ---
306
+
307
+ ### `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES`
308
+ - **Type:** `int`
309
+ - **Default:** `1440` (24 hours)
310
+ - **Valid Range:** `1` to `10080` (1 minute to 1 week)
311
+ - **Description:** Maximum delay (in minutes) between retry attempts, caps exponential backoff
312
+
313
+ ```python
314
+ BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720 # 12 hours max
315
+ ```
316
+
317
+ > **Performance Impact:** Prevents extremely long delays while maintaining backoff benefits
240
318
 
241
319
 
242
320
  ## 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
  ```
@@ -0,0 +1,46 @@
1
+ from abstract_block_dumper.v1.decorators import block_task
2
+
3
+
4
+ @block_task
5
+ def process_every_block(block_number: int, netuid: int | None = None) -> str:
6
+ """
7
+ Example task that processes every block.
8
+
9
+ Args:
10
+ block_number (int): The block number to process.
11
+ netuid (int | None): The NetUID to process, if applicable.
12
+
13
+ Returns:
14
+ str: A message indicating the block has been processed.
15
+
16
+ """
17
+ return f"Processed block {block_number} for NetUID {netuid}"
18
+
19
+
20
+ @block_task(
21
+ backfilling_lookback=100,
22
+ )
23
+ def backfill_previous_100_blocks(block_number: int, netuid: int | None = None) -> str:
24
+ """
25
+ Example task that backfills the previous 100 blocks.
26
+
27
+ Args:
28
+ block_number (int): The block number to process.
29
+ netuid (int | None): The NetUID to process, if applicable.
30
+
31
+ Returns:
32
+ str: A message indicating the block has been processed.
33
+
34
+ """
35
+ return f"Processed block {block_number} for NetUID {netuid}"
36
+
37
+
38
+ @block_task(
39
+ condition=lambda bn, netuid: (bn + netuid) % 50 == 0,
40
+ args=[{"netuid": i} for i in range(10, 15)], # All subnets
41
+ backfilling_lookback=1000,
42
+ celery_kwargs={"retry": True},
43
+ )
44
+ def subnet_analysis(block_number, netuid) -> str:
45
+ # Analyze subnet data
46
+ return f"Processed block {block_number} for NetUID {netuid}"
@@ -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
+ }
@@ -15,7 +15,7 @@ dependencies = [
15
15
  [package.metadata]
16
16
  requires-dist = [
17
17
  { name = "bittensor", specifier = ">=9.10.1" },
18
- { name = "celery", specifier = ">=5.5.3" },
18
+ { name = "celery", specifier = ">=5.3" },
19
19
  { name = "django", specifier = ">=3.2,<6.0" },
20
20
  { name = "structlog", specifier = ">=25.4.0" },
21
21
  ]