updates
This commit is contained in:
@@ -140,7 +140,12 @@ func collectEligibleCandidates(nearby []redis.GeoLocation) ([]*milerCandidate, [
|
||||
continue
|
||||
}
|
||||
|
||||
if profile.Availabilitystatus != constants.MilerAvailable {
|
||||
// Carrying an order is not a reason to be skipped — maxActive below
|
||||
// is what decides how much one miler can hold. Testing for Available
|
||||
// here made that cap unreachable: a rider was eligible only while
|
||||
// idle, so activeCount was always 0 and the multi-stop round the cap
|
||||
// was written for could never be built.
|
||||
if !constants.MilerCanTakeWork(profile.Availabilitystatus) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -152,7 +157,7 @@ func collectEligibleCandidates(nearby []redis.GeoLocation) ([]*milerCandidate, [
|
||||
}).
|
||||
Count(&activeCount)
|
||||
|
||||
if activeCount >= maxActive {
|
||||
if activeCount >= maxActiveBookings() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"doormile/constants"
|
||||
@@ -21,9 +24,28 @@ const (
|
||||
retryDelay = 2 * time.Minute
|
||||
geoRadiusKm = 10.0
|
||||
geoMaxCount = 10
|
||||
maxActive = 3
|
||||
|
||||
// defaultMaxActive is how many open stops one miler may hold at once.
|
||||
// Override with MILER_MAX_ACTIVE_BOOKINGS — how many parcels a rider can
|
||||
// realistically run in one round is an operational call, not a constant,
|
||||
// and it differs between a dense city round and an intercity leg.
|
||||
defaultMaxActive = 3
|
||||
)
|
||||
|
||||
// maxActiveBookings reads the per-miler concurrent-stop cap, read per call so
|
||||
// it can be changed without a redeploy. A non-numeric or non-positive value
|
||||
// falls back to the default rather than uncapping the fleet by typo.
|
||||
func maxActiveBookings() int64 {
|
||||
if v := strings.TrimSpace(os.Getenv("MILER_MAX_ACTIVE_BOOKINGS")); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil && n > 0 {
|
||||
return int64(n)
|
||||
}
|
||||
utils.Warn("MILER_MAX_ACTIVE_BOOKINGS is not a positive integer, using the default",
|
||||
"value", v, "default", defaultMaxActive)
|
||||
}
|
||||
return defaultMaxActive
|
||||
}
|
||||
|
||||
type milerCandidate struct {
|
||||
profile models.MilerProfile
|
||||
distanceKm float64
|
||||
|
||||
32
internal/assignment/maxactive_test.go
Normal file
32
internal/assignment/maxactive_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package assignment
|
||||
|
||||
import "testing"
|
||||
|
||||
// The per-miler concurrent-stop cap.
|
||||
//
|
||||
// This is the knob that now decides how much one rider carries, because
|
||||
// eligibility no longer stops at the first order. It is read per call so it
|
||||
// can be changed without a redeploy — and a typo must never uncap the fleet.
|
||||
func TestMaxActiveBookings(t *testing.T) {
|
||||
cases := []struct {
|
||||
env string
|
||||
want int64
|
||||
why string
|
||||
}{
|
||||
{"", defaultMaxActive, "unset falls back to the default"},
|
||||
{"10", 10, "a plain number is honoured"},
|
||||
{" 8 ", 8, "surrounding whitespace is tolerated"},
|
||||
{"1", 1, "one stop at a time is a legitimate policy"},
|
||||
{"0", defaultMaxActive, "zero would assign to nobody — treated as unset, not as a cap"},
|
||||
{"-4", defaultMaxActive, "negative is meaningless here"},
|
||||
{"lots", defaultMaxActive, "a typo must not uncap the fleet"},
|
||||
{"3.5", defaultMaxActive, "not an integer"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Setenv("MILER_MAX_ACTIVE_BOOKINGS", tc.env)
|
||||
if got := maxActiveBookings(); got != tc.want {
|
||||
t.Errorf("MILER_MAX_ACTIVE_BOOKINGS=%q gave %d, want %d (%s)", tc.env, got, tc.want, tc.why)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user