176
176
--- }
177
177
--- }
178
178
179
+ local json = require (' json' )
179
180
local utils = require (' graphql.utils' )
180
181
local core_util = require (' graphql.core.util' )
181
182
local core_types = require (' graphql.core.types' )
@@ -725,14 +726,20 @@ local function invoke_resolve_list(prepared_object_list, context, opts)
725
726
local qcontext = opts .qcontext
726
727
local accessor = opts .accessor
727
728
local is_item_cache_only = opts .is_item_cache_only or false
728
-
729
- fetch_resolve_list (prepared_object_list , {accessor = accessor ,
730
- qcontext = qcontext })
729
+ local max_batch_size = opts .max_batch_size
731
730
732
731
local open_set = {}
733
732
local cache_only_open_set = {}
734
733
735
- for _ , prepared_object in ipairs (prepared_object_list ) do
734
+ local last_fetched_object_num = 0
735
+ for i , prepared_object in ipairs (prepared_object_list ) do
736
+ if i > last_fetched_object_num then
737
+ local _ , size = fetch_resolve_list (prepared_object_list ,
738
+ {accessor = accessor , qcontext = qcontext ,
739
+ max_batch_size = max_batch_size , start_from = i })
740
+ last_fetched_object_num = last_fetched_object_num + size
741
+ end
742
+
736
743
local child = invoke_resolve (prepared_object , context ,
737
744
{qcontext = qcontext , is_item_cache_only = is_item_cache_only })
738
745
local child_open_set = child .open_set
@@ -753,23 +760,26 @@ end
753
760
-- Analyze prepared requests and prefetch in batches {{{
754
761
755
762
fetch_first_same = function (open_set , opts )
763
+ local func_name = ' bfs_executor.fetch_first_same'
756
764
local opts = opts or {}
757
765
local accessor = opts .accessor
758
766
local qcontext = opts .qcontext
767
+ local max_batch_size = opts .max_batch_size
759
768
760
769
if not accessor :cache_is_supported () then return nil , 0 end
761
770
762
771
local size = 0
763
772
764
773
local batches = {}
765
774
for i , item in ipairs (open_set ) do
775
+ if i > max_batch_size then break end
766
776
if item .prepared_object == nil then break end
767
777
local prepared_object = item .prepared_object
768
778
769
779
for field_name , field_info in pairs (prepared_object .fields_info ) do
770
780
local prepared_resolve = field_info .prepared_resolve
771
781
if prepared_resolve .is_calculated then
772
- -- don't update size, because we don't add request to 'batches'
782
+ size = i
773
783
break
774
784
end
775
785
local prepared_select = prepared_resolve .prepared_select
@@ -780,7 +790,9 @@ fetch_first_same = function(open_set, opts)
780
790
local iterator_opts = request_opts .iterator_opts
781
791
782
792
if i == 1 then
783
- assert (batches [field_name ] == nil , ' XXX' ) -- XXX
793
+ assert (batches [field_name ] == nil ,
794
+ (' internal error: %s: field names "%s" clash' ):format (
795
+ func_name , field_name ))
784
796
batches [field_name ] = {
785
797
collection_name = collection_name ,
786
798
index_name = index_name ,
@@ -792,10 +804,8 @@ fetch_first_same = function(open_set, opts)
792
804
local ok =
793
805
batches [field_name ].collection_name == collection_name and
794
806
batches [field_name ].index_name == index_name and
795
- utils .is_subtable (batches [field_name ].iterator_opts or {},
796
- iterator_opts or {}) and
797
- utils .is_subtable (iterator_opts or {},
798
- batches [field_name ].iterator_opts or {})
807
+ utils .are_tables_same (batches [field_name ].iterator_opts or
808
+ {}, iterator_opts or {})
799
809
if not ok then break end -- XXX: continue here and return first
800
810
-- non-match instead of size?
801
811
table.insert (batches [field_name ].keys , key )
@@ -805,28 +815,35 @@ fetch_first_same = function(open_set, opts)
805
815
end
806
816
807
817
-- don't flood cache with single-key (non-batch) select results
808
- if size = = 1 then
809
- return nil , 1
818
+ if size < = 1 then
819
+ return nil , size
810
820
end
811
821
812
822
local fetch_id = accessor :cache_fetch (batches , qcontext )
813
823
return fetch_id , size
814
824
end
815
825
816
- -- XXX: is the function redundant because of fetch_first_same?
817
826
fetch_resolve_list = function (prepared_object_list , opts )
827
+ local func_name = ' bfs_executor.fetch_resolve_list'
818
828
local opts = opts or {}
819
829
local accessor = opts .accessor
820
830
local qcontext = opts .qcontext
831
+ local max_batch_size = opts .max_batch_size
832
+ local start_from = opts .start_from or 1
821
833
822
- if not accessor :cache_is_supported () then return nil end
834
+ if not accessor :cache_is_supported () then return nil , 0 end
835
+
836
+ local size = 0
823
837
824
838
local batches = {}
825
- for i , prepared_object in ipairs (prepared_object_list ) do
839
+ for i = 1 , # prepared_object_list - start_from + 1 do
840
+ if i > max_batch_size then break end
841
+ local prepared_object = prepared_object_list [i + start_from - 1 ]
842
+
826
843
for field_name , field_info in pairs (prepared_object .fields_info ) do
827
844
local prepared_resolve = field_info .prepared_resolve
828
845
if prepared_resolve .is_calculated then
829
- -- don't update size, because we don't add request to 'batches'
846
+ size = i
830
847
break
831
848
end
832
849
local prepared_select = prepared_resolve .prepared_select
@@ -837,33 +854,49 @@ fetch_resolve_list = function(prepared_object_list, opts)
837
854
local iterator_opts = request_opts .iterator_opts
838
855
839
856
if i == 1 then
840
- assert (batches [field_name ] == nil , ' XXX' ) -- XXX
857
+ assert (batches [field_name ] == nil ,
858
+ (' internal error: %s: field names "%s" clash' ):format (
859
+ func_name , field_name ))
841
860
batches [field_name ] = {
842
861
collection_name = collection_name ,
843
862
index_name = index_name ,
844
863
keys = {key },
845
864
iterator_opts = iterator_opts ,
846
865
}
866
+ size = i
847
867
else
848
868
assert (batches [field_name ].collection_name == collection_name ,
849
- ' XXX' ) -- XXX
869
+ (' internal error: %s: prepared object list has ' ..
870
+ ' different collection names: "%s" and "%s"' ):format (
871
+ func_name , batches [field_name ].collection_name ,
872
+ collection_name ))
850
873
assert (batches [field_name ].index_name == index_name ,
851
- ' XXX' ) -- XXX
852
- assert (utils .is_subtable (batches [field_name ].iterator_opts or
853
- {}, iterator_opts or {}), ' XXX' ) -- XXX
854
- assert (utils .is_subtable (iterator_opts or {},
855
- batches [field_name ].iterator_opts or {}), ' XXX' ) -- XXX
874
+ (' internal error: %s: prepared object list has ' ..
875
+ ' different index names: "%s" and "%s"' ):format (func_name ,
876
+ tostring (batches [field_name ].index_name ),
877
+ tostring (index_name )))
878
+ local ok = utils .are_tables_same (batches [field_name ].iterator_opts or
879
+ {}, iterator_opts or {})
880
+ if not ok then -- avoid extra json.encode()
881
+ assert (ok , (' internal error: %s: prepared object list ' ..
882
+ ' has different iterator options: "%s" and "%s"' ):format (
883
+ func_name ,
884
+ json .encode (batches [field_name ].iterator_opts ),
885
+ json .encode (iterator_opts )))
886
+ end
856
887
table.insert (batches [field_name ].keys , key )
888
+ size = i
857
889
end
858
890
end
859
891
end
860
892
861
- if next (batches ) == nil then
862
- return nil
893
+ -- don't flood cache with single-key (non-batch) select results
894
+ if size <= 1 then
895
+ return nil , size
863
896
end
864
897
865
898
local fetch_id = accessor :cache_fetch (batches , qcontext )
866
- return fetch_id
899
+ return fetch_id , size
867
900
end
868
901
869
902
-- }}}
@@ -937,12 +970,14 @@ end
937
970
---
938
971
--- * qcontext
939
972
--- * accessor
973
+ --- * max_batch_size
940
974
---
941
975
--- @treturn table result of the query
942
976
function bfs_executor .execute (schema , query_ast , variables , operation_name , opts )
943
977
local opts = opts or {}
944
978
local qcontext = opts .qcontext
945
979
local accessor = opts .accessor
980
+ local max_batch_size = opts .max_batch_size
946
981
947
982
local operation = core_query_util .getOperation (query_ast , operation_name )
948
983
local root_object_type = schema [operation .operation ]
@@ -1024,7 +1059,8 @@ function bfs_executor.execute(schema, query_ast, variables, operation_name, opts
1024
1059
elseif item .prepared_object_list ~= nil then
1025
1060
local child = invoke_resolve_list (item .prepared_object_list ,
1026
1061
context , {qcontext = qcontext , accessor = accessor ,
1027
- is_item_cache_only = is_item_cache_only })
1062
+ is_item_cache_only = is_item_cache_only ,
1063
+ max_batch_size = max_batch_size })
1028
1064
local child_open_set = child .open_set
1029
1065
local child_cache_only_open_set = child .cache_only_open_set
1030
1066
utils .expand_list (cache_only_open_set , child_cache_only_open_set )
@@ -1033,7 +1069,8 @@ function bfs_executor.execute(schema, query_ast, variables, operation_name, opts
1033
1069
local open_set_to_fetch = is_item_cache_only and
1034
1070
cache_only_open_set or open_set
1035
1071
local fetch_id , size = fetch_first_same (open_set_to_fetch ,
1036
- {accessor = accessor , qcontext = qcontext })
1072
+ {accessor = accessor , qcontext = qcontext ,
1073
+ max_batch_size = max_batch_size })
1037
1074
if # open_set_to_fetch > 0 then
1038
1075
table.insert (open_set_to_fetch , math.max (2 , size + 1 ), {
1039
1076
squash_marker = true ,
0 commit comments