1
1
module RustyObjectStore
2
2
3
3
export init_object_store, get_object!, put_object, delete_object
4
- export StaticConfig, ClientOptions, Config, AzureConfig, AWSConfig
4
+ export StaticConfig, ClientOptions, Config, AzureConfig, AWSConfig, SnowflakeConfig
5
5
export status_code, is_connection, is_timeout, is_early_eof, is_unknown, is_parse_url
6
6
export get_object_stream, ReadStream, finish!
7
7
export put_object_stream, WriteStream, cancel!, shutdown!
8
- export current_metrics
8
+ export current_metrics, invalidate_config
9
9
export max_entries_per_chunk, ListEntry, list_objects, list_objects_stream, next_chunk!
10
10
11
11
using Base. Libc. Libdl: dlext
@@ -550,6 +550,156 @@ function Base.show(io::IO, conf::AWSConfig)
550
550
print (io, " , " , " opts=" , repr (conf. opts), " )" )
551
551
end
552
552
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
+
553
703
mutable struct Response
554
704
result:: Cint
555
705
length:: Culonglong
@@ -1755,6 +1905,105 @@ function finish!(stream::ListStream)
1755
1905
return true
1756
1906
end
1757
1907
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
+
1758
2007
struct Metrics
1759
2008
live_bytes:: Int64
1760
2009
end
@@ -1763,4 +2012,8 @@ function current_metrics()
1763
2012
return @ccall rust_lib. current_metrics ():: Metrics
1764
2013
end
1765
2014
1766
- end # module
2015
+ module Test
2016
+ include (" mock_server.jl" )
2017
+ end # Test module
2018
+
2019
+ end # RustyObjectStore module
0 commit comments