Capabilities and native clients

Halyx doesn't reduce every cloud to the lowest common denominator. Where providers differ, the differences are declared as capabilities, and anything provider-specific is one unwrap away.

Capabilities#

Services that have optional features expose capabilities(). Asking for a feature the provider doesn't have fails with UnsupportedFeatureException, rather than being silently ignored:

Java
if (store.capabilities().supports(StorageCapability.PRESIGNED_URL)) {
    return store.presignDownload(key, Duration.ofMinutes(10)).uri();
}
return proxyDownload(key);

Publisher events = halyx.provider("gcp").publisher("orders");
events.capabilities().require(MessagingCapability.DEDUPLICATION, events.provider());   // fails on Pub/Sub

capabilities().asSet() lists them all. Capabilities reflect the configured credentials as well as the provider: GCS can presign only with credentials that can sign.

Storage#

StorageCapability AWS Azure Google Cloud
CONDITIONAL_WRITE, CONDITIONAL_DELETE yes yes yes
RANGE_READ, SERVER_SIDE_COPY, USER_METADATA yes yes yes
CHECKSUM, BATCH_DELETE, STORAGE_TIERS yes yes yes
PRESIGNED_URL, PRESIGNED_MULTIPART yes yes with signing credentials

Messaging#

MessagingCapability AWS Azure Google Cloud
ORDERING FIFO topics and queues session-enabled entities ordering-enabled subscriptions
DEDUPLICATION FIFO topics and queues duplicate-detection entities no
DELAYED_DELIVERY SQS queues yes no
NACK_DELAY yes no (redelivers at once) yes

Streams#

StreamCapability AWS Azure Google Cloud
IDEMPOTENCE yes yes yes
TRANSACTIONS yes no yes

Functions#

FunctionCapability AWS Azure Google Cloud
TRIGGER yes no no
TRIGGER_HEADERS no no no

Parameters and secrets#

ParameterCapability Supported by
VERSIONS SSM Parameter Store, Secrets Manager, Key Vault, Secret Manager, Parameter Manager, Vault. Not App Configuration.

Native clients with unwrap#

Every service except Database can hand you the native SDK client it uses, already configured with the provider's region, credentials and HTTP settings:

Java
S3Client s3 = store.unwrap(S3Client.class);
s3.putBucketTagging(b -> b.bucket(store.bucket()).tagging(t -> t.tagSet(tags)));

unwrap throws IllegalArgumentException if the provider has no client of that type, so code written for one cloud fails clearly on another instead of misbehaving. Prefer the portable API, and keep unwrap calls in one place, behind your own interface, so the rest of your code stays portable.

Service AWS Azure Google Cloud
BlobStore S3Client, S3Presigner BlobContainerClient, BlobServiceClient Storage
DocumentStore DynamoDbClient CosmosContainer Firestore
Publisher SnsClient (SqsClient with aws.target=sqs) ServiceBusSenderClient TopicAdminClient
Subscription SqsClient ServiceBusReceiverClient (ServiceBusSessionReceiverClient for session entities) SubscriptionAdminClient
ParameterStore SsmClient, SecretsManagerClient ConfigurationClient, SecretClient ParameterManagerClient, SecretManagerServiceClient
FunctionInvoker LambdaClient HttpPipeline HttpRequestFactory
StreamProducer Kafka's Producer (same) (same)
StreamConsumer Kafka's Consumer, used only from the polling thread (same) (same)
Cache Lettuce StatefulRedisConnection / StatefulRedisClusterConnection, RedisClient / RedisClusterClient (same) (same)

HashiCorp Vault parameter stores unwrap to VaultClient, Halyx's own client. A Database has no unwrap: its dataSource() is the HikariCP pool, which JDBC's own DataSource.unwrap(HikariDataSource.class) reaches.

When to reach for unwrap#

Good uses:

  • bucket or table administration: tags, lifecycle rules, CORS
  • data structures beyond strings on Redis
  • provider features not in the portable API yet, such as S3 Object Lock or Cosmos DB change feed

If you find yourself unwrapping for something common, it may belong in Halyx; see the roadmap.