feat: fallback rpc endpoint
This commit is contained in:
parent
f77bff6d25
commit
60e1978bb5
|
@ -48,6 +48,7 @@
|
|||
// 1676368933_keypairs_to_keycards.up.sql (639B)
|
||||
// 1676951398_add_currency_format_cache.up.sql (291B)
|
||||
// 1676968196_keycards_add_clock_column.up.sql (73B)
|
||||
// 1676968197_add_fallback_rpc_to_networks.up.sql (112B)
|
||||
// doc.go (74B)
|
||||
|
||||
package migrations
|
||||
|
@ -132,7 +133,7 @@ func _1640111208_dummyUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1640111208_dummy.up.sql", size: 258, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1640111208_dummy.up.sql", size: 258, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xf0, 0xae, 0x20, 0x6e, 0x75, 0xd1, 0x36, 0x14, 0xf2, 0x40, 0xe5, 0xd6, 0x7a, 0xc4, 0xa5, 0x72, 0xaa, 0xb5, 0x4d, 0x71, 0x97, 0xb8, 0xe8, 0x95, 0x22, 0x95, 0xa2, 0xac, 0xaf, 0x48, 0x58}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -152,7 +153,7 @@ func _1642666031_add_removed_clock_to_bookmarksUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1642666031_add_removed_clock_to_bookmarks.up.sql", size: 117, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1642666031_add_removed_clock_to_bookmarks.up.sql", size: 117, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x84, 0x4e, 0x38, 0x99, 0x7a, 0xc, 0x90, 0x13, 0xec, 0xfe, 0x2f, 0x55, 0xff, 0xb7, 0xb6, 0xaa, 0x96, 0xc6, 0x92, 0x79, 0xcc, 0xee, 0x4e, 0x99, 0x53, 0xfe, 0x1c, 0xbb, 0x32, 0x2, 0xa4, 0x27}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ func _1643644541_gif_api_key_settingUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1643644541_gif_api_key_setting.up.sql", size: 108, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1643644541_gif_api_key_setting.up.sql", size: 108, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1b, 0x94, 0x28, 0xfb, 0x66, 0xd1, 0x7c, 0xb8, 0x89, 0xe2, 0xb4, 0x71, 0x65, 0x24, 0x57, 0x22, 0x95, 0x38, 0x97, 0x3, 0x9b, 0xc6, 0xa4, 0x41, 0x7b, 0xba, 0xf7, 0xdb, 0x70, 0xf7, 0x20, 0x3a}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -192,7 +193,7 @@ func _1644188994_recent_stickersUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1644188994_recent_stickers.up.sql", size: 79, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1644188994_recent_stickers.up.sql", size: 79, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1e, 0xad, 0xaa, 0x30, 0xbf, 0x4, 0x7, 0xf8, 0xc3, 0x3, 0xb8, 0x97, 0x23, 0x2b, 0xbd, 0x1c, 0x60, 0x69, 0xb0, 0x42, 0x5e, 0x6b, 0xd, 0xa7, 0xa3, 0x6b, 0x2e, 0xdc, 0x70, 0x13, 0x72, 0x7}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -212,7 +213,7 @@ func _1646659233_add_address_to_dapp_permisssionUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1646659233_add_address_to_dapp_permisssion.up.sql", size: 700, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1646659233_add_address_to_dapp_permisssion.up.sql", size: 700, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xed, 0xb0, 0x35, 0xcc, 0x2e, 0x16, 0xe6, 0x15, 0x86, 0x2c, 0x37, 0x80, 0xae, 0xa3, 0xc5, 0x31, 0x78, 0x5, 0x9d, 0xcd, 0x7b, 0xeb, 0x5f, 0xf2, 0xb3, 0x74, 0x72, 0xdf, 0xcf, 0x88, 0xb, 0x40}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -232,7 +233,7 @@ func _1646841105_add_emoji_accountUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1646841105_add_emoji_account.up.sql", size: 96, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1646841105_add_emoji_account.up.sql", size: 96, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe6, 0x77, 0x29, 0x95, 0x18, 0x64, 0x82, 0x63, 0xe7, 0xaf, 0x6c, 0xa9, 0x15, 0x7d, 0x46, 0xa6, 0xbc, 0xdf, 0xa7, 0xd, 0x2b, 0xd2, 0x2d, 0x97, 0x4d, 0xa, 0x6b, 0xd, 0x6e, 0x90, 0x42, 0x5c}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -252,7 +253,7 @@ func _1647278782_display_nameUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1647278782_display_name.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1647278782_display_name.up.sql", size: 110, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xa1, 0x1f, 0x3e, 0x61, 0x65, 0x8d, 0xff, 0xee, 0xde, 0xc5, 0x91, 0xd9, 0x5c, 0xb5, 0xe2, 0xf0, 0xb7, 0xe7, 0x5c, 0x5c, 0x16, 0x25, 0x89, 0xee, 0x78, 0x12, 0xea, 0x3e, 0x48, 0x41, 0xa6}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -272,7 +273,7 @@ func _1647862838_reset_last_backupUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1647862838_reset_last_backup.up.sql", size: 37, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1647862838_reset_last_backup.up.sql", size: 37, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x21, 0xe3, 0xd5, 0xf6, 0x5f, 0xfe, 0x65, 0xfa, 0x1d, 0x88, 0xf8, 0x5f, 0x24, 0x71, 0x34, 0x68, 0x96, 0x2a, 0x60, 0x87, 0x15, 0x82, 0x4d, 0x8a, 0x59, 0x3d, 0x1f, 0xd8, 0x56, 0xd4, 0xfb, 0xda}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -292,7 +293,7 @@ func _1647871652_add_settings_sync_clock_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1647871652_add_settings_sync_clock_table.up.sql", size: 1044, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1647871652_add_settings_sync_clock_table.up.sql", size: 1044, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd8, 0x58, 0xec, 0x85, 0x90, 0xfa, 0x30, 0x98, 0x98, 0x9a, 0xa6, 0xa8, 0x96, 0x2b, 0x38, 0x93, 0xf3, 0xae, 0x46, 0x74, 0xa4, 0x41, 0x62, 0x9b, 0x2, 0x86, 0xbf, 0xe5, 0x2a, 0xce, 0xe2, 0xc0}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -312,7 +313,7 @@ func _1647880168_add_torrent_configUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1647880168_add_torrent_config.up.sql", size: 211, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1647880168_add_torrent_config.up.sql", size: 211, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0x92, 0x22, 0x37, 0x96, 0xf3, 0xb5, 0x5b, 0x27, 0xd0, 0x7d, 0x43, 0x5, 0x4e, 0x9d, 0xe2, 0x49, 0xbe, 0x86, 0x31, 0xa1, 0x89, 0xff, 0xd6, 0x51, 0xe0, 0x9c, 0xb, 0xda, 0xfc, 0xf2, 0x93}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -332,7 +333,7 @@ func _1647882837_add_communities_settings_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1647882837_add_communities_settings_table.up.sql", size: 206, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1647882837_add_communities_settings_table.up.sql", size: 206, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xbd, 0x87, 0x78, 0x99, 0xd9, 0x5d, 0xbd, 0xf7, 0x57, 0x9c, 0xca, 0x97, 0xbd, 0xb3, 0xe9, 0xb5, 0x89, 0x31, 0x3f, 0xf6, 0x5c, 0x13, 0xb, 0xc3, 0x54, 0x93, 0x18, 0x40, 0x7, 0x82, 0xfe, 0x7e}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -352,7 +353,7 @@ func _1647956635_add_waku_messages_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1647956635_add_waku_messages_table.up.sql", size: 266, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1647956635_add_waku_messages_table.up.sql", size: 266, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xe, 0xe1, 0xdc, 0xda, 0x2e, 0x89, 0x8d, 0xdc, 0x2a, 0x1c, 0x13, 0xa1, 0xfc, 0xfe, 0xf, 0xb2, 0xb9, 0x85, 0xc8, 0x45, 0xd6, 0xd1, 0x7, 0x5c, 0xa3, 0x8, 0x47, 0x44, 0x6d, 0x96, 0xe0}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -372,7 +373,7 @@ func _1648554928_network_testUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1648554928_network_test.up.sql", size: 132, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1648554928_network_test.up.sql", size: 132, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9a, 0xc5, 0x7f, 0x87, 0xf3, 0x2c, 0xf7, 0xbb, 0xd3, 0x3a, 0x4e, 0x76, 0x88, 0xca, 0xaf, 0x73, 0xce, 0x8f, 0xa1, 0xf6, 0x3d, 0x4d, 0xed, 0x6f, 0x49, 0xf2, 0xfe, 0x56, 0x2a, 0x60, 0x68, 0xca}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -392,7 +393,7 @@ func _1649174829_add_visitble_tokenUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1649174829_add_visitble_token.up.sql", size: 84, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1649174829_add_visitble_token.up.sql", size: 84, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa3, 0x22, 0xc0, 0x2b, 0x3f, 0x4f, 0x3d, 0x5e, 0x4c, 0x68, 0x7c, 0xd0, 0x15, 0x36, 0x9f, 0xec, 0xa1, 0x2a, 0x7b, 0xb4, 0xe3, 0xc6, 0xc9, 0xb4, 0x81, 0x50, 0x4a, 0x11, 0x3b, 0x35, 0x7, 0xcf}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -412,7 +413,7 @@ func _1649882262_add_derived_from_accountsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1649882262_add_derived_from_accounts.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1649882262_add_derived_from_accounts.up.sql", size: 110, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x11, 0xb9, 0x44, 0x4d, 0x85, 0x8d, 0x7f, 0xb4, 0xae, 0x4f, 0x5c, 0x66, 0x64, 0xb6, 0xe2, 0xe, 0x3d, 0xad, 0x9d, 0x8, 0x4f, 0xab, 0x6e, 0xa8, 0x7d, 0x76, 0x3, 0xad, 0x96, 0x1, 0xee, 0x5c}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -432,7 +433,7 @@ func _1650612625_add_community_message_archive_hashes_tableUpSql() (*asset, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1650612625_add_community_message_archive_hashes_table.up.sql", size: 130, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1650612625_add_community_message_archive_hashes_table.up.sql", size: 130, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x48, 0x31, 0xb3, 0x75, 0x23, 0xe2, 0x45, 0xe, 0x47, 0x1b, 0x35, 0xa5, 0x6e, 0x83, 0x4e, 0x64, 0x7d, 0xd7, 0xa2, 0xda, 0xe9, 0x53, 0xf1, 0x16, 0x86, 0x2c, 0x57, 0xad, 0xfa, 0xca, 0x39, 0xde}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -452,7 +453,7 @@ func _1650616788_add_communities_archives_info_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1650616788_add_communities_archives_info_table.up.sql", size: 208, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1650616788_add_communities_archives_info_table.up.sql", size: 208, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0x4f, 0x80, 0x45, 0xb9, 0xd9, 0x15, 0xe2, 0x78, 0xd0, 0xcb, 0x71, 0xc1, 0x1b, 0xb7, 0x1b, 0x1b, 0x97, 0xfe, 0x47, 0x53, 0x3c, 0x62, 0xbc, 0xdd, 0x3a, 0x94, 0x1a, 0xc, 0x48, 0x76, 0xe}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -472,7 +473,7 @@ func _1652715604_add_clock_accountsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1652715604_add_clock_accounts.up.sql", size: 62, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1652715604_add_clock_accounts.up.sql", size: 62, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb6, 0xd9, 0x8d, 0x73, 0xc9, 0xef, 0xfa, 0xb1, 0x4b, 0xa5, 0xf3, 0x5, 0x19, 0x26, 0x46, 0xf8, 0x47, 0x93, 0xdb, 0xac, 0x2, 0xef, 0xf9, 0x71, 0x56, 0x83, 0xe6, 0x2d, 0xb0, 0xd7, 0x83, 0x5c}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -492,7 +493,7 @@ func _1653037334_add_notifications_settings_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1653037334_add_notifications_settings_table.up.sql", size: 1276, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1653037334_add_notifications_settings_table.up.sql", size: 1276, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4b, 0xc4, 0x65, 0xac, 0xa, 0xf2, 0xef, 0xb6, 0x39, 0x3c, 0xc5, 0xb1, 0xb2, 0x9c, 0x86, 0x58, 0xe0, 0x38, 0xcb, 0x57, 0x3c, 0x76, 0x73, 0x87, 0x79, 0x4e, 0xf6, 0xed, 0xb0, 0x8e, 0x9e, 0xa}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -512,7 +513,7 @@ func _1654702119_add_mutual_contact_settingsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1654702119_add_mutual_contact_settings.up.sql", size: 78, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1654702119_add_mutual_contact_settings.up.sql", size: 78, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x26, 0x66, 0x67, 0x50, 0xfe, 0xd7, 0xe3, 0x29, 0x8b, 0xff, 0x9d, 0x5a, 0x87, 0xa7, 0x99, 0x6e, 0xd6, 0xcd, 0x2e, 0xbb, 0x17, 0xdf, 0x7f, 0xf7, 0xa3, 0xfa, 0x32, 0x7c, 0x2d, 0x92, 0xc8, 0x74}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -532,7 +533,7 @@ func _1655375270_add_clock_field_to_communities_settings_tableUpSql() (*asset, e
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1655375270_add_clock_field_to_communities_settings_table.up.sql", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1655375270_add_clock_field_to_communities_settings_table.up.sql", size: 74, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x19, 0xc5, 0xc0, 0xf9, 0x84, 0x53, 0xdf, 0x83, 0xcf, 0xb6, 0x40, 0x6d, 0xf5, 0xdc, 0x77, 0x37, 0xb7, 0xe3, 0xa, 0x75, 0xe7, 0x6, 0x11, 0xca, 0x2b, 0x51, 0x92, 0xdd, 0x7d, 0xdb, 0xc3, 0xf5}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -552,7 +553,7 @@ func _1655385721_drop_networks_configUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1655385721_drop_networks_config.up.sql", size: 27, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1655385721_drop_networks_config.up.sql", size: 27, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfc, 0xa7, 0x20, 0xbb, 0x67, 0x21, 0xe, 0xc6, 0xc8, 0x21, 0x74, 0xe0, 0xce, 0xc8, 0xe2, 0x2, 0xb4, 0xea, 0xf0, 0xe5, 0xc4, 0x4d, 0xdd, 0xd4, 0x52, 0x31, 0xa9, 0x3d, 0xcd, 0xd8, 0x9b, 0xab}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -572,7 +573,7 @@ func _1655385724_networks_chaincolor_shortnameUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1655385724_networks_chainColor_shortName.up.sql", size: 220, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1655385724_networks_chainColor_shortName.up.sql", size: 220, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd9, 0xe7, 0x84, 0xbb, 0x5f, 0xd2, 0x2c, 0x42, 0x88, 0x62, 0x52, 0xb6, 0x58, 0x31, 0xac, 0xc, 0x96, 0x2b, 0x1b, 0xe5, 0x4e, 0x9a, 0x3a, 0xf6, 0xf6, 0xfc, 0xa9, 0x1a, 0x35, 0x62, 0x28, 0x88}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -592,7 +593,7 @@ func _1655456688_add_deleted_at_field_to_bookmarks_tableUpSql() (*asset, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1655456688_add_deleted_at_field_to_bookmarks_table.up.sql", size: 69, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1655456688_add_deleted_at_field_to_bookmarks_table.up.sql", size: 69, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0x9a, 0xbd, 0x9a, 0xc9, 0xf, 0xdf, 0x90, 0x0, 0x5d, 0xea, 0x6e, 0x7d, 0x51, 0x95, 0xcd, 0x90, 0xd3, 0x1a, 0x36, 0x6c, 0xf4, 0xbd, 0xa7, 0x6b, 0xbf, 0xe5, 0xdb, 0xa3, 0x88, 0xe3, 0x50}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -612,7 +613,7 @@ func _1655462032_create_bookmarks_deleted_at_indexUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1655462032_create_bookmarks_deleted_at_index.up.sql", size: 81, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1655462032_create_bookmarks_deleted_at_index.up.sql", size: 81, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf, 0x8e, 0x20, 0x6b, 0x14, 0x9e, 0xcd, 0x97, 0xd3, 0xfe, 0x62, 0x3, 0x26, 0x59, 0x1, 0x6c, 0x99, 0xef, 0x6d, 0x21, 0xd4, 0xb5, 0xa3, 0xf4, 0x39, 0x40, 0x54, 0x6, 0xd, 0x60, 0x13, 0x38}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -632,7 +633,7 @@ func _1657617291_add_multi_transactions_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1657617291_add_multi_transactions_table.up.sql", size: 412, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1657617291_add_multi_transactions_table.up.sql", size: 412, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x86, 0xb0, 0x4e, 0x8c, 0x4, 0x82, 0xb4, 0x43, 0xaa, 0xd0, 0x16, 0xdd, 0xcb, 0x88, 0x81, 0xac, 0x4, 0x34, 0x1a, 0x8f, 0x2e, 0xc5, 0x69, 0xb, 0xf0, 0x17, 0xf7, 0xe3, 0x9, 0xe, 0x54, 0xe0}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -652,7 +653,7 @@ func _1660134042_add_social_links_settings_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1660134042_add_social_links_settings_table.up.sql", size: 334, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1660134042_add_social_links_settings_table.up.sql", size: 334, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x84, 0x73, 0xb6, 0xe7, 0x3f, 0xaa, 0x39, 0x9a, 0x56, 0x56, 0x31, 0xf1, 0x8e, 0x26, 0x23, 0x1, 0xe4, 0xfa, 0x98, 0xfe, 0x78, 0x87, 0x20, 0xcb, 0x52, 0xf4, 0x38, 0x7f, 0xc4, 0x1c, 0x4, 0x22}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -672,7 +673,7 @@ func _1660134060_settings_bioUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1660134060_settings_bio.up.sql", size: 91, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1660134060_settings_bio.up.sql", size: 91, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x46, 0x25, 0xa0, 0xa6, 0x47, 0xff, 0xbc, 0x2a, 0x0, 0xff, 0x59, 0x4b, 0xb0, 0xc9, 0x4e, 0x15, 0xe4, 0xd9, 0xda, 0xeb, 0xfe, 0x55, 0x98, 0xc3, 0x9d, 0x96, 0xe7, 0xf, 0xd1, 0x5c, 0x93, 0x73}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -692,7 +693,7 @@ func _1660134070_add_wakuv2_storeUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1660134070_add_wakuv2_store.up.sql", size: 269, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1660134070_add_wakuv2_store.up.sql", size: 269, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1d, 0xe6, 0xc3, 0x9, 0xef, 0xdc, 0xae, 0x49, 0x30, 0x78, 0x54, 0xd6, 0xdb, 0xbf, 0xc0, 0x8e, 0x25, 0x8f, 0xfc, 0x67, 0x80, 0x39, 0x37, 0xd4, 0x86, 0xc1, 0x85, 0xc8, 0x99, 0xc4, 0x59, 0xd4}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -712,7 +713,7 @@ func _1660134072_waku2_store_messagesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1660134072_waku2_store_messages.up.sql", size: 497, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1660134072_waku2_store_messages.up.sql", size: 497, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xeb, 0xb4, 0xa0, 0xa1, 0x2b, 0xcb, 0x4c, 0x3c, 0xc6, 0xd0, 0xe8, 0x96, 0xe3, 0x96, 0xf1, 0x4f, 0x1f, 0xe0, 0xe7, 0x1f, 0x85, 0xa3, 0xe, 0xf7, 0x52, 0x56, 0x63, 0x2b, 0xb0, 0x87, 0x7b}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -732,7 +733,7 @@ func _1662365868_add_key_uid_accountsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1662365868_add_key_uid_accounts.up.sql", size: 68, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1662365868_add_key_uid_accounts.up.sql", size: 68, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc6, 0xd8, 0x2f, 0x2f, 0x3b, 0xa8, 0xbd, 0x6d, 0xf6, 0x87, 0x7e, 0xd2, 0xf1, 0xa2, 0xf7, 0x81, 0x6a, 0x23, 0x10, 0xbc, 0xbf, 0x5b, 0xe7, 0x2b, 0x9c, 0xa9, 0x8a, 0x18, 0xbb, 0xd0, 0x86, 0x91}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -752,7 +753,7 @@ func _1662447680_add_keypairs_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1662447680_add_keypairs_table.up.sql", size: 218, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1662447680_add_keypairs_table.up.sql", size: 218, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdc, 0x25, 0xa9, 0xc7, 0x63, 0x27, 0x97, 0x35, 0x5f, 0x6b, 0xab, 0x26, 0xcb, 0xf9, 0xbd, 0x5e, 0xac, 0x3, 0xa0, 0x5e, 0xb9, 0x71, 0xa3, 0x1f, 0xb3, 0x4f, 0x7f, 0x79, 0x28, 0x48, 0xbe, 0xc}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -772,7 +773,7 @@ func _1662460056_move_favourites_to_saved_addressesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1662460056_move_favourites_to_saved_addresses.up.sql", size: 233, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1662460056_move_favourites_to_saved_addresses.up.sql", size: 233, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x10, 0xa2, 0x8c, 0xa3, 0xec, 0xad, 0xdf, 0xc3, 0x48, 0x5, 0x9b, 0x50, 0x25, 0x59, 0xae, 0x7d, 0xee, 0x58, 0xd2, 0x41, 0x27, 0xf2, 0x22, 0x2e, 0x9a, 0xb9, 0x4a, 0xcc, 0x38, 0x6e, 0x3a, 0xb2}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -792,7 +793,7 @@ func _1662738097_add_base_fee_transactionUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1662738097_add_base_fee_transaction.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1662738097_add_base_fee_transaction.up.sql", size: 112, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6b, 0xfb, 0x10, 0xae, 0xfc, 0x77, 0x70, 0x98, 0x6f, 0xec, 0xaa, 0xcd, 0x7, 0xc7, 0x74, 0x23, 0xc, 0xd5, 0x1e, 0x82, 0xdd, 0xfe, 0xff, 0x3b, 0xd2, 0x49, 0x10, 0x5b, 0x30, 0xc, 0x2d, 0xb0}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -812,7 +813,7 @@ func _1662972194_add_keypairs_tableUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1662972194_add_keypairs_table.up.sql", size: 345, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1662972194_add_keypairs_table.up.sql", size: 345, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xab, 0x76, 0xf2, 0x86, 0xe1, 0x7e, 0xe9, 0x47, 0x32, 0x48, 0xd5, 0x6b, 0xe5, 0xd, 0xab, 0xb7, 0xf1, 0xd4, 0xf1, 0xad, 0x38, 0xa6, 0x11, 0xe7, 0xce, 0x5c, 0x11, 0x11, 0xf, 0x47, 0xb2, 0x4}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -832,7 +833,7 @@ func _1664392661_add_third_party_id_to_waku_messagesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x67, 0x66, 0x9e, 0x66, 0x74, 0xce, 0x1c, 0xb, 0x1b, 0x9d, 0xd5, 0xfc, 0x65, 0xe, 0x83, 0x90, 0x4c, 0x61, 0x4e, 0x6b, 0xe7, 0x86, 0xbe, 0x36, 0x4f, 0x91, 0x36, 0x4, 0x47, 0x7b, 0x82}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -852,7 +853,7 @@ func _1664783660_add_sync_info_to_saved_addressesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1664783660_add_sync_info_to_saved_addresses.up.sql", size: 388, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1664783660_add_sync_info_to_saved_addresses.up.sql", size: 388, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x67, 0x7c, 0x3a, 0x95, 0x4e, 0x55, 0xb2, 0xbd, 0xb4, 0x18, 0x93, 0xc1, 0xcf, 0x9f, 0x12, 0xbb, 0x49, 0x8a, 0x2a, 0x6a, 0x2a, 0x7f, 0xad, 0x44, 0xc3, 0xf, 0x3a, 0x79, 0x18, 0xb9, 0x4c, 0x64}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -872,7 +873,7 @@ func _1668109917_wakunodesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1668109917_wakunodes.up.sql", size: 99, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1668109917_wakunodes.up.sql", size: 99, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x29, 0xaa, 0x9e, 0x2, 0x66, 0x85, 0x69, 0xa8, 0xd9, 0xe2, 0x4b, 0x8d, 0x2a, 0x9c, 0xdf, 0xd2, 0xef, 0x64, 0x58, 0xe3, 0xa6, 0xe7, 0xc1, 0xd1, 0xc8, 0x9c, 0xc0, 0x2c, 0x1, 0xa8, 0x7b, 0x81}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -892,7 +893,7 @@ func _1670249678_display_name_to_settings_sync_clock_tableUpSql() (*asset, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1670249678_display_name_to_settings_sync_clock_table.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1670249678_display_name_to_settings_sync_clock_table.up.sql", size: 83, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0x18, 0xdc, 0xc4, 0x1f, 0x79, 0x22, 0x16, 0x4d, 0xdf, 0x6c, 0x66, 0xd5, 0xa4, 0x88, 0x5d, 0x5, 0x37, 0xa7, 0x41, 0x5, 0x50, 0xae, 0x12, 0xfa, 0x7e, 0x89, 0x24, 0x5c, 0xae, 0x30, 0xfc}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -912,7 +913,7 @@ func _1670836810_add_imported_flag_to_community_archive_hashesUpSql() (*asset, e
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1670836810_add_imported_flag_to_community_archive_hashes.up.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1670836810_add_imported_flag_to_community_archive_hashes.up.sql", size: 144, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6f, 0xf, 0xf0, 0xbd, 0xfe, 0x63, 0x25, 0x8f, 0x5e, 0x46, 0x4b, 0x45, 0x31, 0x8b, 0x3e, 0xd8, 0x6b, 0x5d, 0x9d, 0x6d, 0x10, 0x9a, 0x87, 0x4b, 0x18, 0xc6, 0x39, 0x81, 0x6e, 0xe4, 0x75, 0xfb}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -932,7 +933,7 @@ func _1671438731_add_magnetlink_uri_to_communities_archive_infoUpSql() (*asset,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1671438731_add_magnetlink_uri_to_communities_archive_info.up.sql", size: 86, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1671438731_add_magnetlink_uri_to_communities_archive_info.up.sql", size: 86, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xda, 0x8b, 0x4b, 0xd6, 0xd8, 0xe2, 0x3d, 0xf7, 0x6b, 0xcd, 0x1e, 0x70, 0x9, 0x2e, 0x35, 0x4, 0x61, 0xc3, 0xb5, 0x9d, 0xc5, 0x27, 0x21, 0xa, 0x5a, 0xd6, 0x3e, 0xa6, 0x24, 0xa2, 0x12, 0xdf}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -952,7 +953,7 @@ func _1672933930_switcher_cardUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1672933930_switcher_card.up.sql", size: 162, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1672933930_switcher_card.up.sql", size: 162, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0xba, 0xdc, 0xbb, 0x40, 0x4, 0xf2, 0x10, 0xdf, 0xb4, 0xd2, 0x80, 0x8a, 0x74, 0x4d, 0xf6, 0xbc, 0x50, 0x7, 0xd, 0x22, 0x7f, 0xc4, 0xaf, 0xaa, 0xde, 0xdc, 0x71, 0xe9, 0x42, 0x98, 0x36}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -972,7 +973,7 @@ func _1674056187_add_price_cacheUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1674056187_add_price_cache.up.sql", size: 255, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1674056187_add_price_cache.up.sql", size: 255, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb7, 0x79, 0x6a, 0x9b, 0x28, 0xd1, 0x22, 0xf0, 0x84, 0x76, 0x40, 0x39, 0x49, 0x15, 0x5d, 0xaa, 0xfd, 0x11, 0xff, 0x13, 0x27, 0x42, 0x12, 0xfa, 0x82, 0xe6, 0x7a, 0xf0, 0x5e, 0x1f, 0xe3, 0xba}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -992,7 +993,7 @@ func _1674136690_ens_usernamesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1674136690_ens_usernames.up.sql", size: 98, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1674136690_ens_usernames.up.sql", size: 98, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x81, 0x7a, 0xf3, 0xa8, 0x88, 0x99, 0xd6, 0x9c, 0x69, 0x48, 0x3c, 0x10, 0xda, 0x72, 0xdc, 0x14, 0xd, 0x6e, 0x8c, 0x82, 0x92, 0x2d, 0x2c, 0xee, 0x4c, 0x70, 0xa4, 0xdc, 0x5c, 0x5, 0x2, 0xc3}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -1012,7 +1013,7 @@ func _1674232431_add_balance_historyUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1674232431_add_balance_history.up.sql", size: 698, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "1674232431_add_balance_history.up.sql", size: 698, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf7, 0xb5, 0x18, 0xca, 0x4a, 0x93, 0xbb, 0x6f, 0xa4, 0xee, 0xe4, 0x3e, 0xff, 0x6a, 0x4b, 0xe2, 0xe1, 0x61, 0x28, 0xee, 0xc5, 0x26, 0x57, 0x61, 0x5e, 0x6d, 0x44, 0x1e, 0x85, 0x43, 0x70, 0xa2}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -1032,7 +1033,7 @@ func _1676368933_keypairs_to_keycardsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1676368933_keypairs_to_keycards.up.sql", size: 639, mode: os.FileMode(0644), modTime: time.Unix(1676888614, 0)}
|
||||
info := bindataFileInfo{name: "1676368933_keypairs_to_keycards.up.sql", size: 639, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x81, 0x93, 0x27, 0x2, 0xf0, 0x37, 0x81, 0x65, 0xa4, 0xb3, 0x5b, 0x60, 0x36, 0x95, 0xfc, 0x81, 0xf0, 0x3b, 0x7c, 0xc3, 0x2c, 0x85, 0xbd, 0x38, 0x46, 0xa4, 0x95, 0x4a, 0x6, 0x3e, 0x74, 0xd5}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -1052,7 +1053,7 @@ func _1676951398_add_currency_format_cacheUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1676951398_add_currency_format_cache.up.sql", size: 291, mode: os.FileMode(0644), modTime: time.Unix(1676888614, 0)}
|
||||
info := bindataFileInfo{name: "1676951398_add_currency_format_cache.up.sql", size: 291, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf9, 0xa3, 0x76, 0x35, 0xca, 0xf, 0xe8, 0xdf, 0xd9, 0x61, 0xf9, 0xed, 0xfc, 0x6d, 0xf5, 0xe, 0x11, 0x88, 0xbd, 0x14, 0x92, 0xc6, 0x57, 0x53, 0xe, 0xcd, 0x52, 0xf4, 0xa9, 0xb1, 0xdd, 0xfd}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -1072,11 +1073,31 @@ func _1676968196_keycards_add_clock_columnUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1676968196_keycards_add_clock_column.up.sql", size: 73, mode: os.FileMode(0644), modTime: time.Unix(1677485977, 0)}
|
||||
info := bindataFileInfo{name: "1676968196_keycards_add_clock_column.up.sql", size: 73, mode: os.FileMode(0664), modTime: time.Unix(1677517539, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4c, 0xf, 0x1c, 0x28, 0x41, 0x57, 0x57, 0x6c, 0xe, 0x75, 0x6b, 0x75, 0x12, 0x0, 0x18, 0x1e, 0x88, 0x1e, 0x45, 0xe0, 0x32, 0xb9, 0xd4, 0xd9, 0x2e, 0xc8, 0xb, 0x80, 0x6, 0x51, 0x3d, 0x28}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1676968197_add_fallback_rpc_to_networksUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\x4b\x2d\x29\xcf\x2f\xca\x2e\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x48\x4b\xcc\xc9\x49\x4a\x4c\xce\x8e\x2f\x2d\xca\x51\x08\x73\x0c\x72\xf6\x70\x0c\x52\xf0\xf3\x0f\x51\xf0\x0b\xf5\xf1\x51\x70\x71\x75\x73\x0c\xf5\x09\x51\x50\x52\xb2\xe6\x0a\x0d\x70\x71\x0c\x41\x32\x24\xd8\x35\x04\x55\xb7\x2d\x48\x19\x20\x00\x00\xff\xff\xe1\xb6\xd7\x0b\x70\x00\x00\x00")
|
||||
|
||||
func _1676968197_add_fallback_rpc_to_networksUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1676968197_add_fallback_rpc_to_networksUpSql,
|
||||
"1676968197_add_fallback_rpc_to_networks.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1676968197_add_fallback_rpc_to_networksUpSql() (*asset, error) {
|
||||
bytes, err := _1676968197_add_fallback_rpc_to_networksUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1676968197_add_fallback_rpc_to_networks.up.sql", size: 112, mode: os.FileMode(0664), modTime: time.Unix(1677517540, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x6a, 0xc6, 0x45, 0xfa, 0x62, 0x84, 0x74, 0x6d, 0x7c, 0xd7, 0x1d, 0x79, 0xb6, 0x38, 0x43, 0xa8, 0x8, 0x6b, 0x75, 0x3d, 0x9, 0x2, 0xc5, 0x9f, 0xbb, 0x45, 0x56, 0x4c, 0x4e, 0x17, 0x89}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
|
@ -1092,7 +1113,7 @@ func docGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
|
||||
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -1284,6 +1305,8 @@ var _bindata = map[string]func() (*asset, error){
|
|||
|
||||
"1676968196_keycards_add_clock_column.up.sql": _1676968196_keycards_add_clock_columnUpSql,
|
||||
|
||||
"1676968197_add_fallback_rpc_to_networks.up.sql": _1676968197_add_fallback_rpc_to_networksUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
|
@ -1376,6 +1399,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
|
|||
"1676368933_keypairs_to_keycards.up.sql": &bintree{_1676368933_keypairs_to_keycardsUpSql, map[string]*bintree{}},
|
||||
"1676951398_add_currency_format_cache.up.sql": &bintree{_1676951398_add_currency_format_cacheUpSql, map[string]*bintree{}},
|
||||
"1676968196_keycards_add_clock_column.up.sql": &bintree{_1676968196_keycards_add_clock_columnUpSql, map[string]*bintree{}},
|
||||
"1676968197_add_fallback_rpc_to_networks.up.sql": &bintree{_1676968197_add_fallback_rpc_to_networksUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE networks ADD COLUMN fallback_url VARCHAR NOT NULL DEFAULT "";
|
||||
UPDATE networks SET fallback_url = "";
|
1
go.mod
1
go.mod
|
@ -73,6 +73,7 @@ require (
|
|||
require github.com/fogleman/gg v1.3.0
|
||||
|
||||
require (
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/ipfs/go-log/v2 v2.5.1
|
||||
github.com/ladydascalie/currency v1.6.0
|
||||
|
|
5
go.sum
5
go.sum
|
@ -163,6 +163,7 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c
|
|||
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
|
||||
|
@ -1044,6 +1045,7 @@ github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97Dwqy
|
|||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
|
@ -1255,6 +1257,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
|||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
|
@ -1952,10 +1955,12 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
|||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
|
|
|
@ -499,6 +499,7 @@ type Network struct {
|
|||
ChainID uint64 `json:"chainId"`
|
||||
ChainName string `json:"chainName"`
|
||||
RPCURL string `json:"rpcUrl"`
|
||||
FallbackURL string `json:"fallbackURL"`
|
||||
BlockExplorerURL string `json:"blockExplorerUrl,omitempty"`
|
||||
IconURL string `json:"iconUrl,omitempty"`
|
||||
NativeCurrencyName string `json:"nativeCurrencyName,omitempty"`
|
||||
|
|
|
@ -0,0 +1,699 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/services/rpcstats"
|
||||
)
|
||||
|
||||
type FeeHistory struct {
|
||||
BaseFeePerGas []string `json:"baseFeePerGas"`
|
||||
}
|
||||
|
||||
type ClientWithFallback struct {
|
||||
ChainID uint64
|
||||
main *ethclient.Client
|
||||
fallback *ethclient.Client
|
||||
|
||||
mainRPC *rpc.Client
|
||||
fallbackRPC *rpc.Client
|
||||
|
||||
IsConnected bool
|
||||
LastCheckedAt int64
|
||||
}
|
||||
|
||||
func NewSimpleClient(main *rpc.Client, chainID uint64) *ClientWithFallback {
|
||||
hystrix.ConfigureCommand(fmt.Sprintf("ethClient_%d", chainID), hystrix.CommandConfig{
|
||||
Timeout: 10000,
|
||||
MaxConcurrentRequests: 100,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
|
||||
return &ClientWithFallback{
|
||||
ChainID: chainID,
|
||||
main: ethclient.NewClient(main),
|
||||
fallback: nil,
|
||||
mainRPC: main,
|
||||
fallbackRPC: nil,
|
||||
IsConnected: true,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(main, fallback *rpc.Client, chainID uint64) *ClientWithFallback {
|
||||
hystrix.ConfigureCommand(fmt.Sprintf("ethClient_%d", chainID), hystrix.CommandConfig{
|
||||
Timeout: 10000,
|
||||
MaxConcurrentRequests: 100,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
|
||||
var fallbackEthClient *ethclient.Client
|
||||
if fallback != nil {
|
||||
fallbackEthClient = ethclient.NewClient(fallback)
|
||||
}
|
||||
return &ClientWithFallback{
|
||||
ChainID: chainID,
|
||||
main: ethclient.NewClient(main),
|
||||
fallback: fallbackEthClient,
|
||||
mainRPC: main,
|
||||
fallbackRPC: fallback,
|
||||
IsConnected: true,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) Close() {
|
||||
c.main.Close()
|
||||
if c.fallback != nil {
|
||||
c.fallback.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) makeCallNoReturn(main func() error, fallback func() error) error {
|
||||
output := make(chan struct{}, 1)
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
|
||||
err := main()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.IsConnected = true
|
||||
output <- struct{}{}
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if c.fallback == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = fallback()
|
||||
if err != nil {
|
||||
c.IsConnected = false
|
||||
return err
|
||||
}
|
||||
c.IsConnected = true
|
||||
output <- struct{}{}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case <-output:
|
||||
return nil
|
||||
case err := <-errChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) makeCallSingleReturn(main func() (any, error), fallback func() (any, error)) (any, error) {
|
||||
resultChan := make(chan any, 1)
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
|
||||
res, err := main()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.IsConnected = true
|
||||
resultChan <- res
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if c.fallback == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := fallback()
|
||||
if err != nil {
|
||||
c.IsConnected = false
|
||||
return err
|
||||
}
|
||||
c.IsConnected = true
|
||||
resultChan <- res
|
||||
return nil
|
||||
})
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
return result, nil
|
||||
case err := <-errChan:
|
||||
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) makeCallDoubleReturn(main func() (any, any, error), fallback func() (any, any, error)) (any, any, error) {
|
||||
resultChan := make(chan []any, 1)
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
|
||||
a, b, err := main()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.IsConnected = true
|
||||
resultChan <- []any{a, b}
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if c.fallback == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a, b, err := fallback()
|
||||
if err != nil {
|
||||
c.IsConnected = false
|
||||
return err
|
||||
}
|
||||
c.IsConnected = true
|
||||
resultChan <- []any{a, b}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
return result[0], result[1], nil
|
||||
case err := <-errChan:
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
rpcstats.CountCall("eth_BlockByHash")
|
||||
|
||||
block, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BlockByHash(ctx, hash) },
|
||||
func() (any, error) { return c.fallback.BlockByHash(ctx, hash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return block.(*types.Block), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||
rpcstats.CountCall("eth_BlockByNumber")
|
||||
block, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BlockByNumber(ctx, number) },
|
||||
func() (any, error) { return c.fallback.BlockByNumber(ctx, number) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return block.(*types.Block), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BlockNumber(ctx context.Context) (uint64, error) {
|
||||
rpcstats.CountCall("eth_BlockNumber")
|
||||
|
||||
number, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BlockNumber(ctx) },
|
||||
func() (any, error) { return c.fallback.BlockNumber(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return number.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PeerCount(ctx context.Context) (uint64, error) {
|
||||
rpcstats.CountCall("eth_PeerCount")
|
||||
|
||||
peerCount, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PeerCount(ctx) },
|
||||
func() (any, error) { return c.fallback.PeerCount(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return peerCount.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
rpcstats.CountCall("eth_HeaderByHash")
|
||||
header, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.HeaderByHash(ctx, hash) },
|
||||
func() (any, error) { return c.fallback.HeaderByHash(ctx, hash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return header.(*types.Header), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
rpcstats.CountCall("eth_HeaderByNumber")
|
||||
header, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.HeaderByNumber(ctx, number) },
|
||||
func() (any, error) { return c.fallback.HeaderByNumber(ctx, number) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return header.(*types.Header), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
|
||||
rpcstats.CountCall("eth_TransactionByHash")
|
||||
|
||||
tx, isPending, err := c.makeCallDoubleReturn(
|
||||
func() (any, any, error) { return c.main.TransactionByHash(ctx, hash) },
|
||||
func() (any, any, error) { return c.fallback.TransactionByHash(ctx, hash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return tx.(*types.Transaction), isPending.(bool), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) {
|
||||
rpcstats.CountCall("eth_TransactionSender")
|
||||
|
||||
address, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionSender(ctx, tx, block, index) },
|
||||
func() (any, error) { return c.fallback.TransactionSender(ctx, tx, block, index) },
|
||||
)
|
||||
|
||||
return address.(common.Address), err
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
||||
rpcstats.CountCall("eth_TransactionCount")
|
||||
|
||||
count, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionCount(ctx, blockHash) },
|
||||
func() (any, error) { return c.fallback.TransactionCount(ctx, blockHash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count.(uint), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
||||
rpcstats.CountCall("eth_TransactionInBlock")
|
||||
|
||||
transactions, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionInBlock(ctx, blockHash, index) },
|
||||
func() (any, error) { return c.fallback.TransactionInBlock(ctx, blockHash, index) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transactions.(*types.Transaction), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
rpcstats.CountCall("eth_TransactionReceipt")
|
||||
|
||||
receipt, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.TransactionReceipt(ctx, txHash) },
|
||||
func() (any, error) { return c.fallback.TransactionReceipt(ctx, txHash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return receipt.(*types.Receipt), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
|
||||
rpcstats.CountCall("eth_SyncProgress")
|
||||
|
||||
progress, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SyncProgress(ctx) },
|
||||
func() (any, error) { return c.fallback.SyncProgress(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return progress.(*ethereum.SyncProgress), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
||||
rpcstats.CountCall("eth_SubscribeNewHead")
|
||||
|
||||
sub, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SubscribeNewHead(ctx, ch) },
|
||||
func() (any, error) { return c.fallback.SubscribeNewHead(ctx, ch) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sub.(ethereum.Subscription), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) NetworkID(ctx context.Context) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_NetworkID")
|
||||
|
||||
networkID, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.NetworkID(ctx) },
|
||||
func() (any, error) { return c.fallback.NetworkID(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkID.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_BalanceAt")
|
||||
|
||||
balance, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.BalanceAt(ctx, account, blockNumber) },
|
||||
func() (any, error) { return c.fallback.BalanceAt(ctx, account, blockNumber) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return balance.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_StorageAt")
|
||||
|
||||
storage, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.StorageAt(ctx, account, key, blockNumber) },
|
||||
func() (any, error) { return c.fallback.StorageAt(ctx, account, key, blockNumber) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storage.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_CodeAt")
|
||||
|
||||
code, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.CodeAt(ctx, account, blockNumber) },
|
||||
func() (any, error) { return c.fallback.CodeAt(ctx, account, blockNumber) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return code.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
rpcstats.CountCall("eth_NonceAt")
|
||||
|
||||
nonce, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.NonceAt(ctx, account, blockNumber) },
|
||||
func() (any, error) { return c.fallback.NonceAt(ctx, account, blockNumber) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nonce.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
||||
rpcstats.CountCall("eth_FilterLogs")
|
||||
|
||||
logs, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.FilterLogs(ctx, q) },
|
||||
func() (any, error) { return c.fallback.FilterLogs(ctx, q) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logs.([]types.Log), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
||||
rpcstats.CountCall("eth_SubscribeFilterLogs")
|
||||
|
||||
sub, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SubscribeFilterLogs(ctx, q, ch) },
|
||||
func() (any, error) { return c.fallback.SubscribeFilterLogs(ctx, q, ch) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sub.(ethereum.Subscription), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_PendingBalanceAt")
|
||||
|
||||
balance, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingBalanceAt(ctx, account) },
|
||||
func() (any, error) { return c.fallback.PendingBalanceAt(ctx, account) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return balance.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_PendingStorageAt")
|
||||
|
||||
storage, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingStorageAt(ctx, account, key) },
|
||||
func() (any, error) { return c.fallback.PendingStorageAt(ctx, account, key) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storage.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_PendingCodeAt")
|
||||
|
||||
code, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingCodeAt(ctx, account) },
|
||||
func() (any, error) { return c.fallback.PendingCodeAt(ctx, account) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return code.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||
rpcstats.CountCall("eth_PendingNonceAt")
|
||||
|
||||
nonce, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingNonceAt(ctx, account) },
|
||||
func() (any, error) { return c.fallback.PendingNonceAt(ctx, account) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nonce.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||
rpcstats.CountCall("eth_PendingTransactionCount")
|
||||
|
||||
count, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingTransactionCount(ctx) },
|
||||
func() (any, error) { return c.fallback.PendingTransactionCount(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count.(uint), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_CallContract")
|
||||
|
||||
data, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.CallContract(ctx, msg, blockNumber) },
|
||||
func() (any, error) { return c.fallback.CallContract(ctx, msg, blockNumber) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_CallContractAtHash")
|
||||
|
||||
data, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.CallContractAtHash(ctx, msg, blockHash) },
|
||||
func() (any, error) { return c.fallback.CallContractAtHash(ctx, msg, blockHash) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_PendingCallContract")
|
||||
|
||||
data, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.PendingCallContract(ctx, msg) },
|
||||
func() (any, error) { return c.fallback.PendingCallContract(ctx, msg) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.([]byte), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_SuggestGasPrice")
|
||||
|
||||
gasPrice, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SuggestGasPrice(ctx) },
|
||||
func() (any, error) { return c.fallback.SuggestGasPrice(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gasPrice.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_SuggestGasTipCap")
|
||||
|
||||
tip, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.SuggestGasTipCap(ctx) },
|
||||
func() (any, error) { return c.fallback.SuggestGasTipCap(ctx) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tip.(*big.Int), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
|
||||
rpcstats.CountCall("eth_FeeHistory")
|
||||
|
||||
feeHistory, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) },
|
||||
func() (any, error) { return c.fallback.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return feeHistory.(*ethereum.FeeHistory), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
|
||||
rpcstats.CountCall("eth_EstimateGas")
|
||||
|
||||
estimate, err := c.makeCallSingleReturn(
|
||||
func() (any, error) { return c.main.EstimateGas(ctx, msg) },
|
||||
func() (any, error) { return c.fallback.EstimateGas(ctx, msg) },
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return estimate.(uint64), nil
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||
rpcstats.CountCall("eth_SendTransaction")
|
||||
|
||||
return c.makeCallNoReturn(
|
||||
func() error { return c.main.SendTransaction(ctx, tx) },
|
||||
func() error { return c.fallback.SendTransaction(ctx, tx) },
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
rpcstats.CountCall("eth_CallContext")
|
||||
|
||||
return c.makeCallNoReturn(
|
||||
func() error { return c.mainRPC.CallContext(ctx, result, method, args...) },
|
||||
func() error { return c.fallbackRPC.CallContext(ctx, result, method, args...) },
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) ToBigInt() *big.Int {
|
||||
return big.NewInt(int64(c.ChainID))
|
||||
}
|
||||
|
||||
func (c *ClientWithFallback) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
|
||||
rpcstats.CountCall("eth_GetBaseFeeFromBlock")
|
||||
var feeHistory FeeHistory
|
||||
err := c.mainRPC.Call(&feeHistory, "eth_feeHistory", "0x1", (*hexutil.Big)(blockNumber), nil)
|
||||
if err != nil {
|
||||
if err.Error() == "the method eth_feeHistory does not exist/is not available" {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
var baseGasFee string = ""
|
||||
if len(feeHistory.BaseFeePerGas) > 0 {
|
||||
baseGasFee = feeHistory.BaseFeePerGas[0]
|
||||
}
|
||||
|
||||
return baseGasFee, err
|
||||
}
|
|
@ -10,11 +10,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/rpc/network"
|
||||
"github.com/status-im/status-go/services/rpcstats"
|
||||
)
|
||||
|
@ -43,8 +43,8 @@ type Client struct {
|
|||
UpstreamChainID uint64
|
||||
|
||||
local *gethrpc.Client
|
||||
upstream *gethrpc.Client
|
||||
rpcClients map[uint64]*gethrpc.Client
|
||||
upstream *chain.ClientWithFallback
|
||||
rpcClients map[uint64]*chain.ClientWithFallback
|
||||
|
||||
router *router
|
||||
NetworkManager *network.Manager
|
||||
|
@ -73,7 +73,7 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
|
|||
local: client,
|
||||
NetworkManager: networkManager,
|
||||
handlers: make(map[string]Handler),
|
||||
rpcClients: make(map[uint64]*gethrpc.Client),
|
||||
rpcClients: make(map[uint64]*chain.ClientWithFallback),
|
||||
log: log,
|
||||
}
|
||||
|
||||
|
@ -81,10 +81,11 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
|
|||
c.UpstreamChainID = upstreamChainID
|
||||
c.upstreamEnabled = upstream.Enabled
|
||||
c.upstreamURL = upstream.URL
|
||||
c.upstream, err = gethrpc.Dial(c.upstreamURL)
|
||||
upstreamClient, err := gethrpc.Dial(c.upstreamURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial upstream server: %s", err)
|
||||
}
|
||||
c.upstream = chain.NewSimpleClient(upstreamClient, upstreamChainID)
|
||||
}
|
||||
|
||||
c.router = newRouter(c.upstreamEnabled)
|
||||
|
@ -92,21 +93,16 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
|
|||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *Client) getRPCClientWithCache(chainID uint64) (*gethrpc.Client, error) {
|
||||
if !c.upstreamEnabled {
|
||||
return c.local, nil
|
||||
}
|
||||
|
||||
if c.UpstreamChainID == chainID {
|
||||
return c.upstream, nil
|
||||
}
|
||||
|
||||
func (c *Client) getClientUsingCache(chainID uint64) (*chain.ClientWithFallback, error) {
|
||||
if rpcClient, ok := c.rpcClients[chainID]; ok {
|
||||
return rpcClient, nil
|
||||
}
|
||||
|
||||
network := c.NetworkManager.Find(chainID)
|
||||
if network == nil {
|
||||
if c.UpstreamChainID == chainID {
|
||||
return c.upstream, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not find network: %d", chainID)
|
||||
}
|
||||
|
||||
|
@ -115,18 +111,40 @@ func (c *Client) getRPCClientWithCache(chainID uint64) (*gethrpc.Client, error)
|
|||
return nil, fmt.Errorf("dial upstream server: %s", err)
|
||||
}
|
||||
|
||||
c.rpcClients[chainID] = rpcClient
|
||||
return rpcClient, nil
|
||||
var rpcFallbackClient *gethrpc.Client
|
||||
if len(network.FallbackURL) > 0 {
|
||||
rpcFallbackClient, err = gethrpc.Dial(network.FallbackURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial upstream server: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
client := chain.NewClient(rpcClient, rpcFallbackClient, chainID)
|
||||
c.rpcClients[chainID] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Ethclient returns ethclient.Client per chain
|
||||
func (c *Client) EthClient(chainID uint64) (*ethclient.Client, error) {
|
||||
rpcClient, err := c.getRPCClientWithCache(chainID)
|
||||
func (c *Client) EthClient(chainID uint64) (*chain.ClientWithFallback, error) {
|
||||
client, err := c.getClientUsingCache(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ethclient.NewClient(rpcClient), nil
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) EthClients(chainIDs []uint64) ([]*chain.ClientWithFallback, error) {
|
||||
clients := make([]*chain.ClientWithFallback, 0)
|
||||
for _, chainID := range chainIDs {
|
||||
client, err := c.getClientUsingCache(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clients = append(clients, client)
|
||||
}
|
||||
|
||||
return clients, nil
|
||||
}
|
||||
|
||||
// UpdateUpstreamURL changes the upstream RPC client URL, if the upstream is enabled.
|
||||
|
@ -139,9 +157,8 @@ func (c *Client) UpdateUpstreamURL(url string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.upstream = rpcClient
|
||||
c.upstream = chain.NewSimpleClient(rpcClient, c.UpstreamChainID)
|
||||
c.upstreamURL = url
|
||||
c.Unlock()
|
||||
|
||||
|
@ -195,11 +212,11 @@ func (c *Client) CallContextIgnoringLocalHandlers(ctx context.Context, result in
|
|||
}
|
||||
|
||||
if c.router.routeRemote(method) {
|
||||
ethClient, err := c.getRPCClientWithCache(chainID)
|
||||
client, err := c.getClientUsingCache(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ethClient.CallContext(ctx, result, method, args...)
|
||||
return client.CallContext(ctx, result, method, args...)
|
||||
}
|
||||
|
||||
if c.local == nil {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/status-im/status-go/params"
|
||||
)
|
||||
|
||||
const baseQuery = "SELECT chain_id, chain_name, rpc_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name FROM networks"
|
||||
const baseQuery = "SELECT chain_id, chain_name, rpc_url, fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name FROM networks"
|
||||
|
||||
func newNetworksQuery() *networksQuery {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
@ -56,7 +56,7 @@ func (nq *networksQuery) exec(db *sql.DB) ([]*params.Network, error) {
|
|||
for rows.Next() {
|
||||
network := params.Network{}
|
||||
err := rows.Scan(
|
||||
&network.ChainID, &network.ChainName, &network.RPCURL, &network.BlockExplorerURL, &network.IconURL,
|
||||
&network.ChainID, &network.ChainName, &network.RPCURL, &network.FallbackURL, &network.BlockExplorerURL, &network.IconURL,
|
||||
&network.NativeCurrencyName, &network.NativeCurrencySymbol,
|
||||
&network.NativeCurrencyDecimals, &network.IsTest, &network.Layer, &network.Enabled, &network.ChainColor, &network.ShortName,
|
||||
)
|
||||
|
@ -121,6 +121,14 @@ func (nm *Manager) Init(networks []params.Network) error {
|
|||
errors += fmt.Sprintf("error updating network rpc_url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if currentNetworks[j].FallbackURL != networks[i].FallbackURL {
|
||||
// Update fallback_url if it's different
|
||||
err := nm.UpdateFallbackURL(currentNetworks[j].ChainID, networks[i].FallbackURL)
|
||||
if err != nil {
|
||||
errors += fmt.Sprintf("error updating network fallback_url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -143,8 +151,8 @@ func (nm *Manager) Init(networks []params.Network) error {
|
|||
|
||||
func (nm *Manager) Upsert(network *params.Network) error {
|
||||
_, err := nm.db.Exec(
|
||||
"INSERT OR REPLACE INTO networks (chain_id, chain_name, rpc_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
network.ChainID, network.ChainName, network.RPCURL, network.BlockExplorerURL, network.IconURL,
|
||||
"INSERT OR REPLACE INTO networks (chain_id, chain_name, rpc_url, fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
network.ChainID, network.ChainName, network.RPCURL, network.FallbackURL, network.BlockExplorerURL, network.IconURL,
|
||||
network.NativeCurrencyName, network.NativeCurrencySymbol, network.NativeCurrencyDecimals,
|
||||
network.IsTest, network.Layer, network.Enabled, network.ChainColor, network.ShortName,
|
||||
)
|
||||
|
@ -161,6 +169,11 @@ func (nm *Manager) UpdateRPCURL(chainID uint64, rpcURL string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) UpdateFallbackURL(chainID uint64, fallbackURL string) error {
|
||||
_, err := nm.db.Exec(`UPDATE networks SET fallback_url = ? WHERE chain_id = ?`, fallbackURL, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (nm *Manager) Find(chainID uint64) *params.Network {
|
||||
networks, err := newNetworksQuery().filterChainID(chainID).exec(nm.db)
|
||||
if len(networks) != 1 || err != nil {
|
||||
|
|
|
@ -12,9 +12,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/currency"
|
||||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
@ -114,15 +114,15 @@ func (api *API) GetCachedBalancesbyChainID(ctx context.Context, chainID uint64,
|
|||
|
||||
// GetTokensBalances return mapping of token balances for every account.
|
||||
func (api *API) GetTokensBalances(ctx context.Context, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
chainClient, err := chain.NewLegacyClient(api.s.rpcClient)
|
||||
chainClient, err := api.s.rpcClient.EthClient(api.s.rpcClient.UpstreamChainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.s.tokenManager.GetBalances(ctx, []*chain.Client{chainClient}, accounts, addresses)
|
||||
return api.s.tokenManager.GetBalances(ctx, []*chain.ClientWithFallback{chainClient}, accounts, addresses)
|
||||
}
|
||||
|
||||
func (api *API) GetTokensBalancesForChainIDs(ctx context.Context, chainIDs []uint64, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
clients, err := chain.NewClients(api.s.rpcClient, chainIDs)
|
||||
clients, err := api.s.rpcClient.EthClients(chainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ func (api *API) DeletePendingTransactionByChainID(ctx context.Context, chainID u
|
|||
}
|
||||
|
||||
func (api *API) WatchTransaction(ctx context.Context, transactionHash common.Hash) error {
|
||||
chainClient, err := chain.NewLegacyClient(api.s.rpcClient)
|
||||
chainClient, err := api.s.rpcClient.EthClient(api.s.rpcClient.UpstreamChainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ func (api *API) WatchTransaction(ctx context.Context, transactionHash common.Has
|
|||
}
|
||||
|
||||
func (api *API) WatchTransactionByChainID(ctx context.Context, chainID uint64, transactionHash common.Hash) error {
|
||||
chainClient, err := chain.NewClient(api.s.rpcClient, chainID)
|
||||
chainClient, err := api.s.rpcClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/rpcstats"
|
||||
)
|
||||
|
||||
var ChainClientInstances = make(map[uint64]*Client)
|
||||
|
||||
type Client struct {
|
||||
eth *ethclient.Client
|
||||
ChainID uint64
|
||||
rpcClient *rpc.Client
|
||||
IsConnected bool
|
||||
LastCheckedAt int64
|
||||
IsConnectedLock sync.RWMutex
|
||||
}
|
||||
|
||||
type FeeHistory struct {
|
||||
BaseFeePerGas []string `json:"baseFeePerGas"`
|
||||
}
|
||||
|
||||
func NewClient(rpc *rpc.Client, chainID uint64) (*Client, error) {
|
||||
if client, ok := ChainClientInstances[chainID]; ok {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
ethClient, err := rpc.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &Client{eth: ethClient, ChainID: chainID, rpcClient: rpc, IsConnected: true, LastCheckedAt: time.Now().Unix()}
|
||||
ChainClientInstances[chainID] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func NewLegacyClient(rpc *rpc.Client) (*Client, error) {
|
||||
return NewClient(rpc, rpc.UpstreamChainID)
|
||||
}
|
||||
|
||||
func NewClients(rpc *rpc.Client, chainIDs []uint64) (res []*Client, err error) {
|
||||
for _, chainID := range chainIDs {
|
||||
client, err := NewClient(rpc, chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, client)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (cc *Client) toggleIsConnected(err error) {
|
||||
cc.IsConnectedLock.Lock()
|
||||
defer cc.IsConnectedLock.Unlock()
|
||||
cc.LastCheckedAt = time.Now().Unix()
|
||||
if err != nil {
|
||||
cc.IsConnected = false
|
||||
} else {
|
||||
cc.IsConnected = true
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *Client) ToBigInt() *big.Int {
|
||||
return big.NewInt(int64(cc.ChainID))
|
||||
}
|
||||
|
||||
func (cc *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
rpcstats.CountCall("eth_getBlockByHash")
|
||||
resp, err := cc.eth.HeaderByHash(ctx, hash)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
rpcstats.CountCall("eth_getBlockByNumber")
|
||||
resp, err := cc.eth.HeaderByNumber(ctx, number)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
rpcstats.CountCall("eth_getBlockByHash")
|
||||
resp, err := cc.eth.BlockByHash(ctx, hash)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||
rpcstats.CountCall("eth_getBlockByNumber")
|
||||
resp, err := cc.eth.BlockByNumber(ctx, number)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
rpcstats.CountCall("eth_getBalance")
|
||||
resp, err := cc.eth.BalanceAt(ctx, account, blockNumber)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
rpcstats.CountCall("eth_getTransactionCount")
|
||||
resp, err := cc.eth.NonceAt(ctx, account, blockNumber)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
rpcstats.CountCall("eth_getTransactionReceipt")
|
||||
resp, err := cc.eth.TransactionReceipt(ctx, txHash)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||
rpcstats.CountCall("eth_getTransactionByHash")
|
||||
tx, isPending, err = cc.eth.TransactionByHash(ctx, hash)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return tx, isPending, err
|
||||
}
|
||||
|
||||
func (cc *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
||||
rpcstats.CountCall("eth_getLogs")
|
||||
resp, err := cc.eth.FilterLogs(ctx, q)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_getCode")
|
||||
resp, err := cc.eth.CodeAt(ctx, contract, blockNumber)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
rpcstats.CountCall("eth_call")
|
||||
resp, err := cc.eth.CallContract(ctx, call, blockNumber)
|
||||
defer cc.toggleIsConnected(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *Client) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
|
||||
var feeHistory FeeHistory
|
||||
err := cc.rpcClient.Call(&feeHistory, cc.ChainID, "eth_feeHistory", "0x1", (*hexutil.Big)(blockNumber), nil)
|
||||
if err != nil {
|
||||
if err.Error() == "the method eth_feeHistory does not exist/is not available" {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
var baseGasFee string = ""
|
||||
if len(feeHistory.BaseFeePerGas) > 0 {
|
||||
baseGasFee = feeHistory.BaseFeePerGas[0]
|
||||
}
|
||||
|
||||
return baseGasFee, err
|
||||
}
|
|
@ -21,7 +21,7 @@ import (
|
|||
statusrpc "github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/rpc/network"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
|
@ -147,7 +147,7 @@ func (s *Service) isTokenVisible(tokenSymbol string) bool {
|
|||
|
||||
// Native token implementation of DataSource interface
|
||||
type chainClientSource struct {
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
currency string
|
||||
}
|
||||
|
||||
|
@ -505,7 +505,7 @@ func (s *Service) updateBalanceHistoryForAllEnabledNetworks(ctx context.Context)
|
|||
}
|
||||
|
||||
var dataSource DataSource
|
||||
chainClient, err := chain.NewClient(s.rpcClient, network.ChainID)
|
||||
chainClient, err := s.rpcClient.EthClient(network.ChainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
||||
|
@ -211,11 +210,10 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
})
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
clients, err := chain.NewClients(r.rpcClient, chainIDs)
|
||||
clients, err := r.rpcClient.EthClients(chainIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
balances, err = r.tokenManager.GetBalancesByChain(ctx, clients, addresses, tokenAddresses)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
@ -428,12 +427,12 @@ func (r *Router) requireApproval(ctx context.Context, bridge bridge.Bridge, acco
|
|||
}
|
||||
|
||||
func (r *Router) getBalance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
clients, err := chain.NewClients(r.s.rpcClient, []uint64{network.ChainID})
|
||||
client, err := r.s.rpcClient.EthClient(network.ChainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.s.tokenManager.GetBalance(ctx, clients[0], account, token.Address)
|
||||
return r.s.tokenManager.GetBalance(ctx, client, account, token.Address)
|
||||
}
|
||||
|
||||
func (r *Router) suggestedRoutes(
|
||||
|
|
|
@ -3,6 +3,7 @@ package wallet
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
@ -15,7 +16,6 @@ import (
|
|||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/ens"
|
||||
"github.com/status-im/status-go/services/stickers"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/currency"
|
||||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
|
@ -33,7 +33,7 @@ type Connection struct {
|
|||
}
|
||||
|
||||
type ConnectedResult struct {
|
||||
Blockchain map[uint64]Connection `json:"blockchain"`
|
||||
Blockchains map[uint64]Connection `json:"blockchains"`
|
||||
Market Connection `json:"market"`
|
||||
Collectibles map[uint64]Connection `json:"collectibles"`
|
||||
}
|
||||
|
@ -166,11 +166,21 @@ func (s *Service) IsStarted() bool {
|
|||
}
|
||||
|
||||
func (s *Service) CheckConnected(ctx context.Context) *ConnectedResult {
|
||||
blockchain := make(map[uint64]Connection)
|
||||
for chainID, client := range chain.ChainClientInstances {
|
||||
blockchain[chainID] = Connection{
|
||||
Up: client.IsConnected,
|
||||
LastCheckedAt: client.LastCheckedAt,
|
||||
networks, err := s.rpcClient.NetworkManager.Get(false)
|
||||
blockchains := make(map[uint64]Connection)
|
||||
if err == nil {
|
||||
for _, network := range networks {
|
||||
ethClient, err := s.rpcClient.EthClient(network.ChainID)
|
||||
if err != nil {
|
||||
blockchains[network.ChainID] = Connection{
|
||||
Up: true,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
blockchains[network.ChainID] = Connection{
|
||||
Up: ethClient.IsConnected,
|
||||
LastCheckedAt: ethClient.LastCheckedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +192,7 @@ func (s *Service) CheckConnected(ctx context.Context) *ConnectedResult {
|
|||
}
|
||||
}
|
||||
return &ConnectedResult{
|
||||
Blockchain: blockchain,
|
||||
Blockchains: blockchains,
|
||||
Collectibles: collectibles,
|
||||
Market: Connection{
|
||||
Up: s.marketManager.IsConnected,
|
||||
|
|
|
@ -16,9 +16,9 @@ import (
|
|||
"github.com/status-im/status-go/contracts/ierc20"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/rpc/network"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
)
|
||||
|
||||
var requestTimeout = 20 * time.Second
|
||||
|
@ -393,7 +393,7 @@ func (tm *Manager) DeleteCustom(chainID uint64, address common.Address) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||
func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address) (*big.Int, error) {
|
||||
caller, err := ierc20.NewIERC20Caller(token, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -404,7 +404,7 @@ func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.Client, ac
|
|||
}, account)
|
||||
}
|
||||
|
||||
func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.Client, account common.Address, token common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
caller, err := ierc20.NewIERC20Caller(token, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -416,11 +416,11 @@ func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.Client,
|
|||
}, account)
|
||||
}
|
||||
|
||||
func (tm *Manager) GetChainBalance(ctx context.Context, client *chain.Client, account common.Address) (*big.Int, error) {
|
||||
func (tm *Manager) GetChainBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address) (*big.Int, error) {
|
||||
return client.BalanceAt(ctx, account, nil)
|
||||
}
|
||||
|
||||
func (tm *Manager) GetBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||
func (tm *Manager) GetBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address) (*big.Int, error) {
|
||||
if token == nativeChainAddress {
|
||||
return tm.GetChainBalance(ctx, client, account)
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ func (tm *Manager) GetBalance(ctx context.Context, client *chain.Client, account
|
|||
return tm.GetTokenBalance(ctx, client, account, token)
|
||||
}
|
||||
|
||||
func (tm *Manager) GetBalances(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
func (tm *Manager) GetBalances(parent context.Context, clients []*chain.ClientWithFallback, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
var (
|
||||
group = async.NewAtomicGroup(parent)
|
||||
mu sync.Mutex
|
||||
|
@ -558,7 +558,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients []*chain.Client,
|
|||
return response, group.Error()
|
||||
}
|
||||
|
||||
func (tm *Manager) GetBalancesByChain(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
func (tm *Manager) GetBalancesByChain(parent context.Context, clients []*chain.ClientWithFallback, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
var (
|
||||
group = async.NewAtomicGroup(parent)
|
||||
mu sync.Mutex
|
||||
|
|
|
@ -16,10 +16,10 @@ import (
|
|||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
|
@ -218,7 +218,7 @@ func (tm *TransactionManager) deletePending(chainID uint64, hash common.Hash) er
|
|||
return err
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) watch(ctx context.Context, transactionHash common.Hash, client *chain.Client) error {
|
||||
func (tm *TransactionManager) watch(ctx context.Context, transactionHash common.Hash, client *chain.ClientWithFallback) error {
|
||||
watchTxCommand := &watchTransactionCommand{
|
||||
hash: transactionHash,
|
||||
client: client,
|
||||
|
@ -316,7 +316,7 @@ func (tm *TransactionManager) getVerifiedWalletAccount(address, password string)
|
|||
}
|
||||
|
||||
type watchTransactionCommand struct {
|
||||
client *chain.Client
|
||||
client *chain.ClientWithFallback
|
||||
hash common.Hash
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
)
|
||||
|
||||
type BlocksRange struct {
|
||||
|
@ -63,7 +63,7 @@ func (b *Block) mergeBlocksRanges(chainIDs []uint64, accounts []common.Address)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *Block) setInitialBlocksRange(chainClient *chain.Client) error {
|
||||
func (b *Block) setInitialBlocksRange(chainClient *chain.ClientWithFallback) error {
|
||||
accountsDB, err := accounts.NewDB(b.db)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
||||
|
@ -45,7 +45,7 @@ type ethHistoricalCommand struct {
|
|||
db *Database
|
||||
eth Downloader
|
||||
address common.Address
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
balanceCache *balanceCache
|
||||
feed *event.Feed
|
||||
foundHeaders []*DBHeader
|
||||
|
@ -96,7 +96,7 @@ type erc20HistoricalCommand struct {
|
|||
db *Database
|
||||
erc20 BatchDownloader
|
||||
address common.Address
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
feed *event.Feed
|
||||
|
||||
iterator *IterativeDownloader
|
||||
|
@ -164,7 +164,7 @@ type controlCommand struct {
|
|||
block *Block
|
||||
eth *ETHDownloader
|
||||
erc20 *ERC20TransfersDownloader
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
feed *event.Feed
|
||||
errorsCount int
|
||||
nonArchivalRPCNode bool
|
||||
|
@ -337,7 +337,7 @@ type transfersCommand struct {
|
|||
eth *ETHDownloader
|
||||
block *big.Int
|
||||
address common.Address
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
fetchedTransfers []Transfer
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ type loadTransfersCommand struct {
|
|||
accounts []common.Address
|
||||
db *Database
|
||||
block *Block
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
blocksByAddress map[common.Address][]*big.Int
|
||||
foundTransfersByAddress map[common.Address][]Transfer
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ func (c *loadTransfersCommand) Run(parent context.Context) (err error) {
|
|||
type findAndCheckBlockRangeCommand struct {
|
||||
accounts []common.Address
|
||||
db *Database
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
balanceCache *balanceCache
|
||||
feed *event.Feed
|
||||
fromByAddress map[common.Address]*LastKnownBlock
|
||||
|
@ -570,7 +570,7 @@ func (c *findAndCheckBlockRangeCommand) fastIndexErc20(ctx context.Context, from
|
|||
}
|
||||
}
|
||||
|
||||
func loadTransfers(ctx context.Context, accounts []common.Address, block *Block, db *Database, chainClient *chain.Client, limit int, blocksByAddress map[common.Address][]*big.Int) (map[common.Address][]Transfer, error) {
|
||||
func loadTransfers(ctx context.Context, accounts []common.Address, block *Block, db *Database, chainClient *chain.ClientWithFallback, limit int, blocksByAddress map[common.Address][]*big.Int) (map[common.Address][]Transfer, error) {
|
||||
start := time.Now()
|
||||
group := async.NewGroup(ctx)
|
||||
|
||||
|
@ -635,7 +635,7 @@ func getLowestFrom(chainID uint64, to *big.Int) *big.Int {
|
|||
return from
|
||||
}
|
||||
|
||||
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.Client) (*big.Int, error) {
|
||||
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.ClientWithFallback) (*big.Int, error) {
|
||||
from := getLowestFrom(client.ChainID, initialTo)
|
||||
to := initialTo
|
||||
goal := uint64(20)
|
||||
|
@ -691,7 +691,7 @@ func findFirstRange(c context.Context, account common.Address, initialTo *big.In
|
|||
return from, nil
|
||||
}
|
||||
|
||||
func findFirstRanges(c context.Context, accounts []common.Address, initialTo *big.Int, client *chain.Client) (map[common.Address]*big.Int, error) {
|
||||
func findFirstRanges(c context.Context, accounts []common.Address, initialTo *big.Int, client *chain.ClientWithFallback) (map[common.Address]*big.Int, error) {
|
||||
res := map[common.Address]*big.Int{}
|
||||
|
||||
for _, address := range accounts {
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
||||
|
@ -55,7 +55,7 @@ func (c *Controller) Stop() {
|
|||
}
|
||||
|
||||
func (c *Controller) SetInitialBlocksRange(chainIDs []uint64) error {
|
||||
chainClients, err := chain.NewClients(c.rpcClient, chainIDs)
|
||||
chainClients, err := c.rpcClient.EthClients(chainIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func (c *Controller) CheckRecentHistory(chainIDs []uint64, accounts []common.Add
|
|||
return err
|
||||
}
|
||||
|
||||
chainClients, err := chain.NewClients(c.rpcClient, chainIDs)
|
||||
chainClients, err := c.rpcClient.EthClients(chainIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func (c *Controller) CheckRecentHistory(chainIDs []uint64, accounts []common.Add
|
|||
|
||||
// watchAccountsChanges subsribes to a feed and watches for changes in accounts list. If there are new or removed accounts
|
||||
// reactor will be restarted.
|
||||
func watchAccountsChanges(ctx context.Context, accountFeed *event.Feed, reactor *Reactor, chainClients []*chain.Client, initial []common.Address) error {
|
||||
func watchAccountsChanges(ctx context.Context, accountFeed *event.Feed, reactor *Reactor, chainClients []*chain.ClientWithFallback, initial []common.Address) error {
|
||||
accounts := make(chan []*accounts.Account, 1) // it may block if the rate of updates will be significantly higher
|
||||
sub := accountFeed.Subscribe(accounts)
|
||||
defer sub.Unsubscribe()
|
||||
|
@ -164,7 +164,7 @@ func mapToList(m map[common.Address]struct{}) []common.Address {
|
|||
}
|
||||
|
||||
func (c *Controller) LoadTransferByHash(ctx context.Context, rpcClient *rpc.Client, address common.Address, hash common.Hash) error {
|
||||
chainClient, err := chain.NewClient(rpcClient, rpcClient.UpstreamChainID)
|
||||
chainClient, err := rpcClient.EthClient(rpcClient.UpstreamChainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ func (c *Controller) GetTransfersByAddress(ctx context.Context, chainID uint64,
|
|||
}
|
||||
|
||||
transfersCount := int64(len(rst))
|
||||
chainClient, err := chain.NewClient(c.rpcClient, chainID)
|
||||
chainClient, err := c.rpcClient.EthClient(c.rpcClient.UpstreamChainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
)
|
||||
|
||||
// Type type of the asset that was transferred.
|
||||
|
@ -53,7 +53,7 @@ type Transfer struct {
|
|||
|
||||
// ETHDownloader downloads regular eth transfers.
|
||||
type ETHDownloader struct {
|
||||
chainClient *chain.Client
|
||||
chainClient *chain.ClientWithFallback
|
||||
accounts []common.Address
|
||||
signer types.Signer
|
||||
db *Database
|
||||
|
@ -92,7 +92,7 @@ func (d *ETHDownloader) GetTransfersByNumber(ctx context.Context, number *big.In
|
|||
return rst, err
|
||||
}
|
||||
|
||||
func getTransferByHash(ctx context.Context, client *chain.Client, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
|
||||
func getTransferByHash(ctx context.Context, client *chain.ClientWithFallback, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
|
||||
transaction, _, err := client.TransactionByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -202,7 +202,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc
|
|||
}
|
||||
|
||||
// NewERC20TransfersDownloader returns new instance.
|
||||
func NewERC20TransfersDownloader(client *chain.Client, accounts []common.Address, signer types.Signer) *ERC20TransfersDownloader {
|
||||
func NewERC20TransfersDownloader(client *chain.ClientWithFallback, accounts []common.Address, signer types.Signer) *ERC20TransfersDownloader {
|
||||
signature := crypto.Keccak256Hash([]byte(erc20TransferEventSignature))
|
||||
return &ERC20TransfersDownloader{
|
||||
client: client,
|
||||
|
@ -214,7 +214,7 @@ func NewERC20TransfersDownloader(client *chain.Client, accounts []common.Address
|
|||
|
||||
// ERC20TransfersDownloader is a downloader for erc20 tokens transfers.
|
||||
type ERC20TransfersDownloader struct {
|
||||
client *chain.Client
|
||||
client *chain.ClientWithFallback
|
||||
accounts []common.Address
|
||||
|
||||
// hash of the Transfer event signature
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
)
|
||||
|
||||
var errAlreadyRunning = errors.New("already running")
|
||||
|
@ -37,7 +37,7 @@ type Reactor struct {
|
|||
group *async.Group
|
||||
}
|
||||
|
||||
func (r *Reactor) newControlCommand(chainClient *chain.Client, accounts []common.Address) *controlCommand {
|
||||
func (r *Reactor) newControlCommand(chainClient *chain.ClientWithFallback, accounts []common.Address) *controlCommand {
|
||||
signer := types.NewLondonSigner(chainClient.ToBigInt())
|
||||
ctl := &controlCommand{
|
||||
db: r.db,
|
||||
|
@ -59,7 +59,7 @@ func (r *Reactor) newControlCommand(chainClient *chain.Client, accounts []common
|
|||
}
|
||||
|
||||
// Start runs reactor loop in background.
|
||||
func (r *Reactor) start(chainClients []*chain.Client, accounts []common.Address) error {
|
||||
func (r *Reactor) start(chainClients []*chain.ClientWithFallback, accounts []common.Address) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
|
@ -86,7 +86,7 @@ func (r *Reactor) stop() {
|
|||
r.group = nil
|
||||
}
|
||||
|
||||
func (r *Reactor) restart(chainClients []*chain.Client, accounts []common.Address) error {
|
||||
func (r *Reactor) restart(chainClients []*chain.ClientWithFallback, accounts []common.Address) error {
|
||||
r.stop()
|
||||
return r.start(chainClients, accounts)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 keith
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,196 @@
|
|||
package hystrix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CircuitBreaker is created for each ExecutorPool to track whether requests
|
||||
// should be attempted, or rejected if the Health of the circuit is too low.
|
||||
type CircuitBreaker struct {
|
||||
Name string
|
||||
open bool
|
||||
forceOpen bool
|
||||
mutex *sync.RWMutex
|
||||
openedOrLastTestedTime int64
|
||||
|
||||
executorPool *executorPool
|
||||
metrics *metricExchange
|
||||
}
|
||||
|
||||
var (
|
||||
circuitBreakersMutex *sync.RWMutex
|
||||
circuitBreakers map[string]*CircuitBreaker
|
||||
)
|
||||
|
||||
func init() {
|
||||
circuitBreakersMutex = &sync.RWMutex{}
|
||||
circuitBreakers = make(map[string]*CircuitBreaker)
|
||||
}
|
||||
|
||||
// GetCircuit returns the circuit for the given command and whether this call created it.
|
||||
func GetCircuit(name string) (*CircuitBreaker, bool, error) {
|
||||
circuitBreakersMutex.RLock()
|
||||
_, ok := circuitBreakers[name]
|
||||
if !ok {
|
||||
circuitBreakersMutex.RUnlock()
|
||||
circuitBreakersMutex.Lock()
|
||||
defer circuitBreakersMutex.Unlock()
|
||||
// because we released the rlock before we obtained the exclusive lock,
|
||||
// we need to double check that some other thread didn't beat us to
|
||||
// creation.
|
||||
if cb, ok := circuitBreakers[name]; ok {
|
||||
return cb, false, nil
|
||||
}
|
||||
circuitBreakers[name] = newCircuitBreaker(name)
|
||||
} else {
|
||||
defer circuitBreakersMutex.RUnlock()
|
||||
}
|
||||
|
||||
return circuitBreakers[name], !ok, nil
|
||||
}
|
||||
|
||||
// Flush purges all circuit and metric information from memory.
|
||||
func Flush() {
|
||||
circuitBreakersMutex.Lock()
|
||||
defer circuitBreakersMutex.Unlock()
|
||||
|
||||
for name, cb := range circuitBreakers {
|
||||
cb.metrics.Reset()
|
||||
cb.executorPool.Metrics.Reset()
|
||||
delete(circuitBreakers, name)
|
||||
}
|
||||
}
|
||||
|
||||
// newCircuitBreaker creates a CircuitBreaker with associated Health
|
||||
func newCircuitBreaker(name string) *CircuitBreaker {
|
||||
c := &CircuitBreaker{}
|
||||
c.Name = name
|
||||
c.metrics = newMetricExchange(name)
|
||||
c.executorPool = newExecutorPool(name)
|
||||
c.mutex = &sync.RWMutex{}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// toggleForceOpen allows manually causing the fallback logic for all instances
|
||||
// of a given command.
|
||||
func (circuit *CircuitBreaker) toggleForceOpen(toggle bool) error {
|
||||
circuit, _, err := GetCircuit(circuit.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
circuit.forceOpen = toggle
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsOpen is called before any Command execution to check whether or
|
||||
// not it should be attempted. An "open" circuit means it is disabled.
|
||||
func (circuit *CircuitBreaker) IsOpen() bool {
|
||||
circuit.mutex.RLock()
|
||||
o := circuit.forceOpen || circuit.open
|
||||
circuit.mutex.RUnlock()
|
||||
|
||||
if o {
|
||||
return true
|
||||
}
|
||||
|
||||
if uint64(circuit.metrics.Requests().Sum(time.Now())) < getSettings(circuit.Name).RequestVolumeThreshold {
|
||||
return false
|
||||
}
|
||||
|
||||
if !circuit.metrics.IsHealthy(time.Now()) {
|
||||
// too many failures, open the circuit
|
||||
circuit.setOpen()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowRequest is checked before a command executes, ensuring that circuit state and metric health allow it.
|
||||
// When the circuit is open, this call will occasionally return true to measure whether the external service
|
||||
// has recovered.
|
||||
func (circuit *CircuitBreaker) AllowRequest() bool {
|
||||
return !circuit.IsOpen() || circuit.allowSingleTest()
|
||||
}
|
||||
|
||||
func (circuit *CircuitBreaker) allowSingleTest() bool {
|
||||
circuit.mutex.RLock()
|
||||
defer circuit.mutex.RUnlock()
|
||||
|
||||
now := time.Now().UnixNano()
|
||||
openedOrLastTestedTime := atomic.LoadInt64(&circuit.openedOrLastTestedTime)
|
||||
if circuit.open && now > openedOrLastTestedTime+getSettings(circuit.Name).SleepWindow.Nanoseconds() {
|
||||
swapped := atomic.CompareAndSwapInt64(&circuit.openedOrLastTestedTime, openedOrLastTestedTime, now)
|
||||
if swapped {
|
||||
log.Printf("hystrix-go: allowing single test to possibly close circuit %v", circuit.Name)
|
||||
}
|
||||
return swapped
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (circuit *CircuitBreaker) setOpen() {
|
||||
circuit.mutex.Lock()
|
||||
defer circuit.mutex.Unlock()
|
||||
|
||||
if circuit.open {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("hystrix-go: opening circuit %v", circuit.Name)
|
||||
|
||||
circuit.openedOrLastTestedTime = time.Now().UnixNano()
|
||||
circuit.open = true
|
||||
}
|
||||
|
||||
func (circuit *CircuitBreaker) setClose() {
|
||||
circuit.mutex.Lock()
|
||||
defer circuit.mutex.Unlock()
|
||||
|
||||
if !circuit.open {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("hystrix-go: closing circuit %v", circuit.Name)
|
||||
|
||||
circuit.open = false
|
||||
circuit.metrics.Reset()
|
||||
}
|
||||
|
||||
// ReportEvent records command metrics for tracking recent error rates and exposing data to the dashboard.
|
||||
func (circuit *CircuitBreaker) ReportEvent(eventTypes []string, start time.Time, runDuration time.Duration) error {
|
||||
if len(eventTypes) == 0 {
|
||||
return fmt.Errorf("no event types sent for metrics")
|
||||
}
|
||||
|
||||
circuit.mutex.RLock()
|
||||
o := circuit.open
|
||||
circuit.mutex.RUnlock()
|
||||
if eventTypes[0] == "success" && o {
|
||||
circuit.setClose()
|
||||
}
|
||||
|
||||
var concurrencyInUse float64
|
||||
if circuit.executorPool.Max > 0 {
|
||||
concurrencyInUse = float64(circuit.executorPool.ActiveCount()) / float64(circuit.executorPool.Max)
|
||||
}
|
||||
|
||||
select {
|
||||
case circuit.metrics.Updates <- &commandExecution{
|
||||
Types: eventTypes,
|
||||
Start: start,
|
||||
RunDuration: runDuration,
|
||||
ConcurrencyInUse: concurrencyInUse,
|
||||
}:
|
||||
default:
|
||||
return CircuitError{Message: fmt.Sprintf("metrics channel (%v) is at capacity", circuit.Name)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Package hystrix is a latency and fault tolerance library designed to isolate
|
||||
points of access to remote systems, services and 3rd party libraries, stop
|
||||
cascading failure and enable resilience in complex distributed systems where
|
||||
failure is inevitable.
|
||||
|
||||
Based on the java project of the same name, by Netflix. https://github.com/Netflix/Hystrix
|
||||
|
||||
Execute code as a Hystrix command
|
||||
|
||||
Define your application logic which relies on external systems, passing your function to Go. When that system is healthy this will be the only thing which executes.
|
||||
|
||||
hystrix.Go("my_command", func() error {
|
||||
// talk to other services
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
Defining fallback behavior
|
||||
|
||||
If you want code to execute during a service outage, pass in a second function to Go. Ideally, the logic here will allow your application to gracefully handle external services being unavailable.
|
||||
|
||||
This triggers when your code returns an error, or whenever it is unable to complete based on a variety of health checks https://github.com/Netflix/Hystrix/wiki/How-it-Works.
|
||||
|
||||
hystrix.Go("my_command", func() error {
|
||||
// talk to other services
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
// do this when services are down
|
||||
return nil
|
||||
})
|
||||
|
||||
Waiting for output
|
||||
|
||||
Calling Go is like launching a goroutine, except you receive a channel of errors you can choose to monitor.
|
||||
|
||||
output := make(chan bool, 1)
|
||||
errors := hystrix.Go("my_command", func() error {
|
||||
// talk to other services
|
||||
output <- true
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
select {
|
||||
case out := <-output:
|
||||
// success
|
||||
case err := <-errors:
|
||||
// failure
|
||||
}
|
||||
|
||||
Synchronous API
|
||||
|
||||
Since calling a command and immediately waiting for it to finish is a common pattern, a synchronous API is available with the Do function which returns a single error.
|
||||
|
||||
err := hystrix.Do("my_command", func() error {
|
||||
// talk to other services
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
Configure settings
|
||||
|
||||
During application boot, you can call ConfigureCommand to tweak the settings for each command.
|
||||
|
||||
hystrix.ConfigureCommand("my_command", hystrix.CommandConfig{
|
||||
Timeout: 1000,
|
||||
MaxConcurrentRequests: 100,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
|
||||
You can also use Configure which accepts a map[string]CommandConfig.
|
||||
|
||||
Enable dashboard metrics
|
||||
|
||||
In your main.go, register the event stream HTTP handler on a port and launch it in a goroutine. Once you configure turbine for your Hystrix Dashboard https://github.com/Netflix/Hystrix/tree/master/hystrix-dashboard to start streaming events, your commands will automatically begin appearing.
|
||||
|
||||
hystrixStreamHandler := hystrix.NewStreamHandler()
|
||||
hystrixStreamHandler.Start()
|
||||
go http.ListenAndServe(net.JoinHostPort("", "81"), hystrixStreamHandler)
|
||||
*/
|
||||
package hystrix
|
|
@ -0,0 +1,326 @@
|
|||
package hystrix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix/rolling"
|
||||
)
|
||||
|
||||
const (
|
||||
streamEventBufferSize = 10
|
||||
)
|
||||
|
||||
// NewStreamHandler returns a server capable of exposing dashboard metrics via HTTP.
|
||||
func NewStreamHandler() *StreamHandler {
|
||||
return &StreamHandler{}
|
||||
}
|
||||
|
||||
// StreamHandler publishes metrics for each command and each pool once a second to all connected HTTP client.
|
||||
type StreamHandler struct {
|
||||
requests map[*http.Request]chan []byte
|
||||
mu sync.RWMutex
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Start begins watching the in-memory circuit breakers for metrics
|
||||
func (sh *StreamHandler) Start() {
|
||||
sh.requests = make(map[*http.Request]chan []byte)
|
||||
sh.done = make(chan struct{})
|
||||
go sh.loop()
|
||||
}
|
||||
|
||||
// Stop shuts down the metric collection routine
|
||||
func (sh *StreamHandler) Stop() {
|
||||
close(sh.done)
|
||||
}
|
||||
|
||||
var _ http.Handler = (*StreamHandler)(nil)
|
||||
|
||||
func (sh *StreamHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
// Make sure that the writer supports flushing.
|
||||
f, ok := rw.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(rw, "Streaming unsupported!", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
events := sh.register(req)
|
||||
defer sh.unregister(req)
|
||||
|
||||
notify := rw.(http.CloseNotifier).CloseNotify()
|
||||
|
||||
rw.Header().Add("Content-Type", "text/event-stream")
|
||||
rw.Header().Set("Cache-Control", "no-cache")
|
||||
rw.Header().Set("Connection", "keep-alive")
|
||||
for {
|
||||
select {
|
||||
case <-notify:
|
||||
// client is gone
|
||||
return
|
||||
case event := <-events:
|
||||
_, err := rw.Write(event)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *StreamHandler) loop() {
|
||||
tick := time.Tick(1 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-tick:
|
||||
circuitBreakersMutex.RLock()
|
||||
for _, cb := range circuitBreakers {
|
||||
sh.publishMetrics(cb)
|
||||
sh.publishThreadPools(cb.executorPool)
|
||||
}
|
||||
circuitBreakersMutex.RUnlock()
|
||||
case <-sh.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *StreamHandler) publishMetrics(cb *CircuitBreaker) error {
|
||||
now := time.Now()
|
||||
reqCount := cb.metrics.Requests().Sum(now)
|
||||
errCount := cb.metrics.DefaultCollector().Errors().Sum(now)
|
||||
errPct := cb.metrics.ErrorPercent(now)
|
||||
|
||||
eventBytes, err := json.Marshal(&streamCmdMetric{
|
||||
Type: "HystrixCommand",
|
||||
Name: cb.Name,
|
||||
Group: cb.Name,
|
||||
Time: currentTime(),
|
||||
ReportingHosts: 1,
|
||||
|
||||
RequestCount: uint32(reqCount),
|
||||
ErrorCount: uint32(errCount),
|
||||
ErrorPct: uint32(errPct),
|
||||
CircuitBreakerOpen: cb.IsOpen(),
|
||||
|
||||
RollingCountSuccess: uint32(cb.metrics.DefaultCollector().Successes().Sum(now)),
|
||||
RollingCountFailure: uint32(cb.metrics.DefaultCollector().Failures().Sum(now)),
|
||||
RollingCountThreadPoolRejected: uint32(cb.metrics.DefaultCollector().Rejects().Sum(now)),
|
||||
RollingCountShortCircuited: uint32(cb.metrics.DefaultCollector().ShortCircuits().Sum(now)),
|
||||
RollingCountTimeout: uint32(cb.metrics.DefaultCollector().Timeouts().Sum(now)),
|
||||
RollingCountFallbackSuccess: uint32(cb.metrics.DefaultCollector().FallbackSuccesses().Sum(now)),
|
||||
RollingCountFallbackFailure: uint32(cb.metrics.DefaultCollector().FallbackFailures().Sum(now)),
|
||||
|
||||
LatencyTotal: generateLatencyTimings(cb.metrics.DefaultCollector().TotalDuration()),
|
||||
LatencyTotalMean: cb.metrics.DefaultCollector().TotalDuration().Mean(),
|
||||
LatencyExecute: generateLatencyTimings(cb.metrics.DefaultCollector().RunDuration()),
|
||||
LatencyExecuteMean: cb.metrics.DefaultCollector().RunDuration().Mean(),
|
||||
|
||||
// TODO: all hard-coded values should become configurable settings, per circuit
|
||||
|
||||
RollingStatsWindow: 10000,
|
||||
ExecutionIsolationStrategy: "THREAD",
|
||||
|
||||
CircuitBreakerEnabled: true,
|
||||
CircuitBreakerForceClosed: false,
|
||||
CircuitBreakerForceOpen: cb.forceOpen,
|
||||
CircuitBreakerErrorThresholdPercent: uint32(getSettings(cb.Name).ErrorPercentThreshold),
|
||||
CircuitBreakerSleepWindow: uint32(getSettings(cb.Name).SleepWindow.Seconds() * 1000),
|
||||
CircuitBreakerRequestVolumeThreshold: uint32(getSettings(cb.Name).RequestVolumeThreshold),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sh.writeToRequests(eventBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sh *StreamHandler) publishThreadPools(pool *executorPool) error {
|
||||
now := time.Now()
|
||||
|
||||
eventBytes, err := json.Marshal(&streamThreadPoolMetric{
|
||||
Type: "HystrixThreadPool",
|
||||
Name: pool.Name,
|
||||
ReportingHosts: 1,
|
||||
|
||||
CurrentActiveCount: uint32(pool.ActiveCount()),
|
||||
CurrentTaskCount: 0,
|
||||
CurrentCompletedTaskCount: 0,
|
||||
|
||||
RollingCountThreadsExecuted: uint32(pool.Metrics.Executed.Sum(now)),
|
||||
RollingMaxActiveThreads: uint32(pool.Metrics.MaxActiveRequests.Max(now)),
|
||||
|
||||
CurrentPoolSize: uint32(pool.Max),
|
||||
CurrentCorePoolSize: uint32(pool.Max),
|
||||
CurrentLargestPoolSize: uint32(pool.Max),
|
||||
CurrentMaximumPoolSize: uint32(pool.Max),
|
||||
|
||||
RollingStatsWindow: 10000,
|
||||
QueueSizeRejectionThreshold: 0,
|
||||
CurrentQueueSize: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sh.writeToRequests(eventBytes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sh *StreamHandler) writeToRequests(eventBytes []byte) error {
|
||||
var b bytes.Buffer
|
||||
_, err := b.Write([]byte("data:"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = b.Write(eventBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = b.Write([]byte("\n\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataBytes := b.Bytes()
|
||||
sh.mu.RLock()
|
||||
|
||||
for _, requestEvents := range sh.requests {
|
||||
select {
|
||||
case requestEvents <- dataBytes:
|
||||
default:
|
||||
}
|
||||
}
|
||||
sh.mu.RUnlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sh *StreamHandler) register(req *http.Request) <-chan []byte {
|
||||
sh.mu.RLock()
|
||||
events, ok := sh.requests[req]
|
||||
sh.mu.RUnlock()
|
||||
if ok {
|
||||
return events
|
||||
}
|
||||
|
||||
events = make(chan []byte, streamEventBufferSize)
|
||||
sh.mu.Lock()
|
||||
sh.requests[req] = events
|
||||
sh.mu.Unlock()
|
||||
return events
|
||||
}
|
||||
|
||||
func (sh *StreamHandler) unregister(req *http.Request) {
|
||||
sh.mu.Lock()
|
||||
delete(sh.requests, req)
|
||||
sh.mu.Unlock()
|
||||
}
|
||||
|
||||
func generateLatencyTimings(r *rolling.Timing) streamCmdLatency {
|
||||
return streamCmdLatency{
|
||||
Timing0: r.Percentile(0),
|
||||
Timing25: r.Percentile(25),
|
||||
Timing50: r.Percentile(50),
|
||||
Timing75: r.Percentile(75),
|
||||
Timing90: r.Percentile(90),
|
||||
Timing95: r.Percentile(95),
|
||||
Timing99: r.Percentile(99),
|
||||
Timing995: r.Percentile(99.5),
|
||||
Timing100: r.Percentile(100),
|
||||
}
|
||||
}
|
||||
|
||||
type streamCmdMetric struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Group string `json:"group"`
|
||||
Time int64 `json:"currentTime"`
|
||||
ReportingHosts uint32 `json:"reportingHosts"`
|
||||
|
||||
// Health
|
||||
RequestCount uint32 `json:"requestCount"`
|
||||
ErrorCount uint32 `json:"errorCount"`
|
||||
ErrorPct uint32 `json:"errorPercentage"`
|
||||
CircuitBreakerOpen bool `json:"isCircuitBreakerOpen"`
|
||||
|
||||
RollingCountCollapsedRequests uint32 `json:"rollingCountCollapsedRequests"`
|
||||
RollingCountExceptionsThrown uint32 `json:"rollingCountExceptionsThrown"`
|
||||
RollingCountFailure uint32 `json:"rollingCountFailure"`
|
||||
RollingCountFallbackFailure uint32 `json:"rollingCountFallbackFailure"`
|
||||
RollingCountFallbackRejection uint32 `json:"rollingCountFallbackRejection"`
|
||||
RollingCountFallbackSuccess uint32 `json:"rollingCountFallbackSuccess"`
|
||||
RollingCountResponsesFromCache uint32 `json:"rollingCountResponsesFromCache"`
|
||||
RollingCountSemaphoreRejected uint32 `json:"rollingCountSemaphoreRejected"`
|
||||
RollingCountShortCircuited uint32 `json:"rollingCountShortCircuited"`
|
||||
RollingCountSuccess uint32 `json:"rollingCountSuccess"`
|
||||
RollingCountThreadPoolRejected uint32 `json:"rollingCountThreadPoolRejected"`
|
||||
RollingCountTimeout uint32 `json:"rollingCountTimeout"`
|
||||
|
||||
CurrentConcurrentExecutionCount uint32 `json:"currentConcurrentExecutionCount"`
|
||||
|
||||
LatencyExecuteMean uint32 `json:"latencyExecute_mean"`
|
||||
LatencyExecute streamCmdLatency `json:"latencyExecute"`
|
||||
LatencyTotalMean uint32 `json:"latencyTotal_mean"`
|
||||
LatencyTotal streamCmdLatency `json:"latencyTotal"`
|
||||
|
||||
// Properties
|
||||
CircuitBreakerRequestVolumeThreshold uint32 `json:"propertyValue_circuitBreakerRequestVolumeThreshold"`
|
||||
CircuitBreakerSleepWindow uint32 `json:"propertyValue_circuitBreakerSleepWindowInMilliseconds"`
|
||||
CircuitBreakerErrorThresholdPercent uint32 `json:"propertyValue_circuitBreakerErrorThresholdPercentage"`
|
||||
CircuitBreakerForceOpen bool `json:"propertyValue_circuitBreakerForceOpen"`
|
||||
CircuitBreakerForceClosed bool `json:"propertyValue_circuitBreakerForceClosed"`
|
||||
CircuitBreakerEnabled bool `json:"propertyValue_circuitBreakerEnabled"`
|
||||
ExecutionIsolationStrategy string `json:"propertyValue_executionIsolationStrategy"`
|
||||
ExecutionIsolationThreadTimeout uint32 `json:"propertyValue_executionIsolationThreadTimeoutInMilliseconds"`
|
||||
ExecutionIsolationThreadInterruptOnTimeout bool `json:"propertyValue_executionIsolationThreadInterruptOnTimeout"`
|
||||
ExecutionIsolationThreadPoolKeyOverride string `json:"propertyValue_executionIsolationThreadPoolKeyOverride"`
|
||||
ExecutionIsolationSemaphoreMaxConcurrentRequests uint32 `json:"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests"`
|
||||
FallbackIsolationSemaphoreMaxConcurrentRequests uint32 `json:"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests"`
|
||||
RollingStatsWindow uint32 `json:"propertyValue_metricsRollingStatisticalWindowInMilliseconds"`
|
||||
RequestCacheEnabled bool `json:"propertyValue_requestCacheEnabled"`
|
||||
RequestLogEnabled bool `json:"propertyValue_requestLogEnabled"`
|
||||
}
|
||||
|
||||
type streamCmdLatency struct {
|
||||
Timing0 uint32 `json:"0"`
|
||||
Timing25 uint32 `json:"25"`
|
||||
Timing50 uint32 `json:"50"`
|
||||
Timing75 uint32 `json:"75"`
|
||||
Timing90 uint32 `json:"90"`
|
||||
Timing95 uint32 `json:"95"`
|
||||
Timing99 uint32 `json:"99"`
|
||||
Timing995 uint32 `json:"99.5"`
|
||||
Timing100 uint32 `json:"100"`
|
||||
}
|
||||
|
||||
type streamThreadPoolMetric struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
ReportingHosts uint32 `json:"reportingHosts"`
|
||||
|
||||
CurrentActiveCount uint32 `json:"currentActiveCount"`
|
||||
CurrentCompletedTaskCount uint32 `json:"currentCompletedTaskCount"`
|
||||
CurrentCorePoolSize uint32 `json:"currentCorePoolSize"`
|
||||
CurrentLargestPoolSize uint32 `json:"currentLargestPoolSize"`
|
||||
CurrentMaximumPoolSize uint32 `json:"currentMaximumPoolSize"`
|
||||
CurrentPoolSize uint32 `json:"currentPoolSize"`
|
||||
CurrentQueueSize uint32 `json:"currentQueueSize"`
|
||||
CurrentTaskCount uint32 `json:"currentTaskCount"`
|
||||
|
||||
RollingMaxActiveThreads uint32 `json:"rollingMaxActiveThreads"`
|
||||
RollingCountThreadsExecuted uint32 `json:"rollingCountThreadsExecuted"`
|
||||
|
||||
RollingStatsWindow uint32 `json:"propertyValue_metricsRollingStatisticalWindowInMilliseconds"`
|
||||
QueueSizeRejectionThreshold uint32 `json:"propertyValue_queueSizeRejectionThreshold"`
|
||||
}
|
||||
|
||||
func currentTime() int64 {
|
||||
return time.Now().UnixNano() / int64(1000000)
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
package hystrix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type runFunc func() error
|
||||
type fallbackFunc func(error) error
|
||||
type runFuncC func(context.Context) error
|
||||
type fallbackFuncC func(context.Context, error) error
|
||||
|
||||
// A CircuitError is an error which models various failure states of execution,
|
||||
// such as the circuit being open or a timeout.
|
||||
type CircuitError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e CircuitError) Error() string {
|
||||
return "hystrix: " + e.Message
|
||||
}
|
||||
|
||||
// command models the state used for a single execution on a circuit. "hystrix command" is commonly
|
||||
// used to describe the pairing of your run/fallback functions with a circuit.
|
||||
type command struct {
|
||||
sync.Mutex
|
||||
|
||||
ticket *struct{}
|
||||
start time.Time
|
||||
errChan chan error
|
||||
finished chan bool
|
||||
circuit *CircuitBreaker
|
||||
run runFuncC
|
||||
fallback fallbackFuncC
|
||||
runDuration time.Duration
|
||||
events []string
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrMaxConcurrency occurs when too many of the same named command are executed at the same time.
|
||||
ErrMaxConcurrency = CircuitError{Message: "max concurrency"}
|
||||
// ErrCircuitOpen returns when an execution attempt "short circuits". This happens due to the circuit being measured as unhealthy.
|
||||
ErrCircuitOpen = CircuitError{Message: "circuit open"}
|
||||
// ErrTimeout occurs when the provided function takes too long to execute.
|
||||
ErrTimeout = CircuitError{Message: "timeout"}
|
||||
)
|
||||
|
||||
// Go runs your function while tracking the health of previous calls to it.
|
||||
// If your function begins slowing down or failing repeatedly, we will block
|
||||
// new calls to it for you to give the dependent service time to repair.
|
||||
//
|
||||
// Define a fallback function if you want to define some code to execute during outages.
|
||||
func Go(name string, run runFunc, fallback fallbackFunc) chan error {
|
||||
runC := func(ctx context.Context) error {
|
||||
return run()
|
||||
}
|
||||
var fallbackC fallbackFuncC
|
||||
if fallback != nil {
|
||||
fallbackC = func(ctx context.Context, err error) error {
|
||||
return fallback(err)
|
||||
}
|
||||
}
|
||||
return GoC(context.Background(), name, runC, fallbackC)
|
||||
}
|
||||
|
||||
// GoC runs your function while tracking the health of previous calls to it.
|
||||
// If your function begins slowing down or failing repeatedly, we will block
|
||||
// new calls to it for you to give the dependent service time to repair.
|
||||
//
|
||||
// Define a fallback function if you want to define some code to execute during outages.
|
||||
func GoC(ctx context.Context, name string, run runFuncC, fallback fallbackFuncC) chan error {
|
||||
cmd := &command{
|
||||
run: run,
|
||||
fallback: fallback,
|
||||
start: time.Now(),
|
||||
errChan: make(chan error, 1),
|
||||
finished: make(chan bool, 1),
|
||||
}
|
||||
|
||||
// dont have methods with explicit params and returns
|
||||
// let data come in and out naturally, like with any closure
|
||||
// explicit error return to give place for us to kill switch the operation (fallback)
|
||||
|
||||
circuit, _, err := GetCircuit(name)
|
||||
if err != nil {
|
||||
cmd.errChan <- err
|
||||
return cmd.errChan
|
||||
}
|
||||
cmd.circuit = circuit
|
||||
ticketCond := sync.NewCond(cmd)
|
||||
ticketChecked := false
|
||||
// When the caller extracts error from returned errChan, it's assumed that
|
||||
// the ticket's been returned to executorPool. Therefore, returnTicket() can
|
||||
// not run after cmd.errorWithFallback().
|
||||
returnTicket := func() {
|
||||
cmd.Lock()
|
||||
// Avoid releasing before a ticket is acquired.
|
||||
for !ticketChecked {
|
||||
ticketCond.Wait()
|
||||
}
|
||||
cmd.circuit.executorPool.Return(cmd.ticket)
|
||||
cmd.Unlock()
|
||||
}
|
||||
// Shared by the following two goroutines. It ensures only the faster
|
||||
// goroutine runs errWithFallback() and reportAllEvent().
|
||||
returnOnce := &sync.Once{}
|
||||
reportAllEvent := func() {
|
||||
err := cmd.circuit.ReportEvent(cmd.events, cmd.start, cmd.runDuration)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() { cmd.finished <- true }()
|
||||
|
||||
// Circuits get opened when recent executions have shown to have a high error rate.
|
||||
// Rejecting new executions allows backends to recover, and the circuit will allow
|
||||
// new traffic when it feels a healthly state has returned.
|
||||
if !cmd.circuit.AllowRequest() {
|
||||
cmd.Lock()
|
||||
// It's safe for another goroutine to go ahead releasing a nil ticket.
|
||||
ticketChecked = true
|
||||
ticketCond.Signal()
|
||||
cmd.Unlock()
|
||||
returnOnce.Do(func() {
|
||||
returnTicket()
|
||||
cmd.errorWithFallback(ctx, ErrCircuitOpen)
|
||||
reportAllEvent()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// As backends falter, requests take longer but don't always fail.
|
||||
//
|
||||
// When requests slow down but the incoming rate of requests stays the same, you have to
|
||||
// run more at a time to keep up. By controlling concurrency during these situations, you can
|
||||
// shed load which accumulates due to the increasing ratio of active commands to incoming requests.
|
||||
cmd.Lock()
|
||||
select {
|
||||
case cmd.ticket = <-circuit.executorPool.Tickets:
|
||||
ticketChecked = true
|
||||
ticketCond.Signal()
|
||||
cmd.Unlock()
|
||||
default:
|
||||
ticketChecked = true
|
||||
ticketCond.Signal()
|
||||
cmd.Unlock()
|
||||
returnOnce.Do(func() {
|
||||
returnTicket()
|
||||
cmd.errorWithFallback(ctx, ErrMaxConcurrency)
|
||||
reportAllEvent()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
runStart := time.Now()
|
||||
runErr := run(ctx)
|
||||
returnOnce.Do(func() {
|
||||
defer reportAllEvent()
|
||||
cmd.runDuration = time.Since(runStart)
|
||||
returnTicket()
|
||||
if runErr != nil {
|
||||
cmd.errorWithFallback(ctx, runErr)
|
||||
return
|
||||
}
|
||||
cmd.reportEvent("success")
|
||||
})
|
||||
}()
|
||||
|
||||
go func() {
|
||||
timer := time.NewTimer(getSettings(name).Timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-cmd.finished:
|
||||
// returnOnce has been executed in another goroutine
|
||||
case <-ctx.Done():
|
||||
returnOnce.Do(func() {
|
||||
returnTicket()
|
||||
cmd.errorWithFallback(ctx, ctx.Err())
|
||||
reportAllEvent()
|
||||
})
|
||||
return
|
||||
case <-timer.C:
|
||||
returnOnce.Do(func() {
|
||||
returnTicket()
|
||||
cmd.errorWithFallback(ctx, ErrTimeout)
|
||||
reportAllEvent()
|
||||
})
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
return cmd.errChan
|
||||
}
|
||||
|
||||
// Do runs your function in a synchronous manner, blocking until either your function succeeds
|
||||
// or an error is returned, including hystrix circuit errors
|
||||
func Do(name string, run runFunc, fallback fallbackFunc) error {
|
||||
runC := func(ctx context.Context) error {
|
||||
return run()
|
||||
}
|
||||
var fallbackC fallbackFuncC
|
||||
if fallback != nil {
|
||||
fallbackC = func(ctx context.Context, err error) error {
|
||||
return fallback(err)
|
||||
}
|
||||
}
|
||||
return DoC(context.Background(), name, runC, fallbackC)
|
||||
}
|
||||
|
||||
// DoC runs your function in a synchronous manner, blocking until either your function succeeds
|
||||
// or an error is returned, including hystrix circuit errors
|
||||
func DoC(ctx context.Context, name string, run runFuncC, fallback fallbackFuncC) error {
|
||||
done := make(chan struct{}, 1)
|
||||
|
||||
r := func(ctx context.Context) error {
|
||||
err := run(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
f := func(ctx context.Context, e error) error {
|
||||
err := fallback(ctx, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errChan chan error
|
||||
if fallback == nil {
|
||||
errChan = GoC(ctx, name, r, nil)
|
||||
} else {
|
||||
errChan = GoC(ctx, name, r, f)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case err := <-errChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *command) reportEvent(eventType string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.events = append(c.events, eventType)
|
||||
}
|
||||
|
||||
// errorWithFallback triggers the fallback while reporting the appropriate metric events.
|
||||
func (c *command) errorWithFallback(ctx context.Context, err error) {
|
||||
eventType := "failure"
|
||||
if err == ErrCircuitOpen {
|
||||
eventType = "short-circuit"
|
||||
} else if err == ErrMaxConcurrency {
|
||||
eventType = "rejected"
|
||||
} else if err == ErrTimeout {
|
||||
eventType = "timeout"
|
||||
} else if err == context.Canceled {
|
||||
eventType = "context_canceled"
|
||||
} else if err == context.DeadlineExceeded {
|
||||
eventType = "context_deadline_exceeded"
|
||||
}
|
||||
|
||||
c.reportEvent(eventType)
|
||||
fallbackErr := c.tryFallback(ctx, err)
|
||||
if fallbackErr != nil {
|
||||
c.errChan <- fallbackErr
|
||||
}
|
||||
}
|
||||
|
||||
func (c *command) tryFallback(ctx context.Context, err error) error {
|
||||
if c.fallback == nil {
|
||||
// If we don't have a fallback return the original error.
|
||||
return err
|
||||
}
|
||||
|
||||
fallbackErr := c.fallback(ctx, err)
|
||||
if fallbackErr != nil {
|
||||
c.reportEvent("fallback-failure")
|
||||
return fmt.Errorf("fallback failed with '%v'. run error was '%v'", fallbackErr, err)
|
||||
}
|
||||
|
||||
c.reportEvent("fallback-success")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package hystrix
|
||||
|
||||
type logger interface {
|
||||
Printf(format string, items ...interface{})
|
||||
}
|
||||
|
||||
// NoopLogger does not log anything.
|
||||
type NoopLogger struct{}
|
||||
|
||||
// Printf does nothing.
|
||||
func (l NoopLogger) Printf(format string, items ...interface{}) {}
|
169
vendor/github.com/afex/hystrix-go/hystrix/metric_collector/default_metric_collector.go
generated
vendored
Normal file
169
vendor/github.com/afex/hystrix-go/hystrix/metric_collector/default_metric_collector.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package metricCollector
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix/rolling"
|
||||
)
|
||||
|
||||
// DefaultMetricCollector holds information about the circuit state.
|
||||
// This implementation of MetricCollector is the canonical source of information about the circuit.
|
||||
// It is used for for all internal hystrix operations
|
||||
// including circuit health checks and metrics sent to the hystrix dashboard.
|
||||
//
|
||||
// Metric Collectors do not need Mutexes as they are updated by circuits within a locked context.
|
||||
type DefaultMetricCollector struct {
|
||||
mutex *sync.RWMutex
|
||||
|
||||
numRequests *rolling.Number
|
||||
errors *rolling.Number
|
||||
|
||||
successes *rolling.Number
|
||||
failures *rolling.Number
|
||||
rejects *rolling.Number
|
||||
shortCircuits *rolling.Number
|
||||
timeouts *rolling.Number
|
||||
contextCanceled *rolling.Number
|
||||
contextDeadlineExceeded *rolling.Number
|
||||
|
||||
fallbackSuccesses *rolling.Number
|
||||
fallbackFailures *rolling.Number
|
||||
totalDuration *rolling.Timing
|
||||
runDuration *rolling.Timing
|
||||
}
|
||||
|
||||
func newDefaultMetricCollector(name string) MetricCollector {
|
||||
m := &DefaultMetricCollector{}
|
||||
m.mutex = &sync.RWMutex{}
|
||||
m.Reset()
|
||||
return m
|
||||
}
|
||||
|
||||
// NumRequests returns the rolling number of requests
|
||||
func (d *DefaultMetricCollector) NumRequests() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.numRequests
|
||||
}
|
||||
|
||||
// Errors returns the rolling number of errors
|
||||
func (d *DefaultMetricCollector) Errors() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.errors
|
||||
}
|
||||
|
||||
// Successes returns the rolling number of successes
|
||||
func (d *DefaultMetricCollector) Successes() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.successes
|
||||
}
|
||||
|
||||
// Failures returns the rolling number of failures
|
||||
func (d *DefaultMetricCollector) Failures() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.failures
|
||||
}
|
||||
|
||||
// Rejects returns the rolling number of rejects
|
||||
func (d *DefaultMetricCollector) Rejects() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.rejects
|
||||
}
|
||||
|
||||
// ShortCircuits returns the rolling number of short circuits
|
||||
func (d *DefaultMetricCollector) ShortCircuits() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.shortCircuits
|
||||
}
|
||||
|
||||
// Timeouts returns the rolling number of timeouts
|
||||
func (d *DefaultMetricCollector) Timeouts() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.timeouts
|
||||
}
|
||||
|
||||
// FallbackSuccesses returns the rolling number of fallback successes
|
||||
func (d *DefaultMetricCollector) FallbackSuccesses() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.fallbackSuccesses
|
||||
}
|
||||
|
||||
func (d *DefaultMetricCollector) ContextCanceled() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.contextCanceled
|
||||
}
|
||||
|
||||
func (d *DefaultMetricCollector) ContextDeadlineExceeded() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.contextDeadlineExceeded
|
||||
}
|
||||
|
||||
// FallbackFailures returns the rolling number of fallback failures
|
||||
func (d *DefaultMetricCollector) FallbackFailures() *rolling.Number {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.fallbackFailures
|
||||
}
|
||||
|
||||
// TotalDuration returns the rolling total duration
|
||||
func (d *DefaultMetricCollector) TotalDuration() *rolling.Timing {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.totalDuration
|
||||
}
|
||||
|
||||
// RunDuration returns the rolling run duration
|
||||
func (d *DefaultMetricCollector) RunDuration() *rolling.Timing {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
return d.runDuration
|
||||
}
|
||||
|
||||
func (d *DefaultMetricCollector) Update(r MetricResult) {
|
||||
d.mutex.RLock()
|
||||
defer d.mutex.RUnlock()
|
||||
|
||||
d.numRequests.Increment(r.Attempts)
|
||||
d.errors.Increment(r.Errors)
|
||||
d.successes.Increment(r.Successes)
|
||||
d.failures.Increment(r.Failures)
|
||||
d.rejects.Increment(r.Rejects)
|
||||
d.shortCircuits.Increment(r.ShortCircuits)
|
||||
d.timeouts.Increment(r.Timeouts)
|
||||
d.fallbackSuccesses.Increment(r.FallbackSuccesses)
|
||||
d.fallbackFailures.Increment(r.FallbackFailures)
|
||||
d.contextCanceled.Increment(r.ContextCanceled)
|
||||
d.contextDeadlineExceeded.Increment(r.ContextDeadlineExceeded)
|
||||
|
||||
d.totalDuration.Add(r.TotalDuration)
|
||||
d.runDuration.Add(r.RunDuration)
|
||||
}
|
||||
|
||||
// Reset resets all metrics in this collector to 0.
|
||||
func (d *DefaultMetricCollector) Reset() {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
d.numRequests = rolling.NewNumber()
|
||||
d.errors = rolling.NewNumber()
|
||||
d.successes = rolling.NewNumber()
|
||||
d.rejects = rolling.NewNumber()
|
||||
d.shortCircuits = rolling.NewNumber()
|
||||
d.failures = rolling.NewNumber()
|
||||
d.timeouts = rolling.NewNumber()
|
||||
d.fallbackSuccesses = rolling.NewNumber()
|
||||
d.fallbackFailures = rolling.NewNumber()
|
||||
d.contextCanceled = rolling.NewNumber()
|
||||
d.contextDeadlineExceeded = rolling.NewNumber()
|
||||
d.totalDuration = rolling.NewTiming()
|
||||
d.runDuration = rolling.NewTiming()
|
||||
}
|
67
vendor/github.com/afex/hystrix-go/hystrix/metric_collector/metric_collector.go
generated
vendored
Normal file
67
vendor/github.com/afex/hystrix-go/hystrix/metric_collector/metric_collector.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
package metricCollector
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Registry is the default metricCollectorRegistry that circuits will use to
|
||||
// collect statistics about the health of the circuit.
|
||||
var Registry = metricCollectorRegistry{
|
||||
lock: &sync.RWMutex{},
|
||||
registry: []func(name string) MetricCollector{
|
||||
newDefaultMetricCollector,
|
||||
},
|
||||
}
|
||||
|
||||
type metricCollectorRegistry struct {
|
||||
lock *sync.RWMutex
|
||||
registry []func(name string) MetricCollector
|
||||
}
|
||||
|
||||
// InitializeMetricCollectors runs the registried MetricCollector Initializers to create an array of MetricCollectors.
|
||||
func (m *metricCollectorRegistry) InitializeMetricCollectors(name string) []MetricCollector {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
metrics := make([]MetricCollector, len(m.registry))
|
||||
for i, metricCollectorInitializer := range m.registry {
|
||||
metrics[i] = metricCollectorInitializer(name)
|
||||
}
|
||||
return metrics
|
||||
}
|
||||
|
||||
// Register places a MetricCollector Initializer in the registry maintained by this metricCollectorRegistry.
|
||||
func (m *metricCollectorRegistry) Register(initMetricCollector func(string) MetricCollector) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
m.registry = append(m.registry, initMetricCollector)
|
||||
}
|
||||
|
||||
type MetricResult struct {
|
||||
Attempts float64
|
||||
Errors float64
|
||||
Successes float64
|
||||
Failures float64
|
||||
Rejects float64
|
||||
ShortCircuits float64
|
||||
Timeouts float64
|
||||
FallbackSuccesses float64
|
||||
FallbackFailures float64
|
||||
ContextCanceled float64
|
||||
ContextDeadlineExceeded float64
|
||||
TotalDuration time.Duration
|
||||
RunDuration time.Duration
|
||||
ConcurrencyInUse float64
|
||||
}
|
||||
|
||||
// MetricCollector represents the contract that all collectors must fulfill to gather circuit statistics.
|
||||
// Implementations of this interface do not have to maintain locking around thier data stores so long as
|
||||
// they are not modified outside of the hystrix context.
|
||||
type MetricCollector interface {
|
||||
// Update accepts a set of metrics from a command execution for remote instrumentation
|
||||
Update(MetricResult)
|
||||
// Reset resets the internal counters and timers.
|
||||
Reset()
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package hystrix
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix/metric_collector"
|
||||
"github.com/afex/hystrix-go/hystrix/rolling"
|
||||
)
|
||||
|
||||
type commandExecution struct {
|
||||
Types []string `json:"types"`
|
||||
Start time.Time `json:"start_time"`
|
||||
RunDuration time.Duration `json:"run_duration"`
|
||||
ConcurrencyInUse float64 `json:"concurrency_inuse"`
|
||||
}
|
||||
|
||||
type metricExchange struct {
|
||||
Name string
|
||||
Updates chan *commandExecution
|
||||
Mutex *sync.RWMutex
|
||||
|
||||
metricCollectors []metricCollector.MetricCollector
|
||||
}
|
||||
|
||||
func newMetricExchange(name string) *metricExchange {
|
||||
m := &metricExchange{}
|
||||
m.Name = name
|
||||
|
||||
m.Updates = make(chan *commandExecution, 2000)
|
||||
m.Mutex = &sync.RWMutex{}
|
||||
m.metricCollectors = metricCollector.Registry.InitializeMetricCollectors(name)
|
||||
m.Reset()
|
||||
|
||||
go m.Monitor()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// The Default Collector function will panic if collectors are not setup to specification.
|
||||
func (m *metricExchange) DefaultCollector() *metricCollector.DefaultMetricCollector {
|
||||
if len(m.metricCollectors) < 1 {
|
||||
panic("No Metric Collectors Registered.")
|
||||
}
|
||||
collection, ok := m.metricCollectors[0].(*metricCollector.DefaultMetricCollector)
|
||||
if !ok {
|
||||
panic("Default metric collector is not registered correctly. The default metric collector must be registered first.")
|
||||
}
|
||||
return collection
|
||||
}
|
||||
|
||||
func (m *metricExchange) Monitor() {
|
||||
for update := range m.Updates {
|
||||
// we only grab a read lock to make sure Reset() isn't changing the numbers.
|
||||
m.Mutex.RLock()
|
||||
|
||||
totalDuration := time.Since(update.Start)
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, collector := range m.metricCollectors {
|
||||
wg.Add(1)
|
||||
go m.IncrementMetrics(wg, collector, update, totalDuration)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
m.Mutex.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metricExchange) IncrementMetrics(wg *sync.WaitGroup, collector metricCollector.MetricCollector, update *commandExecution, totalDuration time.Duration) {
|
||||
// granular metrics
|
||||
r := metricCollector.MetricResult{
|
||||
Attempts: 1,
|
||||
TotalDuration: totalDuration,
|
||||
RunDuration: update.RunDuration,
|
||||
ConcurrencyInUse: update.ConcurrencyInUse,
|
||||
}
|
||||
|
||||
switch update.Types[0] {
|
||||
case "success":
|
||||
r.Successes = 1
|
||||
case "failure":
|
||||
r.Failures = 1
|
||||
r.Errors = 1
|
||||
case "rejected":
|
||||
r.Rejects = 1
|
||||
r.Errors = 1
|
||||
case "short-circuit":
|
||||
r.ShortCircuits = 1
|
||||
r.Errors = 1
|
||||
case "timeout":
|
||||
r.Timeouts = 1
|
||||
r.Errors = 1
|
||||
case "context_canceled":
|
||||
r.ContextCanceled = 1
|
||||
case "context_deadline_exceeded":
|
||||
r.ContextDeadlineExceeded = 1
|
||||
}
|
||||
|
||||
if len(update.Types) > 1 {
|
||||
// fallback metrics
|
||||
if update.Types[1] == "fallback-success" {
|
||||
r.FallbackSuccesses = 1
|
||||
}
|
||||
if update.Types[1] == "fallback-failure" {
|
||||
r.FallbackFailures = 1
|
||||
}
|
||||
}
|
||||
|
||||
collector.Update(r)
|
||||
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (m *metricExchange) Reset() {
|
||||
m.Mutex.Lock()
|
||||
defer m.Mutex.Unlock()
|
||||
|
||||
for _, collector := range m.metricCollectors {
|
||||
collector.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metricExchange) Requests() *rolling.Number {
|
||||
m.Mutex.RLock()
|
||||
defer m.Mutex.RUnlock()
|
||||
return m.requestsLocked()
|
||||
}
|
||||
|
||||
func (m *metricExchange) requestsLocked() *rolling.Number {
|
||||
return m.DefaultCollector().NumRequests()
|
||||
}
|
||||
|
||||
func (m *metricExchange) ErrorPercent(now time.Time) int {
|
||||
m.Mutex.RLock()
|
||||
defer m.Mutex.RUnlock()
|
||||
|
||||
var errPct float64
|
||||
reqs := m.requestsLocked().Sum(now)
|
||||
errs := m.DefaultCollector().Errors().Sum(now)
|
||||
|
||||
if reqs > 0 {
|
||||
errPct = (float64(errs) / float64(reqs)) * 100
|
||||
}
|
||||
|
||||
return int(errPct + 0.5)
|
||||
}
|
||||
|
||||
func (m *metricExchange) IsHealthy(now time.Time) bool {
|
||||
return m.ErrorPercent(now) < getSettings(m.Name).ErrorPercentThreshold
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package hystrix
|
||||
|
||||
type executorPool struct {
|
||||
Name string
|
||||
Metrics *poolMetrics
|
||||
Max int
|
||||
Tickets chan *struct{}
|
||||
}
|
||||
|
||||
func newExecutorPool(name string) *executorPool {
|
||||
p := &executorPool{}
|
||||
p.Name = name
|
||||
p.Metrics = newPoolMetrics(name)
|
||||
p.Max = getSettings(name).MaxConcurrentRequests
|
||||
|
||||
p.Tickets = make(chan *struct{}, p.Max)
|
||||
for i := 0; i < p.Max; i++ {
|
||||
p.Tickets <- &struct{}{}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *executorPool) Return(ticket *struct{}) {
|
||||
if ticket == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.Metrics.Updates <- poolMetricsUpdate{
|
||||
activeCount: p.ActiveCount(),
|
||||
}
|
||||
p.Tickets <- ticket
|
||||
}
|
||||
|
||||
func (p *executorPool) ActiveCount() int {
|
||||
return p.Max - len(p.Tickets)
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package hystrix
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix/rolling"
|
||||
)
|
||||
|
||||
type poolMetrics struct {
|
||||
Mutex *sync.RWMutex
|
||||
Updates chan poolMetricsUpdate
|
||||
|
||||
Name string
|
||||
MaxActiveRequests *rolling.Number
|
||||
Executed *rolling.Number
|
||||
}
|
||||
|
||||
type poolMetricsUpdate struct {
|
||||
activeCount int
|
||||
}
|
||||
|
||||
func newPoolMetrics(name string) *poolMetrics {
|
||||
m := &poolMetrics{}
|
||||
m.Name = name
|
||||
m.Updates = make(chan poolMetricsUpdate)
|
||||
m.Mutex = &sync.RWMutex{}
|
||||
|
||||
m.Reset()
|
||||
|
||||
go m.Monitor()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *poolMetrics) Reset() {
|
||||
m.Mutex.Lock()
|
||||
defer m.Mutex.Unlock()
|
||||
|
||||
m.MaxActiveRequests = rolling.NewNumber()
|
||||
m.Executed = rolling.NewNumber()
|
||||
}
|
||||
|
||||
func (m *poolMetrics) Monitor() {
|
||||
for u := range m.Updates {
|
||||
m.Mutex.RLock()
|
||||
|
||||
m.Executed.Increment(1)
|
||||
m.MaxActiveRequests.UpdateMax(float64(u.activeCount))
|
||||
|
||||
m.Mutex.RUnlock()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package rolling
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Number tracks a numberBucket over a bounded number of
|
||||
// time buckets. Currently the buckets are one second long and only the last 10 seconds are kept.
|
||||
type Number struct {
|
||||
Buckets map[int64]*numberBucket
|
||||
Mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
type numberBucket struct {
|
||||
Value float64
|
||||
}
|
||||
|
||||
// NewNumber initializes a RollingNumber struct.
|
||||
func NewNumber() *Number {
|
||||
r := &Number{
|
||||
Buckets: make(map[int64]*numberBucket),
|
||||
Mutex: &sync.RWMutex{},
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Number) getCurrentBucket() *numberBucket {
|
||||
now := time.Now().Unix()
|
||||
var bucket *numberBucket
|
||||
var ok bool
|
||||
|
||||
if bucket, ok = r.Buckets[now]; !ok {
|
||||
bucket = &numberBucket{}
|
||||
r.Buckets[now] = bucket
|
||||
}
|
||||
|
||||
return bucket
|
||||
}
|
||||
|
||||
func (r *Number) removeOldBuckets() {
|
||||
now := time.Now().Unix() - 10
|
||||
|
||||
for timestamp := range r.Buckets {
|
||||
// TODO: configurable rolling window
|
||||
if timestamp <= now {
|
||||
delete(r.Buckets, timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increment increments the number in current timeBucket.
|
||||
func (r *Number) Increment(i float64) {
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
r.Mutex.Lock()
|
||||
defer r.Mutex.Unlock()
|
||||
|
||||
b := r.getCurrentBucket()
|
||||
b.Value += i
|
||||
r.removeOldBuckets()
|
||||
}
|
||||
|
||||
// UpdateMax updates the maximum value in the current bucket.
|
||||
func (r *Number) UpdateMax(n float64) {
|
||||
r.Mutex.Lock()
|
||||
defer r.Mutex.Unlock()
|
||||
|
||||
b := r.getCurrentBucket()
|
||||
if n > b.Value {
|
||||
b.Value = n
|
||||
}
|
||||
r.removeOldBuckets()
|
||||
}
|
||||
|
||||
// Sum sums the values over the buckets in the last 10 seconds.
|
||||
func (r *Number) Sum(now time.Time) float64 {
|
||||
sum := float64(0)
|
||||
|
||||
r.Mutex.RLock()
|
||||
defer r.Mutex.RUnlock()
|
||||
|
||||
for timestamp, bucket := range r.Buckets {
|
||||
// TODO: configurable rolling window
|
||||
if timestamp >= now.Unix()-10 {
|
||||
sum += bucket.Value
|
||||
}
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
// Max returns the maximum value seen in the last 10 seconds.
|
||||
func (r *Number) Max(now time.Time) float64 {
|
||||
var max float64
|
||||
|
||||
r.Mutex.RLock()
|
||||
defer r.Mutex.RUnlock()
|
||||
|
||||
for timestamp, bucket := range r.Buckets {
|
||||
// TODO: configurable rolling window
|
||||
if timestamp >= now.Unix()-10 {
|
||||
if bucket.Value > max {
|
||||
max = bucket.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
func (r *Number) Avg(now time.Time) float64 {
|
||||
return r.Sum(now) / 10
|
||||
}
|
148
vendor/github.com/afex/hystrix-go/hystrix/rolling/rolling_timing.go
generated
vendored
Normal file
148
vendor/github.com/afex/hystrix-go/hystrix/rolling/rolling_timing.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
package rolling
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Timing maintains time Durations for each time bucket.
|
||||
// The Durations are kept in an array to allow for a variety of
|
||||
// statistics to be calculated from the source data.
|
||||
type Timing struct {
|
||||
Buckets map[int64]*timingBucket
|
||||
Mutex *sync.RWMutex
|
||||
|
||||
CachedSortedDurations []time.Duration
|
||||
LastCachedTime int64
|
||||
}
|
||||
|
||||
type timingBucket struct {
|
||||
Durations []time.Duration
|
||||
}
|
||||
|
||||
// NewTiming creates a RollingTiming struct.
|
||||
func NewTiming() *Timing {
|
||||
r := &Timing{
|
||||
Buckets: make(map[int64]*timingBucket),
|
||||
Mutex: &sync.RWMutex{},
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type byDuration []time.Duration
|
||||
|
||||
func (c byDuration) Len() int { return len(c) }
|
||||
func (c byDuration) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c byDuration) Less(i, j int) bool { return c[i] < c[j] }
|
||||
|
||||
// SortedDurations returns an array of time.Duration sorted from shortest
|
||||
// to longest that have occurred in the last 60 seconds.
|
||||
func (r *Timing) SortedDurations() []time.Duration {
|
||||
r.Mutex.RLock()
|
||||
t := r.LastCachedTime
|
||||
r.Mutex.RUnlock()
|
||||
|
||||
if t+time.Duration(1*time.Second).Nanoseconds() > time.Now().UnixNano() {
|
||||
// don't recalculate if current cache is still fresh
|
||||
return r.CachedSortedDurations
|
||||
}
|
||||
|
||||
var durations byDuration
|
||||
now := time.Now()
|
||||
|
||||
r.Mutex.Lock()
|
||||
defer r.Mutex.Unlock()
|
||||
|
||||
for timestamp, b := range r.Buckets {
|
||||
// TODO: configurable rolling window
|
||||
if timestamp >= now.Unix()-60 {
|
||||
for _, d := range b.Durations {
|
||||
durations = append(durations, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(durations)
|
||||
|
||||
r.CachedSortedDurations = durations
|
||||
r.LastCachedTime = time.Now().UnixNano()
|
||||
|
||||
return r.CachedSortedDurations
|
||||
}
|
||||
|
||||
func (r *Timing) getCurrentBucket() *timingBucket {
|
||||
r.Mutex.RLock()
|
||||
now := time.Now()
|
||||
bucket, exists := r.Buckets[now.Unix()]
|
||||
r.Mutex.RUnlock()
|
||||
|
||||
if !exists {
|
||||
r.Mutex.Lock()
|
||||
defer r.Mutex.Unlock()
|
||||
|
||||
r.Buckets[now.Unix()] = &timingBucket{}
|
||||
bucket = r.Buckets[now.Unix()]
|
||||
}
|
||||
|
||||
return bucket
|
||||
}
|
||||
|
||||
func (r *Timing) removeOldBuckets() {
|
||||
now := time.Now()
|
||||
|
||||
for timestamp := range r.Buckets {
|
||||
// TODO: configurable rolling window
|
||||
if timestamp <= now.Unix()-60 {
|
||||
delete(r.Buckets, timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add appends the time.Duration given to the current time bucket.
|
||||
func (r *Timing) Add(duration time.Duration) {
|
||||
b := r.getCurrentBucket()
|
||||
|
||||
r.Mutex.Lock()
|
||||
defer r.Mutex.Unlock()
|
||||
|
||||
b.Durations = append(b.Durations, duration)
|
||||
r.removeOldBuckets()
|
||||
}
|
||||
|
||||
// Percentile computes the percentile given with a linear interpolation.
|
||||
func (r *Timing) Percentile(p float64) uint32 {
|
||||
sortedDurations := r.SortedDurations()
|
||||
length := len(sortedDurations)
|
||||
if length <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
pos := r.ordinal(len(sortedDurations), p) - 1
|
||||
return uint32(sortedDurations[pos].Nanoseconds() / 1000000)
|
||||
}
|
||||
|
||||
func (r *Timing) ordinal(length int, percentile float64) int64 {
|
||||
if percentile == 0 && length > 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return int64(math.Ceil((percentile / float64(100)) * float64(length)))
|
||||
}
|
||||
|
||||
// Mean computes the average timing in the last 60 seconds.
|
||||
func (r *Timing) Mean() uint32 {
|
||||
sortedDurations := r.SortedDurations()
|
||||
var sum time.Duration
|
||||
for _, d := range sortedDurations {
|
||||
sum += d
|
||||
}
|
||||
|
||||
length := int64(len(sortedDurations))
|
||||
if length == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint32(sum.Nanoseconds()/length) / 1000000
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package hystrix
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTimeout is how long to wait for command to complete, in milliseconds
|
||||
DefaultTimeout = 1000
|
||||
// DefaultMaxConcurrent is how many commands of the same type can run at the same time
|
||||
DefaultMaxConcurrent = 10
|
||||
// DefaultVolumeThreshold is the minimum number of requests needed before a circuit can be tripped due to health
|
||||
DefaultVolumeThreshold = 20
|
||||
// DefaultSleepWindow is how long, in milliseconds, to wait after a circuit opens before testing for recovery
|
||||
DefaultSleepWindow = 5000
|
||||
// DefaultErrorPercentThreshold causes circuits to open once the rolling measure of errors exceeds this percent of requests
|
||||
DefaultErrorPercentThreshold = 50
|
||||
// DefaultLogger is the default logger that will be used in the Hystrix package. By default prints nothing.
|
||||
DefaultLogger = NoopLogger{}
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Timeout time.Duration
|
||||
MaxConcurrentRequests int
|
||||
RequestVolumeThreshold uint64
|
||||
SleepWindow time.Duration
|
||||
ErrorPercentThreshold int
|
||||
}
|
||||
|
||||
// CommandConfig is used to tune circuit settings at runtime
|
||||
type CommandConfig struct {
|
||||
Timeout int `json:"timeout"`
|
||||
MaxConcurrentRequests int `json:"max_concurrent_requests"`
|
||||
RequestVolumeThreshold int `json:"request_volume_threshold"`
|
||||
SleepWindow int `json:"sleep_window"`
|
||||
ErrorPercentThreshold int `json:"error_percent_threshold"`
|
||||
}
|
||||
|
||||
var circuitSettings map[string]*Settings
|
||||
var settingsMutex *sync.RWMutex
|
||||
var log logger
|
||||
|
||||
func init() {
|
||||
circuitSettings = make(map[string]*Settings)
|
||||
settingsMutex = &sync.RWMutex{}
|
||||
log = DefaultLogger
|
||||
}
|
||||
|
||||
// Configure applies settings for a set of circuits
|
||||
func Configure(cmds map[string]CommandConfig) {
|
||||
for k, v := range cmds {
|
||||
ConfigureCommand(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigureCommand applies settings for a circuit
|
||||
func ConfigureCommand(name string, config CommandConfig) {
|
||||
settingsMutex.Lock()
|
||||
defer settingsMutex.Unlock()
|
||||
|
||||
timeout := DefaultTimeout
|
||||
if config.Timeout != 0 {
|
||||
timeout = config.Timeout
|
||||
}
|
||||
|
||||
max := DefaultMaxConcurrent
|
||||
if config.MaxConcurrentRequests != 0 {
|
||||
max = config.MaxConcurrentRequests
|
||||
}
|
||||
|
||||
volume := DefaultVolumeThreshold
|
||||
if config.RequestVolumeThreshold != 0 {
|
||||
volume = config.RequestVolumeThreshold
|
||||
}
|
||||
|
||||
sleep := DefaultSleepWindow
|
||||
if config.SleepWindow != 0 {
|
||||
sleep = config.SleepWindow
|
||||
}
|
||||
|
||||
errorPercent := DefaultErrorPercentThreshold
|
||||
if config.ErrorPercentThreshold != 0 {
|
||||
errorPercent = config.ErrorPercentThreshold
|
||||
}
|
||||
|
||||
circuitSettings[name] = &Settings{
|
||||
Timeout: time.Duration(timeout) * time.Millisecond,
|
||||
MaxConcurrentRequests: max,
|
||||
RequestVolumeThreshold: uint64(volume),
|
||||
SleepWindow: time.Duration(sleep) * time.Millisecond,
|
||||
ErrorPercentThreshold: errorPercent,
|
||||
}
|
||||
}
|
||||
|
||||
func getSettings(name string) *Settings {
|
||||
settingsMutex.RLock()
|
||||
s, exists := circuitSettings[name]
|
||||
settingsMutex.RUnlock()
|
||||
|
||||
if !exists {
|
||||
ConfigureCommand(name, CommandConfig{})
|
||||
s = getSettings(name)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func GetCircuitSettings() map[string]*Settings {
|
||||
copy := make(map[string]*Settings)
|
||||
|
||||
settingsMutex.RLock()
|
||||
for key, val := range circuitSettings {
|
||||
copy[key] = val
|
||||
}
|
||||
settingsMutex.RUnlock()
|
||||
|
||||
return copy
|
||||
}
|
||||
|
||||
// SetLogger configures the logger that will be used. This only applies to the hystrix package.
|
||||
func SetLogger(l logger) {
|
||||
log = l
|
||||
}
|
|
@ -12,6 +12,11 @@ github.com/StackExchange/wmi
|
|||
# github.com/VictoriaMetrics/fastcache v1.6.0
|
||||
## explicit; go 1.13
|
||||
github.com/VictoriaMetrics/fastcache
|
||||
# github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
|
||||
## explicit
|
||||
github.com/afex/hystrix-go/hystrix
|
||||
github.com/afex/hystrix-go/hystrix/metric_collector
|
||||
github.com/afex/hystrix-go/hystrix/rolling
|
||||
# github.com/anacrolix/chansync v0.3.0
|
||||
## explicit; go 1.16
|
||||
github.com/anacrolix/chansync
|
||||
|
|
Loading…
Reference in New Issue