@@ -46,6 +46,7 @@ import (
46
46
"unsafe"
47
47
48
48
"github.com/gogo/protobuf/proto"
49
+ "github.com/pingcap/failpoint"
49
50
"github.com/pingcap/kvproto/pkg/errorpb"
50
51
"github.com/pingcap/kvproto/pkg/metapb"
51
52
"github.com/stretchr/testify/suite"
@@ -1782,3 +1783,108 @@ func (s *testRegionRequestToSingleStoreSuite) TestRefreshCacheConcurrency() {
1782
1783
1783
1784
cancel ()
1784
1785
}
1786
+
1787
+ func (s * testRegionCacheSuite ) TestRangesAreCoveredCheck () {
1788
+ check := func (ranges []string , regions []string , limit int , expect bool ) {
1789
+ s .Len (ranges , 2 )
1790
+ rgs := make ([]* pd.Region , 0 , len (regions ))
1791
+ for i := 0 ; i < len (regions ); i += 2 {
1792
+ rgs = append (rgs , & pd.Region {Meta : & metapb.Region {
1793
+ StartKey : []byte (regions [i ]),
1794
+ EndKey : []byte (regions [i + 1 ]),
1795
+ }})
1796
+ }
1797
+ s .Equal (expect , regionsHaveGapInRange ([]byte (ranges [0 ]), []byte (ranges [1 ]), rgs , limit ))
1798
+ }
1799
+
1800
+ boundCase := []string {"a" , "c" }
1801
+ // positive
1802
+ check (boundCase , []string {"a" , "c" }, - 1 , false )
1803
+ check (boundCase , []string {"a" , "" }, - 1 , false )
1804
+ check (boundCase , []string {"" , "c" }, - 1 , false )
1805
+ // negative
1806
+ check (boundCase , []string {"a" , "b" }, - 1 , true )
1807
+ check (boundCase , []string {"b" , "c" }, - 1 , true )
1808
+ check (boundCase , []string {"b" , "" }, - 1 , true )
1809
+ check (boundCase , []string {"" , "b" }, - 1 , true )
1810
+ // positive
1811
+ check (boundCase , []string {"a" , "b" , "b" , "c" }, - 1 , false )
1812
+ check (boundCase , []string {"" , "b" , "b" , "c" }, - 1 , false )
1813
+ check (boundCase , []string {"a" , "b" , "b" , "" }, - 1 , false )
1814
+ check (boundCase , []string {"" , "b" , "b" , "" }, - 1 , false )
1815
+ // negative
1816
+ check (boundCase , []string {"a" , "b" , "b1" , "c" }, - 1 , true )
1817
+ check (boundCase , []string {"" , "b" , "b1" , "c" }, - 1 , true )
1818
+ check (boundCase , []string {"a" , "b" , "b1" , "" }, - 1 , true )
1819
+ check (boundCase , []string {"" , "b" , "b1" , "" }, - 1 , true )
1820
+ check (boundCase , []string {}, - 1 , true )
1821
+
1822
+ unboundCase := []string {"" , "" }
1823
+ // positive
1824
+ check (unboundCase , []string {"" , "" }, - 1 , false )
1825
+ // negative
1826
+ check (unboundCase , []string {"a" , "c" }, - 1 , true )
1827
+ check (unboundCase , []string {"a" , "" }, - 1 , true )
1828
+ check (unboundCase , []string {"" , "c" }, - 1 , true )
1829
+ // positive
1830
+ check (unboundCase , []string {"" , "b" , "b" , "" }, - 1 , false )
1831
+ // negative
1832
+ check (unboundCase , []string {"" , "b" , "b1" , "" }, - 1 , true )
1833
+ check (unboundCase , []string {"a" , "b" , "b" , "" }, - 1 , true )
1834
+ check (unboundCase , []string {"" , "b" , "b" , "c" }, - 1 , true )
1835
+ check (unboundCase , []string {}, - 1 , true )
1836
+
1837
+ // test half bounded ranges
1838
+ check ([]string {"" , "b" }, []string {"" , "a" }, - 1 , true )
1839
+ check ([]string {"" , "b" }, []string {"" , "a" }, 1 , false ) // it's just limitation reached
1840
+ check ([]string {"" , "b" }, []string {"" , "a" }, 2 , true )
1841
+ check ([]string {"a" , "" }, []string {"b" , "" }, - 1 , true )
1842
+ check ([]string {"a" , "" }, []string {"b" , "" }, 1 , true )
1843
+ check ([]string {"a" , "" }, []string {"b" , "c" }, 1 , true )
1844
+ check ([]string {"a" , "" }, []string {"a" , "" }, - 1 , false )
1845
+ }
1846
+
1847
+ func (s * testRegionCacheSuite ) TestScanRegionsWithGaps () {
1848
+ // Split at "a", "c", "e"
1849
+ // nil --- 'a' --- 'c' --- 'e' --- nil
1850
+ // <- 0 -> <- 1 -> <- 2 -> <- 3 -->
1851
+ regions := s .cluster .AllocIDs (3 )
1852
+ regions = append ([]uint64 {s .region1 }, regions ... )
1853
+
1854
+ peers := [][]uint64 {{s .peer1 , s .peer2 }}
1855
+ for i := 0 ; i < 3 ; i ++ {
1856
+ peers = append (peers , s .cluster .AllocIDs (2 ))
1857
+ }
1858
+
1859
+ for i := 0 ; i < 3 ; i ++ {
1860
+ s .cluster .Split (regions [i ], regions [i + 1 ], []byte {'a' + 2 * byte (i )}, peers [i + 1 ], peers [i + 1 ][0 ])
1861
+ }
1862
+
1863
+ // the last region is not reported to PD yet
1864
+ getRegionIDsWithInject := func (fn func () ([]* Region , error )) []uint64 {
1865
+ s .cache .clear ()
1866
+ err := failpoint .Enable ("tikvclient/mockSplitRegionNotReportToPD" , fmt .Sprintf (`return(%d)` , regions [2 ]))
1867
+ s .Nil (err )
1868
+ resCh := make (chan []* Region )
1869
+ errCh := make (chan error )
1870
+ go func () {
1871
+ rs , err := fn ()
1872
+ errCh <- err
1873
+ resCh <- rs
1874
+ }()
1875
+ time .Sleep (time .Second )
1876
+ failpoint .Disable ("tikvclient/mockSplitRegionNotReportToPD" )
1877
+ s .Nil (<- errCh )
1878
+ rs := <- resCh
1879
+ regionIDs := make ([]uint64 , 0 , len (rs ))
1880
+ for _ , r := range rs {
1881
+ regionIDs = append (regionIDs , r .GetID ())
1882
+ }
1883
+ return regionIDs
1884
+ }
1885
+
1886
+ scanRegionRes := getRegionIDsWithInject (func () ([]* Region , error ) {
1887
+ return s .cache .BatchLoadRegionsWithKeyRange (s .bo , []byte ("" ), []byte ("" ), 10 )
1888
+ })
1889
+ s .Equal (scanRegionRes , regions )
1890
+ }
0 commit comments