11module RustyObjectStore
22
33export init_object_store, get_object!, put_object, delete_object
4- export StaticConfig, ClientOptions, Config, AzureConfig, AWSConfig
4+ export StaticConfig, ClientOptions, Config, AzureConfig, AWSConfig, SnowflakeConfig
55export status_code, is_connection, is_timeout, is_early_eof, is_unknown, is_parse_url
66export get_object_stream, ReadStream, finish!
77export put_object_stream, WriteStream, cancel!, shutdown!
8- export current_metrics
8+ export current_metrics, invalidate_config
99export max_entries_per_chunk, ListEntry, list_objects, list_objects_stream, next_chunk!
1010
1111using Base. Libc. Libdl: dlext
@@ -550,6 +550,156 @@ function Base.show(io::IO, conf::AWSConfig)
550550 print (io, " , " , " opts=" , repr (conf. opts), " )" )
551551end
552552
553+ """
554+ $TYPEDEF
555+
556+ Configuration for the Snowflake stage object store backend.
557+
558+ It is recommended to reuse an instance for many operations.
559+
560+ # Keyword Arguments
561+ - `stage::String`: Snowflake stage
562+ - `encryption_scheme::Option{String}`: (Optional) Encryption scheme to enforce (one of AES_128_CBC, AES_256_GCM)
563+ - `account::Option{String}`: (Optional) Snowflake account (read from SNOWFLAKE_ACCOUNT env var if missing)
564+ - `database::Option{String}`: (Optional) Snwoflake database (read from SNOWFLAKE_DATABASE env var if missing)
565+ - `schema::Option{String}`: (Optional) Snowflake schema (read from SNOWFLAKE_SCHEMA env var if missing)
566+ - `endpoint::Option{String}`: (Optional) Snowflake endpoint (read from SNOWFLAKE_ENDPOINT or SNOWFLAKE_HOST env vars if missing)
567+ - `warehouse::Option{String}`: (Optional) Snowflake warehouse
568+ - `username::Option{String}`: (Optional) Snowflake username (required for user/pass flow)
569+ - `password::Option{String}`: (Optional) Snowflake password (required for user/pass flow)
570+ - `role::Option{String}`: (Optional) Snowflake role (required for user/pass flow)
571+ - `master_token_path::Option{String}`: (Optional) Path to Snowflake master token (read from MASTER_TOKEN_PATH or defaults to `/snowflake/session/token` if missing)
572+ - `keyring_capacity::Option{Int}`: (Optional) Maximum number of keys to be kept in the in-memory keyring (key cache)
573+ - `keyring_ttl_secs::Option{Int}`: (Optional) Duration in seconds after which a key is removed from the keyring
574+ - `opts::ClientOptions`: (Optional) Client configuration options.
575+ """
576+ struct SnowflakeConfig <: AbstractConfig
577+ stage:: String
578+ encryption_scheme:: Option{String}
579+ account:: Option{String}
580+ database:: Option{String}
581+ schema:: Option{String}
582+ endpoint:: Option{String}
583+ warehouse:: Option{String}
584+ username:: Option{String}
585+ password:: Option{String}
586+ role:: Option{String}
587+ master_token_path:: Option{String}
588+ keyring_capacity:: Option{Int}
589+ keyring_ttl_secs:: Option{Int}
590+ opts:: ClientOptions
591+ cached_config:: Config
592+ function SnowflakeConfig (;
593+ stage:: String ,
594+ encryption_scheme:: Option{String} = nothing ,
595+ account:: Option{String} = nothing ,
596+ database:: Option{String} = nothing ,
597+ schema:: Option{String} = nothing ,
598+ endpoint:: Option{String} = nothing ,
599+ warehouse:: Option{String} = nothing ,
600+ username:: Option{String} = nothing ,
601+ password:: Option{String} = nothing ,
602+ role:: Option{String} = nothing ,
603+ master_token_path:: Option{String} = nothing ,
604+ keyring_capacity:: Option{Int} = nothing ,
605+ keyring_ttl_secs:: Option{Int} = nothing ,
606+ opts:: ClientOptions = ClientOptions ()
607+ )
608+ params = copy (opts. params)
609+
610+ params[" snowflake_stage" ] = stage
611+
612+ if ! isnothing (encryption_scheme)
613+ params[" snowflake_encryption_scheme" ] = encryption_scheme
614+ end
615+
616+ if ! isnothing (account)
617+ params[" snowflake_account" ] = account
618+ end
619+
620+ if ! isnothing (database)
621+ params[" snowflake_database" ] = database
622+ end
623+
624+ if ! isnothing (schema)
625+ params[" snowflake_schema" ] = schema
626+ end
627+
628+ if ! isnothing (endpoint)
629+ params[" snowflake_endpoint" ] = endpoint
630+ end
631+
632+ if ! isnothing (warehouse)
633+ params[" snowflake_warehouse" ] = warehouse
634+ end
635+
636+ if ! isnothing (username)
637+ params[" snowflake_username" ] = username
638+ end
639+
640+ if ! isnothing (password)
641+ params[" snowflake_password" ] = password
642+ end
643+
644+ if ! isnothing (role)
645+ params[" snowflake_role" ] = role
646+ end
647+
648+ if ! isnothing (master_token_path)
649+ params[" snowflake_master_token_path" ] = master_token_path
650+ end
651+
652+ if ! isnothing (keyring_capacity)
653+ params[" snowflake_keyring_capacity" ] = string (keyring_capacity)
654+ end
655+
656+ if ! isnothing (keyring_ttl_secs)
657+ params[" snowflake_keyring_ttl_secs" ] = string (keyring_ttl_secs)
658+ end
659+
660+ # All defaults for the optional values are defined on the Rust side.
661+ map! (v -> strip (v), values (params))
662+ cached_config = Config (" snowflake://$(strip (stage)) /" , params)
663+ return new (
664+ stage,
665+ encryption_scheme,
666+ account,
667+ database,
668+ schema,
669+ endpoint,
670+ warehouse,
671+ username,
672+ password,
673+ role,
674+ master_token_path,
675+ keyring_capacity,
676+ keyring_ttl_secs,
677+ opts,
678+ cached_config
679+ )
680+ end
681+ end
682+
683+ into_config (conf:: SnowflakeConfig ) = conf. cached_config
684+
685+ function Base. show (io:: IO , conf:: SnowflakeConfig )
686+ print (io, " SnowflakeConfig(" ),
687+ print (io, " stage=" , repr (conf. stage))
688+ @option_print (conf, encryption_scheme)
689+ @option_print (conf, account)
690+ @option_print (conf, database)
691+ @option_print (conf, schema)
692+ @option_print (conf, endpoint)
693+ @option_print (conf, warehouse)
694+ @option_print (conf, username)
695+ @option_print (conf, password, true )
696+ @option_print (conf, role)
697+ @option_print (conf, master_token_path)
698+ @option_print (conf, keyring_capacity)
699+ @option_print (conf, keyring_ttl_secs)
700+ print (io, " , " , " opts=" , repr (conf. opts), " )" )
701+ end
702+
553703mutable struct Response
554704 result:: Cint
555705 length:: Culonglong
@@ -1755,6 +1905,105 @@ function finish!(stream::ListStream)
17551905 return true
17561906end
17571907
1908+ mutable struct StageInfoResponseFFI
1909+ result:: Cint
1910+ stage_info:: Ptr{Cchar}
1911+ error_message:: Ptr{Cchar}
1912+ context:: Ptr{Cvoid}
1913+
1914+ StageInfoResponseFFI () = new (- 1 , C_NULL , C_NULL , C_NULL )
1915+ end
1916+
1917+ function current_stage_info (conf:: AbstractConfig )
1918+ response = StageInfoResponseFFI ()
1919+ ct = current_task ()
1920+ event = Base. Event ()
1921+ handle = pointer_from_objref (event)
1922+ config = into_config (conf)
1923+ while true
1924+ preserve_task (ct)
1925+ result = GC. @preserve config response event try
1926+ result = @ccall rust_lib. current_stage_info (
1927+ config:: Ref{Config} ,
1928+ response:: Ref{StageInfoResponseFFI} ,
1929+ handle:: Ptr{Cvoid}
1930+ ):: Cint
1931+
1932+ wait_or_cancel (event, response)
1933+
1934+ result
1935+ finally
1936+ unpreserve_task (ct)
1937+ end
1938+
1939+ if result == 2
1940+ # backoff
1941+ sleep (0.01 )
1942+ continue
1943+ end
1944+
1945+ # No need to destroy_cstring(response.stage_info) in case of errors here
1946+ @throw_on_error (response, " current_stage_info" , GetException)
1947+
1948+ info_string = unsafe_string (response. stage_info)
1949+ @ccall rust_lib. destroy_cstring (response. stage_info:: Ptr{Cchar} ):: Cint
1950+
1951+ stage_info = JSON3. read (info_string, Dict{String, String})
1952+ return stage_info
1953+ end
1954+ end
1955+
1956+ """
1957+ invalidate_config(conf::Option{AbstractConfig}) -> Bool
1958+
1959+ Invalidates the specified config (or all if no config is provided) in the Rust
1960+ config cache. This is useful to mitigate test interference.
1961+
1962+ # Arguments
1963+ - `conf::AbstractConfig`: (Optional) The config to be invalidated.
1964+ """
1965+ function invalidate_config (conf:: Option{AbstractConfig} = nothing )
1966+ response = Response ()
1967+ ct = current_task ()
1968+ event = Base. Event ()
1969+ handle = pointer_from_objref (event)
1970+ while true
1971+ preserve_task (ct)
1972+ result = GC. @preserve conf response event try
1973+ result = if ! isnothing (conf)
1974+ config = into_config (conf)
1975+ @ccall rust_lib. invalidate_config (
1976+ config:: Ref{Config} ,
1977+ response:: Ref{Response} ,
1978+ handle:: Ptr{Cvoid}
1979+ ):: Cint
1980+ else
1981+ @ccall rust_lib. invalidate_config (
1982+ C_NULL :: Ptr{Cvoid} ,
1983+ response:: Ref{Response} ,
1984+ handle:: Ptr{Cvoid}
1985+ ):: Cint
1986+ end
1987+
1988+ wait_or_cancel (event, response)
1989+
1990+ result
1991+ finally
1992+ unpreserve_task (ct)
1993+ end
1994+
1995+ if result == 2
1996+ # backoff
1997+ sleep (0.01 )
1998+ continue
1999+ end
2000+
2001+ @throw_on_error (response, " invalidate_config" , PutException)
2002+
2003+ return true
2004+ end
2005+ end
2006+
17582007struct Metrics
17592008 live_bytes:: Int64
17602009end
@@ -1763,4 +2012,8 @@ function current_metrics()
17632012 return @ccall rust_lib. current_metrics ():: Metrics
17642013end
17652014
1766- end # module
2015+ module Test
2016+ include (" mock_server.jl" )
2017+ end # Test module
2018+
2019+ end # RustyObjectStore module
0 commit comments