chao 10a577c310
feat: Supports launching multiple monolithic instances, load-balanced by nginx, requiring only MongoDB and Redis at a minimum (#3759)
* fix: performance issues with Kafka caused by encapsulating the MQ interface

* fix: admin token in standalone mode

* fix: full id version

* fix: resolve deadlock in cache eviction and improve GetBatch implementation

* refactor: replace LongConn with ClientConn interface and simplify message handling

* refactor: replace LongConn with ClientConn interface and simplify message handling

* fix: seq use $setOnInsert for min_seq in conversation update

* feat: add error code for handled friend requests and improve error handling in friend operations

* refactor(msg): update regex pattern for conversationID to include a trailing colon

* refactor: streamline cache initialization and enhance queue engine configuration

* refactor: streamline cache initialization and enhance queue engine configuration

* feat: add RegisterIP to API config and implement Redis server registration

* feat(redis): implement standalone gateway registration with Redis

* chore: update openimsdk/tools dependency to v0.0.50-alpha.121

* fix: change timer to ticker for improved periodic execution

* refactor: replace Kafka topic configuration with mqbuild constants for improved readability

* feat(redis): implement Redis-based locking mechanism for cron tasks
2026-07-03 10:28:06 +00:00

84 lines
1.9 KiB
Go

package cron
import (
"context"
"fmt"
"os"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
"github.com/openimsdk/tools/log"
)
type EtcdLocker struct {
client *clientv3.Client
instanceID string
}
// NewEtcdLocker creates a new etcd distributed lock
func NewEtcdLocker(client *clientv3.Client) (*EtcdLocker, error) {
hostname, _ := os.Hostname()
pid := os.Getpid()
instanceID := fmt.Sprintf("%s-pid-%d-%d", hostname, pid, time.Now().UnixNano())
locker := &EtcdLocker{
client: client,
instanceID: instanceID,
}
return locker, nil
}
func (e *EtcdLocker) ExecuteWithLock(ctx context.Context, taskName string, task func()) {
session, err := concurrency.NewSession(e.client, concurrency.WithTTL(int(lockLeaseTTL/time.Second)))
if err != nil {
log.ZWarn(ctx, "Failed to create etcd session", err,
"taskName", taskName,
"instanceID", e.instanceID)
return
}
defer session.Close()
lockKey := fmt.Sprintf("openim/crontask/%s", taskName)
mutex := concurrency.NewMutex(session, lockKey)
ctxWithTimeout, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer cancel()
err = mutex.TryLock(ctxWithTimeout)
if err != nil {
// errors.Is(err, concurrency.ErrLocked)
log.ZDebug(ctx, "Task is being executed by another instance, skipping",
"taskName", taskName,
"instanceID", e.instanceID,
"error", err.Error())
return
}
defer func() {
if err := mutex.Unlock(ctx); err != nil {
log.ZWarn(ctx, "Failed to release task lock", err,
"taskName", taskName,
"instanceID", e.instanceID)
} else {
log.ZInfo(ctx, "Successfully released task lock",
"taskName", taskName,
"instanceID", e.instanceID)
}
}()
log.ZInfo(ctx, "Successfully acquired task lock, starting execution",
"taskName", taskName,
"instanceID", e.instanceID,
"sessionID", session.Lease())
task()
log.ZInfo(ctx, "Task execution completed",
"taskName", taskName,
"instanceID", e.instanceID)
}