20
20
21
21
import java .io .FileNotFoundException ;
22
22
import java .io .IOException ;
23
+ import java .net .SocketTimeoutException ;
23
24
import java .util .ArrayList ;
24
25
import java .util .List ;
25
26
import java .util .concurrent .Callable ;
28
29
import java .util .concurrent .Future ;
29
30
30
31
import org .junit .Test ;
32
+ import org .mockito .Mockito ;
33
+ import org .mockito .stubbing .Stubber ;
31
34
32
35
import org .apache .hadoop .conf .Configuration ;
33
36
import org .apache .hadoop .fs .FileSystem ;
36
39
import org .apache .hadoop .fs .LocatedFileStatus ;
37
40
import org .apache .hadoop .fs .Path ;
38
41
import org .apache .hadoop .fs .azurebfs .constants .FSOperationType ;
42
+ import org .apache .hadoop .fs .azurebfs .constants .HttpHeaderConfigurations ;
43
+ import org .apache .hadoop .fs .azurebfs .contracts .services .ListResultEntrySchema ;
44
+ import org .apache .hadoop .fs .azurebfs .contracts .services .ListResultSchema ;
45
+ import org .apache .hadoop .fs .azurebfs .services .AbfsClient ;
46
+ import org .apache .hadoop .fs .azurebfs .services .AbfsClientTestUtil ;
47
+ import org .apache .hadoop .fs .azurebfs .utils .TracingContext ;
48
+ import org .apache .hadoop .fs .azurebfs .utils .TracingHeaderFormat ;
39
49
import org .apache .hadoop .fs .azurebfs .utils .TracingHeaderValidator ;
40
50
import org .apache .hadoop .fs .contract .ContractTestUtils ;
41
51
52
+ import static java .net .HttpURLConnection .HTTP_OK ;
53
+ import static org .apache .hadoop .fs .azurebfs .constants .AbfsHttpConstants .EMPTY_STRING ;
42
54
import static org .apache .hadoop .fs .azurebfs .constants .ConfigurationKeys .AZURE_LIST_MAX_RESULTS ;
55
+ import static org .apache .hadoop .fs .azurebfs .services .RetryReasonConstants .CONNECTION_TIMEOUT_JDK_MESSAGE ;
43
56
import static org .apache .hadoop .fs .contract .ContractTestUtils .assertMkdirs ;
44
57
import static org .apache .hadoop .fs .contract .ContractTestUtils .createFile ;
45
58
import static org .apache .hadoop .fs .contract .ContractTestUtils .assertPathExists ;
46
59
import static org .apache .hadoop .fs .contract .ContractTestUtils .rename ;
47
60
48
61
import static org .apache .hadoop .test .LambdaTestUtils .intercept ;
62
+ import static org .mockito .ArgumentMatchers .any ;
63
+ import static org .mockito .ArgumentMatchers .nullable ;
64
+ import static org .mockito .Mockito .times ;
65
+ import static org .mockito .Mockito .when ;
49
66
50
67
/**
51
68
* Test listStatus operation.
52
69
*/
53
70
public class ITestAzureBlobFileSystemListStatus extends
54
71
AbstractAbfsIntegrationTest {
55
72
private static final int TEST_FILES_NUMBER = 6000 ;
73
+ private static final String TEST_CONTINUATION_TOKEN = "continuation" ;
56
74
57
75
public ITestAzureBlobFileSystemListStatus () throws Exception {
58
76
super ();
@@ -62,34 +80,105 @@ public ITestAzureBlobFileSystemListStatus() throws Exception {
62
80
public void testListPath () throws Exception {
63
81
Configuration config = new Configuration (this .getRawConfiguration ());
64
82
config .set (AZURE_LIST_MAX_RESULTS , "5000" );
65
- final AzureBlobFileSystem fs = (AzureBlobFileSystem ) FileSystem
66
- .newInstance (getFileSystem ().getUri (), config );
67
- final List <Future <Void >> tasks = new ArrayList <>();
68
-
69
- ExecutorService es = Executors .newFixedThreadPool (10 );
70
- for (int i = 0 ; i < TEST_FILES_NUMBER ; i ++) {
71
- final Path fileName = new Path ("/test" + i );
72
- Callable <Void > callable = new Callable <Void >() {
73
- @ Override
74
- public Void call () throws Exception {
75
- touch (fileName );
76
- return null ;
77
- }
78
- };
79
-
80
- tasks .add (es .submit (callable ));
81
- }
82
-
83
- for (Future <Void > task : tasks ) {
84
- task .get ();
83
+ try (final AzureBlobFileSystem fs = (AzureBlobFileSystem ) FileSystem
84
+ .newInstance (getFileSystem ().getUri (), config )) {
85
+ final List <Future <Void >> tasks = new ArrayList <>();
86
+
87
+ ExecutorService es = Executors .newFixedThreadPool (10 );
88
+ for (int i = 0 ; i < TEST_FILES_NUMBER ; i ++) {
89
+ final Path fileName = new Path ("/test" + i );
90
+ Callable <Void > callable = new Callable <Void >() {
91
+ @ Override
92
+ public Void call () throws Exception {
93
+ touch (fileName );
94
+ return null ;
95
+ }
96
+ };
97
+
98
+ tasks .add (es .submit (callable ));
99
+ }
100
+
101
+ for (Future <Void > task : tasks ) {
102
+ task .get ();
103
+ }
104
+
105
+ es .shutdownNow ();
106
+ fs .registerListener (
107
+ new TracingHeaderValidator (getConfiguration ().getClientCorrelationId (),
108
+ fs .getFileSystemId (), FSOperationType .LISTSTATUS , true , 0 ));
109
+ FileStatus [] files = fs .listStatus (new Path ("/" ));
110
+ assertEquals (TEST_FILES_NUMBER , files .length /* user directory */ );
85
111
}
112
+ }
86
113
87
- es .shutdownNow ();
88
- fs .registerListener (
89
- new TracingHeaderValidator (getConfiguration ().getClientCorrelationId (),
90
- fs .getFileSystemId (), FSOperationType .LISTSTATUS , true , 0 ));
91
- FileStatus [] files = fs .listStatus (new Path ("/" ));
92
- assertEquals (TEST_FILES_NUMBER , files .length /* user directory */ );
114
+ /**
115
+ * Test to verify that each paginated call to ListBlobs uses a new tracing context.
116
+ * @throws Exception
117
+ */
118
+ @ Test
119
+ public void testListPathTracingContext () throws Exception {
120
+ final AzureBlobFileSystem fs = getFileSystem ();
121
+ final AzureBlobFileSystem spiedFs = Mockito .spy (fs );
122
+ final AzureBlobFileSystemStore spiedStore = Mockito .spy (fs .getAbfsStore ());
123
+ final AbfsClient spiedClient = Mockito .spy (fs .getAbfsClient ());
124
+ final TracingContext spiedTracingContext = Mockito .spy (
125
+ new TracingContext (
126
+ fs .getClientCorrelationId (), fs .getFileSystemId (),
127
+ FSOperationType .LISTSTATUS , true , TracingHeaderFormat .ALL_ID_FORMAT , null ));
128
+
129
+ Mockito .doReturn (spiedStore ).when (spiedFs ).getAbfsStore ();
130
+ spiedStore .setClient (spiedClient );
131
+ spiedFs .setWorkingDirectory (new Path ("/" ));
132
+
133
+ AbfsClientTestUtil .setMockAbfsRestOperationForListPathOperation (spiedClient ,
134
+ (httpOperation ) -> {
135
+
136
+ ListResultEntrySchema entry = new ListResultEntrySchema ()
137
+ .withName ("a" )
138
+ .withIsDirectory (true );
139
+ List <ListResultEntrySchema > paths = new ArrayList <>();
140
+ paths .add (entry );
141
+ paths .clear ();
142
+ entry = new ListResultEntrySchema ()
143
+ .withName ("abc.txt" )
144
+ .withIsDirectory (false );
145
+ paths .add (entry );
146
+ ListResultSchema schema1 = new ListResultSchema ().withPaths (paths );
147
+ ListResultSchema schema2 = new ListResultSchema ().withPaths (paths );
148
+
149
+ when (httpOperation .getListResultSchema ()).thenReturn (schema1 )
150
+ .thenReturn (schema2 );
151
+ when (httpOperation .getResponseHeader (
152
+ HttpHeaderConfigurations .X_MS_CONTINUATION ))
153
+ .thenReturn (TEST_CONTINUATION_TOKEN )
154
+ .thenReturn (EMPTY_STRING );
155
+
156
+ Stubber stubber = Mockito .doThrow (
157
+ new SocketTimeoutException (CONNECTION_TIMEOUT_JDK_MESSAGE ));
158
+ stubber .doNothing ().when (httpOperation ).processResponse (
159
+ nullable (byte [].class ), nullable (int .class ), nullable (int .class ));
160
+
161
+ when (httpOperation .getStatusCode ()).thenReturn (-1 ).thenReturn (HTTP_OK );
162
+ return httpOperation ;
163
+ });
164
+
165
+ List <FileStatus > fileStatuses = new ArrayList <>();
166
+ spiedStore .listStatus (new Path ("/" ), "" , fileStatuses , true , null , spiedTracingContext );
167
+
168
+ // Assert that there were 2 paginated ListPath calls were made 1 and 2.
169
+ // 1. Without continuation token
170
+ Mockito .verify (spiedClient , times (1 )).listPath (
171
+ "/" , false ,
172
+ spiedFs .getAbfsStore ().getAbfsConfiguration ().getListMaxResults (),
173
+ null , spiedTracingContext );
174
+ // 2. With continuation token
175
+ Mockito .verify (spiedClient , times (1 )).listPath (
176
+ "/" , false ,
177
+ spiedFs .getAbfsStore ().getAbfsConfiguration ().getListMaxResults (),
178
+ TEST_CONTINUATION_TOKEN , spiedTracingContext );
179
+
180
+ // Assert that none of the API calls used the same tracing header.
181
+ Mockito .verify (spiedTracingContext , times (0 )).constructHeader (any (), any ());
93
182
}
94
183
95
184
/**
0 commit comments