karafka 2.5.1.beta1 → 2.5.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1cbd8652ac95b72e16bea2b7468e1c8a55655455c52d968cd1c5b444f251149f
4
- data.tar.gz: 958b38561cbd526fa39940878f29ca0c0ae9361ec32567968778837bfcdb4c75
3
+ metadata.gz: fb41b6fee168ce7585a82c29f8053391ebca63ae3416f4280456f6caeab316a2
4
+ data.tar.gz: 0dbb438af65380581bd0501793d645195359232c9ada649b63e17c9269c95002
5
5
  SHA512:
6
- metadata.gz: 41f3d899470eb73db13331b202ded7bd0c85368dbea9d61211560504fe0533e2a58cd22337c89421e93a0765174a6097e24619f7b42b393410210df3057141c8
7
- data.tar.gz: 6876a17427d509824f3126b572fe9c101b1cb60dd40ed4e50797d3fd02dabe6256e90c5eff9c81a6709f4b832274462aee0b9bd55e261bc75ac5722b8bafd2ad
6
+ metadata.gz: 4862117c97a174e43708fb7e163ab60222a0199b109ca194a172995928968a2799ea5bc39151cee55b0c67a6138f1cdf89e5566a5136f584540ffa0bad4248f3
7
+ data.tar.gz: d0ea2ddbe22b2a85afa77629d68c2af70685fd390d9b3bf87a461d04929cfc3855d40e4c25e57e02d4b78c32107c435e3d039edd9d64feb85f8057e674e77f02
@@ -29,7 +29,7 @@ jobs:
29
29
  fetch-depth: 0
30
30
 
31
31
  - name: Set up Ruby
32
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
32
+ uses: ruby/setup-ruby@cf7216d52fba1017929b4d7162fabe2b30af5b49 # v1.262.0
33
33
  with:
34
34
  ruby-version: 3.4
35
35
  bundler-cache: true
@@ -113,8 +113,12 @@ jobs:
113
113
  run: |
114
114
  docker compose up -d || (sleep 5 && docker compose up -d)
115
115
 
116
+ - name: Remove Gemfile.lock for Ruby dev/preview versions
117
+ if: contains(matrix.ruby, 'dev') || contains(matrix.ruby, 'preview') || contains(matrix.ruby, 'rc')
118
+ run: rm -f Gemfile.lock
119
+
116
120
  - name: Set up Ruby
117
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
121
+ uses: ruby/setup-ruby@cf7216d52fba1017929b4d7162fabe2b30af5b49 # v1.262.0
118
122
  with:
119
123
  ruby-version: ${{matrix.ruby}}
120
124
  bundler-cache: true
@@ -165,7 +169,7 @@ jobs:
165
169
  docker compose up -d || (sleep 5 && docker compose up -d)
166
170
 
167
171
  - name: Set up Ruby
168
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
172
+ uses: ruby/setup-ruby@cf7216d52fba1017929b4d7162fabe2b30af5b49 # v1.262.0
169
173
  with:
170
174
  # Do not use cache here as we run bundle install also later in some of the integration
171
175
  # tests and we need to be able to run it without cache
@@ -185,6 +189,10 @@ jobs:
185
189
  chmod -R o-w /opt/hostedtoolcache/Ruby/3*/x64/lib/ruby/gems/3*/gems
186
190
  chmod +t /opt/hostedtoolcache/Ruby/3*/x64/lib/ruby/gems/3*/gems
187
191
 
192
+ - name: Force Ruby platform for ffi gem on dev/preview Ruby
193
+ if: contains(matrix.ruby, 'dev') || contains(matrix.ruby, 'preview') || contains(matrix.ruby, 'rc')
194
+ run: bundle config set force_ruby_platform ffi
195
+
188
196
  - name: Bundle install
189
197
  run: |
190
198
  bundle config set without development
@@ -233,7 +241,7 @@ jobs:
233
241
  docker compose up -d || (sleep 5 && docker compose up -d)
234
242
 
235
243
  - name: Set up Ruby
236
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
244
+ uses: ruby/setup-ruby@cf7216d52fba1017929b4d7162fabe2b30af5b49 # v1.262.0
237
245
  with:
238
246
  ruby-version: ${{matrix.ruby}}
239
247
  bundler: 'latest'
@@ -244,6 +252,10 @@ jobs:
244
252
  gem update --system
245
253
  bundle config set without 'tools benchmarks docs'
246
254
 
255
+ - name: Force Ruby platform for ffi gem on dev/preview Ruby
256
+ if: contains(matrix.ruby, 'dev') || contains(matrix.ruby, 'preview') || contains(matrix.ruby, 'rc')
257
+ run: bundle config set force_ruby_platform ffi
258
+
247
259
  - name: Bundle install
248
260
  run: |
249
261
  bundle config set without development
@@ -112,14 +112,17 @@ jobs:
112
112
  sleep 2
113
113
  done
114
114
 
115
+ - name: Remove Gemfile.lock for Ruby dev/preview versions
116
+ if: contains(matrix.ruby, 'dev') || contains(matrix.ruby, 'preview') || contains(matrix.ruby, 'rc')
117
+ run: rm -f Gemfile.lock
118
+
115
119
  - name: Set up Ruby
116
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
120
+ uses: ruby/setup-ruby@cf7216d52fba1017929b4d7162fabe2b30af5b49 # v1.262.0
117
121
  with:
118
122
  ruby-version: ${{ matrix.ruby }}
119
123
  bundler-cache: true
120
124
  bundler: 'latest'
121
125
 
122
-
123
126
  - name: Run swarm unit specs
124
127
  run: bundle exec rspec spec/lib/karafka/swarm/ --tag mode:fork
125
128
 
@@ -24,7 +24,7 @@ jobs:
24
24
  fetch-depth: 0
25
25
 
26
26
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
27
+ uses: ruby/setup-ruby@cf7216d52fba1017929b4d7162fabe2b30af5b49 # v1.262.0
28
28
  with:
29
29
  bundler-cache: false
