Aynı dosyaları yedeklememek için ne yapmalı?

Google Drive API kullarak dosya yedekleme işlemi yapıyorum.

Aynı dosyaları birden fazla kez yedeklemek istemiyorum;

•Bunun için kontrol sağlamam gerekiyor ama dosya isimleri üzerinden kontrol edemem çünkü aynı dosya farklı isimlerle var olabiliyor; image.png veya imagenew.png veya image_1.png gibi…

•Yedeklemeden önce yedeklenecek dosyaları kendi aralarında kontrol edip aynı olmayanları yedekleyebilirim ancak aradan bir süre geçince hangi dosyaları yedeklediğimi unutucağımı düşünüyorum ve bu yüzden bunun verimli olacağını düşünmüyorum.

•Aklıma gelen çözüm ise yedeklenmiş dosyaları çekmek ve yedeklenecek dosya ile karşılaştırmak oldu.

from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials


SCOPES = ['https://www.googleapis.com/auth/drive']
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
service = build('drive', 'v3', credentials=creds)

yedeklenecekDosya = open('img.png', 'rb').read()
yedeklenmisDosyalar=[]

results = service.files().list(q=f"'{'1Lo5VUyLGj0xo_EIGvYO1XFcCTQ8TdzYr'}' in parents and trashed = false", fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])

for i in items:
    yedeklenmisDosyalar.append(service.files().get_media(fileId=i['id']).execute())
        
        
if yedeklenecekDosya in yedeklenmisDosyalar:
    print("zaten önceden yedeklenmis!")
else:
    print("yedeklenebilir")

Bunun bi tık verimli hâli şu şekilde;

from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
import hashlib

SCOPES = ['https://www.googleapis.com/auth/drive']
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
service = build('drive', 'v3', credentials=creds)

# Yedeklenecek dosyanın özeti (hash) hesaplanır
yedeklenecekDosya = open('img.png', 'rb').read()
yedeklenecekDosyaHash = hashlib.md5(yedeklenecekDosya).hexdigest()

