22
22
import com .google .common .util .concurrent .Striped ;
23
23
24
24
import java .io .IOException ;
25
+ import java .time .Duration ;
25
26
import java .util .ArrayList ;
26
27
import java .util .Collection ;
27
28
import java .util .Iterator ;
38
39
import org .dcache .nfs .status .InvalException ;
39
40
import org .dcache .nfs .status .ShareDeniedException ;
40
41
import org .dcache .nfs .status .StaleException ;
42
+ import org .dcache .nfs .util .AdaptiveDelegationLogic ;
41
43
import org .dcache .nfs .v4 .xdr .nfs4_prot ;
42
44
import org .dcache .nfs .v4 .xdr .nfs_fh4 ;
43
45
import org .dcache .nfs .v4 .xdr .open_delegation_type4 ;
@@ -72,6 +74,15 @@ public class FileTracker {
72
74
*/
73
75
private final Map <Opaque , List <DelegationState >> delegations = new ConcurrentHashMap <>();
74
76
77
+ /**
78
+ * Heuristic to offer delegations.
79
+ *
80
+ * FIXME: for now we use a fixed sizes and timeout. THe best practice still should be identified.
81
+ */
82
+ private final AdaptiveDelegationLogic adlHeuristic =
83
+ new AdaptiveDelegationLogic (4096 , 4096 , Duration .ofSeconds (120 ));
84
+
85
+
75
86
private static class OpenState {
76
87
77
88
private final NFS4Client client ;
@@ -226,7 +237,13 @@ public record OpenRecord(stateid4 openStateId, stateid4 delegationStateId, boole
226
237
*/
227
238
public OpenRecord addOpen (NFS4Client client , StateOwner owner , Inode inode , int shareAccess , int shareDeny ) throws ChimeraNFSException {
228
239
240
+ // client explicitly refused delegation
241
+ boolean acceptsDelegation = (shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_WANT_NO_DELEG ) == 0 ;
242
+
243
+ // client explicitly requested read delegation
229
244
boolean wantReadDelegation = (shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_WANT_READ_DELEG ) != 0 ;
245
+
246
+ // client explicitly requested write delegation
230
247
boolean wantWriteDelegation = (shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG ) != 0 ;
231
248
232
249
Opaque fileId = new Opaque (inode .getFileId ());
@@ -255,13 +272,17 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
255
272
256
273
/*
257
274
* delegation is possible if:
275
+ * - client has not explicitly requested no delegation
258
276
* - client has a callback channel
259
277
* - client does not have a delegation for this file
260
278
* - no other open has write access
261
279
*/
262
- boolean canDelegateRead = client .getCB () != null &&
263
- (existingDelegations == null || existingDelegations .stream ().noneMatch (d -> d .client ().getId () == client .getId ())) &&
264
- opens .stream ().noneMatch (os -> (os .shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_WRITE ) != 0 );
280
+ boolean canDelegateRead = acceptsDelegation && (client .getCB () != null &&
281
+ (existingDelegations == null ||
282
+ existingDelegations .stream ()
283
+ .noneMatch (d -> d .client ().getId () == client .getId ())) &&
284
+ opens .stream ()
285
+ .noneMatch (os -> (os .shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_WRITE ) != 0 ));
265
286
266
287
// recall any read delegations if write
267
288
if ((existingDelegations != null ) && (shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_WRITE ) != 0 ) {
@@ -290,8 +311,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
290
311
// access mode and return the same stateid as required by rfc5661#18.16.3
291
312
292
313
for (OpenState os : opens ) {
293
- if (os .client .getId () == client .getId () &&
294
- os .getOwner ().equals (owner )) {
314
+ if (os .client .getId () == client .getId ()) {
295
315
os .shareAccess |= shareAccess ;
296
316
os .shareDeny |= shareDeny ;
297
317
@@ -305,6 +325,19 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
305
325
os .stateid .seqid ++;
306
326
//we need to return copy to avoid modification by concurrent opens
307
327
var openStateid = new stateid4 (os .stateid .other , os .stateid .seqid );
328
+
329
+ // yet another open from the same client. Let's check if we can delegate.
330
+ if (canDelegateRead && (os .shareAccess & nfs4_prot .OPEN4_SHARE_ACCESS_BOTH ) == nfs4_prot .OPEN4_SHARE_ACCESS_READ &&
331
+ (wantReadDelegation || adlHeuristic .shouldDelegate (client , inode ))) {
332
+
333
+ var delegationState = client .createDelegationState (os .getOwner ());
334
+ var delegation = new DelegationState (client , delegationState , open_delegation_type4 .OPEN_DELEGATE_READ );
335
+ delegations .computeIfAbsent (fileId , x -> new ArrayList <>(1 ))
336
+ .add (delegation );
337
+
338
+ return new OpenRecord (openStateid , delegationState .stateid (), true );
339
+ }
340
+
308
341
return new OpenRecord (openStateid , null , false );
309
342
}
310
343
}
@@ -320,8 +353,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
320
353
var openStateid = new stateid4 (stateid .other , stateid .seqid );
321
354
322
355
// REVISIT: currently only read-delegations are supported
323
- if (wantReadDelegation && canDelegateRead ) {
324
- // REVISIT: currently only read-delegations are supported
356
+ if (canDelegateRead && (wantReadDelegation || adlHeuristic .shouldDelegate (client , inode ))) {
325
357
var delegationStateid = client .createDelegationState (state .getStateOwner ());
326
358
delegations .computeIfAbsent (fileId , x -> new ArrayList <>(1 ))
327
359
.add (new DelegationState (client , delegationStateid , open_delegation_type4 .OPEN_DELEGATE_READ ));
0 commit comments