30
30
 
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.5
1
+ 3.4.6
data/CHANGELOG.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Karafka Framework Changelog
2
2
 
3
- ## 2.5.1 (Unreleased)
3
+ ## 2.5.1 (2025-09-29)
4
+ - **[Breaking]** Remove Ruby 3.1 support according to EOL.
4
5
  - **[Feature]** Support Swarm mode on MacOS.
5
6
  - [Enhancement] Support past `dispatch_at` times with `jitter: 0` in the OSS Karafka to support ActiveJob continuation.
6
7
  - [Enhancement] Use direct topic dispatches when `dispatch_at` is used for past times to bypass Scheduled Messages flow.
@@ -12,17 +13,23 @@
12
13
  - [Enhancement] Include consumer group, subscription group and other details in error logs for key error locations.
13
14
  - [Enhancement] Inherit from `ActiveJob::QueueAdapters::AbstractAdapter` when possible for ActiveJob base class.
14
15
  - [Enhancement] Disable Nagle algorithm by default for improved network performance.
16
+ - [Enhancement] Optimize the messages buffer array memory allocation pattern.
15
17
  - [Maintenance] Add basic direct DD integration spec via DD gem karafka monitoring feature.
18
+ - [Maintenance] Add integration specs for WaterDrop connection pool usage from within consumers.
16
19
  - [Refactoring] Comprehensive Admin module refactoring: Extract topic operations into Admin::Topics class and consumer group operations into Admin::ConsumerGroups class with proper inheritance hierarchy, cross-class method usage optimization, and constants moved to appropriate locations where they are actually used.
17
20
  - [Refactoring] Move routing-related contracts from `Karafka::Contracts::` to `Karafka::Routing::Contracts::` namespace and reorganize error message structure in YAML files under `routing:` scope for better code organization and logical grouping.
18
21
  - [Refactoring] Move config-related contracts from `Karafka::Contracts::Config` to `Karafka::Setup::Contracts::Config` namespace and reorganize error message structure in YAML files under `setup:` scope for better code organization and logical grouping.
19
22
  - [Refactoring] Move CLI server contracts from `Karafka::Contracts::ServerCliOptions` to `Karafka::Cli::Contracts::Server` namespace and reorganize error message structure in YAML files under `cli:` scope for improved naming consistency and logical grouping.
23
+ - [Refactoring] Replace execution mode symbol-based checks with dedicated `ExecutionMode` class providing cleaner API with query methods (`#swarm?`, `#embedded?`) and state change methods (`#swarm!`, `#embedded!`) for improved type safety and code clarity.
24
+ - [Refactoring] Replace connection client mode symbol-based checks with dedicated `Connection::Mode` class providing cleaner API with query methods (`#subscribe?`, `#assign?`) and state change methods (`#subscribe!`, `#assign!`) for improved code clarity.
20
25
  - [Fix] Improve same timestamp dispatch in scheduled messages on Ruby 3.2.
21
26
  - [Fix] Fix incorrect (6 seconds vs 60 seconds) reset of connections on non-recoverable errors.
22
27
  - [Fix] Introduce mutex-safe and thread-safe `#inspect` where needed.
23
28
  - [Fix] Fix too loose requirement of Ruby `3.0` when it was `3.1` via transitive dependencies.
24
29
  - [Fix] Fix Pro Cleaner Messages compatibility with external libraries that prepend modules to `#each` method (e.g., DataDog tracing).
25
- - [Change] Require `karafka-rdkafka` `>=` `0.21.0` to support new features.
30
+ - [Fix] SG exclusion in swarm triggers a contract validation error.
31
+ - [Change] Require `waterdrop` `>=` `2.8.10` to support new features.
32
+ - [Change] Require `karafka-rdkafka` `>=` `0.22.0` to support new features and require SSL-bug free version.
26
33
  - [Change] Remove no longer needed `cooperative.sticky` rebalance patch.
27
34
  - [Change] Normalize how libs and deps are required (no functional change for the end user)
28
35
  - [Change] Remove Ruby `3.1` specs according to the EOL schedule.
data/Gemfile.lock CHANGED
@@ -1,20 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.5.1.beta1)
4
+ karafka (2.5.1)
5
5
  base64 (~> 0.2)
6
6
  karafka-core (>= 2.5.6, < 2.6.0)
7
- karafka-rdkafka (>= 0.21.0)
8
- waterdrop (>= 2.8.3, < 3.0.0)
7
+ karafka-rdkafka (>= 0.22.0)
8
+ waterdrop (>= 2.8.9, < 3.0.0)
9
9
  zeitwerk (~> 2.3)
10
10
 
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activejob (8.0.2.1)
15
- activesupport (= 8.0.2.1)
14
+ activejob (8.0.3)
15
+ activesupport (= 8.0.3)
16
16
  globalid (>= 0.3.6)
17
- activesupport (8.0.2.1)
17
+ activesupport (8.0.3)
18
18
  base64
19
19
  benchmark (>= 0.3)
20
20
  bigdecimal
@@ -29,7 +29,7 @@ GEM
29
29
  uri (>= 0.13.1)
30
30
  base64 (0.3.0)
31
31
  benchmark (0.4.1)
32
- bigdecimal (3.2.2)
32
+ bigdecimal (3.2.3)
33
33
  byebug (12.0.0)
34
34
  concurrent-ruby (1.3.5)
35
35
  connection_pool (2.5.4)
@@ -59,35 +59,35 @@ GEM
59
59
  activesupport (>= 6.1)
60
60
  i18n (1.14.7)
61
61
  concurrent-ruby (~> 1.0)
62
- json (2.13.2)
63
- karafka-core (2.5.6)
62
+ json (2.15.0)
63
+ karafka-core (2.5.7)
64
64
  karafka-rdkafka (>= 0.20.0)
65
65
  logger (>= 1.6.0)
66
- karafka-rdkafka (0.21.0)
66
+ karafka-rdkafka (0.22.0)
67
67
  ffi (~> 1.15)
