)]}'
{"cloud/shepherd/equinix/client/client.go":[{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"fb09dc6b8509aa87842e90f5223b94644fe96ef9","unresolved":true,"context_lines":[{"line_number":88,"context_line":""},{"line_number":89,"context_line":"func (e *Client) CreateDevice(r *packngo.DeviceCreateRequest) (*packngo.Device, error) {"},{"line_number":90,"context_line":"\tvar rd *packngo.Device"},{"line_number":91,"context_line":"\terr :\u003d backoff.Retry(func() error {"},{"line_number":92,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":93,"context_line":"\t\td, _, err :\u003d e.cl.Devices.Create(r)"},{"line_number":94,"context_line":"\t\tif err !\u003d nil {"}],"source_content_type":"text/x-go","patch_set":3,"id":"52c18eda_112e3f65","line":91,"range":{"start_line":91,"start_character":8,"end_line":91,"end_character":21},"updated":"2022-12-05 11:33:29.000000000","message":"I\u0027m not sure I like having all these be \u0027automagically\u0027 retried. This can create issues with non-idempotent calls, for example:\n\n1. wrapper.CreateDevice(...) gets called by some business logic\n2. packngo.CreateDevice(...) gets called by the wrapper\n3. CreateDevice gets executed by the service, state mutation occurs\n4. Before packngo.CreateDevice can receive the result of the successful mutation, a network issue occurs which can be visible to the wrapper as a timeout\n5. Backoff/retry kicks in and calls packngo.CreateDevice(...) again\n6. Either another device gets created (inconsistency) or an obscure error about a device already existing gets returned.\n7. wrapper.CreateDevice(...) returns above error to caller\n8. Business logic sees unexpected error or causes inconsistency. This pollutes logs, causes fake alerts and generally causes (possibly runaway) system inconsistency because of the obfuscated real error (a transient network issue happening).\n\nInstead, the business logic should be the one performing retries/backoff, usually at a lower granularity than per-call. This allows it to recover at a higher level, and eg. check whether the device did get created or not before blindly firing the same RPC again.","commit_id":"ba4ea328863a57731fdbc5c3268925afaf7580f3"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"b9f3ebd81709b1fa3658a50a463a1a028d32146d","unresolved":false,"context_lines":[{"line_number":88,"context_line":""},{"line_number":89,"context_line":"func (e *Client) CreateDevice(r *packngo.DeviceCreateRequest) (*packngo.Device, error) {"},{"line_number":90,"context_line":"\tvar rd *packngo.Device"},{"line_number":91,"context_line":"\terr :\u003d backoff.Retry(func() error {"},{"line_number":92,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":93,"context_line":"\t\td, _, err :\u003d e.cl.Devices.Create(r)"},{"line_number":94,"context_line":"\t\tif err !\u003d nil {"}],"source_content_type":"text/x-go","patch_set":3,"id":"90f85c4b_2973e4ea","line":91,"range":{"start_line":91,"start_character":8,"end_line":91,"end_character":21},"in_reply_to":"52c18eda_112e3f65","updated":"2022-12-19 12:08:56.000000000","message":"Done","commit_id":"ba4ea328863a57731fdbc5c3268925afaf7580f3"}],"cloud/shepherd/equinix/wrapngo/wrapn.go":[{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":11,"context_line":"// return nil data when a non-nil error value is returned. An"},{"line_number":12,"context_line":"// os.ErrDeadlineExceeded will be returned after the underlying API calls time"},{"line_number":13,"context_line":"// out beyond the chosen back-off algorithm implementation\u0027s maximum allowed"},{"line_number":14,"context_line":"// retry interval. Other errors, excluding context.Canceled and"},{"line_number":15,"context_line":"// context.DeadlineExceeded, indicate an error originating at Equinix\u0027 API"},{"line_number":16,"context_line":"// endpoint."},{"line_number":17,"context_line":"//"},{"line_number":18,"context_line":"// Packngo wrappers included below may return timeout errors even after the"},{"line_number":19,"context_line":"// wrapped calls succeed in the event server reply could not have been"}],"source_content_type":"text/x-go","patch_set":4,"id":"b58f9dfa_7b00c347","line":16,"range":{"start_line":14,"start_character":19,"end_line":16,"end_character":12},"updated":"2022-12-20 10:26:09.000000000","message":"Wait, how does that happen?","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"a5fb1186802394a6c589488ee114aa862c7810ca","unresolved":false,"context_lines":[{"line_number":11,"context_line":"// return nil data when a non-nil error value is returned. An"},{"line_number":12,"context_line":"// os.ErrDeadlineExceeded will be returned after the underlying API calls time"},{"line_number":13,"context_line":"// out beyond the chosen back-off algorithm implementation\u0027s maximum allowed"},{"line_number":14,"context_line":"// retry interval. Other errors, excluding context.Canceled and"},{"line_number":15,"context_line":"// context.DeadlineExceeded, indicate an error originating at Equinix\u0027 API"},{"line_number":16,"context_line":"// endpoint."},{"line_number":17,"context_line":"//"},{"line_number":18,"context_line":"// Packngo wrappers included below may return timeout errors even after the"},{"line_number":19,"context_line":"// wrapped calls succeed in the event server reply could not have been"}],"source_content_type":"text/x-go","patch_set":4,"id":"9db93b58_063397df","line":16,"range":{"start_line":14,"start_character":19,"end_line":16,"end_character":12},"in_reply_to":"049035a1_25162dd6","updated":"2023-01-18 12:57:37.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":true,"context_lines":[{"line_number":11,"context_line":"// return nil data when a non-nil error value is returned. An"},{"line_number":12,"context_line":"// os.ErrDeadlineExceeded will be returned after the underlying API calls time"},{"line_number":13,"context_line":"// out beyond the chosen back-off algorithm implementation\u0027s maximum allowed"},{"line_number":14,"context_line":"// retry interval. Other errors, excluding context.Canceled and"},{"line_number":15,"context_line":"// context.DeadlineExceeded, indicate an error originating at Equinix\u0027 API"},{"line_number":16,"context_line":"// endpoint."},{"line_number":17,"context_line":"//"},{"line_number":18,"context_line":"// Packngo wrappers included below may return timeout errors even after the"},{"line_number":19,"context_line":"// wrapped calls succeed in the event server reply could not have been"}],"source_content_type":"text/x-go","patch_set":4,"id":"049035a1_25162dd6","line":16,"range":{"start_line":14,"start_character":19,"end_line":16,"end_character":12},"in_reply_to":"b58f9dfa_7b00c347","updated":"2023-01-12 19:39:49.000000000","message":"I\u0027ve added network errors to the list. What else could go wrong here?","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":95,"context_line":"\t}"},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"\treturn \u0026Client{"},{"line_number":98,"context_line":"\t\t// PACKNGO_DEBUG environment variable can be set prior to the below call"},{"line_number":99,"context_line":"\t\t// to enable verbose packngo debug logs."},{"line_number":100,"context_line":"\t\tcl:  packngo.NewClientWithAuth(opts.User, opts.APIKey, nil),"},{"line_number":101,"context_line":"\t\to:   opts,"},{"line_number":102,"context_line":"\t\trlt: time.NewTicker(opts.APIRate),"}],"source_content_type":"text/x-go","patch_set":4,"id":"341a5e45_885df1c6","line":99,"range":{"start_line":98,"start_character":0,"end_line":99,"end_character":42},"updated":"2022-12-20 10:26:09.000000000","message":"That probably should be a comment on New","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":false,"context_lines":[{"line_number":95,"context_line":"\t}"},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"\treturn \u0026Client{"},{"line_number":98,"context_line":"\t\t// PACKNGO_DEBUG environment variable can be set prior to the below call"},{"line_number":99,"context_line":"\t\t// to enable verbose packngo debug logs."},{"line_number":100,"context_line":"\t\tcl:  packngo.NewClientWithAuth(opts.User, opts.APIKey, nil),"},{"line_number":101,"context_line":"\t\to:   opts,"},{"line_number":102,"context_line":"\t\trlt: time.NewTicker(opts.APIRate),"}],"source_content_type":"text/x-go","patch_set":4,"id":"f564cd07_d69b0152","line":99,"range":{"start_line":98,"start_character":0,"end_line":99,"end_character":42},"in_reply_to":"341a5e45_885df1c6","updated":"2023-01-12 19:39:49.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":99,"context_line":"\t\t// to enable verbose packngo debug logs."},{"line_number":100,"context_line":"\t\tcl:  packngo.NewClientWithAuth(opts.User, opts.APIKey, nil),"},{"line_number":101,"context_line":"\t\to:   opts,"},{"line_number":102,"context_line":"\t\trlt: time.NewTicker(opts.APIRate),"},{"line_number":103,"context_line":"\t}"},{"line_number":104,"context_line":"}"},{"line_number":105,"context_line":""}],"source_content_type":"text/x-go","patch_set":4,"id":"847cfd19_c4ba0650","line":102,"range":{"start_line":102,"start_character":2,"end_line":102,"end_character":5},"updated":"2022-12-20 10:26:09.000000000","message":"You should clean this up (ie. provide Client.Close which calls rlt.Stop).","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":false,"context_lines":[{"line_number":99,"context_line":"\t\t// to enable verbose packngo debug logs."},{"line_number":100,"context_line":"\t\tcl:  packngo.NewClientWithAuth(opts.User, opts.APIKey, nil),"},{"line_number":101,"context_line":"\t\to:   opts,"},{"line_number":102,"context_line":"\t\trlt: time.NewTicker(opts.APIRate),"},{"line_number":103,"context_line":"\t}"},{"line_number":104,"context_line":"}"},{"line_number":105,"context_line":""}],"source_content_type":"text/x-go","patch_set":4,"id":"d16a7f5e_44f465ca","line":102,"range":{"start_line":102,"start_character":2,"end_line":102,"end_character":5},"in_reply_to":"847cfd19_c4ba0650","updated":"2023-01-12 19:39:49.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":130,"context_line":"\t\treturn nil"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"\t// I/O errors:"},{"line_number":133,"context_line":"\tcase errors.Is(err, os.ErrDeadlineExceeded):"},{"line_number":134,"context_line":"\t\t// Handle I/O descriptor timeouts:"},{"line_number":135,"context_line":"\t\treturn err"},{"line_number":136,"context_line":"\tcase netErr \u0026\u0026 err.(net.Error).Timeout():"}],"source_content_type":"text/x-go","patch_set":4,"id":"b174c3fa_152cd847","line":133,"range":{"start_line":133,"start_character":21,"end_line":133,"end_character":43},"updated":"2022-12-20 10:26:09.000000000","message":"So what actually throws these errors? Does packngo have some kind of hardcoded timeout which isn\u0027t context-compatible?","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":true,"context_lines":[{"line_number":130,"context_line":"\t\treturn nil"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"\t// I/O errors:"},{"line_number":133,"context_line":"\tcase errors.Is(err, os.ErrDeadlineExceeded):"},{"line_number":134,"context_line":"\t\t// Handle I/O descriptor timeouts:"},{"line_number":135,"context_line":"\t\treturn err"},{"line_number":136,"context_line":"\tcase netErr \u0026\u0026 err.(net.Error).Timeout():"}],"source_content_type":"text/x-go","patch_set":4,"id":"e2366ba0_5b38ed65","line":133,"range":{"start_line":133,"start_character":21,"end_line":133,"end_character":43},"in_reply_to":"b174c3fa_152cd847","updated":"2023-01-12 19:39:49.000000000","message":"net.Http probably? It might be coming from here, although I haven\u0027t traced it: https://cs.opensource.google/go/go/+/master:src/net/http/h2_bundle.go;l\u003d5636?q\u003derrdeadlineexceeded\u0026ss\u003dgo%2Fgo:src%2Fnet%2Fhttp%2F\n\nI just realized packngo permits http.Client specialization through NewClientWithAuth. The HTTP connection timeout can be disabled this way, but I think this implementation is better off left with http.Client defaults.\n\nwrapngo calls currently aren\u0027t specifically being limited with expiring contents.","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"ef4174b6cb42ce8cc7bd223369ff0feccb28fc34","unresolved":false,"context_lines":[{"line_number":130,"context_line":"\t\treturn nil"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"\t// I/O errors:"},{"line_number":133,"context_line":"\tcase errors.Is(err, os.ErrDeadlineExceeded):"},{"line_number":134,"context_line":"\t\t// Handle I/O descriptor timeouts:"},{"line_number":135,"context_line":"\t\treturn err"},{"line_number":136,"context_line":"\tcase netErr \u0026\u0026 err.(net.Error).Timeout():"}],"source_content_type":"text/x-go","patch_set":4,"id":"747b4e7f_57b60fbf","line":133,"range":{"start_line":133,"start_character":21,"end_line":133,"end_character":43},"in_reply_to":"e2366ba0_5b38ed65","updated":"2023-01-12 19:43:36.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":166,"context_line":"\te.muBackOff.Lock()"},{"line_number":167,"context_line":"\tdefer e.muBackOff.Unlock()"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":"\treturn backoff.Retry(func() error {"},{"line_number":170,"context_line":"\t\tif err :\u003d ctx.Err(); err !\u003d nil {"},{"line_number":171,"context_line":"\t\t\treturn wrapErrors(err)"},{"line_number":172,"context_line":"\t\t}"}],"source_content_type":"text/x-go","patch_set":4,"id":"4e2430f0_d580d4d3","line":169,"range":{"start_line":169,"start_character":8,"end_line":169,"end_character":21},"updated":"2022-12-20 10:26:09.000000000","message":"Make the backoff aware of the ctx here (backoff.WithContext).","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":false,"context_lines":[{"line_number":166,"context_line":"\te.muBackOff.Lock()"},{"line_number":167,"context_line":"\tdefer e.muBackOff.Unlock()"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":"\treturn backoff.Retry(func() error {"},{"line_number":170,"context_line":"\t\tif err :\u003d ctx.Err(); err !\u003d nil {"},{"line_number":171,"context_line":"\t\t\treturn wrapErrors(err)"},{"line_number":172,"context_line":"\t\t}"}],"source_content_type":"text/x-go","patch_set":4,"id":"cf376956_934d59a0","line":169,"range":{"start_line":169,"start_character":8,"end_line":169,"end_character":21},"in_reply_to":"4e2430f0_d580d4d3","updated":"2023-01-12 19:39:49.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":172,"context_line":"\t\t}"},{"line_number":173,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":174,"context_line":"\t\treturn wrapErrors(cf())"},{"line_number":175,"context_line":"\t}, e.o.BackOff)"},{"line_number":176,"context_line":"}"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"// createResource, like Client.call, retries cf until it returns either a nil"}],"source_content_type":"text/x-go","patch_set":4,"id":"5aac1eca_92e9af66","line":175,"range":{"start_line":175,"start_character":4,"end_line":175,"end_character":15},"updated":"2022-12-20 10:26:09.000000000","message":"Why not re-create the backoff here every time? Do you actually want to keep backoff state (ie. current backoff value / retry count) between calls?","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":172,"context_line":"\t\t}"},{"line_number":173,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":174,"context_line":"\t\treturn wrapErrors(cf())"},{"line_number":175,"context_line":"\t}, e.o.BackOff)"},{"line_number":176,"context_line":"}"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"// createResource, like Client.call, retries cf until it returns either a nil"}],"source_content_type":"text/x-go","patch_set":4,"id":"d2dab382_e3816cd7","line":175,"range":{"start_line":175,"start_character":4,"end_line":175,"end_character":15},"updated":"2022-12-20 10:26:09.000000000","message":"Why not recreate the backoff here every time? Do you actually want to keep state between calls?\n\nAlso, if you make the backoff context-aware (backoff.WithContext), it will automatically treat context errors as permanent erors, and you don\u0027t have to do at least some of the translation.","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"ef4174b6cb42ce8cc7bd223369ff0feccb28fc34","unresolved":false,"context_lines":[{"line_number":172,"context_line":"\t\t}"},{"line_number":173,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":174,"context_line":"\t\treturn wrapErrors(cf())"},{"line_number":175,"context_line":"\t}, e.o.BackOff)"},{"line_number":176,"context_line":"}"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"// createResource, like Client.call, retries cf until it returns either a nil"}],"source_content_type":"text/x-go","patch_set":4,"id":"be817381_ad6abc6a","line":175,"range":{"start_line":175,"start_character":4,"end_line":175,"end_character":15},"in_reply_to":"1451c344_907aa25f","updated":"2023-01-12 19:43:36.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":true,"context_lines":[{"line_number":172,"context_line":"\t\t}"},{"line_number":173,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":174,"context_line":"\t\treturn wrapErrors(cf())"},{"line_number":175,"context_line":"\t}, e.o.BackOff)"},{"line_number":176,"context_line":"}"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"// createResource, like Client.call, retries cf until it returns either a nil"}],"source_content_type":"text/x-go","patch_set":4,"id":"1451c344_907aa25f","line":175,"range":{"start_line":175,"start_character":4,"end_line":175,"end_character":15},"in_reply_to":"5aac1eca_92e9af66","updated":"2023-01-12 19:39:49.000000000","message":"Backoff state resets inbetween calls according to the package docs. Keeping it there made it easier to specialize it through Opts.","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":false,"context_lines":[{"line_number":172,"context_line":"\t\t}"},{"line_number":173,"context_line":"\t\t\u003c-e.rlt.C"},{"line_number":174,"context_line":"\t\treturn wrapErrors(cf())"},{"line_number":175,"context_line":"\t}, e.o.BackOff)"},{"line_number":176,"context_line":"}"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"// createResource, like Client.call, retries cf until it returns either a nil"}],"source_content_type":"text/x-go","patch_set":4,"id":"7206b4ca_190890bd","line":175,"range":{"start_line":175,"start_character":4,"end_line":175,"end_character":15},"in_reply_to":"d2dab382_e3816cd7","updated":"2023-01-12 19:39:49.000000000","message":"Done","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"92ba31f6053904b43585670574f3e2e3acaf35a5","unresolved":true,"context_lines":[{"line_number":230,"context_line":"\t\t\t\t\t// Resource has not been created. Retry the resource creation loop."},{"line_number":231,"context_line":"\t\t\t\t\tbreak RECOVER"},{"line_number":232,"context_line":""},{"line_number":233,"context_line":"\t\t\t\tcase errors.Is(cerr, context.DeadlineExceeded):"},{"line_number":234,"context_line":"\t\t\t\t\tfallthrough"},{"line_number":235,"context_line":"\t\t\t\tcase errors.Is(cerr, context.Canceled):"},{"line_number":236,"context_line":"\t\t\t\t\treturn rerr"},{"line_number":237,"context_line":""},{"line_number":238,"context_line":"\t\t\t\tdefault:"},{"line_number":239,"context_line":"\t\t\t\t\t// Got a permanent api error."},{"line_number":240,"context_line":"\t\t\t\t\treturn fmt.Errorf(\"received an API error while in recovery: %w\", rerr)"}],"source_content_type":"text/x-go","patch_set":4,"id":"eda9da80_9bf5d99e","line":237,"range":{"start_line":233,"start_character":4,"end_line":237,"end_character":0},"updated":"2022-12-20 10:26:09.000000000","message":"That should be probably cerr.\n\nAnd you can also do `case errors.Is(rerr, ctx.Err())` to cover both cases.","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"edf9e8baabdc73904c46263a8bb6d4e15d5332b0","unresolved":false,"context_lines":[{"line_number":230,"context_line":"\t\t\t\t\t// Resource has not been created. Retry the resource creation loop."},{"line_number":231,"context_line":"\t\t\t\t\tbreak RECOVER"},{"line_number":232,"context_line":""},{"line_number":233,"context_line":"\t\t\t\tcase errors.Is(cerr, context.DeadlineExceeded):"},{"line_number":234,"context_line":"\t\t\t\t\tfallthrough"},{"line_number":235,"context_line":"\t\t\t\tcase errors.Is(cerr, context.Canceled):"},{"line_number":236,"context_line":"\t\t\t\t\treturn rerr"},{"line_number":237,"context_line":""},{"line_number":238,"context_line":"\t\t\t\tdefault:"},{"line_number":239,"context_line":"\t\t\t\t\t// Got a permanent api error."},{"line_number":240,"context_line":"\t\t\t\t\treturn fmt.Errorf(\"received an API error while in recovery: %w\", rerr)"}],"source_content_type":"text/x-go","patch_set":4,"id":"3fba168e_e92d8130","line":237,"range":{"start_line":233,"start_character":4,"end_line":237,"end_character":0},"in_reply_to":"eda9da80_9bf5d99e","updated":"2023-01-12 19:39:49.000000000","message":"\u0027rerr\u0027 seems to be correct while \u0027cerr\u0027 isn\u0027t: see first line of the loop.","commit_id":"8293104b7ca1969be821578a95ada935eaaf4ee2"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"06f580ea696266416af398e72511f40ada6b3b0a","unresolved":true,"context_lines":[{"line_number":75,"context_line":"\to   *Opts"},{"line_number":76,"context_line":"\trlt *time.Ticker"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"\t// muBackOff protects the chosen o.BackOff implementation, since these are"},{"line_number":79,"context_line":"\t// not guaranteed to be concurrent."},{"line_number":80,"context_line":"\tmuBackOff sync.Mutex"},{"line_number":81,"context_line":"}"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"var ("}],"source_content_type":"text/x-go","patch_set":5,"id":"e9338cbf_ce0e051c","line":80,"range":{"start_line":78,"start_character":1,"end_line":80,"end_character":21},"updated":"2023-01-17 12:19:44.000000000","message":"This isn\u0027t true anymore, right? Instead this mutex is just being used to serialize all API calls now.","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"a5fb1186802394a6c589488ee114aa862c7810ca","unresolved":true,"context_lines":[{"line_number":75,"context_line":"\to   *Opts"},{"line_number":76,"context_line":"\trlt *time.Ticker"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"\t// muBackOff protects the chosen o.BackOff implementation, since these are"},{"line_number":79,"context_line":"\t// not guaranteed to be concurrent."},{"line_number":80,"context_line":"\tmuBackOff sync.Mutex"},{"line_number":81,"context_line":"}"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"var ("}],"source_content_type":"text/x-go","patch_set":5,"id":"d90a9800_1a300315","line":80,"range":{"start_line":78,"start_character":1,"end_line":80,"end_character":21},"in_reply_to":"e9338cbf_ce0e051c","updated":"2023-01-18 12:57:37.000000000","message":"This remains true, as default implementations exposed by the backoff package, like the one used here, are as of now *not* concurrent.\n\nHowever it is correct that muBackOff effectively ends up serializing Client.call invocations.","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"06f580ea696266416af398e72511f40ada6b3b0a","unresolved":true,"context_lines":[{"line_number":172,"context_line":"// indicate the underlying API call is timing out. Other kinds of error values"},{"line_number":173,"context_line":"// will be interpreted as permanent API errors."},{"line_number":174,"context_line":"func (e *Client) call(ctx context.Context, cf func() error) error {"},{"line_number":175,"context_line":"\te.muBackOff.Lock()"},{"line_number":176,"context_line":"\tdefer e.muBackOff.Unlock()"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"\tbc :\u003d backoff.WithContext(e.o.BackOff(), ctx)"},{"line_number":179,"context_line":"\top :\u003d func() error {"}],"source_content_type":"text/x-go","patch_set":5,"id":"bd846ee4_fe387362","line":176,"range":{"start_line":175,"start_character":0,"end_line":176,"end_character":27},"updated":"2023-01-17 12:19:44.000000000","message":"A better implementation of concurrent call serialization is to use a channel-as-a-1-semaphore instead of a mutex.\n\nFor example, to make a N-semaphore:\n\n```\nsemC :\u003d make(chan struct{}, N)\n```\n  \nThen, to up the semaphore:\n\n```\nsemC \u003c- struct{}{}\n```\n  \nAnd to down the semaphore:\n\n```\n\u003c-semC\n```\n  \nOf course, in this case, N \u003d\u003d 1, so effectively this is a mutex. However, this has one benefit over using a plain sync.Mutex: you can make the semaphore up function context-aware.\n\n```\nselect {\n   case semC \u003c- struct{}{}:\n   case \u003c-ctx.Done():\n      return ctx.Err()\n}\n```","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"e4684e02681b917223d859ef3abaea55dec0b370","unresolved":true,"context_lines":[{"line_number":172,"context_line":"// indicate the underlying API call is timing out. Other kinds of error values"},{"line_number":173,"context_line":"// will be interpreted as permanent API errors."},{"line_number":174,"context_line":"func (e *Client) call(ctx context.Context, cf func() error) error {"},{"line_number":175,"context_line":"\te.muBackOff.Lock()"},{"line_number":176,"context_line":"\tdefer e.muBackOff.Unlock()"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"\tbc :\u003d backoff.WithContext(e.o.BackOff(), ctx)"},{"line_number":179,"context_line":"\top :\u003d func() error {"}],"source_content_type":"text/x-go","patch_set":5,"id":"fff6cd7a_1e707b15","line":176,"range":{"start_line":175,"start_character":0,"end_line":176,"end_character":27},"in_reply_to":"498f9da6_3d80bc0d","updated":"2023-01-23 12:59:35.000000000","message":"backoff.WithContext would fail immediately given an expired context, wouldn\u0027t it?","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"a5fb1186802394a6c589488ee114aa862c7810ca","unresolved":true,"context_lines":[{"line_number":172,"context_line":"// indicate the underlying API call is timing out. Other kinds of error values"},{"line_number":173,"context_line":"// will be interpreted as permanent API errors."},{"line_number":174,"context_line":"func (e *Client) call(ctx context.Context, cf func() error) error {"},{"line_number":175,"context_line":"\te.muBackOff.Lock()"},{"line_number":176,"context_line":"\tdefer e.muBackOff.Unlock()"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"\tbc :\u003d backoff.WithContext(e.o.BackOff(), ctx)"},{"line_number":179,"context_line":"\top :\u003d func() error {"}],"source_content_type":"text/x-go","patch_set":5,"id":"cfaeff36_fc8b02f7","line":176,"range":{"start_line":175,"start_character":0,"end_line":176,"end_character":27},"in_reply_to":"bd846ee4_fe387362","updated":"2023-01-18 12:57:37.000000000","message":"The semaphore/mutex doesn\u0027t have to be function context aware, since backoff.WithContext will exit as soon as the context is canceled. In my opinion this is not worth revisiting.","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"c5563ff3c34b434356b581298239c08a87b1a690","unresolved":true,"context_lines":[{"line_number":172,"context_line":"// indicate the underlying API call is timing out. Other kinds of error values"},{"line_number":173,"context_line":"// will be interpreted as permanent API errors."},{"line_number":174,"context_line":"func (e *Client) call(ctx context.Context, cf func() error) error {"},{"line_number":175,"context_line":"\te.muBackOff.Lock()"},{"line_number":176,"context_line":"\tdefer e.muBackOff.Unlock()"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"\tbc :\u003d backoff.WithContext(e.o.BackOff(), ctx)"},{"line_number":179,"context_line":"\top :\u003d func() error {"}],"source_content_type":"text/x-go","patch_set":5,"id":"498f9da6_3d80bc0d","line":176,"range":{"start_line":175,"start_character":0,"end_line":176,"end_character":27},"in_reply_to":"cfaeff36_fc8b02f7","updated":"2023-01-23 12:38:09.000000000","message":"Imagine the following series of operations:\n\n```\ncall(ctx1, f1)\ncall(ctx2, f2)\ncall(ctx3, f3)\n```\n\nThe first call blocks for some reason. Long-standing operation, whatever. But its context is not expired, so it continues to block and hold up the line.\n\nIn the meantime, ctx3 expires. But the call is stuck waiting on a mutex.\n\nAfterwards, the first call finally ends. Mutex contention means that whichever function may pick it up next.\n\nIn this case, let\u0027s say the second call pick it up. It starts executing, and it blocks. Meanwhile, the third call is _still_ blocked, even though its context expired a long time ago.\n\nBeing able to acquire a mutex but cancel in case a context expires prevents this from happening, and respects the contract of context cancellation allowing for an exit as soon as possible.","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000002,"name":"Serge Bazanski","display_name":"Serge","email":"serge@monogon.tech","username":"serge","avatars":[{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/52c41428b6369f2c02b9717425216f7d.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"06f580ea696266416af398e72511f40ada6b3b0a","unresolved":true,"context_lines":[{"line_number":199,"context_line":"// being returned will cause createResource to retry cf. Other kinds of error"},{"line_number":200,"context_line":"// values will be interpreted as permanent API errors. rf timeouts can\u0027t be"},{"line_number":201,"context_line":"// recovered from."},{"line_number":202,"context_line":"func (e *Client) createResource(ctx context.Context, cf, rf func() error) error {"},{"line_number":203,"context_line":"\t// Stay in the following retry/recovery loop until either the device is"},{"line_number":204,"context_line":"\t// created, or ctx is canceled:"},{"line_number":205,"context_line":"\tfor {"}],"source_content_type":"text/x-go","patch_set":5,"id":"bff0ce31_8bea2586","line":202,"range":{"start_line":202,"start_character":17,"end_line":202,"end_character":31},"updated":"2023-01-17 12:19:44.000000000","message":"I think this can be expressed much simpler (pseudocode follows):\n\n```\nin backoff(with ctx):\n    exists, err \u003d exists(target_resource)\n    if err !\u003d nil:\n        retry\n    if exists:\n        return okay\n        \n    err \u003d create(target_resource)\n    if err \u003d\u003d nil || err \u003d\u003d already_exists:\n        return okay\n    retry\n```\n\nThis seems much simpler to me, no? We could then get rid off the weird behaviour of os.ErrDeadlineExceeded, and just treat it as any other transient error that would get processed by the backoff mechanism.","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"},{"author":{"_account_id":1000010,"name":"Mateusz Zalega","display_name":"msgctl","email":"mateusz@monogon.tech","username":"mateusz","avatars":[{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32","height":32},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56","height":56},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100","height":100},{"url":"https://www.gravatar.com/avatar/30cae8ca0782f23ce0a60ac80fda3dd9.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120","height":120}]},"change_message_id":"a5fb1186802394a6c589488ee114aa862c7810ca","unresolved":true,"context_lines":[{"line_number":199,"context_line":"// being returned will cause createResource to retry cf. Other kinds of error"},{"line_number":200,"context_line":"// values will be interpreted as permanent API errors. rf timeouts can\u0027t be"},{"line_number":201,"context_line":"// recovered from."},{"line_number":202,"context_line":"func (e *Client) createResource(ctx context.Context, cf, rf func() error) error {"},{"line_number":203,"context_line":"\t// Stay in the following retry/recovery loop until either the device is"},{"line_number":204,"context_line":"\t// created, or ctx is canceled:"},{"line_number":205,"context_line":"\tfor {"}],"source_content_type":"text/x-go","patch_set":5,"id":"880d00c5_f9c12dd3","line":202,"range":{"start_line":202,"start_character":17,"end_line":202,"end_character":31},"in_reply_to":"bff0ce31_8bea2586","updated":"2023-01-18 12:57:37.000000000","message":"I\u0027m OK with simpler equivalents. There are at least three functional differences I noticed in this pseudocode:\n1) API errors won\u0027t get wrapped, assuming that\u0027s something wrapngo consumers would appreciate.\n2) It doesn\u0027t ever produce errors if created resources already exist. This is OK as long as this fact is documented.\n3) Checking for every possible error in these switch statements makes the returned error surface clearer to the user.","commit_id":"5408af5ae30b49abd802068c23961a62b1821086"}]}
