kiss_gcp_pubsub_queue 0.2.0 copy "kiss_gcp_pubsub_queue: ^0.2.0" to clipboard
kiss_gcp_pubsub_queue: ^0.2.0 copied to clipboard

A Google Cloud Pub/Sub backend for kiss_queue providing SQS-style queue semantics

kiss_gcp_pubsub_queue #

A Google Cloud Pub/Sub backend for kiss_queue โ€” part of the KISS (Keep It Simple, Stupid) family of libraries.

๐ŸŽฏ Purpose #

kiss_gcp_pubsub_queue provides a production-ready Google Cloud Pub/Sub implementation of the kiss_queue interface. It brings SQS-style queue semantics to GCP, including visibility timeouts, dead letter queues, and FIFO ordering with deduplication.

Just queues on GCP. No ceremony. No complexity.


โœจ Features #

  • ๐Ÿ”„ Standard Queue: High throughput, unordered, at-least-once delivery
  • ๐Ÿ“‹ FIFO Queue: Ordered message delivery per group with client-side deduplication
  • โฑ๏ธ Visibility Timeout: Configurable invisibility window via ack deadline management
  • ๐Ÿ’€ Dead Letter Queues: Automatic routing after max delivery attempts
  • ๐Ÿ”Œ Serialization Support: Pluggable serialization for any data format
  • ๐Ÿงช Comprehensive Testing: 113 tests covering all functionality
  • ๐Ÿ—๏ธ Dual Client Architecture: Uses gcloud for publishing, googleapis for pull and control plane

๐Ÿš€ Quick Start #

Basic Usage #

import 'package:gcloud/pubsub.dart';
import 'package:googleapis/pubsub/v1.dart' as pubsub_api;
import 'package:kiss_gcp_pubsub_queue/kiss_gcp_pubsub_queue.dart';

void main() async {
  // Create authenticated clients (see Authentication section)
  final pubsub = PubSub(client, projectId);
  final pubsubApi = pubsub_api.PubsubApi(client);

  // Create admin and factory
  final admin = PubSubAdmin(pubsubApi, projectId: projectId);
  final factory = PubSubQueueFactory<String, String>(
    pubsub: pubsub,
    admin: admin,
  );

  // Create a queue
  final queue = await factory.createQueue('my-queue');

  // Enqueue a message
  await queue.enqueuePayload('Hello, World!');

  // Dequeue and process
  final message = await queue.dequeue();
  if (message != null) {
    print('Received: ${message.payload}');
    await queue.acknowledge(message.id!);
  }

  // Cleanup
  await factory.dispose();
}

Serialization Example #

import 'dart:convert';
import 'package:kiss_queue/kiss_queue.dart';
import 'package:kiss_gcp_pubsub_queue/kiss_gcp_pubsub_queue.dart';

class Order {
  final String orderId;
  final double amount;
  Order(this.orderId, this.amount);

  Map<String, dynamic> toJson() => {'orderId': orderId, 'amount': amount};
  static Order fromJson(Map<String, dynamic> json) =>
      Order(json['orderId'], json['amount']);
}

class OrderSerializer implements MessageSerializer<Order, String> {
  @override
  String serialize(Order payload) => jsonEncode(payload.toJson());

  @override
  Order deserialize(String data) => Order.fromJson(jsonDecode(data));
}

void main() async {
  final factory = PubSubQueueFactory<Order, String>(
    pubsub: pubsub,
    admin: admin,
    serializer: OrderSerializer(),
  );

  final queue = await factory.createQueue('order-queue');

  // Enqueue with automatic serialization
  await queue.enqueuePayload(Order('ORD-123', 99.99));

  // Dequeue with automatic deserialization
  final message = await queue.dequeue();
  if (message != null) {
    print('Order: ${message.payload.orderId}'); // Fully typed Order object
    await queue.acknowledge(message.id!);
  }
}

FIFO Queue with Ordering #

final fifoQueue = await factory.createQueue(
  'ordered-queue',
  configuration: const PubSubQueueConfiguration(
    isFifo: true,
    groupId: 'my-group',
  ),
);

// Messages with the same groupId are delivered in order
await fifoQueue.enqueuePayload('First');
await fifoQueue.enqueuePayload('Second');
await fifoQueue.enqueuePayload('Third');

Dead Letter Queue #

// Create DLQ first
final dlq = await factory.createQueue('my-queue-dlq');

// Create main queue with DLQ
final queue = await factory.createQueue(
  'my-queue',
  configuration: const PubSubQueueConfiguration(
    maxReceiveCount: 5, // Move to DLQ after 5 failed attempts
  ),
  deadLetterQueue: dlq,
);

๐Ÿ—๏ธ Architecture #

Dual Client Design #

This package uses two Google Cloud client libraries:

Library Purpose Operations
gcloud Publishing publish messages to topics
googleapis Pull & control plane pull, acknowledge, modifyAckDeadline, create/delete topics and subscriptions, configure DLQ