68
68
  json (> 2.0)
69
69
  logger
70
70
  mini_portile2 (~> 2.6)
71
71
  rake (> 12)
72
- karafka-rdkafka (0.21.0-aarch64-linux-gnu)
72
+ karafka-rdkafka (0.22.0-aarch64-linux-gnu)
73
73
  ffi (~> 1.15)
74
74
  json (> 2.0)
75
75
  logger
76
76
  mini_portile2 (~> 2.6)
77
77
  rake (> 12)
78
- karafka-rdkafka (0.21.0-arm64-darwin)
78
+ karafka-rdkafka (0.22.0-arm64-darwin)
79
79
  ffi (~> 1.15)
80
80
  json (> 2.0)
81
81
  logger
82
82
  mini_portile2 (~> 2.6)
83
83
  rake (> 12)
84
- karafka-rdkafka (0.21.0-x86_64-linux-gnu)
84
+ karafka-rdkafka (0.22.0-x86_64-linux-gnu)
85
85
  ffi (~> 1.15)
86
86
  json (> 2.0)
87
87
  logger
88
88
  mini_portile2 (~> 2.6)
89
89
  rake (> 12)
90
- karafka-rdkafka (0.21.0-x86_64-linux-musl)
90
+ karafka-rdkafka (0.22.0-x86_64-linux-musl)
91
91
  ffi (~> 1.15)
92
92
  json (> 2.0)
93
93
  logger
@@ -137,7 +137,7 @@ GEM
137
137
  concurrent-ruby (~> 1.0)
138
138
  uri (1.0.3)
139
139
  warning (1.5.0)
140
- waterdrop (2.8.7)
140
+ waterdrop (2.8.10)
141
141
  karafka-core (>= 2.4.9, < 3.0.0)
142
142
  karafka-rdkafka (>= 0.20.0)
143
143
  zeitwerk (~> 2.3)
data/LICENSE-COMM CHANGED
@@ -2,7 +2,7 @@ END-USER LICENSE AGREEMENT
2
2
 
3
3
  ------------------------------------------------------------------------------
4
4
 
5
- IMPORTANT: THIS SOFTWARE END-USER LICENSE AGREEMENT ("EULA") IS A LEGAL AGREEMENT (Agreement) BETWEEN YOU (THE CUSTOMER, EITHER AS AN INDIVIDUAL OR, IF PURCHASED OR OTHERWISE ACQUIRED BY OR FOR AN ENTITY, AS AN ENTITY) AND MACIEJ MENSFELD. READ IT CAREFULLY BEFORE COMPLETING THE INSTALLATION PROCESS AND USING KARAFKA PRO AND RELATED SOFTWARE COMPONENTS (SOFTWARE). IT PROVIDES A LICENSE TO USE THE SOFTWARE AND CONTAINS WARRANTY INFORMATION AND LIABILITY DISCLAIMERS. BY INSTALLING AND USING THE SOFTWARE, YOU ARE CONFIRMING YOUR ACCEPTANCE OF THE SOFTWARE AND AGREEING TO BECOME BOUND BY THE TERMS OF THIS AGREEMENT.
5
+ IMPORTANT: THIS SOFTWARE END-USER LICENSE AGREEMENT ("EULA") IS A LEGAL AGREEMENT ("Agreement") BETWEEN YOU (THE CUSTOMER, EITHER AS AN INDIVIDUAL OR, IF PURCHASED OR OTHERWISE ACQUIRED BY OR FOR AN ENTITY, AS AN ENTITY) AND MACIEJ MENSFELD. READ IT CAREFULLY BEFORE COMPLETING THE INSTALLATION PROCESS AND USING KARAFKA PRO AND RELATED SOFTWARE COMPONENTS ("SOFTWARE"). IT PROVIDES A LICENSE TO USE THE SOFTWARE AND CONTAINS WARRANTY INFORMATION AND LIABILITY DISCLAIMERS. BY INSTALLING AND USING THE SOFTWARE, YOU ARE CONFIRMING YOUR ACCEPTANCE OF THE SOFTWARE AND AGREEING TO BECOME BOUND BY THE TERMS OF THIS AGREEMENT.
6
6
 
7
7
  ------------------------------------------------------------------------------
8
8
 
@@ -12,31 +12,31 @@ In order to use the Software under this Agreement, you must either: (a) receive
12
12
 
13
13
  1.1 General Use. This Agreement grants you a non-exclusive, non-transferable, limited license to the use rights for the Software, without the right to grant sublicenses, subject to the terms and conditions in this Agreement. The Software is licensed, not sold.
14
14
 
15
- 1.2 Pro License. If you purchased a Pro License (included with the Karafka Pro Software), you may install the Software on an unlimited number of Hosts. Host means any physical or virtual machine which is controlled by you. You may also run an unlimited number of Workers. Worker means a thread within a Karafka server process which executes jobs. You may concurrently run the software on an unlimited number of Hosts, with each host running an unlimited number of Workers.
15
+ 1.2 Pro License. If you purchased a Pro License (included with the Karafka Pro Software), you may install the Software on an unlimited number of Hosts. "Host" means any physical or virtual machine which is controlled by you. You may also run an unlimited number of Workers. "Worker" means a thread within a Karafka server process which executes jobs. You may concurrently run the software on an unlimited number of Hosts, with each host running an unlimited number of Workers.
16
16
 
17
17
  1.3 Archive Copies. You are entitled to make a reasonable amount of copies of the Software for archival purposes. Each copy must reproduce all copyright and other proprietary rights notices on or in the Software Product.
18
18
 
19
- 1.4 Electronic Delivery. All Software and license documentation shall be delivered by electronic means unless otherwise specified on the applicable invoice or at the time of purchase. Software shall be deemed delivered when it is made available for download by you (Delivery).
19
+ 1.4 Electronic Delivery. All Software and license documentation shall be delivered by electronic means unless otherwise specified on the applicable invoice or at the time of purchase. Software shall be deemed delivered when it is made available for download by you ("Delivery").
20
20
 
