这是indexloc提供的服务,不要输入任何密码
Skip to content

Calculate local and remote time saved #754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5789fc2
Start work on analytics client
Feb 17, 2022
000710d
Reverse event name order
Feb 17, 2022
4310f58
sink -> recorder
Feb 17, 2022
2103196
Add uuid, wire up to api client, add some tests
Feb 17, 2022
e3dc036
Handle logged-out case
Feb 17, 2022
3482cbe
Tests a little less race-y
Feb 17, 2022
8444de4
Refactor team params for api client
Feb 17, 2022
1d90929
Split event and source
Feb 18, 2022
8382d9c
Caps for consistency
Feb 18, 2022
166dfcc
Handle api changes from rebase
Feb 18, 2022
47ca935
Make buffer flushes asynchronous
Feb 18, 2022
2eac35f
Properly label json fields for a CacheEvent, add a test for the api c…
Feb 18, 2022
318ae01
Do sessionID copy on the client side before sending analytics
Feb 19, 2022
676bf7f
Merge branch 'main' into gsoltis/analytics_client
jaredpalmer Feb 23, 2022
a91c16e
Add timeout to closing the analytics client (#773)
Feb 23, 2022
30c6b11
Store local artifact durations (#774)
jaredpalmer Feb 24, 2022
ecb1fc9
Fix error handling and debug logging of remote sink
jaredpalmer Feb 24, 2022
3113f26
Merge branch 'main' into gsoltis/analytics_client
jaredpalmer Feb 24, 2022
984ed2a
Bump ci timeout
jaredpalmer Feb 24, 2022
e6ceed4
Merge branch 'gsoltis/analytics_client' of https://github.com/vercel/…
jaredpalmer Feb 24, 2022
111e204
Fix failing test
jaredpalmer Feb 25, 2022
21149bf
Create more realistic events in client test
jaredpalmer Feb 25, 2022
7e3dad1
Fix test
jaredpalmer Feb 25, 2022
8bb3527
Fix upsteam w/eslint-plugin-react by pinning it
jaredpalmer Feb 25, 2022
ca31229
Merge branch 'main' into gsoltis/analytics_client
jaredpalmer Feb 25, 2022
db63af6
Fix examples
jaredpalmer Feb 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ on:
pull_request:
types: [opened, synchronize]


jobs:
build:
name: build and test
timeout-minutes: 10
timeout-minutes: 15
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand Down
3 changes: 2 additions & 1 deletion cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/gobwas/glob v0.2.3
github.com/google/chrometracing v0.0.0-20210413150014-55fded0163e7
github.com/google/go-cmp v0.5.5 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-gatedio v0.5.0
github.com/hashicorp/go-hclog v1.1.0
Expand All @@ -25,6 +25,7 @@ require (
github.com/mattn/go-isatty v0.0.12
github.com/mitchellh/cli v1.1.2
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1
Expand Down
4 changes: 4 additions & 0 deletions cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -90,6 +92,8 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
Expand Down
175 changes: 175 additions & 0 deletions cli/internal/analytics/analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package analytics

import (
"context"
"sync"
"time"

"github.com/google/uuid"
"github.com/hashicorp/go-hclog"
"github.com/mitchellh/mapstructure"
"github.com/vercel/turborepo/cli/internal/util"
)

type Events = []map[string]interface{}

type EventPayload = interface{}

type Recorder interface {
LogEvent(payload EventPayload)
}

type Client interface {
Recorder
Close()
CloseWithTimeout(timeout time.Duration)
}

type Sink interface {
RecordAnalyticsEvents(events Events) error
}

type nullSink struct{}

func (n *nullSink) RecordAnalyticsEvents(events Events) error {
return nil
}

// NullSink is an analytics sink to use in the event that we don't want to send
// analytics
var NullSink = &nullSink{}

type client struct {
ch chan<- EventPayload
cancel func()

worker *worker
}

type worker struct {
buffer []EventPayload
ch <-chan EventPayload
ctx context.Context
doneSemaphore util.Semaphore
sessionID uuid.UUID
sink Sink
wg sync.WaitGroup
logger hclog.Logger
}

const bufferThreshold = 10
const eventTimeout = 200 * time.Millisecond
const noTimeout = 24 * time.Hour

func newWorker(ctx context.Context, ch <-chan EventPayload, sink Sink, logger hclog.Logger) *worker {
buffer := []EventPayload{}
sessionID := uuid.New()
w := &worker{
buffer: buffer,
ch: ch,
ctx: ctx,
doneSemaphore: util.NewSemaphore(1),
sessionID: sessionID,
sink: sink,
logger: logger,
}
w.doneSemaphore.Acquire()
go w.analyticsClient()
return w
}

func NewClient(parent context.Context, sink Sink, logger hclog.Logger) Client {
ch := make(chan EventPayload)
ctx, cancel := context.WithCancel(parent)
// creates and starts the worker
worker := newWorker(ctx, ch, sink, logger)
s := &client{
ch: ch,
cancel: cancel,
worker: worker,
}
return s
}

func (s *client) LogEvent(event EventPayload) {
s.ch <- event
}

func (s *client) Close() {
s.cancel()
s.worker.Wait()
}

func (s *client) CloseWithTimeout(timeout time.Duration) {
ch := make(chan struct{})
go func() {
s.Close()
close(ch)
}()
select {
case <-ch:
case <-time.After(timeout):
}
}

func (w *worker) Wait() {
w.doneSemaphore.Acquire()
w.wg.Wait()
}

func (w *worker) analyticsClient() {
timeout := time.After(noTimeout)
for {
select {
case e := <-w.ch:
w.buffer = append(w.buffer, e)
if len(w.buffer) == bufferThreshold {
w.flush()
timeout = time.After(noTimeout)
} else {
timeout = time.After(eventTimeout)
}
case <-timeout:
w.flush()
timeout = time.After(noTimeout)
case <-w.ctx.Done():
w.flush()
w.doneSemaphore.Release()
return
}
}
}

func (w *worker) flush() {
if len(w.buffer) > 0 {
w.sendEvents(w.buffer)
w.buffer = []EventPayload{}
}
}

func (w *worker) sendEvents(events []EventPayload) {
w.wg.Add(1)
go func() {
payload, err := addSessionID(w.sessionID.String(), events)
if err != nil {
w.logger.Debug("failed to encode cache usage analytics", "error", err)
}
err = w.sink.RecordAnalyticsEvents(payload)
if err != nil {
w.logger.Debug("failed to record cache usage analytics", "error", err)
}
w.wg.Done()
}()
}

func addSessionID(sessionID string, events []EventPayload) (Events, error) {
eventMaps := []map[string]interface{}{}
err := mapstructure.Decode(events, &eventMaps)
if err != nil {
return nil, err
}
for _, event := range eventMaps {
event["sessionId"] = sessionID
}
return eventMaps, nil
}
Loading