szczanieckiej.git

commit 04a2a6732f642ecc1dfda800ab52680f3ddd11b8

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 {