|
11 | 11 | import java.sql.DriverManager; |
12 | 12 | import java.sql.PreparedStatement; |
13 | 13 | import java.sql.ResultSet; |
| 14 | +import java.sql.SQLException; |
14 | 15 | import java.util.ArrayList; |
15 | 16 | import java.util.List; |
16 | 17 | import java.util.UUID; |
17 | 18 |
|
18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 20 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
19 | 21 | import static org.junit.jupiter.api.Assertions.assertTrue; |
20 | 22 |
|
21 | 23 | public class CatchupServiceTest { |
@@ -81,6 +83,14 @@ private void createProcessingGap() throws Exception { |
81 | 83 | """); |
82 | 84 | } |
83 | 85 |
|
| 86 | + private void copyEventsToMessages(long lowestIdn) throws Exception { |
| 87 | + connection.createStatement().execute(""" |
| 88 | + INSERT INTO postevent.messages (id, source, datacontenttype, dataschema, subject, data, idn) |
| 89 | + select id, source, datacontenttype, dataschema, subject, data, idn |
| 90 | + from postevent.test_events |
| 91 | + where idn >= """ + lowestIdn); |
| 92 | + } |
| 93 | + |
84 | 94 | @Test |
85 | 95 | public void testCatchupProcessesNewEvents() throws Exception { |
86 | 96 | // Publish some test events |
@@ -191,15 +201,126 @@ public void testCatchupWithExistingHwm() throws Exception { |
191 | 201 | assertTrue(newHwm > initialHwm, "HWM should have increased"); |
192 | 202 | } |
193 | 203 |
|
194 | | - private long getCurrentHwm(String subscriberName) throws Exception { |
| 204 | + @Test |
| 205 | + public void testHasSequenceGapWithNoGap() throws Exception { |
| 206 | + // Publish sequential events |
| 207 | + log.debug("Publishing 5 sequential events"); |
| 208 | + for (int i = 0; i < 5; i++) { |
| 209 | + Event event = new Event( |
| 210 | + UUID.randomUUID().toString(), |
| 211 | + "test-source", |
| 212 | + "test-type", |
| 213 | + "application/json", |
| 214 | + null, |
| 215 | + "test-subject", |
| 216 | + ("{\"value\":" + i + "}").getBytes(), |
| 217 | + null); |
| 218 | + publisher.publish(event, connection, TEST_TOPIC); |
| 219 | + } |
| 220 | + |
| 221 | + copyEventsToMessages(0); |
| 222 | + |
| 223 | + // Initialize HWM to 0 |
| 224 | + initializeHwm(SUBSCRIBER_NAME, 0); |
| 225 | + |
| 226 | + // Check for gaps |
| 227 | + boolean hasGap = catchupService.hasSequenceGap(SUBSCRIBER_NAME, 0); |
| 228 | + |
| 229 | + // Verify no gap was found |
| 230 | + assertFalse(hasGap, "Should not find any gaps in sequential events"); |
| 231 | + |
| 232 | + // Verify HWM was updated to the last event |
| 233 | + long newHwm = getCurrentHwm(SUBSCRIBER_NAME); |
| 234 | + assertEquals(5, newHwm, "HWM should be updated to the last event"); |
| 235 | + } |
| 236 | + |
| 237 | + @Test |
| 238 | + public void testHasSequenceGapWithGap() throws Exception { |
| 239 | + // Create a gap by publishing events with specific IDs |
| 240 | + // We'll manually insert events with IDNs 1, 2, 3, 5, 6 (gap at 4) |
| 241 | + log.debug("Publishing events with a gap"); |
| 242 | + |
| 243 | + // First, insert events 1-3 |
| 244 | + for (int i = 1; i <= 3; i++) { |
| 245 | + Event event = new Event( |
| 246 | + UUID.randomUUID().toString(), |
| 247 | + "test-source", |
| 248 | + "test-type", |
| 249 | + "application/json", |
| 250 | + null, |
| 251 | + "test-subject", |
| 252 | + ("{\"value\":" + i + "}").getBytes(), |
| 253 | + null); |
| 254 | + publisher.publish(event, connection, TEST_TOPIC); |
| 255 | + } |
| 256 | + |
| 257 | + copyEventsToMessages(0); |
| 258 | + |
| 259 | + // Then insert events 4-6 |
| 260 | + for (int i = 4; i <= 6; i++) { |
| 261 | + log.debug("Publishing event {}", i); |
| 262 | + Event event = new Event( |
| 263 | + UUID.randomUUID().toString(), |
| 264 | + "test-source", |
| 265 | + "test-type", |
| 266 | + "application/json", |
| 267 | + null, |
| 268 | + "test-subject", |
| 269 | + ("{\"value\":" + i + "}").getBytes(), |
| 270 | + null); |
| 271 | + publisher.publish(event, connection, TEST_TOPIC); |
| 272 | + } |
| 273 | + copyEventsToMessages(5); |
| 274 | + logEventsInTopicTable(); |
| 275 | + logEventsInMessagesTable(); |
| 276 | + |
| 277 | + // Initialize HWM to 0 |
| 278 | + initializeHwm(SUBSCRIBER_NAME, 0); |
| 279 | + |
| 280 | + // Check for gaps |
| 281 | + boolean hasGap = catchupService.hasSequenceGap(SUBSCRIBER_NAME, 0); |
| 282 | + |
| 283 | + // Verify a gap was found |
| 284 | + assertTrue(hasGap, "Should find a gap in the sequence"); |
| 285 | + |
| 286 | + // Verify HWM was updated to the last event before the gap |
| 287 | + long newHwm = getCurrentHwm(SUBSCRIBER_NAME); |
| 288 | + assertEquals(3, newHwm, "HWM should be updated to the last event before the gap"); |
| 289 | + |
| 290 | + } |
| 291 | + |
| 292 | + /** |
| 293 | + * Helper method to initialize HWM for a subscriber |
| 294 | + */ |
| 295 | + private void initializeHwm(String subscriberName, long hwm) throws SQLException { |
| 296 | + String sql = "INSERT INTO postevent.contiguous_hwm (subscriber_name, hwm) " + |
| 297 | + "VALUES (?, ?) " + |
| 298 | + "ON CONFLICT (subscriber_name) DO UPDATE SET hwm = ?"; |
| 299 | + |
| 300 | + try (PreparedStatement stmt = connection.prepareStatement(sql)) { |
| 301 | + stmt.setString(1, subscriberName); |
| 302 | + stmt.setLong(2, hwm); |
| 303 | + stmt.setLong(3, hwm); |
| 304 | + |
| 305 | + stmt.executeUpdate(); |
| 306 | + } |
| 307 | + } |
| 308 | + |
| 309 | + /** |
| 310 | + * Helper method to get current HWM for a subscriber |
| 311 | + */ |
| 312 | + private long getCurrentHwm(String subscriberName) throws SQLException { |
195 | 313 | String sql = "SELECT hwm FROM postevent.contiguous_hwm WHERE subscriber_name = ?"; |
| 314 | + |
196 | 315 | try (PreparedStatement stmt = connection.prepareStatement(sql)) { |
197 | 316 | stmt.setString(1, subscriberName); |
| 317 | + |
198 | 318 | try (ResultSet rs = stmt.executeQuery()) { |
199 | 319 | if (rs.next()) { |
200 | 320 | return rs.getLong("hwm"); |
| 321 | + } else { |
| 322 | + return 0; |
201 | 323 | } |
202 | | - return 0; |
203 | 324 | } |
204 | 325 | } |
205 | 326 | } |
|
0 commit comments