github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/dbsync/dbsync.go (about)

     1  package dbsync
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/bingoohuang/gg/pkg/mapp"
    10  	"github.com/bingoohuang/gg/pkg/sqx"
    11  )
    12  
    13  type DbSync struct {
    14  	db     *sql.DB
    15  	table  string
    16  	config *Config
    17  	cache  map[string]string
    18  	stop   chan struct{}
    19  }
    20  
    21  func NewDbSync(db *sql.DB, table string, options ...Option) *DbSync {
    22  	return &DbSync{db: db, table: table, config: createConfig(options), stop: make(chan struct{})}
    23  }
    24  
    25  func WithPk(v string) Option                          { return func(c *Config) { c.pk = v } }
    26  func WithV(v string) Option                           { return func(c *Config) { c.v = v } }
    27  func WithDuration(v string) Option                    { return func(c *Config) { c.duration, _ = time.ParseDuration(v) } }
    28  func WithNotify(f func(e Event, id, v string)) Option { return func(c *Config) { c.notify = f } }
    29  func WithContext(v context.Context) Option            { return func(c *Config) { c.Context = v } }
    30  
    31  func (s *DbSync) Start() {
    32  	go s.loop()
    33  }
    34  
    35  func (s *DbSync) loop() {
    36  	t := time.NewTicker(s.config.duration)
    37  	defer t.Stop()
    38  
    39  	s.cache = make(map[string]string)
    40  	query := s.config.CreateQuery(s.table)
    41  
    42  	s.sync(query)
    43  
    44  	for {
    45  		select {
    46  		case <-t.C:
    47  			s.sync(query)
    48  		case <-s.config.Context.Done():
    49  			return
    50  		case <-s.stop:
    51  			return
    52  		}
    53  	}
    54  }
    55  
    56  type row struct {
    57  	Pk string
    58  	V  string
    59  }
    60  
    61  func (s *DbSync) sync(query string) {
    62  	var rows []row
    63  	err := sqx.NewSQL(query).Query(s.db, &rows)
    64  	if err != nil {
    65  		log.Printf("E! failed to execute query: %s, err: %v", query, err)
    66  		return
    67  	}
    68  
    69  	current := mapp.Clone(s.cache)
    70  
    71  	for _, r := range rows {
    72  		v, ok := s.cache[r.Pk]
    73  		if !ok || v != r.V {
    74  			s.cache[r.Pk] = r.V
    75  
    76  			if !ok { // 不存在
    77  				s.config.notify(EventCreate, r.Pk, r.V)
    78  			} else { // 存在,但是v变更了
    79  				s.config.notify(EventModify, r.Pk, r.V)
    80  			}
    81  		}
    82  
    83  		if ok {
    84  			delete(current, r.Pk)
    85  		}
    86  	}
    87  
    88  	for k, v := range current {
    89  		delete(s.cache, k)
    90  		s.config.notify(EventDelete, k, v)
    91  	}
    92  }
    93  
    94  func (s *DbSync) Stop() {
    95  	s.stop <- struct{}{}
    96  }
    97  
    98  type Option func(*Config)
    99  
   100  type Event int
   101  
   102  const (
   103  	EventCreate Event = iota
   104  	EventDelete
   105  	EventModify
   106  )
   107  
   108  type Config struct {
   109  	context.Context
   110  	v        string
   111  	pk       string
   112  	duration time.Duration
   113  	notify   func(event Event, id, v string)
   114  }
   115  
   116  func (c Config) CreateQuery(t string) string {
   117  	return "select " + c.pk + " as pk," + c.v + " as v from " + t
   118  }
   119  
   120  func createConfig(options []Option) *Config {
   121  	c := &Config{}
   122  
   123  	for _, f := range options {
   124  		f(c)
   125  	}
   126  
   127  	if c.pk == "" {
   128  		c.pk = "id"
   129  	}
   130  	if c.v == "" {
   131  		c.v = "v"
   132  	}
   133  	if c.duration <= 0 {
   134  		c.duration = 3 * time.Second
   135  	}
   136  	if c.notify == nil {
   137  		c.notify = func(e Event, id, v string) {
   138  			log.Printf("event:%s id: %s v:%s,", e, id, v)
   139  		}
   140  	}
   141  	if c.Context == nil {
   142  		c.Context = context.Background()
   143  	}
   144  
   145  	return c
   146  }
   147  
   148  func (e Event) String() string {
   149  	switch e {
   150  	case EventCreate:
   151  		return "EventCreate"
   152  	case EventDelete:
   153  		return "EventDelete"
   154  	case EventModify:
   155  		return "EventModify"
   156  	}
   157  
   158  	return "Unknown"
   159  }