diff options
Diffstat (limited to '')
-rw-r--r-- | config.go | 29 | ||||
-rw-r--r-- | docs/cca.scfg.example | 10 | ||||
-rw-r--r-- | wsc.go | 12 |
3 files changed, 34 insertions, 17 deletions
@@ -65,11 +65,11 @@ var configWithPointers struct { Expr *int `scfg:"expr"` } `scfg:"auth"` Perf struct { - MessageArgumentsCap *int `scfg:"msg_args_cap"` - MessageBytesCap *int `scfg:"msg_bytes_cap"` - ReadHeaderTimeout *int `scfg:"read_header_timeout"` - CourseUpdateInterval *int `scfg:"course_update_interval"` - PropagateImmediate *bool `scfg:"propagate_immediate"` + MessageArgumentsCap *int `scfg:"msg_args_cap"` + MessageBytesCap *int `scfg:"msg_bytes_cap"` + ReadHeaderTimeout *int `scfg:"read_header_timeout"` + UsemDelayShiftBits *int `scfg:"usem_delay_shift_bits"` + PropagateImmediate *bool `scfg:"propagate_immediate"` } `scfg:"perf"` } @@ -100,11 +100,11 @@ var config struct { Expr int } Perf struct { - MessageArgumentsCap int - MessageBytesCap int - ReadHeaderTimeout int - CourseUpdateInterval int - PropagateImmediate bool + MessageArgumentsCap int + MessageBytesCap int + ReadHeaderTimeout int + UsemDelayShiftBits int + PropagateImmediate bool } `scfg:"perf"` } @@ -287,10 +287,13 @@ func fetchConfig(path string) (retErr error) { } config.Perf.ReadHeaderTimeout = *(configWithPointers.Perf.ReadHeaderTimeout) - if configWithPointers.Perf.CourseUpdateInterval == nil { - return fmt.Errorf("%w: perf.course_update_interval", errMissingConfigValue) + if configWithPointers.Perf.UsemDelayShiftBits == nil { + return fmt.Errorf( + "%w: perf.usem_delay_shift_bits", + errMissingConfigValue, + ) } - config.Perf.CourseUpdateInterval = *(configWithPointers.Perf.CourseUpdateInterval) + config.Perf.UsemDelayShiftBits = *(configWithPointers.Perf.UsemDelayShiftBits) if configWithPointers.Perf.PropagateImmediate == nil { return fmt.Errorf( diff --git a/docs/cca.scfg.example b/docs/cca.scfg.example index 471d6e9..b01fe40 100644 --- a/docs/cca.scfg.example +++ b/docs/cca.scfg.example @@ -83,9 +83,13 @@ perf { # vulnerable to Slow Loris attacks. read_header_timeout 5 - # How long, in milliseconds, do we wait before propagating course - # number update events for conjunction between course and user? - course_update_interval 200 + # The number propagation interval per course per connection is + # ((course count * connection count) >> usem_delay_shift_bits) + # milliseconds. You may configure it here. A smaller value (i.e. longer + # delay) could cause more latency in how the numbers update, but a + # larger value (i.e. shorter delay) could cause too much lock + # contention and degrade the system usability overall. + usem_delay_shift_bits 4 # Should we send a course's member count to a user as soon as they # choose the course? Setting this to true may provide a better @@ -25,6 +25,7 @@ import ( "errors" "fmt" "sync" + "sync/atomic" "time" "github.com/coder/websocket" @@ -35,6 +36,8 @@ type errbytesT struct { bytes *[]byte } +var usemCount int64 + /* * This is more appropriately typed as uint64, but it needs to be cast to int64 * later anyway due to time.Duration, so let's just use int64. @@ -83,6 +86,7 @@ func handleConn( usems := make(map[int]*usemT) func() { + atomic.AddInt64(&usemCount, int64(len(courses))) coursesLock.RLock() defer coursesLock.RUnlock() for courseID, course := range courses { @@ -106,6 +110,7 @@ func handleConn( delete(course.Usems, userID) }() } + atomic.AddInt64(&usemCount, -int64(len(courses))) }() usemParent := make(chan int) @@ -122,7 +127,12 @@ func handleConn( case usemParent <- courseID: } } - time.Sleep(time.Duration(config.Perf.CourseUpdateInterval) * time.Millisecond) + time.Sleep( + time.Duration( + atomic.LoadInt64(&usemCount)>> + config.Perf.UsemDelayShiftBits, + ) * time.Millisecond, + ) } }() } |