Author: Adam Evyčędo <git@apiote.xyz>
improve error handling in realtime * return structured errors from lua scripts * return errors from gtfs-rt * handle connection refused * break after error that will repeat on next request
traffic/brussels_stib_mivb.go | 30 ++++++++++++++++++++++++------ traffic/realtime.go | 14 ++++++++++++-- traffic/realtime_gtfs.go | 10 ++++++++-- traffic/realtime_lua.go | 22 +++++++++++++++++++++-
diff --git a/traffic/brussels_stib_mivb.go b/traffic/brussels_stib_mivb.go index 05f62f4528788ec01d76b774f71225ea34e6ee1a..10f10fce61b5a42a6996679066e515166f81aa43 100644 --- a/traffic/brussels_stib_mivb.go +++ b/traffic/brussels_stib_mivb.go @@ -65,23 +65,37 @@ function getUpdates(tripID, sequence, stopID) local http = require("http") local json = require("json") + error_struct = { + httpResponseCode=0, + message="", + willNextRequestFail=false + } + response, error_message = http.get("https://stibmivb.opendatasoft.com/api/explore/v2.1/catalog/datasets/waiting-time-rt-production/records", { query="apikey=` + auth.ApiKey + `&where=pointid%20%3D%20%22" .. stopID .. "%22&limit=20", timeout="30s" }) if response == nil then - return "", "while getting updates: " .. error_message + error_struct.message = "while getting updates: " .. error_message + error_json, _ = json.encode(error_struct) + return "", error_json end if response.status_code ~= 200 then - return "", "api returned code " .. response.status_code .. "; " .. response.body + error_struct.message = "api returned code " .. response.status_code .. "; " .. response.body + error_struct.httpResponseCode = response.status_code + error_struct.willNextRequestFail = true + error_json, _ = json.encode(error_struct) + return "", error_json end struct, error_message = json.decode(response.body) if struct == nil then - return "", "while decoding updates: " .. error_message + error_struct.message = "while decoding updates: " .. error_message + error_json, _ = json.encode(error_struct) + return "", error_json end updates = { @@ -92,7 +106,9 @@ for i,entry in ipairs(struct.results) do times, error_message = json.decode(entry.passingtimes) if times == nil then - return "", "while decoding times for stop " .. entry.pointid .. ", line " .. entry.lineid .. ": " .. error_message + error_struct.message = "while decoding times for stop " .. entry.pointid .. ", line " .. entry.lineid .. ": " .. error_message + error_json, _ = json.encode(error_struct) + return "", error_json end for i, time_entry in ipairs(times) do @@ -103,7 +119,7 @@ delay=0, timetableRelationship=1, vehicleStatus={ lineName=entry.lineid, - headsign=time_entry.destination.fr, -- TODO translations + headsign=time_entry.destination.fr } } end @@ -112,7 +128,9 @@ result, error_message = json.encode(updates) if result == nil then - return "", "while encoding result: " .. error_message + error_struct.message = "while encoding result: " .. error_message + error_json, _ = json.encode(error_struct) + return "", error_json end return result, "" diff --git a/traffic/realtime.go b/traffic/realtime.go index 8d40d8d639bfa7c016476befb297cde782aa64d7..27beb6dd9d5bf82d9b04919dac63d8c9ad9aaf80 100644 --- a/traffic/realtime.go +++ b/traffic/realtime.go @@ -1,9 +1,11 @@ package traffic import ( + "errors" "fmt" "log" "os" + "strings" "time" "git.sr.ht/~sircmpwn/go-bare" @@ -84,6 +86,14 @@ IN_TRANSIT DepartureStatus = iota AT_STOP INCOMING ) + +type BlockingError struct { + cause error +} + +func (e BlockingError) Error() string { + return e.cause.Error() +} type Alerts struct { ByRoute map[string][]uint @@ -195,8 +205,8 @@ ) if enrichMethod != nil { updates, areTripsInTimetable, err = enrichMethod(trips[departure.Order.TripOffset].Id, departure.Order.Sequence, stopID, ctx) if err != nil { - if isTimeout(err) { // TODO or any other connection problem, or BreakingError - log.Printf("connection error while enriching departure %s -> %s (%v): %v", departure.LineName, departure.Headsign, departure.Time, err) + if isTimeout(err) || errors.As(err, &BlockingError{}) || strings.Contains(err.Error(), "connection refused") { // TODO or any other connection problem + log.Printf("blocking error while enriching departure %s -> %s (%v): %v", departure.LineName, departure.Headsign, departure.Time, err) enrichMethod = nil continue } else { diff --git a/traffic/realtime_gtfs.go b/traffic/realtime_gtfs.go index 3b743aff0ea66e9c0953feffeb5e42a452cd91f8..3ae1bc3efbe0c532e23f2607f2b2b3fca2636c7a 100644 --- a/traffic/realtime_gtfs.go +++ b/traffic/realtime_gtfs.go @@ -200,10 +200,16 @@ if err != nil { return map[string]Update{}, true, fmt.Errorf("while getting feedInfo: %w", err) } - getGtfsRealtimeMessages(TRIP_UPDATES, ctx.FeedID, feedInfo.RealtimeFeeds) + err = getGtfsRealtimeMessages(TRIP_UPDATES, ctx.FeedID, feedInfo.RealtimeFeeds) + if err != nil { + return map[string]Update{}, true, fmt.Errorf("while getting updates: %w", err) + } if _, ok := feedInfo.RealtimeFeeds[VEHICLE_POSITIONS]; ok { // TODO should be moved to enrichDepartures and conditional (this, or custom API) - getGtfsRealtimeMessages(VEHICLE_POSITIONS, ctx.FeedID, feedInfo.RealtimeFeeds) + err = getGtfsRealtimeMessages(VEHICLE_POSITIONS, ctx.FeedID, feedInfo.RealtimeFeeds) + if err != nil { + return map[string]Update{}, true, fmt.Errorf("while getting vehicles: %w", err) + } } return updates, true, nil diff --git a/traffic/realtime_lua.go b/traffic/realtime_lua.go index ce517e63a4d7ac8a9174e5358129daee46a3cbaa..067c53a93fd5d412ef3cf80c3b5ccfa930f20785 100644 --- a/traffic/realtime_lua.go +++ b/traffic/realtime_lua.go @@ -22,6 +22,16 @@ Updates map[string]Update // TODO Alerts } +type LuaError struct { + HttpResponseCode int + Message string + WillNextRequestFail bool +} + +func (e LuaError) Error() string { + return e.Message +} + func isLuaUpdatesScript(context Context) bool { _, err := os.Stat(getLuaUpdatesPath(context)) return err == nil @@ -42,6 +52,7 @@ } func getLuaRealtimeUpdates(_ string, _ int, stopID string, ctx Context) (map[string]Update, bool, error) { luaUpdates := LuaUpdates{} + luaError := LuaError{} filePath := getLuaUpdatesPath(ctx) now := uint64(time.Now().Unix()) if lastUpdatedLua[ctx.FeedID] == nil { @@ -74,7 +85,16 @@ l.Pop(1) result := l.Get(-1) l.Pop(1) if luaErr.(lua.LString) != "" { - return map[string]Update{}, true, fmt.Errorf("while executing updates function: %s", luaErr.(lua.LString)) + err := json.Unmarshal([]byte(luaErr.(lua.LString)), &luaError) + if err != nil { + return map[string]Update{}, true, fmt.Errorf("while unmarshalling error: %s", luaErr.(lua.LString)) + } + if luaError.WillNextRequestFail || luaError.HttpResponseCode == 429 { + err = BlockingError{luaError} + } else { + err = luaError + } + return map[string]Update{}, true, fmt.Errorf("in updates function: %s", err) } err := json.Unmarshal([]byte(result.(lua.LString)), &luaUpdates) if err != nil {