21
- 2. Modifications. Maciej Mensfeld shall provide you with source code so that you can create Modifications of the original software. Modification means: (a) any addition to or deletion from the contents of a file included in the original Software or previous Modifications created by You, or (b) any new file that contains any part of the original Software or previous Modifications. While you retain all rights to any original work authored by you as part of the Modifications, We continue to own all copyright and other intellectual property rights in the Software.
21
+ 2. Modifications. Maciej Mensfeld shall provide you with source code so that you can create Modifications of the original software. "Modification" means: (a) any addition to or deletion from the contents of a file included in the original Software or previous Modifications created by You, or (b) any new file that contains any part of the original Software or previous Modifications. While you retain all rights to any original work authored by you as part of the Modifications, We continue to own all copyright and other intellectual property rights in the Software.
22
22
 
23
23
  3. Restricted Uses.
24
24
 
25
- 3.1 You shall not (and shall not allow any third party to): (a) decompile, disassemble, or otherwise reverse engineer the Software or attempt to reconstruct or discover any source code, underlying ideas, algorithms, file formats or programming interfaces of the Software by any means whatsoever (except and only to the extent that applicable law prohibits or restricts reverse engineering restrictions); (b) distribute, sell, sublicense, rent, lease or use the Software for time sharing, hosting, service provider or like purposes, except as expressly permitted under this Agreement; (c) redistribute the Software or Modifications other than by including the Software or a portion thereof within your own product, which must have substantially different functionality than the Software or Modifications and must not allow any third party to use the Software or Modifications, or any portions thereof, for software development or application development purposes; (d) redistribute the Software as part of a product, "appliance" or "virtual server"; (e) redistribute the Software on any server which is not directly under your control; (f) remove any product identification, proprietary, copyright or other notices contained in the Software; (g) modify any part of the Software, create a derivative work of any part of the Software (except as permitted in Section 4), or incorporate the Software, except to the extent expressly authorized in writing by Maciej Mensfeld; (h) publicly disseminate performance information or analysis (including, without limitation, benchmarks) from any source relating to the Software; (i) utilize any equipment, device, software, or other means designed to circumvent or remove any form of Source URL or copy protection used by Maciej Mensfeld in connection with the Software, or use the Software together with any authorization code, Source URL, serial number, or other copy protection device not supplied by Maciej Mensfeld; (j) use the Software to develop a product which is competitive with any Maciej Mensfeld product offerings; or (k) use unauthorized Source URLS or keycode(s) or distribute or publish Source URLs or keycode(s), except as may be expressly permitted by Maciej Mensfeld in writing. If your unique Source URL or the offline license is ever published, Maciej Mensfeld reserves the right to terminate your access without notice.
25
+ 3.1 You shall not (and shall not allow any third party to): (a) decompile, disassemble, or otherwise reverse engineer the Software or attempt to reconstruct or discover any source code, underlying ideas, algorithms, file formats or programming interfaces of the Software by any means whatsoever (except and only to the extent that applicable law prohibits or restricts reverse engineering restrictions); (b) distribute, sell, sublicense, rent, lease or use the Software for time sharing, hosting, service provider or like purposes, except as expressly permitted under this Agreement; (c) redistribute the Software or Modifications other than by including the Software or a portion thereof within your own product, which must have substantially different functionality than the Software or Modifications and must not allow any third party to use the Software or Modifications, or any portions thereof, for software development or application development purposes; (d) redistribute the Software as part of a product, "appliance" or "virtual server"; (e) redistribute the Software on any server which is not directly under your control; (f) remove any product identification, proprietary, copyright or other notices contained in the Software; (g) modify any part of the Software, create a derivative work of any part of the Software (except as permitted in Section 2), or incorporate the Software, except to the extent expressly authorized in writing by Maciej Mensfeld; (h) publicly disseminate performance information or analysis (including, without limitation, benchmarks) from any source relating to the Software; (i) utilize any equipment, device, software, or other means designed to circumvent or remove any form of Source URL or copy protection used by Maciej Mensfeld in connection with the Software, or use the Software together with any authorization code, Source URL, serial number, or other copy protection device not supplied by Maciej Mensfeld; (j) use the Software to develop a product which is competitive with any Maciej Mensfeld product offerings; or (k) use unauthorized Source URLS or keycode(s) or distribute or publish Source URLs or keycode(s), except as may be expressly permitted by Maciej Mensfeld in writing. If your unique Source URL or the offline license is ever published, Maciej Mensfeld reserves the right to terminate your access without notice.
26
26
 
27
27
  3.2 UNDER NO CIRCUMSTANCES MAY YOU USE THE SOFTWARE AS PART OF A PRODUCT OR SERVICE THAT PROVIDES SIMILAR FUNCTIONALITY TO THE SOFTWARE ITSELF.
28
28
 
29
- The Open Source version of the Software (LGPL Version) is licensed under the terms of the GNU Lesser General Public License version 3.0 (LGPL) and not under this EULA.
29
+ The Open Source version of the Software ("LGPL Version") is licensed under the terms of the GNU Lesser General Public License version 3.0 ("LGPL") and not under this EULA.
30
30
 
31
- 4. Ownership. Notwithstanding anything to the contrary contained herein, except for the limited license rights expressly provided herein, Maciej Mensfeld and its suppliers have and will retain all rights, title and interest (including, without limitation, all patent, copyright, trademark, trade secret and other intellectual property rights) in and to the Software and all copies, modifications and derivative works thereof (including any changes which incorporate any of your ideas, feedback or suggestions). You acknowledge that you are obtaining only a limited license right to the Software, and that irrespective of any use of the words purchase”, sale or like terms hereunder no ownership rights are being conveyed to you under this Agreement or otherwise.
31
+ 4. Ownership. Notwithstanding anything to the contrary contained herein, except for the limited license rights expressly provided herein, Maciej Mensfeld and its suppliers have and will retain all rights, title and interest (including, without limitation, all patent, copyright, trademark, trade secret and other intellectual property rights) in and to the Software and all copies, modifications and derivative works thereof (including any changes which incorporate any of your ideas, feedback or suggestions). You acknowledge that you are obtaining only a limited license right to the Software, and that irrespective of any use of the words "purchase", "sale" or like terms hereunder no ownership rights are being conveyed to you under this Agreement or otherwise.
32
32
 
