Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@ This repository ships a Delayed Queue for Java developers, implemented in Kotlin
All public APIs must look and feel like a Java library. The `kotlin-java-library`
skill is the rule of law for any public surface changes.

## CRITICAL RULE: FOLLOW THE ORIGINAL IMPLEMENTATION EXACTLY

**When porting from the Scala original in `old-code/`, match the structure EXACTLY.**

Do NOT deviate from:
- Configuration class fields and their order
- Method signatures and parameters
- Type names and naming conventions
- Behavior and semantics

The original implementation in `old-code/` is the source of truth. Any deviation must be explicitly justified and documented.

## Non-negotiable rules
- Public API is Java-first: no Kotlin-only surface features or Kotlin stdlib types.
- Keep nullability explicit and stable; avoid platform types in public signatures.
- Do not change or remove published public members; add overloads instead.
- Use JVM interop annotations deliberately to shape Java call sites.
- Verify every public entry point with a Java call-site example.
- Agents MUST practice TDD: write the failing test first, then implement the change.
- Library dependencies should never be added by agents, unless instructed to do so.

## Public API constraints (Java consumers)
- Use Java types in signatures: `java.util.List/Map/Set`, `java.time.*`,
Expand All @@ -36,6 +49,25 @@ skill is the rule of law for any public surface changes.
- Add new overloads instead of changing existing ones.
- Use a deprecation cycle before removal.

## Code style / best practices

- NEVER catch `Throwable`, you're only allowed to catch `Exception`
- Use nice imports instead of fully qualified names
- NEVER use default parameters for database-specific behavior (filters, adapters, etc.) - these MUST match the actual driver/config
- Exception handling must be PRECISE - only catch what you intend to handle. Generic catches like `catch (e: SQLException)` are almost always wrong.
- Use exception filters/matchers for specific error types (DuplicateKey, TransientFailure, etc.)
- Let unexpected exceptions propagate to retry logic

## Testing

- Practice TDD: write tests before the implementation.
- Projects strives for full test coverage. Tests have to be clean and easy to read.
- **All tests for public API go into `./src/test/java`, built in Java.**
- If a test calls public methods on `DelayedQueue`, `CronService`, or other public interfaces → Java test
- This ensures the Java API is tested from a Java consumer's perspective
- **All tests for internal implementation go into `./src/test/kotlin`, built in Kotlin.**
- If a test is for internal classes/functions (e.g., `SqlExceptionFilters`, `Raise`, retry logic) → Kotlin test

## Review checklist
- Java call sites compile for all public constructors and methods.
- No Kotlin stdlib types exposed in public signatures.
Expand Down
109 changes: 109 additions & 0 deletions delayedqueue-jvm/api/delayedqueue-jvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public final class org/funfix/delayedqueue/jvm/CronConfigHash : java/lang/Record
public fun equals (Ljava/lang/Object;)Z
public static final fun fromDailyCron (Lorg/funfix/delayedqueue/jvm/CronDailySchedule;)Lorg/funfix/delayedqueue/jvm/CronConfigHash;
public static final fun fromPeriodicTick (Ljava/time/Duration;)Lorg/funfix/delayedqueue/jvm/CronConfigHash;
public static final fun fromString (Ljava/lang/String;)Lorg/funfix/delayedqueue/jvm/CronConfigHash;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
public final fun value ()Ljava/lang/String;
Expand All @@ -68,6 +69,7 @@ public final class org/funfix/delayedqueue/jvm/CronConfigHash : java/lang/Record
public final class org/funfix/delayedqueue/jvm/CronConfigHash$Companion {
public final fun fromDailyCron (Lorg/funfix/delayedqueue/jvm/CronDailySchedule;)Lorg/funfix/delayedqueue/jvm/CronConfigHash;
public final fun fromPeriodicTick (Ljava/time/Duration;)Lorg/funfix/delayedqueue/jvm/CronConfigHash;
public final fun fromString (Ljava/lang/String;)Lorg/funfix/delayedqueue/jvm/CronConfigHash;
}

