urbackup_backend/clouddrive/CloudFile.h
Martin a9c2bd1ec5 New settings to compress cache (meta) objects via btrfs compression
(cherry picked from commit 989e60f639e7add19c0a72667efa4fa9219f0b6a)

# Conflicts:
#	Interface/BackupFileSystem.h
#	btrfs/btrfsplugin/BackupFileSystem.h
#	clouddrive/CloudFile.cpp
#	clouddrive/KvStoreBackendAzure.cpp
#	clouddrive/KvStoreBackendAzure.h
#	clouddrive/KvStoreBackendLocal.cpp
#	clouddrive/KvStoreBackendLocal.h
#	clouddrive/KvStoreBackendS3.cpp
#	clouddrive/KvStoreBackendS3.h
#	clouddrive/KvStoreFrontend.cpp
#	clouddrive/OnlineKvStore.cpp
#	clouddrive/OnlineKvStore.h
#	clouddrive/PassThroughFileSystem.cpp
#	clouddrive/dllmain.cpp
2021-07-12 21:15:27 +02:00

797 lines
20 KiB
C++

#pragma once
#include "../Interface/File.h"
#include "../Interface/Thread.h"
#include "IOnlineKvStore.h"
#include "../Interface/Mutex.h"
#include "../Interface/SharedMutex.h"
#include "../Interface/ThreadPool.h"
#include "ICompressEncrypt.h"
#include "../common/data.h"
#include "TransactionalKvStore.h"
#ifdef HAS_MOUNT_SERVICE
#include "btrfs_chunks.h"
#endif
#include "KvStoreFrontend.h"
#ifdef HAS_ASYNC
#include "fuse_io_context.h"
#endif
#include <atomic>
#include <queue>
class IBackupFileSystem;
namespace
{
class FileBitmap;
class SparseFileBitmap;
class UpdateMissingChunksThread;
class ShareWithUpdater;
}
void task_set_less_throttle();
void task_unset_less_throttle();
bool flush_dev(std::string dev_fn);
void setMountStatus(const std::string& data);
void setMountStatusErr(const std::string& err);
class CloudFile : public IFile, public IThread, public TransactionalKvStore::INumSecondChancesCallback, IOnlineKvStore::IHasKeyCallback
{
public:
CloudFile(const std::string& cache_path,
IBackupFileSystem* cachefs,
int64 cloudfile_size,
int64 max_cloudfile_size,
IOnlineKvStore* online_kv_store,
const std::string& encryption_key,
ICompressEncryptFactory* compress_encrypt_factory,
bool verify_cache, float cpu_multiplier,
bool background_compress,
size_t no_compress_mult,
int64 reserved_cache_device_space,
int64 min_metadata_cache_free,
bool with_prev_link,
bool allow_evict,
bool with_submitted_files,
float resubmit_compressed_ratio,
int64 max_memfile_size,
std::string memcache_path,
float memory_usage_factor,
bool only_memfiles,
std::string mount_path,
std::string share_with_mount_paths,
unsigned int background_comp_method,
const std::string& slog_path,
int64 slog_max_size,
bool is_async,
unsigned int cache_comp, unsigned int meta_cache_comp);
~CloudFile();
CloudFile(CloudFile const&) = delete;
CloudFile(CloudFile&& other) = delete;
CloudFile& operator=(CloudFile&&) = delete;
CloudFile& operator=(CloudFile const&) = delete;
virtual std::string Read( _u32 tr, bool* has_error=NULL);
virtual std::string Read( int64 pos, _u32 tr, bool* has_error = NULL);
_u32 Read(int64 pos, char* buffer, _u32 bsize, bool* has_error = NULL);
virtual _u32 Read( char* buffer, _u32 bsize, bool* has_error = NULL);
virtual _u32 Write( const std::string &tw, bool* has_error = NULL);
virtual _u32 Write(int64 pos, const std::string &tw, bool* has_error = NULL);
virtual _u32 Write( const char* buffer, _u32 bsize, bool* has_error = NULL);
virtual _u32 Write( int64 pos, const char* buffer, _u32 bsize, bool* has_error = NULL);
_u32 WriteNonBlocking( int64 pos, const char* buffer, _u32 bsize, bool* has_error = NULL);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> ReadAsync(fuse_io_context& io, fuse_io_context::FuseIo& fuse_io,
int64 pos, _u32 bsize, unsigned int flags, bool ext_lock);
fuse_io_context::io_uring_task<int> WriteAsync(fuse_io_context& io, fuse_io_context::FuseIo& fuse_io,
int64 pos, _u32 bsize, bool new_block, bool ext_lock, bool can_block);
#endif
virtual bool Seek( _i64 spos );
virtual _i64 Size( void );
#ifdef HAS_ASYNC
virtual fuse_io_context::io_uring_task<_i64> SizeAsync(void);
#endif
virtual _i64 RealSize(void);
virtual bool Resize(int64 nsize);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<bool> ResizeAsync(fuse_io_context& io, int64 nsize);
#endif
virtual bool PunchHole( _i64 spos, _i64 size );
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> PunchHoleAsync(fuse_io_context& io, _i64 spos, _i64 size);
#endif
virtual std::string getFilename( void );
virtual bool Sync();
bool Flush();
bool Flush(bool do_submit, bool for_slog=false);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<bool> FlushAsync(fuse_io_context& io, bool do_submit);
#endif
bool close_bitmaps();
void Reset();
int64 getDirtyBytes();
int64 getSubmittedBytes();
int64 getTotalSubmittedBytes();
int64 getUsedBytes();
std::string getNumDirtyItems();
std::string getNumMemfileItems();
int64 getCacheSize();
std::string getStats();
int64 getCompBytes();
void setDevNames(std::string bdev, std::string ldev);
static std::string block_key(int64 blocknum);
static int64 block_key_rev(const std::string& data);
static std::string big_block_key(int64 blocknum);
static std::string small_block_key(int64 blocknum);
static std::string hex_big_block_key(int64 bytenum);
static std::string hex_small_block_key(int64 bytenum);
int Congested();
int CongestedAsync();
std::string get_raid_groups();
std::string scrub_stats();
void start_scrub(ScrubAction action);
void stop_scrub();
bool add_disk(const std::string& path);
bool remove_disk(const std::string& path, bool completely);
std::string disk_error_info();
int64 current_total_size();
int64 current_free_space();
bool set_target_failure_probability(double t);
bool set_target_overhead(double t);
std::string get_scrub_position();
int64 get_memfile_bytes();
int64 get_submitted_memfile_bytes();
virtual void operator()();
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task_discard<int> run_async(fuse_io_context& io);
#endif
void run_cd_fracture();
bool start_raid_defrag(const std::string& settings);
bool is_background_worker_enabled();
bool is_background_worker_running();
void enable_background_worker(bool b);
bool start_background_worker();
void set_background_worker_result_fn(const std::string& result_fn);
bool has_background_task();
void preload(int64 start, int64 stop, size_t n_threads);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> preload_async(fuse_io_context& io, int64 start, int64 stop, size_t n_threads);
#endif
void cmd(const std::string& c);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> cmd_async(fuse_io_context& io, const std::string& c);
#endif
static int64 key_offset_hex(const std::string& key);
static int64 key_offset(const std::string& key, bool& big_block);
static int64 key_offset(const std::string& key);
std::string meminfo();
void shrink_mem();
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> shrink_mem_async(fuse_io_context& io);
#endif
int64 get_total_hits();
int64 get_total_hits_async();
int64 get_total_memory_hits();
int64 get_total_memory_hits_async();
int64 get_total_cache_miss_backend();
int64 get_total_cache_miss_decompress();
int64 get_total_dirty_ops();
int64 get_total_balance_ops();
int64 get_total_del_ops();
int64 get_total_put_ops();
int64 get_total_compress_ops();
bool has_preload_item(int64 offset_start, int64 offset_end);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<bool> has_preload_item_async(fuse_io_context& io, int64 offset_start, int64 offset_end);
#endif
int64 min_block_size();
void set_is_mounted(const std::string& p_mount_path, IBackupFileSystem* fs);
bool is_metadata(int64 offset, const std::string& key);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<bool> is_metadata_async(fuse_io_context& io, int64 offset, const std::string& key);
#endif
virtual unsigned int get_num_second_chances(const std::string& key);
virtual bool is_metadata(const std::string& key);
bool update_missing_fs_chunks();
bool migrate(std::vector<char>& buf, int64 offset, int64 len);
bool migrate(const std::string& conf_fn, bool continue_migration);
std::string migration_info();
void preload_key(const std::string& key, int64 offset, int64 len, int tag, bool disable_memfiles, bool load_only);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> preload_key_async(fuse_io_context& io, const std::string& key, int64 offset, int64 len, int tag, bool disable_memfiles, bool load_only);
#endif
int64 get_uploaded_bytes();
int64 get_downloaded_bytes();
int64 get_written_bytes();
int64 get_read_bytes();
std::string get_raid_io_stats();
bool replay_slog();
std::string get_mirror_stats();
std::string get_raid_freespace_stats();
virtual bool hasKey(const std::string& key) override;
int64 get_transid();
private:
void preload_items(const std::string& fn, size_t n_threads, int tag, bool disable_memfiles, bool load_only);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> preload_items_async(fuse_io_context& io, const std::string& fn,
size_t n_threads, int tag, bool disable_memfiles, bool load_only);
#endif
_u32 WriteInt(int64 pos, const char* buffer, _u32 bsize, bool new_block, IScopedLock* ext_lock, bool can_block, bool* has_error);
_u32 ReadInt(int64 pos, char* buffer, _u32 bsize, IScopedLock* ext_lock, unsigned int flags, bool* has_error);
_u32 ReadAligned(IFile* block, int64 pos, char* buffer, _u32 toread, bool* has_read_error);
_u32 WriteAligned(IFile* block, int64 pos, const char* buffer, _u32 towrite);
void perform_deletes();
void open_bitmaps();
bool bitmap_has_big_block(int64 blocknum);
bool bitmap_has_small_block(int64 blocknum);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<bool> bitmap_has_big_block_async(fuse_io_context& io, int64 blocknum);
fuse_io_context::io_uring_task<bool> bitmap_has_small_block_async(fuse_io_context& io, int64 blocknum);
#endif
void lock_extent(IScopedLock& lock, int64 start, int64 length, bool exclusive);
void unlock_extent(IScopedLock& lock, int64 start, int64 length, bool exclusive);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<size_t> lock_extent_async(int64 start, int64 length, bool exclusive);
#endif
void unlock_extent_async(int64 start, int64 length, bool exclusive, size_t lock_idx);
void add_fracture_big_block(int64 b);
void update_fs_chunks(bool update_time, IScopedWriteLock& lock);
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<void> update_fs_chunks_async(fuse_io_context& io, bool update_time);
#endif
bool migrate(const std::string& conf_fn, ISettingsReader* settings);
bool update_migration_settings();
IOnlineKvStore* create_migration_endpoint(const std::string& conf_fn, ISettingsReader* settings,
IBackupFileSystem* migrate_cachefs);
bool slog_write(int64 pos, const char* buffer, _u32 bsize, bool& needs_reset);
bool slog_open();
void purge_jemalloc();
void set_jemalloc_dirty_decay(int64 timems);
/*_u32 prepare_zero_n_sqes(_u32 towrite);
std::vector<io_uring_sqe*> prepare_zeros(fuse_io_context& io, fuse_io_context::FuseIo& fuse_io, _u32 towrite, _u32 peek_add);*/
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task<int> empty_pipe(fuse_io_context& io, fuse_io_context::FuseIo& fuse_io);
fuse_io_context::io_uring_task<bool> verify_pipe_empty(fuse_io_context& io, fuse_io_context::FuseIo& fuse_io);
struct AwaiterCoList
{
AwaiterCoList* next;
std::coroutine_handle<> awaiter;
};
fuse_io_context::io_uring_task_discard<int> preload_async_read(fuse_io_context& io, std::vector<std::unique_ptr<fuse_io_context::FuseIo> >& ios, AwaiterCoList*& ios_waiters_head, int64 start, _u32 size);
fuse_io_context::io_uring_task_discard<int> preload_items_single_async(fuse_io_context& io, size_t& available_workers, AwaiterCoList*& worker_waiters_head,
std::string key, int64 offset, _u32 len, int tag, bool disable_memfiles, bool load_only);
struct ReadTask
{
ReadTask(int fd, IFsFile* file, std::string key, uint64_t off, _u32 size)
: fd(fd), file(file), key(std::move(key)), off(off), size(size), flush_mem(false) {}
int fd;
IFsFile* file;
std::string key;
int64 off;
_u32 size;
bool flush_mem;
};
fuse_io_context::io_uring_task<int> complete_read_tasks(fuse_io_context& io, fuse_io_context::FuseIo& fuse_io,
const std::vector<ReadTask>& read_tasks, const size_t flush_mem_n_tasks, const int64 orig_pos, const _u32 bsize,
const unsigned int flags);
#endif
struct SExtent
{
int64 start;
int64 length;
ICondition* cond;
int refcount;
bool alive;
};
std::vector<SExtent> locked_extents;
#ifdef HAS_ASYNC
struct SExtentAsync
{
int64 start;
int64 length;
bool exclusive;
AwaiterCoList* awaiters;
int refcount;
};
std::vector<SExtentAsync> async_locked_extents;
#endif
size_t locked_extents_max_alive;
size_t wait_for_exclusive;
std::unique_ptr<IOnlineKvStore> online_kv_store;
TransactionalKvStore kv_store;
IFsFile* big_blocks_bitmap_file;
IFsFile* bitmap_file;
IFsFile* new_big_blocks_bitmap_file;
IFsFile* old_big_blocks_bitmap_file;
std::unique_ptr<FileBitmap> old_big_blocks_bitmap;
std::unique_ptr<FileBitmap> new_big_blocks_bitmap;
std::unique_ptr<SparseFileBitmap> big_blocks_bitmap;
std::unique_ptr<SparseFileBitmap> bitmap;
int64 cf_pos;
int64 active_big_block;
int64 used_bytes;
int64 last_stat_update_time;
int64 last_flush_check;
int64 cloudfile_size;
std::unique_ptr<IMutex> mutex;
std::string bdev_name;
std::string ldev_name;
size_t writeback_count;
std::string bcache_writeback_percent;
unsigned int io_alignment;
std::map<int64, int64> fracture_big_blogs;
std::unique_ptr<ICondition> thread_cond;
bool exit_thread;
THREADPOOL_TICKET fracture_big_blogs_ticket;
float memory_usage_factor;
int64 bitmaps_file_size;
size_t flush_enabled;
std::string cache_path;
std::string mount_path;
std::unique_ptr<ISharedMutex> chunks_mutex;
std::vector<IBackupFileSystem::SChunk> fs_chunks;
int64 last_fs_chunks_update;
std::vector<std::pair<std::string, int64> > missing_chunk_keys;
bool updating_fs_chunks;
std::unique_ptr<UpdateMissingChunksThread> update_missing_chunks_thread;
THREADPOOL_TICKET update_missing_chunks_thread_ticket;
std::unique_ptr<CloudFile> migrate_to_cf;
std::unique_ptr<IThreadPool> migration_thread_pool;
std::atomic<bool> migration_has_error;
std::atomic<int64> migration_copy_max;
std::atomic<int64> migration_copy_done;
int64 migration_copy_done_lag;
std::string migration_conf_fn;
THREADPOOL_TICKET migration_ticket;
std::set<std::string> in_write_retrieval;
std::unique_ptr<ICondition> in_write_retrieval_cond;
THREADPOOL_TICKET share_with_updater_ticket;
std::unique_ptr<ShareWithUpdater> share_with_updater;
int64 written_bytes;
int64 read_bytes;
std::atomic<int64> slog_size;
std::atomic<int64> slog_last_sync;
std::unique_ptr<IFsFile> slog;
std::string slog_path;
int64 slog_max_size;
bool enable_raid_freespace_stats;
bool is_flushing;
int zero_memfd;
_u32 zero_memfd_size;
#ifdef HAS_ASYNC
AwaiterCoList* exclusive_awaiter_head = nullptr;
struct ExclusiveAwaiter
{
ExclusiveAwaiter(CloudFile& cd)
: cd(cd) {}
ExclusiveAwaiter(ExclusiveAwaiter const&) = delete;
ExclusiveAwaiter(ExclusiveAwaiter&& other) = delete;
ExclusiveAwaiter& operator=(ExclusiveAwaiter&&) = delete;
ExclusiveAwaiter& operator=(ExclusiveAwaiter const&) = delete;
bool await_ready() const noexcept
{
return false;
}
void await_suspend(std::coroutine_handle<> p_awaiter) noexcept
{
awaiter.awaiter = p_awaiter;
awaiter.next = cd.exclusive_awaiter_head;
cd.exclusive_awaiter_head = &awaiter;
}
void await_resume() const noexcept
{
}
private:
AwaiterCoList awaiter;
CloudFile& cd;
};
void resume_awaiters(AwaiterCoList*& head)
{
AwaiterCoList* curr = head;
head = nullptr;
while (curr != nullptr)
{
AwaiterCoList* next = curr->next;
curr->awaiter.resume();
curr = next;
}
}
void resume_exclusive_awaiters()
{
resume_awaiters(exclusive_awaiter_head);
}
AwaiterCoList* write_retrieval_head = nullptr;
struct WriteRetrievalAwaiter
{
WriteRetrievalAwaiter(CloudFile& cd)
: cd(cd) {}
WriteRetrievalAwaiter(WriteRetrievalAwaiter const&) = delete;
WriteRetrievalAwaiter(WriteRetrievalAwaiter&& other) = delete;
WriteRetrievalAwaiter& operator=(WriteRetrievalAwaiter&&) = delete;
WriteRetrievalAwaiter& operator=(WriteRetrievalAwaiter const&) = delete;
bool await_ready() const noexcept
{
return false;
}
void await_suspend(std::coroutine_handle<> p_awaiter) noexcept
{
awaiter.awaiter = p_awaiter;
awaiter.next = cd.write_retrieval_head;
cd.write_retrieval_head = &awaiter;
}
void await_resume() const noexcept
{
}
private:
AwaiterCoList awaiter;
CloudFile& cd;
};
void resume_write_retrieval_awaiters()
{
resume_awaiters(write_retrieval_head);
}
struct ExtentAwaiter
{
ExtentAwaiter(SExtentAsync& extent)
: extent(extent) {}
ExtentAwaiter(ExtentAwaiter const&) = delete;
ExtentAwaiter(ExtentAwaiter&& other) = delete;
ExtentAwaiter& operator=(ExtentAwaiter&&) = delete;
ExtentAwaiter& operator=(ExtentAwaiter const&) = delete;
bool await_ready() const noexcept
{
return false;
}
void await_suspend(std::coroutine_handle<> p_awaiter) noexcept
{
awaiter.awaiter = p_awaiter;
awaiter.next = extent.awaiters;
extent.awaiters = &awaiter;
}
void await_resume() const noexcept
{
}
private:
AwaiterCoList awaiter;
SExtentAsync& extent;
};
struct IosAwaiter
{
IosAwaiter(std::vector<std::unique_ptr<fuse_io_context::FuseIo> >& ios, AwaiterCoList*& ios_awaiter_head)
: ios(ios), ios_awaiter_head(ios_awaiter_head) {}
IosAwaiter(IosAwaiter const&) = delete;
IosAwaiter(IosAwaiter&& other) = delete;
IosAwaiter& operator=(IosAwaiter&&) = delete;
IosAwaiter& operator=(IosAwaiter const&) = delete;
bool await_ready() const noexcept
{
return !ios.empty();
}
void await_suspend(std::coroutine_handle<> p_awaiter) noexcept
{
awaiter.awaiter = p_awaiter;
awaiter.next = ios_awaiter_head;
ios_awaiter_head = &awaiter;
}
std::unique_ptr<fuse_io_context::FuseIo> await_resume() noexcept
{
std::unique_ptr<fuse_io_context::FuseIo> ret = std::move(ios.back());
ios.pop_back();
return std::move(ret);
}
private:
AwaiterCoList awaiter;
std::vector<std::unique_ptr<fuse_io_context::FuseIo> >& ios;
AwaiterCoList*& ios_awaiter_head;
};
void iosWaitersResume(std::vector<std::unique_ptr<fuse_io_context::FuseIo> >& ios,
AwaiterCoList*& ios_waiters_head)
{
AwaiterCoList* curr = ios_waiters_head;
ios_waiters_head = nullptr;
while (curr != nullptr
&& !ios.empty())
{
AwaiterCoList* next = curr->next;
curr->awaiter.resume();
curr = next;
}
if (curr != nullptr)
{
curr->next = ios_waiters_head;
ios_waiters_head = curr;
}
}
struct WorkerAwaiter
{
WorkerAwaiter(size_t& available_workers, AwaiterCoList*& worker_awaiter_head)
: available_workers(available_workers), worker_awaiter_head(worker_awaiter_head) {}
WorkerAwaiter(WorkerAwaiter const&) = delete;
WorkerAwaiter(WorkerAwaiter&& other) = delete;
WorkerAwaiter& operator=(WorkerAwaiter&&) = delete;
WorkerAwaiter& operator=(WorkerAwaiter const&) = delete;
bool await_ready() const noexcept
{
return available_workers > 0;
}
void await_suspend(std::coroutine_handle<> p_awaiter) noexcept
{
awaiter.awaiter = p_awaiter;
awaiter.next = worker_awaiter_head;
worker_awaiter_head = &awaiter;
}
void await_resume() noexcept
{
assert(available_workers > 0);
--available_workers;
}
private:
AwaiterCoList awaiter;
size_t& available_workers;
AwaiterCoList*& worker_awaiter_head;
};
void workerWaitersResume(size_t& available_workers,
AwaiterCoList*& ios_waiters_head)
{
AwaiterCoList* curr = ios_waiters_head;
ios_waiters_head = nullptr;
while (curr != nullptr
&& available_workers>0)
{
AwaiterCoList* next = curr->next;
curr->awaiter.resume();
curr = next;
}
if (curr != nullptr)
{
curr->next = ios_waiters_head;
ios_waiters_head = curr;
}
}
#endif //HAS_ASYNC
std::unique_ptr<IFsFile> null_file;
std::unique_ptr<IFsFile> zero_file;
bool is_async;
#ifdef HAS_ASYNC
fuse_io_context::io_uring_task_discard<int> run_async_queue(fuse_io_context& io);
bool get_async_msg(CWData& msg, CRData& resp);
#endif
int queue_in_eventfd;
int update_fs_chunks_eventfd;
std::unique_ptr<IMutex> msg_queue_mutex;
std::unique_ptr<ICondition> msg_queue_cond;
int64 msg_queue_id;
std::queue<std::vector<char> > msg_queue;
std::map<size_t, std::vector<char> > msg_queue_responses;
std::set<int64> consider_big_blocks;
std::set<int64> curr_consider_big_blocks;
bool fallocate_block_file = true;
IBackupFileSystem* cachefs;
IBackupFileSystem* topfs = nullptr;
};