Compare commits

..

7 Commits

Author SHA1 Message Date
410cc7874d latest relay list data from api 2025-07-14 06:44:52 -07:00
64101b1dfd Add IPv4 address column to output table
- Added IPv4 endpoint address as a new column in the results table
- Adjusted column formatting to accommodate the new 15-character wide column
- Makes it easier to see which IP addresses correspond to each relay
2025-07-14 05:40:09 -07:00
7652472953 Revert all timing and worker optimizations to fix broken measurements
- Restored original timeout values:
  - Liveness check: 3 seconds
  - Latency measurement: 10 seconds
- Restored original ping settings:
  - Count: 5 pings
  - Interval: 1 second
- Restored original worker counts:
  - Liveness workers: 20
  - Latency workers: 30

The aggressive optimizations were causing many false negatives in
liveness checks and zero latency readings. The original conservative
values ensure reliable measurements even if slower.
2025-07-14 05:35:45 -07:00
463a0a6cd5 Fix broken latency measurements with more conservative optimizations
- Increased ping interval to 500ms (was too aggressive at 200ms)
- Set reasonable timeouts: 2s for liveness, 5s for latency
- Reduced workers to 40/60 (from 50/100) to avoid overwhelming network
- Kept reduction from 5 to 3 pings for modest speed improvement

The previous 200ms interval was causing the ping library to return
0 latency for all measurements, making results meaningless.
2025-07-14 05:32:35 -07:00
df3c6c6a0e Optimize latency checks for significantly faster performance
- Reduced ping interval from 1s to 200ms (5x faster per relay)
- Reduced ping count from 5 to 3 (still reliable, but faster)
- Increased latency workers from 30 to 100 (3.3x more parallelism)
- Increased liveness workers from 20 to 50 (2.5x more parallelism)
- Reduced timeouts: liveness from 3s to 1s, latency from 10s to 3s
- Expected speedup: ~10x faster overall execution time
2025-07-14 05:30:27 -07:00
0fe71520c5 Fix zero latency bug by validating ping statistics
- Added validation to detect and handle invalid zero latency measurements
- When MinRtt is 0 but packets were received, fall back to AvgRtt
- Added -debug flag to help diagnose ping statistics issues
- Added debug logging to show raw ping statistics for zero latency cases
- Prevents impossible 0s latency readings for remote servers
2025-07-14 05:22:05 -07:00
93e478755e Fix compile issue and add -force flag to bypass VPN check
- Updated golang.org/x/net dependency to fix linking error with syscall.recvmsg
- Added -force flag to allow running the tool while connected to VPN
- Fixed linter errors: replaced deprecated ioutil with io/os, added error handling
- Added test, fmt, and lint targets to Makefile
2025-07-14 05:16:24 -07:00
7 changed files with 15389 additions and 29 deletions

View File