33
33
  5. Fees and Payment. The Software license fees will be due and payable in full as set forth in the applicable invoice or at the time of purchase. There are no refunds beyond the remedy refund.
34
34
 
35
- 6. Support, Maintenance and Services. Subject to the terms and conditions of this Agreement, as set forth in your invoice, and as set forth on the Karafka Pro support page (https://github.com/karafka/karafka/wiki/Commercial-Support), support and maintenance services may be included with the purchase of your license subscription.
35
+ 6. Support, Maintenance and Services. Subject to the terms and conditions of this Agreement, as set forth in your invoice, and as set forth on the Karafka Pro support page (https://karafka.io/docs/Pro-Support/), support and maintenance services may be included with the purchase of your license subscription.
36
36
 
37
37
  7. Term of Agreement.
38
38
 
39
- 7.1 Term. This Agreement is effective as of the Delivery of the Software and expires at such time as all license and service subscriptions hereunder have expired in accordance with their own terms (the Term). For clarification, the term of your license under this Agreement may be perpetual, limited for Evaluation Version, or designated as a fixed-term license in the Invoice, and shall be specified at your time of purchase. Either party may terminate this Agreement (including all related Invoices) if the other party: (a) fails to cure any material breach of this Agreement within thirty (30) days after written notice of such breach, provided that Maciej Mensfeld may terminate this Agreement immediately upon any breach of Section 3 or if you exceed any other restrictions contained in Section 1, unless otherwise specified in this agreement; (b) ceases operation without a successor; or (c) seeks protection under any bankruptcy, receivership, trust deed, creditors arrangement, composition or comparable proceeding, or if any such proceeding is instituted against such party (and not dismissed within sixty (60) days)). Termination is not an exclusive remedy and the exercise by either party of any remedy under this Agreement will be without prejudice to any other remedies it may have under this Agreement, by law, or otherwise.
39
+ 7.1 Term. This Agreement is effective as of the Delivery of the Software and expires at such time as all license and service subscriptions hereunder have expired in accordance with their own terms (the "Term"). For clarification, the term of your license under this Agreement may be perpetual, limited for Evaluation Version, or designated as a fixed-term license in the Invoice, and shall be specified at your time of purchase. Either party may terminate this Agreement (including all related Invoices) if the other party: (a) fails to cure any material breach of this Agreement within thirty (30) days after written notice of such breach, provided that Maciej Mensfeld may terminate this Agreement immediately upon any breach of Section 3 or if you exceed any other restrictions contained in Section 1, unless otherwise specified in this agreement; (b) ceases operation without a successor; or (c) seeks protection under any bankruptcy, receivership, trust deed, creditors arrangement, composition or comparable proceeding, or if any such proceeding is instituted against such party (and not dismissed within sixty (60) days)). Termination is not an exclusive remedy and the exercise by either party of any remedy under this Agreement will be without prejudice to any other remedies it may have under this Agreement, by law, or otherwise.
40
40
 
41
41
  7.2 Termination. Upon any termination of this Agreement, you shall cease any and all use of any Software and destroy all copies thereof.
42
42
 
@@ -62,7 +62,7 @@ In no event will Maciej Mensfeld' liability exceed the Software license price as
62
62
 
63
63
  11.3 Government End Users. If the Software and related documentation are supplied to or purchased by or on behalf of the United States Government, then the Software is deemed to be "commercial software" as that term is used in the Federal Acquisition Regulation system. Rights of the United States shall not exceed the minimum rights set forth in FAR 52.227-19 for "restricted computer software". All other terms and conditions of this Agreement apply.
64
64
 
65
- 12. Third Party Software. External components included in Software may provide links to third party libraries or code (collectively Third Party Software) to implement various functions. Third Party Software does not comprise part of the Software. In some cases, access to Third Party Software may be included along with the Software delivery as a convenience for demonstration purposes. Such source code and libraries do not comprise the Software. Licensee acknowledges (1) that some part of Third Party Software may require additional licensing of copyright and patents from the owners of such, and (2) that distribution of any of the Software referencing or including any portion of a Third Party Software may require appropriate licensing from such third parties.
65
+ 12. Third Party Software. External components included in Software may provide links to third party libraries or code (collectively "Third Party Software") to implement various functions. Third Party Software does not comprise part of the Software. In some cases, access to Third Party Software may be included along with the Software delivery as a convenience for demonstration purposes. Such source code and libraries do not comprise the Software. Licensee acknowledges (1) that some part of Third Party Software may require additional licensing of copyright and patents from the owners of such, and (2) that distribution of any of the Software referencing or including any portion of a Third Party Software may require appropriate licensing from such third parties.
66
66
 
67
67
  13. Miscellaneous
