Skip to content

Commit 33acd72

Browse files
committed
improve error handling when objects do not exist
1 parent a040b68 commit 33acd72

File tree

2 files changed

+39
-8
lines changed

2 files changed

+39
-8
lines changed

src/AzStorage.jl

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,23 @@ function isretryable(e::HTTP.StatusError)
174174
e.status RETRYABLE_HTTP_ERRORS && (return true)
175175
false
176176
end
177+
function isretryable(e::HTTP.RequestError)
178+
e.request.response.status == 404 && (return false)
179+
true
180+
end
177181
isnoname_error(e::HTTP.Exceptions.ConnectError) = isa(e.error, CapturedException) && isa(e.error.ex, Sockets.DNSError) && Base.uverrorname(e.error.ex.code) == "EAI_NONAME"
178182
isretryable(e::HTTP.Exceptions.ConnectError) = isnoname_error(e) ? false : true
179183
isretryable(e::Base.IOError) = true
180184
isretryable(e::HTTP.Exceptions.HTTPError) = true
181-
isretryable(e::HTTP.Exceptions.RequestError) = true
182185
isretryable(e::HTTP.Exceptions.TimeoutError) = true
183186
isretryable(e::Base.EOFError) = true
184187
isretryable(e::Sockets.DNSError) = Base.uverrorname(e.code) == "EAI_NONAME" ? false : true
185188
isretryable(e) = false
186189

190+
azstorage_exception(e::HTTP.RequestError) = e.request.response.status == 404 ? FileDoesNotExistError() : e
191+
azstorage_exception(e::HTTP.StatusError) = e.status == 404 ? FileDoesNotExistError() : e
192+
azstorage_exception(e) = e
193+
187194
status(e::HTTP.StatusError) = e.status
188195
status(e) = 999
189196

@@ -199,7 +206,7 @@ macro retry(retries, ex::Expr)
199206
r = $(esc(ex))
200207
break
201208
catch e
202-
(i < $(esc(retries)) && isretryable(e)) || throw(e)
209+
(i < $(esc(retries)) && isretryable(e)) || throw(azstorage_exception(e))
203210
maximum_backoff = 256
204211
s = min(2.0^(i-1), maximum_backoff) + rand()
205212
if status(e) == 429
@@ -605,7 +612,7 @@ end
605612

606613
nthreads_effective(nthreads::Integer, nbytes::Integer) = clamp(div(nbytes, _MINBYTES_PER_BLOCK), 1, nthreads)
607614

608-
function readbytes!(c::AzContainer, o::AbstractString, data::DenseArray{UInt8}; offset=0)
615+
function readbytes!(c::AzContainer, o::AbstractString, data::DenseArray{UInt8}; offset=0, serial=Sys.iswindows())
609616
function readbytes_serial!(c, o, data, offset)
610617
@retry c.nretry HTTP.open(
611618
"GET",
@@ -631,15 +638,16 @@ function readbytes!(c::AzContainer, o::AbstractString, data::DenseArray{UInt8};
631638
r = @ccall libAzStorage.curl_readbytes_retry_threaded(_token::Ptr{UInt8}, refresh_token::Ptr{UInt8}, expiry::Ptr{Culong}, scope::Cstring, resource::Cstring, tenant::Cstring,
632639
clientid::Cstring, client_secret::Cstring, c.storageaccount::Cstring, c.containername::Cstring, addprefix(c,o)::Cstring, data::Ptr{UInt8}, offset::Csize_t,
633640
length(data)::Csize_t, _nthreads::Cint, c.nretry::Cint, c.verbose::Cint, c.connect_timeout::Clong, c.read_timeout::Clong)::ResponseCodes
641+
r.http == 404 && throw(FileDoesNotExistError())
634642
(r.http >= 300 || r.curl > 0) && error("readbytes_threaded! error: http code $(r.http), curl code $(r.curl)")
635643
authinfo!(c.session, _token, refresh_token, expiry)
636644
nothing
637645
end
638646

639-
_nthreads = nthreads_effective(c.nthreads, length(data))
640-
if Sys.iswindows()
647+
if serial
641648
readbytes_serial!(c, o, data, offset)
642649
else
650+
_nthreads = nthreads_effective(c.nthreads, length(data))
643651
readbytes_threaded!(c, o, data, offset, _nthreads)
644652
end
645653
data
@@ -662,10 +670,13 @@ method returns `data`. For example,
662670
data = read!(AzContainer("foo";storageaccount="bar"), "baz.bin", Vector{Float32}(undef,10))
663671
```
664672
"""
665-
function Base.read!(c::AzContainer, o::AbstractString, data::AbstractArray{T}; offset=0) where {T}
673+
function Base.read!(c::AzContainer, o::AbstractString, data::AbstractArray{T}; offset=0, serial=false) where {T}
674+
if Sys.iswindows()
675+
serial = true
676+
end
666677
if _iscontiguous(data)
667678
_data = unsafe_wrap(Array, convert(Ptr{UInt8}, pointer(data)), length(data)*sizeof(T), own=false)
668-
readbytes!(c, o, _data; offset=offset*sizeof(T))
679+
readbytes!(c, o, _data; offset=offset*sizeof(T), serial)
669680
else
670681
error("AzStorage does not support reading objects of type $T and/or into a non-contiguous array.")
671682
end
@@ -980,7 +991,9 @@ function Base.isfile(c::AzContainer, object::AbstractString)
980991
connect_timeout = c.connect_timeout,
981992
readtimeout = c.read_timeout)
982993
catch e
983-
if isa(e, HTTP.Exceptions.StatusError) && e.status == 404
994+
if isa(e, FileDoesNotExistError)
995+
return false
996+
elseif isa(e, HTTP.Exceptions.StatusError) && e.status == 404
984997
return false
985998
elseif isnoname_error(e)
986999
return false

test/runtests.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,24 @@ end
649649
@test backend(c) == "azureblob"
650650
end
651651

652+
@testset "file does not exist" for serial in (true,false)
653+
r = uuid4()
654+
c = AzContainer("foo-$r-o", storageaccount=storageaccount, session=session, nthreads=2, nretry=10)
655+
mkpath(c)
656+
@test_throws FileDoesNotExistError read!(c, "doesnotexist", Vector{UInt8}(undef, 10); serial)
657+
rm(c)
658+
end
659+
660+
@testset "force serial read" begin
661+
r = uuid4()
662+
c = AzContainer("foo-$r-o", storageaccount=storageaccount, session=session, nthreads=2, nretry=10)
663+
x = rand(1000)
664+
mkpath(c)
665+
write(c, "test", x)
666+
y = read!(c, "test", zeros(Float64, 1000); serial=true)
667+
@test x y
668+
end
669+
652670
# TODO: CI is showing a seg-fault on Apple, but I do not have an Apple machine to help debug.
653671
if !Sys.iswindows() && !Sys.isapple()
654672
@testset "C token refresh, write" begin

0 commit comments

Comments
 (0)