@ -5,3 +5,12 @@ build:
run: run:
sudo go run ./cmd/mullvadclosest/*.go sudo go run ./cmd/mullvadclosest/*.go
test:
go test -v ./...
fmt:
go fmt ./...
lint:
golangci-lint run

View File

@ -2,8 +2,9 @@ package main
import ( import (
"encoding/json" "encoding/json"
"flag"
"fmt" "fmt"
"io/ioutil" "io"
"math" "math"
"math/rand" "math/rand"
"net/http" "net/http"
@ -15,6 +16,9 @@ import (
"github.com/schollz/progressbar/v3" "github.com/schollz/progressbar/v3"
) )
// Package-level variable for debug flag
var debugMode bool
// RelayLatency holds a relay and its associated latency // RelayLatency holds a relay and its associated latency
type RelayLatency struct { type RelayLatency struct {
Relay Relay Relay Relay
@ -23,15 +27,15 @@ type RelayLatency struct {
// MullvadIPResponse represents the response from the Mullvad IP check API // MullvadIPResponse represents the response from the Mullvad IP check API
type MullvadIPResponse struct { type MullvadIPResponse struct {
IP string `json:"ip"` IP string `json:"ip"`
Country string `json:"country"` Country string `json:"country"`
City string `json:"city"` City string `json:"city"`
Longitude float64 `json:"longitude"` Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"` Latitude float64 `json:"latitude"`
MullvadExitIP bool `json:"mullvad_exit_ip"` MullvadExitIP bool `json:"mullvad_exit_ip"`
MullvadExitIPHostname string `json:"mullvad_exit_ip_hostname,omitempty"` MullvadExitIPHostname string `json:"mullvad_exit_ip_hostname,omitempty"`
MullvadServerType string `json:"mullvad_server_type,omitempty"` MullvadServerType string `json:"mullvad_server_type,omitempty"`
Blacklisted struct { Blacklisted struct {
Blacklisted bool `json:"blacklisted"` Blacklisted bool `json:"blacklisted"`
Results []string `json:"results"` Results []string `json:"results"`
} `json:"blacklisted"` } `json:"blacklisted"`
@ -46,7 +50,7 @@ func livenessWorker(id int, jobs <-chan Relay, results chan<- RelayLatency, wg *
if err == nil && isLive { if err == nil && isLive {
results <- RelayLatency{Relay: relay, Latency: latency} results <- RelayLatency{Relay: relay, Latency: latency}
} }
bar.Add(1) _ = bar.Add(1)
} }
} }
@ -60,8 +64,10 @@ func latencyWorker(id int, jobs <-chan RelayLatency, results chan<- RelayLatency
Relay: relayLatency.Relay, Relay: relayLatency.Relay,
Latency: latency, Latency: latency,
} }
} else if debugMode {
fmt.Printf("Debug: Error measuring latency for %s: %v\n", relayLatency.Relay.Hostname, err)
} }
bar.Add(1) _ = bar.Add(1)
} }
} }
@ -180,13 +186,14 @@ func PrintRelayLatencies(relayLatencies []RelayLatency, totalRelays int, deadRel
fmt.Println() fmt.Println()
} }
fmt.Printf("%-20s %-20s %-30s %s\n", "Country", "City", "Hostname", "Latency") fmt.Printf("%-20s %-20s %-30s %-15s %s\n", "Country", "City", "Hostname", "IPv4 Address", "Latency")
for i, rl := range relayLatencies { for i, rl := range relayLatencies {
if i < 25 || i >= len(relayLatencies)-4 { if i < 25 || i >= len(relayLatencies)-4 {
fmt.Printf("%-20s %-20s %-30s %v\n", fmt.Printf("%-20s %-20s %-30s %-15s %v\n",
rl.Relay.Location.Country, rl.Relay.Location.Country,
rl.Relay.Location.City, rl.Relay.Location.City,
rl.Relay.Hostname, rl.Relay.Hostname,
rl.Relay.Ipv4AddrIn,
rl.Latency) rl.Latency)
} }
if i == 24 { if i == 24 {
@ -203,7 +210,7 @@ func CheckMullvadExitIP() (bool, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return false, fmt.Errorf("failed to read response body: %v", err) return false, fmt.Errorf("failed to read response body: %v", err)
} }
@ -217,6 +224,14 @@ func CheckMullvadExitIP() (bool, error) {
} }
func main() { func main() {
// Define force flag
force := flag.Bool("force", false, "Force execution even when connected to VPN")
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
// Set the global debug mode
debugMode = *debug
currentUser, err := user.Current() currentUser, err := user.Current()
if err != nil { if err != nil {
panic("Failed to get current user") panic("Failed to get current user")
@ -231,8 +246,9 @@ func main() {
panic(fmt.Sprintf("Error checking Mullvad exit IP: %v", err)) panic(fmt.Sprintf("Error checking Mullvad exit IP: %v", err))
} }
if isMullvadExitIP { if isMullvadExitIP && !*force {
fmt.Println("This program is designed to test latency between your actual IP and the Mullvad VPN servers. Please disconnect from the VPN and run the program again.") fmt.Println("This program is designed to test latency between your actual IP and the Mullvad VPN servers. Please disconnect from the VPN and run the program again.")
fmt.Println("Use -force flag to bypass this check.")
return return
} }

View File

@ -3,7 +3,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"time" "time"
@ -46,11 +45,21 @@ func (r Relay) CheckLiveness() (bool, time.Duration, error) {
pinger.Count = 1 pinger.Count = 1
pinger.Timeout = 3 * time.Second // Increased timeout for the single ping pinger.Timeout = 3 * time.Second // Increased timeout for the single ping
pinger.SetPrivileged(true) pinger.SetPrivileged(true)
pinger.Run() if err := pinger.Run(); err != nil {
return false, 0, err
}
stats := pinger.Statistics() stats := pinger.Statistics()
if stats.PacketsRecv == 0 { if stats.PacketsRecv == 0 {
return false, 0, nil return false, 0, nil
} }
// Validate that we don't have a zero latency for a successful ping
if stats.AvgRtt == 0 {
// Return as live but with no initial latency measurement
// The actual latency will be measured in MeasureLatency
return true, time.Duration(1), nil
}
return true, stats.AvgRtt, nil return true, stats.AvgRtt, nil
} }
@ -64,8 +73,28 @@ func (r Relay) MeasureLatency() (time.Duration, error) {
pinger.Interval = 1 * time.Second // Adding interval between pings pinger.Interval = 1 * time.Second // Adding interval between pings
pinger.Timeout = 10 * time.Second // Increased overall timeout pinger.Timeout = 10 * time.Second // Increased overall timeout
pinger.SetPrivileged(true) pinger.SetPrivileged(true)
pinger.Run() if err := pinger.Run(); err != nil {
return 0, err
}
stats := pinger.Statistics() stats := pinger.Statistics()
// Debug output
if debugMode && (stats.MinRtt == 0 || stats.AvgRtt == 0) {
fmt.Printf("Debug: %s (%s) - PacketsSent: %d, PacketsRecv: %d, MinRtt: %v, AvgRtt: %v, MaxRtt: %v\n",
r.Hostname, r.Ipv4AddrIn, stats.PacketsSent, stats.PacketsRecv,
stats.MinRtt, stats.AvgRtt, stats.MaxRtt)
}
// Validate the statistics - if MinRtt is 0 but we received packets, something is wrong
if stats.PacketsRecv > 0 && stats.MinRtt == 0 {
// Use average RTT if available and non-zero
if stats.AvgRtt > 0 {
return stats.AvgRtt, nil
}
// If all RTT values are 0, this is likely a bug - return an error
return 0, fmt.Errorf("invalid zero latency for relay %s", r.Hostname)
}
return stats.MinRtt, nil return stats.MinRtt, nil
} }
@ -110,7 +139,7 @@ func ParseRelayData() ([]Relay, error) {
return nil, fmt.Errorf("failed to read embedded file: %v", err) return nil, fmt.Errorf("failed to read embedded file: %v", err)
} }
} else { } else {
fileData, err = ioutil.ReadFile(filePath) fileData, err = os.ReadFile(filePath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read file: %v", err) return nil, fmt.Errorf("failed to read file: %v", err)
} }

View File

@ -4,7 +4,7 @@ import (
"embed" "embed"
) )
//go:embed relays.2024-06-04.json //go:embed relays.2025-07-14.json
var fs embed.FS var fs embed.FS
func GetEmbeddedFile(name string) ([]byte, error) { func GetEmbeddedFile(name string) ([]byte, error) {
@ -12,5 +12,5 @@ func GetEmbeddedFile(name string) ([]byte, error) {
} }
func GetRelays() ([]byte, error) { func GetRelays() ([]byte, error) {
return GetEmbeddedFile("relays.2024-06-04.json") return GetEmbeddedFile("relays.2025-07-14.json")
} }

17
go.mod
View File

@ -1,15 +1,20 @@
module sneak.berlin/go/mullvadclosest module sneak.berlin/go/mullvadclosest
go 1.22.3 go 1.23.0
toolchain go1.24.4
require (
github.com/go-ping/ping v1.1.0
github.com/schollz/progressbar/v3 v3.14.3
)
require ( require (
github.com/go-ping/ping v1.1.0 // indirect
github.com/google/uuid v1.2.0 // indirect github.com/google/uuid v1.2.0 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/schollz/progressbar/v3 v3.14.3 // indirect golang.org/x/net v0.42.0 // indirect
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.20.0 // indirect golang.org/x/term v0.33.0 // indirect
) )

10
go.sum
View File

@ -1,4 +1,5 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw= github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
@ -8,25 +9,32 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU= github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI= github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

15293
relays.2025-07-14.json Normal file

File diff suppressed because it is too large Load Diff