The gcloud package provides a high-level API for publishing, while googleapis handles all pull and administrative operations, ensuring access to real Pub/Sub ack IDs for reliable message lifecycle management.

Resource Naming #

Queues are created with the following Pub/Sub resources:

Resource Naming Pattern
Topic kiss-{queueName}
FIFO Topic kiss-{queueName}-fifo
Subscription kiss-{queueName}-sub
DLQ Topic kiss-{queueName}-dlq
DLQ Subscription kiss-{queueName}-dlq-sub

Message Lifecycle #

  1. Enqueue: Publish message to Pub/Sub topic (with optional serialization)
  2. Dequeue: Pull message from subscription (becomes invisible via ack deadline)
  3. Process: Handle the message in your application
  4. Acknowledge: Confirm successful processing (removes from subscription)
  5. Reject: Mark as failed (requeue for retry or let DLQ policy handle it)

FIFO Deduplication #

Pub/Sub doesn't natively deduplicate messages, so FIFO queues use client-side deduplication via a DedupeStore. The default implementation uses kiss_repository's InMemoryRepository.

// Custom dedupe store (e.g., Redis-backed for distributed systems)
final queue = PubSubQueue<String, String>(
  topic: topic,
  admin: admin,
  configuration: const PubSubQueueConfiguration(isFifo: true),
  subscriptionName: 'my-sub',
  dedupeStore: MyRedisDedupeStore(),
);

โš™๏ธ Configuration #

PubSubQueueConfiguration #

const config = PubSubQueueConfiguration(
  visibilityTimeout: Duration(seconds: 30),  // Ack deadline
  maxReceiveCount: 5,                        // Max attempts before DLQ
  isFifo: false,                             // Enable ordering + deduplication
  groupId: null,                             // Message group for FIFO
  dedupeTtl: Duration(minutes: 5),           // Deduplication window
);
Option Default Description
visibilityTimeout 30 seconds How long a message is invisible after being received
maxReceiveCount 5 Max delivery attempts before sending to DLQ
isFifo false Enable ordered delivery and deduplication
groupId null Message group for FIFO ordering
contentBasedDeduplication false Use SHA-256 hash of payload for deduplication instead of message ID
dedupeTtl 5 minutes Deduplication window for FIFO queues

๐Ÿ” Authentication #

Using Application Default Credentials #

import 'package:googleapis_auth/auth_io.dart';

final client = await clientViaApplicationDefaultCredentials(
  scopes: [pubsub_api.PubsubApi.pubsubScope],
);

Using Service Account #

import 'package:googleapis_auth/auth_io.dart';

final credentials = ServiceAccountCredentials.fromJson(jsonCredentials);
final client = await clientViaServiceAccount(
  credentials,
  [pubsub_api.PubsubApi.pubsubScope],
);

๐Ÿงช Testing #

The package includes 113 comprehensive tests:

# Run all tests
dart test

# Run specific test file
dart test test/queue_test.dart

Test Coverage #

  • โœ… Queue operations (enqueue, dequeue, acknowledge, reject)
  • โœ… FIFO ordering and deduplication
  • โœ… Dead letter queue routing
  • โœ… Visibility timeout (ack deadline) management
  • โœ… Serialization/deserialization
  • โœ… Factory queue creation and caching
  • โœ… Admin operations (create/delete topics and subscriptions)

๐Ÿ› ๏ธ Installation #

Add to your pubspec.yaml:

dependencies:
  kiss_gcp_pubsub_queue: ^0.2.0

Then run:

dart pub get

๐Ÿ“ฆ Dependencies #

Package Purpose
kiss_queue Core queue interface
gcloud Publishing messages to topics
googleapis Pull, acknowledge, and control plane operations
kiss_repository Dedupe store implementation

๐Ÿค Contributing #

We welcome contributions! Please see our contributing guidelines for details.

Running Tests #

# Run all tests
dart test

# Run with coverage
dart test --coverage=coverage

๐Ÿ“„ License #

This project is licensed under the MIT License - see the LICENSE file for details.


๐ŸŒŸ Why kiss_gcp_pubsub_queue? #

  • Simple: Unified kiss_queue interface, no Pub/Sub complexity exposed
  • Reliable: SQS-style semantics with visibility timeouts and DLQ
  • Flexible: Works with any serialization format
  • Performant: Optimized for high throughput
  • Testable: 113 comprehensive tests included
  • Production Ready: Built for real-world GCP deployments

Perfect for microservices, event-driven architectures, and any GCP application that needs reliable async message processing.


Built with โค๏ธ by the WAMF team. Part of the KISS family of simple, focused Dart packages.

0
likes
130
points
9
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Google Cloud Pub/Sub backend for kiss_queue providing SQS-style queue semantics

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

crypto, gcloud, googleapis, googleapis_auth, http, kiss_queue, kiss_repository, uuid

More

Packages that depend on kiss_gcp_pubsub_queue