# Yedeklenmiş dosyaların listesi alınır ve özetleri hesaplanır
yedeklenmisDosyalar = set()
results = service.files().list(q=f"'{'1Lo5VUyLGj0xo_EIGvYO1XFcCTQ8TdzYr'}' in parents and trashed = false", fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
for i in items:
    yedeklenmisDosyalar.add(service.files().get(fileId=i['id'], fields='md5Checksum').execute()['md5Checksum'])

# Yedeklenecek dosya, önceden yedeklenmiş dosyaların listesinde var mı diye kontrol edilir
if yedeklenecekDosyaHash in yedeklenmisDosyalar:
    print("Zaten önceden yedeklenmiş!")
else:
    print("Yedeklenebilir")

Daha verimli bir şekilde nasıl yapabilirim, mantığını anlamam yeterli olur. Daha verimli yol yoksa 2 örneği kullanmaya devam edeceğim sanırım.

rsync default olarak dosyalarin son degistirilme tarihine ve boyuna bakiyor. Herhangi biri farkliysa, dosyayi degismis kabul ediyor. (Tarihi gec olani “son hal” kabul etmek de bir secenek.) FAT dosya sistemlerinde tarih 2 saniyelik cozunurlukle kaydedildigi icin bir window (aralik) kullanmak gerekebilir: “tarihler arasinda 2 saniyeden fazla fark varsa…”

Dosya icerigine bakmak tabi ki en dogrusu. Her Sayısal Loto kazandiginda bir yanlis sonuc elde etmek sorun olmayacaksa, dosyalarin icerigi yerine iceriklerinin hash/checksum’ini karsilastirmak bandwidth’ten tasarruf saglayan bir alternatif. (Son kodda oyle yapiliyor sanirim?) Fakat karsi tarafta dosyanin checksum’ini olusturabilecek bir agent (birim?) gerektiriyor. Google Drive bunu yapiyorsa sorun yok, hatta tam olarak bu is icin yapiyor olmasi muhtemel.

Dosyalari parcalara bolup parcalari kontrol eden sistemler de var. Degismeyen, veya birden fazla dosyada bulunan parcalarin gonderilmesinden de tasarruf ediyorlar. Anlattiginiz kullanim icin cok gerekli gibi durmuyor.

1 Beğeni

Cevap için teşekkürler, daha iyisini bulana kadar en mantıklı yol bu gibi duruyor.

tekrardan merhaba @aib;
Dedigim gibi dosyalari yedeklemeden önce dosyayı sha256 ile hashleyerek tanımladığım hashList’e ekliyorum, daha önceden yedeklenip yedeklenmediğini de bu sayede kontrol ediyorum, dosyayı hashleyerek kontrol etmek en garanti yol oldu.
Yedeklenecek dosyalar listesinde dosyaları alip tek tek yedeklerken sistem gayet guzel ve hatasiz calisiyor ama örnek olarak yedekledigim 3kb lık dosyayı 5 saniyede yedekliyor, yani yavaş çalışıyor.

Aynı anda birden fazla dosyayi yedeklemek icin threading kullanmaya karar verdim ve listedeki her bir dosya sadece tek sefer işleme alinsin diye Queue kullandim, ama bu sefer istedigim sonucu elde edemedim.

Örnek kodum;

import threading, hashlib, queue, os


def hashFile(fileName):
    with open(fileName, "rb") as f:
        sha256 = hashlib.sha256()
        while chunk := f.read(4096):
            sha256.update(chunk)
        return sha256.hexdigest()


def backupFile(q):
    while not q.empty():
        fileName = q.get()
        
        if hashFile(filesToBackupPath+fileName) in hashList:
            print(f"\033[33m{fileName} daha once yedeklenmis\033[0m")
        else:
            print(f"\033[32m{fileName} yedeklendi\033[0m")
            hashList.append(hashFile(filesToBackupPath+fileName))
            
        q.task_done()
        

filesToBackupPath = "yedeklenecekDosyalar/"
fileList = os.listdir(filesToBackupPath)
hashList = []

q = queue.Queue()

for file in fileList:
    q.put(file)

for i in range(10):
    t = threading.Thread(target=backupFile, args=(q,))
    t.start()

q.join()

print('\n',len(hashList), hashList)

Sorunun; 10 threadın ayni anda 10 islem yaparken hashList’i kontrol etmesi, hashListe öge eklemesi sirasinda oldugunu biliyorum ama nasil duzeltmem gerektigini bilmiyorum. Bu işlemi thread ve queue kullanmadan for dongusu ile yaptigimda gayet normal calisiyor…

Almak istedigim sonuc; (for dongusu ile yapildi)

Aldigim sonuc; (thread ve queue ile yapildi)

Fikrini paylasirsan sevinirim.

Ornegin yavas calismasi normal. (Ayrica 5 saniyede biten cloud backup da muhtesem :slightly_smiling_face:) Normal calisma ne kadar suruyor?

Hashlisti onceden uretmek lazim.

Onun disindaki cozumler karmasiklasiyor. Herhangi bir potansiyel dosyanin mevcut hashleme yapan thread’lerin dosyalariyla ayni olmadigini garantilemek gerekiyor.

Sunlari kullanan bir cozum mevcut sanki: Upload edilmis hash’ler seti, upload edilmekte olan hash’ler seti, upload edilecek dosyalar queue’su, hash’i alinip karar verilecek dosyalar queue’su

1 Beğeni

For dongusuyle ortalama 3kb lık 20 dosya ortalama 97 saniyede yedekleniyor, ama onceden yedeklenip yedeklenmedigini kontrol etmeden thread kullanarak ayni 20 dosyayi ortalama 7-8 saniyede yedekliyorum…

onceden yedeklenip yedeklenmedigini kontrol etmeye calisip bir de thread ekleyince isler karisiyordu

Araştırma yaparken şans eseri sorunun ne oldugunu buldum; race condition

Alıntı;

Race condition, birden fazla iş parçacığının (thread) aynı anda aynı değişken veya kaynağı kullanması durumunda, beklenmedik sonuçların ortaya çıkabileceği bir senkronizasyon hatasıdır. Örneğin, bir programda birden fazla iş parçacığı aynı anda bir değişkeni okuyup güncelleyebilir. Eğer bu işlemler arasında bir zamanlama farkı varsa, bir iş parçacığı değişkeni okurken diğeri değişkeni güncelleyebilir ve bu da beklenmedik hatalara neden olabilir. Bir başka örnek ise, birden fazla iş parçacığının aynı anda bir dosyaya yazmaya çalışması durumudur. Eğer dosya yazma işlemleri senkronize edilmezse, işlemler arasında bir zamanlama farkı olabilir ve bir iş parçacığı dosyaya yazarken diğeri de aynı dosyaya yazabilir. Bu da dosyanın beklenmedik bir şekilde değişmesine ve programın hatalı çalışmasına neden olabilir. Bu nedenle, birden fazla iş parçacığı ile çalışan programlarda senkronizasyon mekanizmalarının kullanılması gerekir. Bu mekanizmalar arasında, kilitler (locks), senkronizasyon nesneleri (semaphores), mesaj kuyrukları (message queues) gibi araçlar bulunur.

Sorunu düzeltmek için thread lock kullandım.

import threading, hashlib, queue, os


def hashFile(fileName):
    with open(fileName, "rb") as f:
        sha256 = hashlib.sha256()
        while chunk := f.read(4096):
            sha256.update(chunk)
        return sha256.hexdigest()


def backupFile(q):
    while not q.empty():
        fileName = q.get()

        # Locking mechanism
        with lock:
            if hashFile(filesToBackupPath+fileName) in hashList:
                print(f"\033[33m{fileName} daha once yedeklenmis\033[0m")
            else:
                print(f"\033[32m{fileName} yedeklendi\033[0m")
                hashList.append(hashFile(filesToBackupPath+fileName))

        q.task_done()


filesToBackupPath = "yedeklenecekDosyalar/"
fileList = os.listdir(filesToBackupPath)
hashList = []

q = queue.Queue()

for file in fileList:
    q.put(file)

lock = threading.Lock()  # Creating a lock object

for i in range(20):
    t = threading.Thread(target=backupFile, args=(q,))
    t.start()

q.join()

print('\n',len(hashList), hashList)

Simdilik işe yariyor bakalim, cevabin icin saol

Edit: evet işe yariyor ama yine benim tabirimle yavas calisiyor, cunku lock kullandigimda birden fazla thread olsa bile her seferinde tek thread islem yapabiliyor, kilitlendigi icin diger threadlar bekliyor, en basta kullandigim for dongusunden farki kalmiyor tabiri caizse. Yine de for dongusune gore yari yariya hizli calisiyor. Arastirmaya devam…

Race condition’in tanimi biraz daha genis (islerin farkli sirayla calismasi sonucu degistiriyor) ama evet, burada bir ornegini goruyoruz. (En basit orneklerinden biri, her biri paylasilan bir sayiyi bir arttiran thread’lerdir bu arada ({ g_counter += 1 }), ilgini cekerse oynamani tavsiye ederim.)

Lock veya benzeri bir senkronizasyon primitifi sorunu cozuyor, evet, fakat biraz daha yakindan bakarsan paralelligi engelledigini goreceksin. Bir tanesi disinda butun thread’ler lock bekliyor.

O yuzden hashlist’i bastan build etmeyi onerdim. Oyle yapinca en azindan upload’lar paralel olabiliyor.

1 Beğeni

Aynen ben de ona yoneldim