265 lines
9.9 KiB
Go
265 lines
9.9 KiB
Go
/*
|
|
Copyright 2021 The Dapr Authors
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package servicebus
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
sbadmin "github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus/admin"
|
|
|
|
mdutils "github.com/dapr/components-contrib/metadata"
|
|
"github.com/dapr/kit/logger"
|
|
"github.com/dapr/kit/ptr"
|
|
)
|
|
|
|
// Metadata options for Service Bus components.
|
|
// Note: AzureAD-related keys are handled separately.
|
|
type Metadata struct {
|
|
/** For bindings and pubsubs **/
|
|
ConnectionString string `mapstructure:"connectionString"`
|
|
ConsumerID string `mapstructure:"consumerID"` // Only topics
|
|
TimeoutInSec int `mapstructure:"timeoutInSec"`
|
|
HandlerTimeoutInSec int `mapstructure:"handlerTimeoutInSec"`
|
|
LockRenewalInSec int `mapstructure:"lockRenewalInSec"`
|
|
MaxActiveMessages int `mapstructure:"maxActiveMessages"`
|
|
MaxConnectionRecoveryInSec int `mapstructure:"maxConnectionRecoveryInSec"`
|
|
MinConnectionRecoveryInSec int `mapstructure:"minConnectionRecoveryInSec"`
|
|
DisableEntityManagement bool `mapstructure:"disableEntityManagement"`
|
|
MaxRetriableErrorsPerSec int `mapstructure:"maxRetriableErrorsPerSec"`
|
|
MaxDeliveryCount *int32 `mapstructure:"maxDeliveryCount"` // Only used during subscription creation - default is set by the server (10)
|
|
LockDurationInSec *int `mapstructure:"lockDurationInSec"` // Only used during subscription creation - default is set by the server (60s)
|
|
DefaultMessageTimeToLiveInSec *int `mapstructure:"defaultMessageTimeToLiveInSec"` // Only used during subscription creation - default is set by the server (depends on the tier)
|
|
AutoDeleteOnIdleInSec *int `mapstructure:"autoDeleteOnIdleInSec"` // Only used during subscription creation - default is set by the server (disabled)
|
|
MaxConcurrentHandlers int `mapstructure:"maxConcurrentHandlers"`
|
|
PublishMaxRetries int `mapstructure:"publishMaxRetries"`
|
|
PublishInitialRetryIntervalInMs int `mapstructure:"publishInitialRetryIntervalInMs"`
|
|
NamespaceName string `mapstructure:"namespaceName"` // Only for Azure AD
|
|
|
|
/** For bindings only **/
|
|
QueueName string `mapstructure:"queueName" mdonly:"bindings"` // Only queues
|
|
}
|
|
|
|
// Keys.
|
|
const (
|
|
keyConnectionString = "connectionString"
|
|
keyConsumerID = "consumerID"
|
|
keyTimeoutInSec = "timeoutInSec"
|
|
keyHandlerTimeoutInSec = "handlerTimeoutInSec"
|
|
keyLockRenewalInSec = "lockRenewalInSec"
|
|
keyMaxActiveMessages = "maxActiveMessages"
|
|
keyMaxConnectionRecoveryInSec = "maxConnectionRecoveryInSec"
|
|
keyMinConnectionRecoveryInSec = "minConnectionRecoveryInSec"
|
|
keyDisableEntityManagement = "disableEntityManagement"
|
|
keyMaxRetriableErrorsPerSec = "maxRetriableErrorsPerSec"
|
|
keyMaxDeliveryCount = "maxDeliveryCount"
|
|
keyLockDurationInSec = "lockDurationInSec"
|
|
keyDefaultMessageTimeToLiveInSec = "defaultMessageTimeToLiveInSec" // Alias: "ttlInSeconds" (mdutils.TTLMetadataKey)
|
|
keyAutoDeleteOnIdleInSec = "autoDeleteOnIdleInSec"
|
|
keyMaxConcurrentHandlers = "maxConcurrentHandlers"
|
|
keyPublishMaxRetries = "publishMaxRetries"
|
|
keyPublishInitialRetryIntervalInMs = "publishInitialRetryIntervalInMs" // Alias: "publishInitialRetryInternalInMs" (backwards compatibility due to typo)
|
|
keyNamespaceName = "namespaceName"
|
|
keyQueueName = "queueName"
|
|
)
|
|
|
|
// Defaults.
|
|
const (
|
|
// Default timeout for network requests.
|
|
defaultTimeoutInSec = 60
|
|
|
|
// Default timeout for handlers.
|
|
defaultHandlerTimeoutInSecPubSub = 60
|
|
defaultHandlerTimeoutInSecBinding = 0 // No timeout for handlers in bindings.
|
|
|
|
// Default lock renewal interval, in seconds.
|
|
defaultLockRenewalInSec = 20
|
|
|
|
// Default rate of retriable errors per second.
|
|
defaultMaxRetriableErrorsPerSec = 10
|
|
|
|
// Default number of maximum active messages.
|
|
defaultMaxActiveMessagesPubSub = 1000
|
|
defaultMaxActiveMessagesBinding = 1
|
|
|
|
defaultDisableEntityManagement = false
|
|
|
|
// Default minimum and maximum recovery time while trying to reconnect.
|
|
defaultMinConnectionRecoveryInSec = 2
|
|
defaultMaxConnectionRecoveryInSec = 300
|
|
|
|
// Max concurrent handlers.
|
|
defaultMaxConcurrentHandlersPubSub = 0 // No limit for pubsubs.
|
|
defaultMaxConcurrentHandlersBinding = 1 // Limited to 1 for backwards-compatibility.
|
|
|
|
defaultPublishMaxRetries = 5
|
|
defaultPublishInitialRetryIntervalInMs = 500
|
|
)
|
|
|
|
// Modes for ParseMetadata.
|
|
const (
|
|
MetadataModeBinding byte = 1 << iota
|
|
MetadataModeTopics
|
|
|
|
MetadataModeQueues byte = 0
|
|
)
|
|
|
|
// ParseMetadata parses metadata keys that are common to all Service Bus components
|
|
func ParseMetadata(md map[string]string, logger logger.Logger, mode byte) (m *Metadata, err error) {
|
|
m = &Metadata{
|
|
TimeoutInSec: defaultTimeoutInSec,
|
|
LockRenewalInSec: defaultLockRenewalInSec,
|
|
MaxActiveMessages: defaultMaxActiveMessagesPubSub,
|
|
MaxConnectionRecoveryInSec: defaultMaxConnectionRecoveryInSec,
|
|
MinConnectionRecoveryInSec: defaultMinConnectionRecoveryInSec,
|
|
DisableEntityManagement: defaultDisableEntityManagement,
|
|
MaxRetriableErrorsPerSec: defaultMaxRetriableErrorsPerSec,
|
|
MaxConcurrentHandlers: defaultMaxConcurrentHandlersPubSub,
|
|
PublishMaxRetries: defaultPublishMaxRetries,
|
|
PublishInitialRetryIntervalInMs: defaultPublishInitialRetryIntervalInMs,
|
|
}
|
|
|
|
if (mode & MetadataModeBinding) != 0 {
|
|
m.HandlerTimeoutInSec = defaultHandlerTimeoutInSecBinding
|
|
m.MaxActiveMessages = defaultMaxActiveMessagesBinding
|
|
m.MaxConcurrentHandlers = defaultMaxConcurrentHandlersBinding
|
|
} else {
|
|
m.HandlerTimeoutInSec = defaultHandlerTimeoutInSecPubSub
|
|
m.MaxActiveMessages = defaultMaxActiveMessagesPubSub
|
|
m.MaxConcurrentHandlers = defaultMaxConcurrentHandlersPubSub
|
|
}
|
|
|
|
// upgrade deprecated metadata keys
|
|
|
|
if val, ok := md["publishInitialRetryInternalInMs"]; ok && val != "" {
|
|
// TODO: Remove in a future Dapr release
|
|
logger.Warn("Found deprecated metadata property 'publishInitialRetryInternalInMs'; please use 'publishInitialRetryIntervalInMs'")
|
|
md["publishInitialRetryIntervalInMs"] = val
|
|
delete(md, "publishInitialRetryInternalInMs")
|
|
}
|
|
|
|
mdErr := mdutils.DecodeMetadata(md, &m)
|
|
if mdErr != nil {
|
|
return m, mdErr
|
|
}
|
|
|
|
/* Required configuration settings - no defaults. */
|
|
if m.ConnectionString != "" {
|
|
// The connection string and the namespace cannot both be present.
|
|
if m.NamespaceName != "" {
|
|
return m, errors.New("connectionString and namespaceName cannot both be specified")
|
|
}
|
|
} else if m.NamespaceName == "" {
|
|
return m, errors.New("either one of connection string or namespace name are required")
|
|
}
|
|
|
|
if (mode & MetadataModeTopics) != 0 {
|
|
if m.ConsumerID == "" {
|
|
return m, errors.New("missing consumerID")
|
|
}
|
|
}
|
|
|
|
if (mode&MetadataModeBinding) != 0 && (mode&MetadataModeTopics) == 0 {
|
|
if m.QueueName == "" {
|
|
return m, errors.New("missing queueName")
|
|
}
|
|
}
|
|
|
|
if m.MaxActiveMessages < 1 {
|
|
err = errors.New("must be 1 or greater")
|
|
return m, err
|
|
}
|
|
|
|
if m.MaxRetriableErrorsPerSec < 0 {
|
|
err = errors.New("must not be negative")
|
|
return m, err
|
|
}
|
|
|
|
/* Nullable configuration settings - defaults will be set by the server. */
|
|
|
|
if m.DefaultMessageTimeToLiveInSec == nil {
|
|
duration, found, ttlErr := mdutils.TryGetTTL(md)
|
|
if ttlErr != nil {
|
|
return m, fmt.Errorf("invalid %s %s: %s", mdutils.TTLMetadataKey, duration, ttlErr)
|
|
}
|
|
if found {
|
|
m.DefaultMessageTimeToLiveInSec = ptr.Of(int(duration.Seconds()))
|
|
}
|
|
}
|
|
|
|
if m.DefaultMessageTimeToLiveInSec != nil && *m.DefaultMessageTimeToLiveInSec == 0 {
|
|
return m, errors.New("defaultMessageTimeToLiveInSec must be greater than 0")
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
// CreateSubscriptionProperties returns the SubscriptionProperties object to create new Subscriptions to Service Bus topics.
|
|
func (a Metadata) CreateSubscriptionProperties(opts SubscribeOptions) *sbadmin.SubscriptionProperties {
|
|
properties := &sbadmin.SubscriptionProperties{}
|
|
|
|
if a.MaxDeliveryCount != nil {
|
|
properties.MaxDeliveryCount = a.MaxDeliveryCount
|
|
}
|
|
|
|
if a.LockDurationInSec != nil {
|
|
properties.LockDuration = toDurationISOString(*a.LockDurationInSec)
|
|
}
|
|
|
|
if a.DefaultMessageTimeToLiveInSec != nil {
|
|
properties.DefaultMessageTimeToLive = toDurationISOString(*a.DefaultMessageTimeToLiveInSec)
|
|
}
|
|
|
|
if a.AutoDeleteOnIdleInSec != nil {
|
|
properties.AutoDeleteOnIdle = toDurationISOString(*a.AutoDeleteOnIdleInSec)
|
|
}
|
|
|
|
if opts.RequireSessions {
|
|
properties.RequiresSession = ptr.Of(true)
|
|
}
|
|
|
|
return properties
|
|
}
|
|
|
|
// CreateQueueProperties returns the QueueProperties object to create new Queues in Service Bus.
|
|
func (a Metadata) CreateQueueProperties() *sbadmin.QueueProperties {
|
|
properties := &sbadmin.QueueProperties{}
|
|
|
|
if a.MaxDeliveryCount != nil {
|
|
properties.MaxDeliveryCount = a.MaxDeliveryCount
|
|
}
|
|
|
|
if a.LockDurationInSec != nil {
|
|
properties.LockDuration = toDurationISOString(*a.LockDurationInSec)
|
|
}
|
|
|
|
if a.DefaultMessageTimeToLiveInSec != nil {
|
|
properties.DefaultMessageTimeToLive = toDurationISOString(*a.DefaultMessageTimeToLiveInSec)
|
|
}
|
|
|
|
if a.AutoDeleteOnIdleInSec != nil {
|
|
properties.AutoDeleteOnIdle = toDurationISOString(*a.AutoDeleteOnIdleInSec)
|
|
}
|
|
|
|
return properties
|
|
}
|
|
|
|
func toDurationISOString(valInSec int) *string {
|
|
valDuration := mdutils.Duration{
|
|
Duration: time.Duration(valInSec) * time.Second,
|
|
}
|
|
return ptr.Of(valDuration.ToISOString())
|
|
}
|