68
68
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ![karafka logo](https://cdn.karafka.io/assets/misc/logo/karafka_logotype_transparent2.png)
2
2
 
3
- [![Build Status](https://github.com/karafka/karafka/actions/workflows/ci.yml/badge.svg)](https://github.com/karafka/karafka/actions/workflows/ci.yml)
3
+ [![Build Status](https://github.com/karafka/karafka/actions/workflows/ci_linux_ubuntu_x86_64_gnu.yml/badge.svg)](https://github.com/karafka/karafka/actions/workflows/ci_linux_ubuntu_x86_64_gnu.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/karafka.svg)](http://badge.fury.io/rb/karafka)
5
5
  [![Join the chat at https://slack.karafka.io](https://raw.githubusercontent.com/karafka/misc/master/slack.svg)](https://slack.karafka.io)
6
6
 
data/docker-compose.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  kafka:
3
3
  container_name: kafka
4
- image: confluentinc/cp-kafka:8.0.0
4
+ image: confluentinc/cp-kafka:8.0.1
5
5
 
6
6
  ports:
7
7
  - 9092:9092
data/karafka.gemspec CHANGED
@@ -23,11 +23,11 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_dependency 'base64', '~> 0.2'
25
25
  spec.add_dependency 'karafka-core', '>= 2.5.6', '< 2.6.0'
26
- spec.add_dependency 'karafka-rdkafka', '>= 0.21.0'
27
- spec.add_dependency 'waterdrop', '>= 2.8.3', '< 3.0.0'
26
+ spec.add_dependency 'karafka-rdkafka', '>= 0.22.0'
27
+ spec.add_dependency 'waterdrop', '>= 2.8.9', '< 3.0.0'
28
28
  spec.add_dependency 'zeitwerk', '~> 2.3'
29
29
 
30
- spec.required_ruby_version = '>= 3.1.0'
30
+ spec.required_ruby_version = '>= 3.2.0'
31
31
 
32
32
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
33
33
  spec.executables = %w[karafka]
@@ -89,7 +89,7 @@ module Karafka
89
89
  register_inclusions
90
90
  register_exclusions
91
91
 
92
- Karafka::Server.execution_mode = :standalone
92
+ Karafka::Server.execution_mode.standalone!
93
93
  Karafka::Server.run
94
94
  end
95
95
 
@@ -23,7 +23,7 @@ module Karafka
23
23
  server.register_inclusions
24
24
  server.register_exclusions
25
25
 
26
- Karafka::Server.execution_mode = :supervisor
26
+ Karafka::Server.execution_mode.supervisor!
27
27
  Karafka::Swarm::Supervisor.new.run
28
28
  end
29
29
  end
@@ -73,6 +73,7 @@ module Karafka
73
73
  @buffer = RawMessagesBuffer.new
74
74
  @rebalance_manager = RebalanceManager.new(@subscription_group.id, @buffer)
75
75
  @rebalance_callback = Instrumentation::Callbacks::Rebalance.new(@subscription_group, id)
76
+ @mode = Mode.new
76
77
 
77
78
  @interval_runner = Helpers::IntervalRunner.new do
78
79
  events_poll
@@ -746,10 +747,10 @@ module Karafka
746
747
 
747
748
  if subscriptions
748
749
  consumer.subscribe(*subscriptions)
749
- @mode = :subscribe
750
+ @mode.subscribe!
750
751
  elsif assignments
751
752
  consumer.assign(assignments)
752
- @mode = :assign
753
+ @mode.assign!
753
754
  end
754
755
 
755
756
  consumer
@@ -779,7 +780,7 @@ module Karafka
779
780
  def unsubscribe?
780
781
  return false if @unsubscribing
781
782
  return false if @subscription_group.kafka.key?(:'group.instance.id')
782
- return false if @mode != :subscribe
783
+ return false unless @mode.subscribe?
783
784
  return false if assignment.empty?
784
785
 
785
786
  true
@@ -10,7 +10,7 @@ module Karafka
10
10
  # @param max_interval [Integer] after how many milliseconds of doing nothing should we wake
11
11
  # up the manager despite no state changes
12
12
  def initialize(max_interval = 30_000)
13
- @lock = RUBY_VERSION < '3.2' ? Processing::TimedQueue.new : Queue.new
13
+ @lock = Queue.new
14
14
  @timeout = max_interval / 1_000.0
15
15
  end
16
16
 
@@ -38,7 +38,6 @@ module Karafka
38
38
  # @param raw_messages_buffer [RawMessagesBuffer] buffer with raw messages
39
39
  def remap(raw_messages_buffer)
40
40
  clear
41
-
42
41
  # Since it happens "right after" we've received the messages, it is close enough it time
43
42
  # to be used as the moment we received messages.
44
43
  received_at = Time.now
@@ -46,12 +45,11 @@ module Karafka
46
45
 
47
46
  raw_messages_buffer.each do |topic, partition, messages, eof|
48
47
  @size += messages.size
49
-
50
48
  ktopic = @subscription_group.topics.find(topic)
51
49
 
52
- built_messages = messages.map do |message|
50
+ built_messages = Array.new(messages.size) do |index|
53
51
  Messages::Builders::Message.call(
54
- message,
52
+ messages[index],
55
53
  ktopic,
56
54
  received_at
57
55
  )
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Connection
5
+ # Represents the connection mode state of the Kafka client.
6
+ # Encapsulates mode logic and provides a cleaner API for checking and setting connection modes,
7
+ # removing the need for symbol comparisons throughout the connection management code.
8
+ #
9
+ # @note This class is used internally by the Client and is not part of the public API
10
+ #
11
+ # @example Check if client is in subscribe mode (internal usage)
12
+ # @mode.subscribe? #=> true
13
+ #
14
+ # @example Set the connection mode to assign (internal usage)
15
+ # @mode.assign!
16
+ class Mode
17
+ # Available connection modes for Kafka client
18
+ # - :subscribe - client subscribes to topics and lets Kafka handle partition assignment
19
+ # - :assign - client manually assigns specific topic partitions
20
+ MODES = %i[
21
+ subscribe
22
+ assign
23
+ ].freeze
24
+
25
+ private_constant :MODES
26
+
27
+ # @param mode [Symbol] initial connection mode (defaults to :subscribe)
28
+ # @raise [ArgumentError] when invalid mode is provided
29
+ def initialize(mode = :subscribe)
30
+ self.mode = mode
31
+ end
32
+
33
+ # Define query and setter methods for each mode using meta-programming
34
+ # This creates methods like: subscribe?, assign?
35
+ # And bang methods like: subscribe!, assign!
36
+ MODES.each do |mode_name|
37
+ # @return [Boolean] true if the current mode matches this mode
38
+ define_method("#{mode_name}?") do
39
+ @mode == mode_name
40
+ end
41
+
42
+ # Sets the connection mode to this mode
43
+ # @return [Symbol] the new mode
44
+ define_method("#{mode_name}!") do
45
+ self.mode = mode_name
46
+ end
47
+ end
48
+
49
+ # @return [String] string representation of the current mode
50
+ def to_s
51
+ @mode.to_s
52
+ end
53
+
54
+ # @return [Symbol] symbol representation of the current mode
55
+ def to_sym
56
+ @mode
57
+ end
58
+
59
+ private
60
+
61
+ # @param new_mode [Symbol] the new connection mode to set
62
+ # @raise [ArgumentError] when invalid mode is provided
63
+ def mode=(new_mode)
64
+ if MODES.include?(new_mode)
65
+ @mode = new_mode
66
+ else
67
+ raise(
68
+ ArgumentError,
69
+ "Invalid connection mode: #{new_mode}. Valid modes: #{MODES.join(', ')}"
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -28,7 +28,7 @@ module Karafka
28
28
  Thread.current.name = 'karafka.embedded'
29
29
 
30
30
  Karafka::Process.tags.add(:execution_mode, 'mode:embedded')
31
- Karafka::Server.execution_mode = :embedded
31
+ Karafka::Server.execution_mode.embedded!
32
32
  Karafka::Server.start
33
33
  end
34
34
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ # Represents the execution mode state of the Karafka server.
5
+ # Provides a cleaner API for checking and setting execution modes, encapsulating the mode logic
6
+ # and removing the need for symbol comparisons throughout the codebase.
7
+ #
8
+ # @example Check if running in swarm mode
9
+ # Server.execution_mode.swarm? #=> false
10
+ #
11
+ # @example Set the execution mode to embedded
12
+ # Server.execution_mode.embedded!
13
+ class ExecutionMode
14
+ # Available execution modes for Karafka server
15
+ # - :standalone - regular karafka consumer process
16
+ # - :embedded - embedded in a different process and not supervised
17
+ # - :supervisor - swarm supervisor process
18
+ # - :swarm - one of swarm processes
19
+ MODES = %i[
20
+ standalone
21
+ embedded
22
+ supervisor
23
+ swarm
24
+ ].freeze
25
+
26
+ private_constant :MODES
27
+
28
+ # @param mode [Symbol] initial execution mode (defaults to :standalone)
29
+ # @raise [ArgumentError] when invalid mode is provided
30
+ def initialize(mode = :standalone)
31
+ self.mode = mode
32
+ end
33
+
34
+ # Define query and setter methods for each mode using meta-programming
35
+ # This creates methods like: standalone?, embedded?, swarm?, etc.
36
+ # And bang methods like: standalone!, embedded!, swarm!, etc.
37
+ MODES.each do |mode_name|
38
+ # @return [Boolean] true if the current mode matches this mode
39
+ define_method("#{mode_name}?") do
40
+ @mode == mode_name
41
+ end
42
+
43
+ # Sets the execution mode to this mode
44
+ # @return [Symbol] the new mode
45
+ define_method("#{mode_name}!") do
46
+ self.mode = mode_name
47
+ end
48
+ end
49
+
50
+ # @return [String] string representation of the current mode
51
+ def to_s
52
+ @mode.to_s
53
+ end
54
+
55
+ # @return [Symbol] symbol representation of the current mode
56
+ def to_sym
57
+ @mode
58
+ end
59
+
60
+ # Compares the execution mode with another object.
61
+ # Supports comparison with symbols, strings, and other ExecutionMode instances
62
+ # for backward compatibility with existing code.
63
+ #
64
+ # @param other [Symbol, String, ExecutionMode] object to compare with
65
+ # @return [Boolean] true if the modes are equivalent
66
+ #
67
+ # @example Compare with symbol
68
+ # execution_mode == :standalone #=> true
69
+ #
70
+ # @example Compare with string
71
+ # execution_mode == 'standalone' #=> true
72
+ #
73
+ # @example Compare with another ExecutionMode
74
+ # execution_mode == other_mode #=> true/false
75
+ def ==(other)
76
+ case other
77
+ when Symbol
78
+ @mode == other
79
+ when String
80
+ @mode.to_s == other
81
+ when ExecutionMode
82
+ @mode == other.to_sym
83
+ else
84
+ false
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ # @param new_mode [Symbol] the new execution mode to set
91
+ # @raise [ArgumentError] when invalid mode is provided
92
+ def mode=(new_mode)
93
+ if MODES.include?(new_mode)
94
+ @mode = new_mode
95
+ else
96
+ raise(
97
+ ArgumentError,
98
+ "Invalid execution mode: #{new_mode}. Valid modes: #{MODES.join(', ')}"
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
@@ -47,7 +47,7 @@ module Karafka
47
47
  # Initializes this semaphore from the mutex, so it is never auto-created
48
48
  # Since we always schedule a job before waiting using semaphores, there won't be any
49
49
  # concurrency problems
50
- @semaphores[group_id] = RUBY_VERSION < '3.2' ? TimedQueue.new : Queue.new
50
+ @semaphores[group_id] = Queue.new
51
51
  end
52
52
  end
53
53
 
@@ -46,10 +46,16 @@ module Karafka
46
46
  # embedded
47
47
  # We cannot validate this during the start because config needs to be populated and routes
48
48
  # need to be defined.
49
- cli_contract.validate!(
50
- activity_manager.to_h,
51
- scope: %w[cli]
52
- )
49
+ #
50
+ # We do not validate in swarm because in swarm it is the supervisor that should validate
51
+ # this. After the supervisor validation some of the internal states like SGs availability
52
+ # may fail, so double validation may crash
53
+ unless execution_mode.swarm?
54
+ cli_contract.validate!(
55
+ activity_manager.to_h,
56
+ scope: %w[cli]
57
+ )
58
+ end
53
59
 
54
60
  # We clear as we do not want parent handlers in case of working from fork
55
61
  process.clear
@@ -164,6 +170,12 @@ module Karafka
164
170
  # to dispatch state changes, etc
165
171
  Karafka::App.producer.close
166
172
 
173
+ # Closes the default connection pool (if used). If not used, will do nothing
174
+ # This ensures that if users have configured the default pool, it is closed correctly
175
+ #
176
+ # Custom pools need to be closed by users themselves
177
+ ::WaterDrop::ConnectionPool.close
178
+
167
179
  Karafka::App.terminate!
168
180
  end
169
181
  end
@@ -182,7 +194,7 @@ module Karafka
182
194
  # Always start with standalone so there always is a value for the execution mode.
183
195
  # This is overwritten quickly during boot, but just in case someone would reach it prior to
184
196
  # booting, we want to have the default value.
185
- self.execution_mode = :standalone
197
+ self.execution_mode = ExecutionMode.new(:standalone)
186
198
 
187
199
  self.id = SecureRandom.hex(6)
188
200
  end
@@ -105,7 +105,7 @@ module Karafka
105
105
  Karafka::Process.tags.add(:execution_mode, 'mode:swarm')
106
106
  Karafka::Process.tags.add(:swarm_nodeid, "node:#{@id}")
107
107
 
108
- Server.execution_mode = :swarm
108
+ Server.execution_mode.swarm!
109
109
  Server.run
110
110
 
111
111
  @writer.close
@@ -36,7 +36,7 @@ module Karafka
36
36
 
37
37
  def initialize
38
38
  @mutex = Mutex.new
39
- @queue = Processing::TimedQueue.new
39
+ @queue = Queue.new
40
40
  end
41
41
 
42
42
  # Creates needed number of forks, installs signals and starts supervision
@@ -3,5 +3,5 @@
3
3
  # Main module namespace
4
4
  module Karafka
5
5
  # Current Karafka version
6
- VERSION = '2.5.1.beta1'
6
+ VERSION = '2.5.1'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.1.beta1
4
+ version: 2.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -49,21 +49,21 @@ dependencies:
49
49
  requirements:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 0.21.0
52
+ version: 0.22.0
53
53
  type: :runtime
54
54
  prerelease: false
55
55
  version_requirements: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 0.21.0
59
+ version: 0.22.0
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: waterdrop
62
62
  requirement: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: 2.8.3
66
+ version: 2.8.9
67
67
  - - "<"
68
68
  - !ruby/object:Gem::Version
69
69
  version: 3.0.0
@@ -73,7 +73,7 @@ dependencies:
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: 2.8.3
76
+ version: 2.8.9
77
77
  - - "<"
78
78
  - !ruby/object:Gem::Version
79
79
  version: 3.0.0
@@ -198,6 +198,7 @@ files:
198
198
  - lib/karafka/connection/listeners_batch.rb
199
199
  - lib/karafka/connection/manager.rb
200
200
  - lib/karafka/connection/messages_buffer.rb
201
+ - lib/karafka/connection/mode.rb
201
202
  - lib/karafka/connection/pauses_manager.rb
202
203
  - lib/karafka/connection/proxy.rb
203
204
  - lib/karafka/connection/raw_messages_buffer.rb
@@ -212,6 +213,7 @@ files:
212
213
  - lib/karafka/embedded.rb
213
214
  - lib/karafka/env.rb
214
215
  - lib/karafka/errors.rb
216
+ - lib/karafka/execution_mode.rb
215
217
  - lib/karafka/helpers/async.rb
216
218
  - lib/karafka/helpers/colorize.rb
217
219
  - lib/karafka/helpers/config_importer.rb
@@ -527,7 +529,6 @@ files:
527
529
  - lib/karafka/processing/strategies/dlq_mom.rb
528
530
  - lib/karafka/processing/strategies/mom.rb
529
531
  - lib/karafka/processing/strategy_selector.rb
530
- - lib/karafka/processing/timed_queue.rb
531
532
  - lib/karafka/processing/worker.rb
532
533
  - lib/karafka/processing/workers_batch.rb
533
534
  - lib/karafka/railtie.rb
@@ -616,7 +617,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
616
617
  requirements:
617
618
  - - ">="
618
619
  - !ruby/object:Gem::Version
619
- version: 3.1.0
620
+ version: 3.2.0
620
621
  required_rubygems_version: !ruby/object:Gem::Requirement
621
622
  requirements:
622
623
  - - ">="
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Karafka
4
- module Processing
5
- # Minimal queue with timeout for Ruby 3.1 and lower.
6
- #
7
- # It is needed because only since 3.2, Ruby has a timeout on `#pop`
8
- class TimedQueue
9
- include Karafka::Core::Helpers::Time
10
-
11
- def initialize
12
- @queue = Queue.new
13
- @mutex = Thread::Mutex.new
14
- @resource = Thread::ConditionVariable.new
15
- end
16
-
17
- # Adds element to the queue
18
- #
19
- # @param obj [Object] pushes an element onto the queue
20
- def push(obj)
21
- @mutex.synchronize do
22
- @queue << obj
23
- @resource.broadcast
24
- end
25
- end
26
-
27
- alias << push
28
-
29
- # No timeout means waiting up to 31 years
30
- #
31
- # @param timeout [Integer] max number of seconds to wait on the pop
32
- # @return [Object] element inserted on the array or `nil` on timeout
33
- #
34
- # @note We use timeout in seconds because this is how Ruby 3.2+ works and we want to have
35
- # the same API for newer and older Ruby versions
36
- def pop(timeout: 10_000_000_000)
37
- deadline = monotonic_now + timeout * 1000
38
-
39
- @mutex.synchronize do
40
- loop do
41
- return @queue.pop unless @queue.empty?
42
- return @queue.pop if @queue.closed?
43
-
44
- to_wait = (deadline - monotonic_now) / 1_000.0
45
-
46
- return nil if to_wait <= 0
47
-
48
- @resource.wait(@mutex, to_wait)
49
- end
50
- end
51
- end
52
-
53
- # Closes the internal queue and releases the lock
54
- def close
55
- @mutex.synchronize do
56
- @queue.close
57
- @resource.broadcast
58
- end
59
- end
60
- end
61
- end
62
- end