public final class org/funfix/delayedqueue/jvm/CronDailySchedule : java/lang/Record {
Expand Down Expand Up @@ -184,6 +186,64 @@ public final class org/funfix/delayedqueue/jvm/DelayedQueueInMemory$Companion {
public static synthetic fun create$default (Lorg/funfix/delayedqueue/jvm/DelayedQueueInMemory$Companion;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;Ljava/time/Clock;ILjava/lang/Object;)Lorg/funfix/delayedqueue/jvm/DelayedQueueInMemory;
}

public final class org/funfix/delayedqueue/jvm/DelayedQueueJDBC : java/lang/AutoCloseable, org/funfix/delayedqueue/jvm/DelayedQueue {
public static final field Companion Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC$Companion;
public synthetic fun <init> (Lorg/funfix/delayedqueue/jvm/internals/utils/Database;Lorg/funfix/delayedqueue/jvm/internals/jdbc/SQLVendorAdapter;Lorg/funfix/delayedqueue/jvm/MessageSerializer;Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;Ljava/time/Clock;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun close ()V
public fun containsMessage (Ljava/lang/String;)Z
public static final fun create (Lorg/funfix/delayedqueue/jvm/MessageSerializer;Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC;
public static final fun create (Lorg/funfix/delayedqueue/jvm/MessageSerializer;Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;Ljava/time/Clock;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC;
public fun dropAllMessages (Ljava/lang/String;)I
public fun dropMessage (Ljava/lang/String;)Z
public fun getCron ()Lorg/funfix/delayedqueue/jvm/CronService;
public fun getTimeConfig ()Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;
public fun offerBatch (Ljava/util/List;)Ljava/util/List;
public fun offerIfNotExists (Ljava/lang/String;Ljava/lang/Object;Ljava/time/Instant;)Lorg/funfix/delayedqueue/jvm/OfferOutcome;
public fun offerOrUpdate (Ljava/lang/String;Ljava/lang/Object;Ljava/time/Instant;)Lorg/funfix/delayedqueue/jvm/OfferOutcome;
public fun poll ()Lorg/funfix/delayedqueue/jvm/AckEnvelope;
public fun read (Ljava/lang/String;)Lorg/funfix/delayedqueue/jvm/AckEnvelope;
public static final fun runMigrations (Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;)V
public fun tryPoll ()Lorg/funfix/delayedqueue/jvm/AckEnvelope;
public fun tryPollMany (I)Lorg/funfix/delayedqueue/jvm/AckEnvelope;
}

public final class org/funfix/delayedqueue/jvm/DelayedQueueJDBC$Companion {
public final fun create (Lorg/funfix/delayedqueue/jvm/MessageSerializer;Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC;
public final fun create (Lorg/funfix/delayedqueue/jvm/MessageSerializer;Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;Ljava/time/Clock;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC;
public static synthetic fun create$default (Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC$Companion;Lorg/funfix/delayedqueue/jvm/MessageSerializer;Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;Ljava/time/Clock;ILjava/lang/Object;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBC;
public final fun runMigrations (Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;)V
}

public final class org/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig : java/lang/Record {
public static final field Companion Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig$Companion;
public fun <init> (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;)V
public fun <init> (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;Ljava/lang/String;)V
public fun <init> (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/RetryConfig;)V
public synthetic fun <init> (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/RetryConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun ackEnvSource ()Ljava/lang/String;
public final fun component1 ()Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Ljava/lang/String;
public final fun component6 ()Lorg/funfix/delayedqueue/jvm/RetryConfig;
public final fun copy (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/RetryConfig;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;
public static synthetic fun copy$default (Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;Ljava/lang/String;Ljava/lang/String;Lorg/funfix/delayedqueue/jvm/RetryConfig;ILjava/lang/Object;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;
public static final fun create (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Ljava/lang/String;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;
public final fun db ()Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public final fun queueName ()Ljava/lang/String;
public final fun retryPolicy ()Lorg/funfix/delayedqueue/jvm/RetryConfig;
public final fun tableName ()Ljava/lang/String;
public final fun time ()Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;
public fun toString ()Ljava/lang/String;
}

public final class org/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig$Companion {
public final fun create (Lorg/funfix/delayedqueue/jvm/JdbcConnectionConfig;Ljava/lang/String;Ljava/lang/String;)Lorg/funfix/delayedqueue/jvm/DelayedQueueJDBCConfig;
}

public final class org/funfix/delayedqueue/jvm/DelayedQueueTimeConfig : java/lang/Record {
public static final field Companion Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig$Companion;
public static final field DEFAULT Lorg/funfix/delayedqueue/jvm/DelayedQueueTimeConfig;
Expand Down Expand Up @@ -274,6 +334,7 @@ public final class org/funfix/delayedqueue/jvm/JdbcDatabasePoolConfig : java/lan

public final class org/funfix/delayedqueue/jvm/JdbcDriver : java/lang/Enum {
public static final field Companion Lorg/funfix/delayedqueue/jvm/JdbcDriver$Companion;
public static final field HSQLDB Lorg/funfix/delayedqueue/jvm/JdbcDriver;
public static final field MsSqlServer Lorg/funfix/delayedqueue/jvm/JdbcDriver;
public static final field Sqlite Lorg/funfix/delayedqueue/jvm/JdbcDriver;
public final fun getClassName ()Ljava/lang/String;
Expand All @@ -298,6 +359,18 @@ public final class org/funfix/delayedqueue/jvm/MessageId : java/lang/Record {
public final fun value ()Ljava/lang/String;
}

public abstract interface class org/funfix/delayedqueue/jvm/MessageSerializer {
public static final field Companion Lorg/funfix/delayedqueue/jvm/MessageSerializer$Companion;
public abstract fun deserialize (Ljava/lang/String;)Ljava/lang/Object;
public static fun forStrings ()Lorg/funfix/delayedqueue/jvm/MessageSerializer;
public abstract fun getTypeName ()Ljava/lang/String;
public abstract fun serialize (Ljava/lang/Object;)Ljava/lang/String;
}

public final class org/funfix/delayedqueue/jvm/MessageSerializer$Companion {
public final fun forStrings ()Lorg/funfix/delayedqueue/jvm/MessageSerializer;
}

public abstract interface class org/funfix/delayedqueue/jvm/OfferOutcome {
public fun isIgnored ()Z
}
Expand All @@ -323,6 +396,42 @@ public final class org/funfix/delayedqueue/jvm/OfferOutcome$Updated : org/funfix
public fun toString ()Ljava/lang/String;
}

public final class org/funfix/delayedqueue/jvm/ResourceUnavailableException : java/lang/Exception {
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
}

public final class org/funfix/delayedqueue/jvm/RetryConfig : java/lang/Record {
public static final field Companion Lorg/funfix/delayedqueue/jvm/RetryConfig$Companion;
public static final field DEFAULT Lorg/funfix/delayedqueue/jvm/RetryConfig;
public static final field NO_RETRIES Lorg/funfix/delayedqueue/jvm/RetryConfig;
public fun <init> (Ljava/time/Duration;Ljava/time/Duration;)V
public fun <init> (Ljava/time/Duration;Ljava/time/Duration;D)V
public fun <init> (Ljava/time/Duration;Ljava/time/Duration;DLjava/lang/Long;)V
public fun <init> (Ljava/time/Duration;Ljava/time/Duration;DLjava/lang/Long;Ljava/time/Duration;)V
public fun <init> (Ljava/time/Duration;Ljava/time/Duration;DLjava/lang/Long;Ljava/time/Duration;Ljava/time/Duration;)V
public synthetic fun <init> (Ljava/time/Duration;Ljava/time/Duration;DLjava/lang/Long;Ljava/time/Duration;Ljava/time/Duration;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun backoffFactor ()D
public final fun component1 ()Ljava/time/Duration;
public final fun component2 ()Ljava/time/Duration;
public final fun component3 ()D
public final fun component4 ()Ljava/lang/Long;
public final fun component5 ()Ljava/time/Duration;
public final fun component6 ()Ljava/time/Duration;
public final fun copy (Ljava/time/Duration;Ljava/time/Duration;DLjava/lang/Long;Ljava/time/Duration;Ljava/time/Duration;)Lorg/funfix/delayedqueue/jvm/RetryConfig;
public static synthetic fun copy$default (Lorg/funfix/delayedqueue/jvm/RetryConfig;Ljava/time/Duration;Ljava/time/Duration;DLjava/lang/Long;Ljava/time/Duration;Ljava/time/Duration;ILjava/lang/Object;)Lorg/funfix/delayedqueue/jvm/RetryConfig;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public final fun initialDelay ()Ljava/time/Duration;
public final fun maxDelay ()Ljava/time/Duration;
public final fun maxRetries ()Ljava/lang/Long;
public final fun perTryHardTimeout ()Ljava/time/Duration;
public fun toString ()Ljava/lang/String;
public final fun totalSoftTimeout ()Ljava/time/Duration;
}

public final class org/funfix/delayedqueue/jvm/RetryConfig$Companion {
}

public final class org/funfix/delayedqueue/jvm/ScheduledMessage : java/lang/Record {
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/time/Instant;)V
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/time/Instant;Z)V
Expand Down
Loading