-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathclient.go
More file actions
131 lines (109 loc) · 3.76 KB
/
client.go
File metadata and controls
131 lines (109 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Package queue provides a simple and flexible job queue implementation for Go applications.
package queue
import (
"fmt"
"time"
"github.com/hibiken/asynq"
)
// Client encapsulates an asynq.Client instance with custom error handling and job retention settings.
type Client struct {
asynqClient *asynq.Client
errorHandler ClientErrorHandler
retention time.Duration
logger Logger
}
// ClientConfig defines the configuration options for the Client.
type ClientConfig struct {
ErrorHandler ClientErrorHandler // Custom handler for enqueue errors.
Retention time.Duration // Default retention duration for jobs.
Logger Logger // Logger instance for logging.
}
// ClientErrorHandler provides an interface for handling enqueue errors.
type ClientErrorHandler interface {
HandleError(err error, job *Job)
}
// NewClient initializes a new Client with specified Redis configuration and client options.
func NewClient(redisConfig *RedisConfig, opts ...ClientOption) (*Client, error) {
if redisConfig == nil {
return nil, ErrInvalidRedisConfig
}
if err := redisConfig.Validate(); err != nil {
return nil, fmt.Errorf("invalid redis config: %w", err)
}
asynqClient := asynq.NewClient(redisConfig.ToAsynqRedisOpt())
config := &ClientConfig{
Logger: NewDefaultLogger(), // Default to slog-based logger.
Retention: 0, // No retention by default.
// ErrorHandler is nil by default - no default error handler
}
// Apply client options to configure the instance.
for _, opt := range opts {
opt(config)
}
return &Client{
asynqClient: asynqClient,
errorHandler: config.ErrorHandler, // May be nil
retention: config.Retention,
logger: config.Logger,
}, nil
}
// ClientOption defines a function signature for configuring the Client.
type ClientOption func(*ClientConfig)
// WithClientLogger sets a custom logger for the client.
func WithClientLogger(logger Logger) ClientOption {
return func(c *ClientConfig) {
c.Logger = logger
}
}
// WithClientErrorHandler sets a custom error handler for the client.
func WithClientErrorHandler(handler ClientErrorHandler) ClientOption {
return func(c *ClientConfig) {
c.ErrorHandler = handler
}
}
// WithClientRetention sets a default retention duration for jobs.
func WithClientRetention(retention time.Duration) ClientOption {
return func(c *ClientConfig) {
c.Retention = retention
}
}
// Enqueue wraps the process of creating a job and enqueueing it with the Asynq client.
func (c *Client) Enqueue(jobType string, payload any, opts ...JobOption) (string, error) {
job := NewJob(jobType, payload, opts...)
return c.EnqueueJob(job)
}
// EnqueueJob adds a job to the queue.
func (c *Client) EnqueueJob(job *Job) (string, error) {
task, opts, err := job.ConvertToAsynqTask()
if err != nil {
c.handleJobError(err, job, "failed to convert job to task")
return "", err
}
retention := job.Options.Retention
if retention <= 0 {
retention = c.retention
}
if retention > 0 {
opts = append(opts, asynq.Retention(retention))
}
result, err := c.asynqClient.Enqueue(task, opts...)
if err != nil {
c.handleJobError(err, job, "failed to enqueue job")
return "", fmt.Errorf("%w: %w", ErrEnqueueJob, err)
}
return result.ID, nil
}
// handleJobError logs the error and calls the custom error handler if one is registered.
func (c *Client) handleJobError(err error, job *Job, msg string) {
// Always log
c.logger.Error(fmt.Sprintf("%s: %v, job_id=%s, job_type=%s, fingerprint=%s",
msg, err, job.ID, job.Type, job.Fingerprint))
// Optional: call custom error handler if provided
if c.errorHandler != nil {
c.errorHandler.HandleError(err, job)
}
}
// Stop terminates the client connection, releasing resources.
func (c *Client) Stop() error {
return c.asynqClient.Close()
}