github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/countlog/stats/window.go (about)

     1  package stats
     2  
     3  import (
     4  	"github.com/v2pro/plz/countlog/spi"
     5  	"unsafe"
     6  	"strings"
     7  	"reflect"
     8  	"sync"
     9  	"github.com/v2pro/plz/gls"
    10  	"time"
    11  	"context"
    12  )
    13  
    14  type Window struct {
    15  	collector          Collector
    16  	event              string
    17  	dimensionElemCount int
    18  	shards             [16]windowShard
    19  }
    20  
    21  type windowShard struct {
    22  	MapMonoid
    23  	lock *sync.Mutex
    24  }
    25  
    26  func newWindow(executor Executor, collector Collector, dimensionElemCount int) *Window {
    27  	window := &Window{
    28  		collector:          collector,
    29  		dimensionElemCount: dimensionElemCount,
    30  	}
    31  	for i := 0; i < 16; i++ {
    32  		window.shards[i] = windowShard{
    33  			MapMonoid: MapMonoid{},
    34  			lock:      &sync.Mutex{},
    35  		}
    36  	}
    37  	executor(window.exportEverySecond)
    38  	return window
    39  }
    40  
    41  func (window *Window) exportEverySecond(ctx context.Context) {
    42  	timer := time.NewTimer(time.Second)
    43  	for {
    44  		select {
    45  		case <-timer.C:
    46  			window.Export(time.Now())
    47  		case <-ctx.Done():
    48  			return
    49  		}
    50  	}
    51  }
    52  
    53  func (window *Window) Mutate() (*sync.Mutex, MapMonoid) {
    54  	shardId := gls.GoID() % 16
    55  	shard := window.shards[shardId]
    56  	return shard.lock, shard.MapMonoid
    57  }
    58  
    59  func (window *Window) Export(now time.Time) {
    60  	for i := 0; i < 16; i++ {
    61  		window.exportShard(now, window.shards[i])
    62  	}
    63  }
    64  
    65  func (window *Window) exportShard(now time.Time, shard windowShard) {
    66  	shard.lock.Lock()
    67  	defer shard.lock.Unlock()
    68  	// batch allocate memory to hold dimensions
    69  	space := make([]string, len(shard.MapMonoid)*window.dimensionElemCount)
    70  	for dimensionObj, monoid := range shard.MapMonoid {
    71  		dimension := space[:window.dimensionElemCount]
    72  		space = space[window.dimensionElemCount:]
    73  		slice := &sliceHeader{
    74  			Cap:  window.dimensionElemCount,
    75  			Len:  window.dimensionElemCount,
    76  			Data: (*emptyInterface)(unsafe.Pointer(&dimensionObj)).word,
    77  		}
    78  		copy(dimension, *(*[]string)(unsafe.Pointer(slice)))
    79  		window.collector.Collect(&Point{
    80  			Event:     window.event,
    81  			Timestamp: now,
    82  			Dimension: dimension,
    83  			Value:     monoid.Export(),
    84  		})
    85  	}
    86  }
    87  
    88  type propIdx int
    89  
    90  type dimensionExtractor interface {
    91  	Extract(event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid
    92  }
    93  
    94  func newDimensionExtractor(site *spi.LogSite) (dimensionExtractor, int) {
    95  	var dimensionElems []string
    96  	for i := 0; i < len(site.Sample); i += 2 {
    97  		key := site.Sample[i].(string)
    98  		if key == "dim" {
    99  			dimensionElems = strings.Split(site.Sample[i+1].(string), ",")
   100  		}
   101  	}
   102  	indices := make([]propIdx, 0, len(dimensionElems))
   103  	for i := 0; i < len(site.Sample); i += 2 {
   104  		key := site.Sample[i].(string)
   105  		for _, dimension := range dimensionElems {
   106  			if key == dimension {
   107  				indices = append(indices, propIdx(i))
   108  				indices = append(indices, propIdx(i+1))
   109  			}
   110  		}
   111  	}
   112  	arrayType := reflect.ArrayOf(len(indices), reflect.TypeOf(""))
   113  	arrayObj := reflect.New(arrayType).Elem().Interface()
   114  	sampleInterface := *(*emptyInterface)(unsafe.Pointer(&arrayObj))
   115  	if len(indices) == 0 {
   116  		return &dimensionExtractor0{}, 0
   117  	}
   118  	if len(indices) <= 2 {
   119  		return &dimensionExtractor2{
   120  			sampleInterface: sampleInterface,
   121  			indices:         indices,
   122  		}, len(indices)
   123  	}
   124  	if len(indices) <= 4 {
   125  		return &dimensionExtractor4{
   126  			sampleInterface: sampleInterface,
   127  			indices:         indices,
   128  		}, len(indices)
   129  	}
   130  	if len(indices) <= 8 {
   131  		return &dimensionExtractor8{
   132  			sampleInterface: sampleInterface,
   133  			indices:         indices,
   134  		}, len(indices)
   135  	}
   136  	return &dimensionExtractorAny{
   137  		sampleInterface: sampleInterface,
   138  		indices:         indices,
   139  	}, len(indices)
   140  }
   141  
   142  type dimensionExtractor0 struct {
   143  }
   144  
   145  func (extractor *dimensionExtractor0) Extract(event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid {
   146  	elem := monoid[0]
   147  	if elem == nil {
   148  		elem = createElem()
   149  		monoid[0] = elem
   150  	}
   151  	return elem
   152  }
   153  
   154  type dimensionExtractor2 struct {
   155  	sampleInterface emptyInterface
   156  	indices         []propIdx
   157  }
   158  
   159  func (extractor *dimensionExtractor2) Extract(event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid {
   160  	dimensionArr := [2]string{}
   161  	dimension := dimensionArr[:len(extractor.indices)]
   162  	return extractDimension(extractor.sampleInterface, dimension,
   163  		extractor.indices, event, monoid, createElem)
   164  }
   165  
   166  type dimensionExtractor4 struct {
   167  	sampleInterface emptyInterface
   168  	indices         []propIdx
   169  }
   170  
   171  func (extractor *dimensionExtractor4) Extract(event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid {
   172  	dimensionArr := [4]string{}
   173  	dimension := dimensionArr[:len(extractor.indices)]
   174  	return extractDimension(extractor.sampleInterface, dimension,
   175  		extractor.indices, event, monoid, createElem)
   176  }
   177  
   178  type dimensionExtractor8 struct {
   179  	sampleInterface emptyInterface
   180  	indices         []propIdx
   181  }
   182  
   183  func (extractor *dimensionExtractor8) Extract(event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid {
   184  	dimensionArr := [8]string{}
   185  	dimension := dimensionArr[:len(extractor.indices)]
   186  	return extractDimension(extractor.sampleInterface, dimension,
   187  		extractor.indices, event, monoid, createElem)
   188  }
   189  
   190  type dimensionExtractorAny struct {
   191  	sampleInterface emptyInterface
   192  	indices         []propIdx
   193  }
   194  
   195  func (extractor *dimensionExtractorAny) Extract(event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid {
   196  	dimension := make([]string, len(extractor.indices))
   197  	return extractDimension(extractor.sampleInterface, dimension,
   198  		extractor.indices, event, monoid, createElem)
   199  }
   200  
   201  func extractDimension(
   202  	sampleInterface emptyInterface, dimension []string, indices []propIdx,
   203  	event *spi.Event, monoid MapMonoid, createElem func() Monoid) Monoid {
   204  	for i, idx := range indices {
   205  		dimension[i] = event.Properties[idx].(string)
   206  	}
   207  	dimensionInterface := sampleInterface
   208  	dimensionInterface.word = unsafe.Pointer(&dimension[0])
   209  	dimensionObj := castEmptyInterface(uintptr(unsafe.Pointer(&dimensionInterface)))
   210  	elem := monoid[dimensionObj]
   211  	if elem == nil {
   212  		elem = createElem()
   213  		monoid[copyDimension(dimensionInterface, dimension)] = elem
   214  	}
   215  	return elem
   216  }
   217  
   218  func copyDimension(dimensionInterface emptyInterface, dimension []string) interface{} {
   219  	copied := make([]string, len(dimension))
   220  	for i, elem := range dimension {
   221  		copyOfElem := string(append([]byte(nil), elem...))
   222  		copied[i] = copyOfElem
   223  	}
   224  	dimensionInterface.word = unsafe.Pointer(&copied[0])
   225  	return *(*interface{})(unsafe.Pointer(&dimensionInterface))
   226  }
   227  
   228  func castEmptyInterface(ptr uintptr) interface{} {
   229  	return *(*interface{})(unsafe.Pointer(ptr))
   230  }
   231  
   232  // emptyInterface is the header for an interface{} value.
   233  type emptyInterface struct {
   234  	typ  unsafe.Pointer
   235  	word unsafe.Pointer
   236  }
   237  
   238  type sliceHeader struct {
   239  	Data unsafe.Pointer
   240  	Len  int
   241  	Cap  int
   242  }