53
53
import org .apache .zookeeper .client .ZooKeeperSaslClient ;
54
54
import org .apache .zookeeper .common .PathUtils ;
55
55
import org .apache .zookeeper .data .ACL ;
56
- import org .apache .zookeeper .data .PathWithStat ;
57
56
import org .apache .zookeeper .data .Stat ;
58
57
import org .apache .zookeeper .proto .AddWatchRequest ;
59
58
import org .apache .zookeeper .proto .CheckWatchesRequest ;
@@ -2769,11 +2768,15 @@ public List<String> getChildren(final String path, Watcher watcher) throws Keepe
2769
2768
* @param maxReturned
2770
2769
* - the maximum number of children returned
2771
2770
* @param minCzxId
2772
- * - only return children whose creation zkid is equal or greater than {@code minCzxId}
2773
- * @param czxIdOffset
2774
- * - how many children with zkid == minCzxId to skip server-side, as they were returned in previous pages
2771
+ * - only return children whose creation zxid is equal or greater than {@code minCzxId}
2772
+ * @param czxidOffset
2773
+ * - how many children with zxid == minCzxId to skip server-side, as they were returned in previous pages
2774
+ * @param nextPage
2775
+ * - if not null, {@link PaginationNextPage} info returned from server will be copied to {@code nextPage}.
2776
+ * The info can be used for fetching the next page of remaining children, or checking whether the
2777
+ * returned page is the last one
2775
2778
* @return
2776
- * an ordered list of children nodes, up to {@code maxReturned}, ordered by czxid
2779
+ * a list of children nodes, up to {@code maxReturned}
2777
2780
* @throws KeeperException
2778
2781
* if the server signals an error with a non-zero error code.
2779
2782
* @throws IllegalArgumentException
@@ -2788,10 +2791,14 @@ public List<String> getChildren(final String path, Watcher watcher) throws Keepe
2788
2791
*
2789
2792
* @since 3.6.2
2790
2793
*/
2791
- public List <PathWithStat > getChildren (final String path , Watcher watcher , final int maxReturned , final long minCzxId , final int czxIdOffset )
2794
+ public List <String > getChildren (final String path ,
2795
+ Watcher watcher ,
2796
+ int maxReturned ,
2797
+ long minCzxId ,
2798
+ int czxidOffset ,
2799
+ final PaginationNextPage nextPage )
2792
2800
throws KeeperException , InterruptedException {
2793
- final String clientPath = path ;
2794
- PathUtils .validatePath (clientPath );
2801
+ PathUtils .validatePath (path );
2795
2802
2796
2803
if (maxReturned <= 0 ) {
2797
2804
throw new IllegalArgumentException ("Cannot return less than 1 children" );
@@ -2800,26 +2807,100 @@ public List<PathWithStat> getChildren(final String path, Watcher watcher, final
2800
2807
// the watch contains the un-chroot path
2801
2808
WatchRegistration wcb = null ;
2802
2809
if (watcher != null ) {
2803
- wcb = new ChildWatchRegistration (watcher , clientPath );
2810
+ wcb = new ChildWatchRegistration (watcher , path );
2804
2811
}
2805
2812
2806
- final String serverPath = prependChroot (clientPath );
2813
+ final String serverPath = prependChroot (path );
2807
2814
2808
2815
RequestHeader h = new RequestHeader ();
2809
2816
h .setType (ZooDefs .OpCode .getChildrenPaginated );
2810
2817
GetChildrenPaginatedRequest request = new GetChildrenPaginatedRequest ();
2811
2818
request .setPath (serverPath );
2812
2819
request .setWatch (watcher != null );
2813
2820
request .setMaxReturned (maxReturned );
2814
- request .setMinCzxId (minCzxId );
2815
- request .setCzxIdOffset (czxIdOffset );
2816
- GetChildrenPaginatedResponse response = new GetChildrenPaginatedResponse ();
2817
- ReplyHeader r = cnxn .submitRequest (h , request , response , wcb );
2818
- if (r .getErr () != 0 ) {
2819
- throw KeeperException .create (KeeperException .Code .get (r .getErr ()),
2820
- clientPath );
2821
+
2822
+ Set <String > children = null ;
2823
+ GetChildrenPaginatedResponse response ;
2824
+ boolean isFirstPage = true ;
2825
+ boolean needNextPage = true ;
2826
+
2827
+ while (needNextPage ) {
2828
+ request .setMinCzxid (minCzxId );
2829
+ // If not the first page, always start from czxidOffset 0, to avoid the case:
2830
+ // if a child with the same czxid is returned in the previous page, and then deleted
2831
+ // on the server, the starting offset for the next page should be shifted smaller accordingly.
2832
+ // If the next page still starts from czxidOffset, the children that not in the previous page
2833
+ // but their offset is less than czxidOffset, they would be missed.
2834
+ // HashSet is used to de-dup the duplicate children.
2835
+ request .setCzxidOffset (isFirstPage ? czxidOffset : 0 );
2836
+ response = new GetChildrenPaginatedResponse ();
2837
+ ReplyHeader r = cnxn .submitRequest (h , request , response , wcb );
2838
+ if (r .getErr () != 0 ) {
2839
+ throw KeeperException .create (KeeperException .Code .get (r .getErr ()),
2840
+ path );
2841
+ }
2842
+ minCzxId = response .getNextPageCzxid ();
2843
+ czxidOffset = response .getNextPageCzxidOffset ();
2844
+ needNextPage = needNextPage (maxReturned , minCzxId , czxidOffset );
2845
+
2846
+ if (isFirstPage ) {
2847
+ // If all children are returned in the first page,
2848
+ // no need to use hash set to de-dup children
2849
+ if (!needNextPage ) {
2850
+ updateNextPage (nextPage , minCzxId , czxidOffset );
2851
+ return response .getChildren ();
2852
+ }
2853
+ children = new HashSet <>();
2854
+ isFirstPage = false ;
2855
+ }
2856
+
2857
+ children .addAll (response .getChildren ());
2858
+ }
2859
+
2860
+ updateNextPage (nextPage , minCzxId , czxidOffset );
2861
+
2862
+ return new ArrayList <>(children );
2863
+ }
2864
+
2865
+ /**
2866
+ * Returns a list of all the children given the path.
2867
+ * <p>
2868
+ * The difference between this API and {@link #getChildren(String, boolean)} is:
2869
+ * when there are lots of children and the network buffer exceeds {@code jute.maxbuffer},
2870
+ * this API will fetch the children using pagination and be able to return all children;
2871
+ * while {@link #getChildren(String, boolean)} will fail.
2872
+ * <p>
2873
+ * The final list of children returned is NOT strongly consistent with the server's data:
2874
+ * the list might contain some deleted children if some children are deleted before the last page is fetched.
2875
+ * <p>
2876
+ * If the watch is true and the call is successful (no exception is thrown),
2877
+ # a watch will be left on the node with the given path. The watch will be
2878
+ * triggered by a successful operation that deletes the node of the given
2879
+ * path or creates/deletes a child under the node.
2880
+ * <p>
2881
+ *
2882
+ * @param path the path of the parent node
2883
+ * @param watch whether or not leave a watch on the given node
2884
+ * @return a list of all children of the given path
2885
+ * @throws KeeperException if the server signals an error with a non-zero error code.
2886
+ * @throws InterruptedException if the server transaction is interrupted.
2887
+ */
2888
+ public List <String > getAllChildrenPaginated (String path , boolean watch )
2889
+ throws KeeperException , InterruptedException {
2890
+ return getChildren (path , watch ? watchManager .defaultWatcher : null , Integer .MAX_VALUE , 0L , 0 , null );
2891
+ }
2892
+
2893
+ private boolean needNextPage (int maxReturned , long minCzxId , int czxIdOffset ) {
2894
+ return maxReturned == Integer .MAX_VALUE
2895
+ && minCzxId != ZooDefs .GetChildrenPaginated .lastPageMinCzxid
2896
+ && czxIdOffset != ZooDefs .GetChildrenPaginated .lastPageMinCzxidOffset ;
2897
+ }
2898
+
2899
+ private void updateNextPage (PaginationNextPage nextPage , long minCzxId , int czxIdOffset ) {
2900
+ if (nextPage != null ) {
2901
+ nextPage .setMinCzxid (minCzxId );
2902
+ nextPage .setMinCzxidOffset (czxIdOffset );
2821
2903
}
2822
- return response .getChildren ();
2823
2904
}
2824
2905
2825
2906
/**
@@ -2853,7 +2934,7 @@ public List<PathWithStat> getChildren(final String path, Watcher watcher, final
2853
2934
*
2854
2935
* @since 3.6.2
2855
2936
*/
2856
- public RemoteIterator <PathWithStat > getChildrenIterator (String path , Watcher watcher , int batchSize , long minCzxId )
2937
+ public RemoteIterator <String > getChildrenIterator (String path , Watcher watcher , int batchSize , long minCzxId )
2857
2938
throws KeeperException , InterruptedException {
2858
2939
return new ChildrenBatchIterator (this , path , watcher , batchSize , minCzxId );
2859
2940
}
0 commit comments