Browse Source
Added change.Timeline, added change submissions to channel mutations, made all change submissions spin off a goroutine
1.0
0.2.0
Added change.Timeline, added change submissions to channel mutations, made all change submissions spin off a goroutine
1.0
0.2.0
Gisle Aune
6 years ago
8 changed files with 200 additions and 15 deletions
-
20cmd/rpdata-graphiql/main.go
-
30model/change/change.go
-
118model/change/timeline.go
-
17resolver/channel.go
-
6resolver/chapter.go
-
6resolver/log.go
-
8resolver/post.go
-
10resolver/story.go
@ -0,0 +1,118 @@ |
|||||
|
package change |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
"sync" |
||||
|
|
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
// ErrInvalidIndex is returned when a timeline subscriber tries to get a value
|
||||
|
// with a negative id
|
||||
|
var ErrInvalidIndex = errors.New("change.timeline: Invalid ID") |
||||
|
|
||||
|
// ErrWaitRequired is returned when a timeline subscriber requests an entry that
|
||||
|
// has not yet been pushed
|
||||
|
var ErrWaitRequired = errors.New("change.timeline: Waiting required") |
||||
|
|
||||
|
// ErrExpired is returned when a timeline subscriber requests a change that happened
|
||||
|
// too long ago for it to be in the database.
|
||||
|
var ErrExpired = errors.New("change.timeline: Change has expired") |
||||
|
|
||||
|
type timeline struct { |
||||
|
mutex sync.Mutex |
||||
|
firstIndex int64 |
||||
|
currentID int64 |
||||
|
cache []Change |
||||
|
notifyCh chan struct{} |
||||
|
} |
||||
|
|
||||
|
func (timeline *timeline) Get(ctx context.Context, id int64) (Change, error) { |
||||
|
change, err := timeline.get(id, false) |
||||
|
if err == ErrWaitRequired { |
||||
|
if err := timeline.wait(ctx); err != nil { |
||||
|
return Change{}, err |
||||
|
} |
||||
|
|
||||
|
return timeline.get(id, true) |
||||
|
} else if err == mgo.ErrNotFound { |
||||
|
return Change{}, ErrExpired |
||||
|
} |
||||
|
|
||||
|
return change, nil |
||||
|
} |
||||
|
|
||||
|
func (timeline *timeline) push(change Change) { |
||||
|
timeline.mutex.Lock() |
||||
|
timeline.cache = append(timeline.cache, change) |
||||
|
if len(timeline.cache) > 128 { |
||||
|
copy(timeline.cache, timeline.cache[32:]) |
||||
|
timeline.cache = timeline.cache[:len(timeline.cache)-32] |
||||
|
} |
||||
|
|
||||
|
// Only update the currentID if this is more recent than the existing ones
|
||||
|
if int64(change.ID) > timeline.currentID { |
||||
|
timeline.currentID = int64(change.ID) |
||||
|
} |
||||
|
|
||||
|
if timeline.notifyCh != nil { |
||||
|
close(timeline.notifyCh) |
||||
|
timeline.notifyCh = nil |
||||
|
} |
||||
|
timeline.mutex.Unlock() |
||||
|
} |
||||
|
|
||||
|
func (timeline *timeline) get(id int64, hasWaited bool) (Change, error) { |
||||
|
if id < 0 { |
||||
|
return Change{}, ErrInvalidIndex |
||||
|
} |
||||
|
|
||||
|
timeline.mutex.Lock() |
||||
|
if !hasWaited && id > timeline.currentID { |
||||
|
timeline.mutex.Unlock() |
||||
|
return Change{}, ErrWaitRequired |
||||
|
} |
||||
|
|
||||
|
for _, change := range timeline.cache { |
||||
|
if change.ID == int(id) { |
||||
|
timeline.mutex.Unlock() |
||||
|
return change, nil |
||||
|
} |
||||
|
} |
||||
|
timeline.mutex.Unlock() |
||||
|
|
||||
|
return FindID(int(id)) |
||||
|
} |
||||
|
|
||||
|
func (timeline *timeline) wait(ctx context.Context) error { |
||||
|
timeline.mutex.Lock() |
||||
|
if timeline.notifyCh == nil { |
||||
|
timeline.notifyCh = make(chan struct{}) |
||||
|
} |
||||
|
|
||||
|
ch := timeline.notifyCh |
||||
|
timeline.mutex.Unlock() |
||||
|
|
||||
|
select { |
||||
|
case <-ctx.Done(): |
||||
|
return ctx.Err() |
||||
|
case <-ch: |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Timeline has operations for subscribing to changes in real time
|
||||
|
var Timeline = &timeline{} |
||||
|
|
||||
|
// InitializeTimeline fills in the timeline with existing entries
|
||||
|
func InitializeTimeline() { |
||||
|
changes, err := List() |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
for _, change := range changes { |
||||
|
Timeline.push(change) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue