github.com/wfusion/gofusion@v1.1.14/lock/construct.go (about)

     1  package lock
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"github.com/pkg/errors"
     8  
     9  	"github.com/wfusion/gofusion/common/di"
    10  	"github.com/wfusion/gofusion/common/utils"
    11  	"github.com/wfusion/gofusion/config"
    12  	"github.com/wfusion/gofusion/db"
    13  	"github.com/wfusion/gofusion/redis"
    14  )
    15  
    16  var (
    17  	appInstances map[string]map[string]Lockable
    18  	rwlock       sync.RWMutex
    19  )
    20  
    21  func Construct(ctx context.Context, confs map[string]*Conf, opts ...utils.OptionExtender) func() {
    22  	opt := utils.ApplyOptions[config.InitOption](opts...)
    23  	optU := utils.ApplyOptions[useOption](opts...)
    24  	if opt.AppName == "" {
    25  		opt.AppName = optU.appName
    26  	}
    27  	for name, conf := range confs {
    28  		addInstance(ctx, name, conf, opt)
    29  	}
    30  
    31  	return func() {
    32  		rwlock.Lock()
    33  		defer rwlock.Unlock()
    34  		if appInstances != nil {
    35  			delete(appInstances, opt.AppName)
    36  		}
    37  	}
    38  }
    39  
    40  func addInstance(ctx context.Context, name string, conf *Conf, opt *config.InitOption) {
    41  	rwlock.Lock()
    42  	defer rwlock.Unlock()
    43  	if appInstances == nil {
    44  		appInstances = make(map[string]map[string]Lockable)
    45  	}
    46  	if appInstances[opt.AppName] == nil {
    47  		appInstances[opt.AppName] = make(map[string]Lockable)
    48  	}
    49  
    50  	if _, ok := appInstances[opt.AppName][name]; ok {
    51  		panic(ErrDuplicatedName)
    52  	}
    53  
    54  	switch conf.Type {
    55  	case lockTypeRedisLua:
    56  		redis.Use(ctx, conf.Instance, redis.AppName(opt.AppName)) // check if instance exists
    57  		appInstances[opt.AppName][name] = newRedisLuaLocker(ctx, opt.AppName, conf.Instance)
    58  	case lockTypeRedisNX:
    59  		redis.Use(ctx, conf.Instance, redis.AppName(opt.AppName)) // check if instance exists
    60  		appInstances[opt.AppName][name] = newRedisNXLocker(ctx, opt.AppName, conf.Instance)
    61  	case lockTypeMySQL:
    62  		db.Use(ctx, conf.Instance, db.AppName(opt.AppName)) // check if instance exists
    63  		appInstances[opt.AppName][name] = newMysqlLocker(ctx, opt.AppName, conf.Instance)
    64  	case lockTypeMariaDB:
    65  		db.Use(ctx, conf.Instance, db.AppName(opt.AppName)) // check if instance exists
    66  		appInstances[opt.AppName][name] = newMysqlLocker(ctx, opt.AppName, conf.Instance)
    67  	case lockTypeMongo:
    68  		appInstances[opt.AppName][name] = newMongoLocker(ctx, opt.AppName, conf.Instance, conf.Scheme)
    69  	default:
    70  		panic(ErrUnsupportedLockType)
    71  	}
    72  
    73  	// ioc
    74  	if opt.DI != nil {
    75  		opt.DI.MustProvide(func() Lockable { return Use(name, AppName(opt.AppName)) }, di.Name(name))
    76  		if _, ok := appInstances[opt.AppName][name].(ReentrantLockable); ok {
    77  			opt.DI.MustProvide(
    78  				func() ReentrantLockable { return UseReentrant(ctx, name, AppName(opt.AppName)) },
    79  				di.Name(name),
    80  			)
    81  		}
    82  	}
    83  }
    84  
    85  type useOption struct {
    86  	appName string
    87  }
    88  
    89  func AppName(name string) utils.OptionFunc[useOption] {
    90  	return func(o *useOption) {
    91  		o.appName = name
    92  	}
    93  }
    94  
    95  func Use(name string, opts ...utils.OptionExtender) Lockable {
    96  	opt := utils.ApplyOptions[useOption](opts...)
    97  
    98  	rwlock.RLock()
    99  	defer rwlock.RUnlock()
   100  	instances, ok := appInstances[opt.appName]
   101  	if !ok {
   102  		panic(errors.Errorf("locker instance not found for app: %s", opt.appName))
   103  	}
   104  	instance, ok := instances[name]
   105  	if !ok {
   106  		panic(errors.Errorf("locker instance not found for name: %s", name))
   107  	}
   108  	return instance
   109  }
   110  
   111  func UseReentrant(ctx context.Context, name string, opts ...utils.OptionExtender) ReentrantLockable {
   112  	opt := utils.ApplyOptions[useOption](opts...)
   113  
   114  	rwlock.RLock()
   115  	defer rwlock.RUnlock()
   116  	instances, ok := appInstances[opt.appName]
   117  	if !ok {
   118  		panic(errors.Errorf("reentrant locker instance not found for app: %s", opt.appName))
   119  	}
   120  	instance, ok := instances[name]
   121  	if !ok {
   122  		panic(errors.Errorf("reentrant locker instance not found for name: %s", name))
   123  	}
   124  	lockable, ok := instance.(ReentrantLockable)
   125  	if !ok {
   126  		panic(errors.Errorf("locker instance is not reentrantable: %s", name))
   127  	}
   128  
   129  	return lockable
   130  }
   131  
   132  func init() {
   133  	config.AddComponent(config.ComponentLock, Construct, config.WithFlag(&flagString))
   134  }