1
1
import logging
2
2
import resource
3
3
import re
4
+ import time
5
+ import os
4
6
5
7
from errno import *
6
8
from threading import Lock , RLock
17
19
from gdrivefs .cache .volume import PathRelations , EntryCache , path_resolver , \
18
20
CLAUSE_ID , CLAUSE_ENTRY
19
21
from gdrivefs .gdtool .drive import drive_proxy
20
- from gdrivefs .general .buffer_segments import BufferSegments
21
22
22
23
_static_log = logging .getLogger ().getChild ('(OF)' )
23
24
@@ -34,19 +35,6 @@ def get_temp_filepath(normalized_entry, mime_type):
34
35
return ("%s/local/%s" % (temp_path , temp_filename ))
35
36
36
37
37
-
38
- # TODO(dustin): LCM runs in a greenlet pool. When we open a file that needs the
39
- # existing data for a file (read, append), a switch is done to an
40
- # LCM worker. If the data is absent or faulted, download the
41
- # content. Then, switch back.
42
-
43
- class LocalCopyManager (object ):
44
- """Manages local copies of files."""
45
-
46
- # def
47
- pass
48
-
49
-
50
38
class OpenedManager (object ):
51
39
"""Manages all of the currently-open files."""
52
40
@@ -296,8 +284,16 @@ def __init__(self, entry_id, path, filename, is_hidden, mime_type):
296
284
297
285
self .reset_state ()
298
286
287
+ try :
288
+ entry = self .__get_entry_or_raise ()
289
+ except :
290
+ self .__log .exception ("Could not get entry with ID [%s] for "
291
+ "write-flush." % (self .__entry_id ))
292
+ raise
293
+
294
+ self .__temp_filepath = get_temp_filepath (entry , self .mime_type )
295
+
299
296
def reset_state (self ):
300
- self .__buffer = None
301
297
self .__is_loaded = False
302
298
self .__is_dirty = False
303
299
@@ -348,7 +344,7 @@ def __load_base_from_remote(self):
348
344
349
345
self .__log .debug ("Ensuring local availability of [%s]." % (entry ))
350
346
351
- temp_file_path = get_temp_filepath (entry , self .mime_type )
347
+ temp_filepath = get_temp_filepath (entry , self .mime_type )
352
348
353
349
self .__log .debug ("__load_base_from_remote about to download." )
354
350
@@ -358,7 +354,7 @@ def __load_base_from_remote(self):
358
354
359
355
self .__log .info ("Attempting local cache update of file [%s] for "
360
356
"entry [%s] and mime-type [%s]." %
361
- (temp_file_path , entry , self .mime_type ))
357
+ (temp_filepath , entry , self .mime_type ))
362
358
363
359
if entry .requires_mimetype :
364
360
length = DisplacedFile .file_size
@@ -367,11 +363,11 @@ def __load_base_from_remote(self):
367
363
d = DisplacedFile (entry )
368
364
stub_data = d .deposit_file (self .mime_type )
369
365
370
- with file (temp_file_path , 'w' ) as f :
366
+ with file (temp_filepath , 'w' ) as f :
371
367
f .write (stub_data )
372
368
except :
373
369
self .__log .exception ("Could not deposit to file [%s] from "
374
- "entry [%s]." % (temp_file_path ,
370
+ "entry [%s]." % (temp_filepath ,
375
371
entry ))
376
372
raise
377
373
@@ -382,9 +378,10 @@ def __load_base_from_remote(self):
382
378
self .__log .info ("Executing the download." )
383
379
384
380
try :
385
- # TODO(dustin): We're not inheriting an existing file (same mtime, same size).
381
+ # TODO(dustin): Confirm that this will inherit an existing file (same mtime,
382
+ # same size).
386
383
result = drive_proxy ('download_to_local' ,
387
- output_file_path = temp_file_path ,
384
+ output_file_path = temp_filepath ,
388
385
normalized_entry = entry ,
389
386
mime_type = self .mime_type )
390
387
@@ -401,45 +398,6 @@ def __load_base_from_remote(self):
401
398
"__is_loaded= [%s]" %
402
399
(cache_fault , self .__is_loaded ))
403
400
404
- # We've either not loaded it, yet, or it has changed.
405
- if cache_fault or not self .__is_loaded :
406
- with self .__class__ .__update_lock :
407
- self .__log .info ("Checking queued items for fault." )
408
-
409
- if cache_fault :
410
- if self .__is_dirty :
411
- self .__log .error ("Entry [%s] has been changed. "
412
- "Forcing buffer updates, and "
413
- "clearing uncommitted updates." %
414
- (entry ))
415
- else :
416
- self .__log .debug ("Entry [%s] has changed. "
417
- "Updating buffers." % (entry ))
418
-
419
- self .__log .debug ("Loading buffers." )
420
-
421
- with open (temp_file_path , 'rb' ) as f :
422
- # Read the locally cached file in.
423
-
424
- try :
425
- # TODO(dustin): Our accounting is broken when it comes to loading and/or update-tracking. If we have a guarantee thawrites only appear in sequence and in increasing order, we can dump BufferSegments.
426
-
427
- # TODO(dustin): This is the source of:
428
- # 1) An enormous slowdown where we first have to write the data, and then have to read it back.
429
- # 2) An enormous resource burden.
430
- data = f .read ()
431
-
432
- read_blocksize = Conf .get ('default_buffer_read_blocksize' )
433
- self .__buffer = BufferSegments (data , read_blocksize )
434
- except :
435
- self .__log .exception ("Could not read current cached "
436
- "file into buffer." )
437
- raise
438
-
439
- self .__is_dirty = False
440
-
441
- self .__is_loaded = True
442
-
443
401
self .__log .debug ("__load_base_from_remote complete." )
444
402
return cache_fault
445
403
@@ -454,15 +412,45 @@ def add_update(self, offset, data):
454
412
self .__load_base_from_remote ()
455
413
except :
456
414
self .__log .exception ("Could not load entry to local cache [%s]." %
457
- (self .temp_file_path ))
415
+ (self .__temp_filepath ))
458
416
raise
459
417
460
418
self .__log .debug ("Base loaded for add_update." )
461
419
462
420
with self .__class__ .__update_lock :
463
- self .__buffer .apply_update (offset , data )
421
+ with open (self .__temp_filepath , 'r+' ) as f :
422
+ f .seek (offset )
423
+ f .write (data )
424
+
464
425
self .__is_dirty = True
465
426
427
+ @dec_hint (['length' ], ['length' ], 'OF' )
428
+ def truncate (self , length ):
429
+ try :
430
+ self .__load_base_from_remote ()
431
+ except :
432
+ self .__log .exception ("Could not load entry to local cache [%s]." %
433
+ (self .__temp_filepath ))
434
+ raise
435
+
436
+ self .__log .debug ("Base loaded for truncate." )
437
+
438
+ entry = self .__get_entry_or_raise ()
439
+
440
+ with self .__class__ .__update_lock :
441
+ with open (self .__temp_filepath , 'r+' ) as f :
442
+ f .truncate (length )
443
+
444
+ gd_mtime_epoch = time .mktime (
445
+ entry .modified_date .timetuple ())
446
+
447
+ # TODO(dustin): Shouldn't we be taking the first time component (atime?) from the entry?
448
+ os .utime (self .__temp_filepath , (time .time (), gd_mtime_epoch ))
449
+
450
+ self .__is_dirty = True
451
+
452
+ # TODO(dustin): We still have to make sure the mtime is set to match
453
+
466
454
@dec_hint (prefix = 'OF' )
467
455
def flush (self ):
468
456
"""The OS wants to effect any changes made to the file."""
@@ -472,81 +460,54 @@ def flush(self):
472
460
entry = self .__get_entry_or_raise ()
473
461
cache_fault = self .__load_base_from_remote ()
474
462
463
+ # TODO(dustin): We need to be able to do updates for separate files in
464
+ # parallel.
475
465
with self .__class__ .__update_lock :
476
466
if self .__is_dirty is False :
477
467
self .__log .debug ("Flush will be skipped because there are no "
478
468
"changes." )
479
- # TODO: Raise an exception?
480
469
return
481
470
482
- # Write back out to the temporary file.
483
-
484
- self .__log .debug ("Writing buffer to temporary file." )
485
- # TODO: Make sure to uncache the temp data if self.temp_file_path is not None.
486
-
487
- mime_type = self .mime_type
488
-
489
- # If we've already opened a work file, use it. Else, use a
490
- # temporary file that we'll close at the end of the method.
491
- if self .__is_loaded :
492
- is_temp = False
493
-
494
- temp_file_path = get_temp_filepath (entry , mime_type )
495
-
496
- with file (temp_file_path , 'w' ) as f :
497
- for block in self .__buffer .read ():
498
- f .write (block )
499
-
500
- write_filepath = temp_file_path
501
- else :
502
- is_temp = True
503
-
504
- with NamedTemporaryFile (delete = False ) as f :
505
- write_filepath = f .name
506
- for block in self .__buffer .read ():
507
- f .write (block )
508
-
509
471
# Push to GD.
510
472
473
+ # os.stat(self.__temp
474
+
511
475
self .__log .debug ("Pushing (%d) bytes for entry with ID from [%s] "
512
476
"to GD for file-path [%s]." %
513
- (self .__buffer .length , entry .id , write_filepath ))
514
-
515
- # print("Sending updates.")
477
+ (self .__buffer .length , entry .id , self .__temp_filepath ))
516
478
517
- # TODO: Update mtime?
479
+ # TODO: Will this automatically update mtime?
480
+ # TODO(dustin): We need to be able to update individual slices of the file
481
+ # (maybe only if we've affected less than X% of the file).
518
482
try :
519
483
entry = drive_proxy ('update_entry' ,
520
484
normalized_entry = entry ,
521
485
filename = entry .title ,
522
- data_filepath = write_filepath ,
523
- mime_type = mime_type ,
486
+ data_filepath = self . __temp_filepath ,
487
+ mime_type = self . mime_type ,
524
488
parents = entry .parents ,
525
489
is_hidden = self .__is_hidden )
526
490
except :
527
491
self .__log .exception ("Could not localize displaced file with "
528
492
"entry having ID [%s]." % (entry .id ))
529
493
raise
530
494
531
- if not is_temp :
532
- unlink (write_filepath )
533
- else :
534
- # Update the write-cache file to the official mtime. We won't
535
- # redownload it on the next flush if it wasn't changed,
536
- # elsewhere.
495
+ # Update the write-cache file to the official mtime. We won't
496
+ # redownload it on the next flush if it wasn't changed,
497
+ # elsewhere.
537
498
538
- self .__log .debug ("Updating local write-cache file to official "
539
- "mtime [%s]." % (entry .modified_date_epoch ))
499
+ self .__log .debug ("Updating local write-cache file to official "
500
+ "mtime [%s]." % (entry .modified_date_epoch ))
540
501
541
- try :
542
- utime (write_filepath , (entry .modified_date_epoch ,
543
- entry .modified_date_epoch ))
544
- except :
545
- self .__log .exception ("Could not update mtime of write-"
546
- "cache [%s] for entry with ID [%s], "
547
- "post-flush." %
548
- (entry .modified_date_epoch , entry .id ))
549
- raise
502
+ try :
503
+ utime (self . __temp_filepath , (entry .modified_date_epoch ,
504
+ entry .modified_date_epoch ))
505
+ except :
506
+ self .__log .exception ("Could not update mtime of write-"
507
+ "cache [%s] for entry with ID [%s], "
508
+ "post-flush." %
509
+ (entry .modified_date_epoch , entry .id ))
510
+ raise
550
511
551
512
# Immediately update our current cached entry.
552
513
0 commit comments