Submitted:
09 October 2024
Posted:
29 October 2024
You are already at the latest version
Abstract

Keywords:
1. Introduction
- Reducing the write amplification problem: Speed-Dedup significantly reduces write amplification compared to other deduplication methods like GRATE [10] and CAO [9,13,23]. It efficiently manages storage by drastically lowering the total data written. For instance, with a 20% deduplication ratio, Speed-Dedup shows a notable reduction in data written to storage, highlighting its capacity for improved storage efficiency.
- Improving data recovery times: Speed-Dedup offers superior recovery times for failed data storage locations (OSDs). As validated in our experiments, Speed-Dedup demonstrates faster recovery with a time of 48.23 seconds for one failed OSD, outperforming GRATE [10] with 53.21 seconds and CAO [9] with (53.29 seconds) and this performance improvement increases as the number of OSD failures increase. This underscores Speed-Dedup’s enhanced system resilience and ability to restore system health more quickly.
- Enhancing I/O performance of deduplication systems: Speed-Dedup improves overall I/O performance by decoupling read and write operations, allowing non-blocking reads. This contributes to reduced latency and increased throughput, optimizing system performance for both read and write tasks.
- Modified fault tolerance and intelligent self-healing capabilities: Speed-Dedup maintains critical fault tolerance and self-healing capabilities while optimizing system efficiency. We implement new mechanisms called the object replica check (ORC) and chunk availability check (CAC) for fault tolerance and recovery. ORC and CAC ensures high data availability, enhancing system resilience and robustness, especially in failure scenarios.
2. Background and Motivation
2.1. Background
- Write I/O Operation: This involves writing data objects to the scale-out storage. The write operation can either create a new data object or update an existing one.
- Read I/O Operation: This operation allows clients to read data stored in the underlying storage system.

2.1.1. Challenges in Conventional Deduplication Systems
2.1.2. Motivation
- Reducing write amplification caused by RPGs: One of the major challenges in conventional deduplication systems is the excessive write amplification caused by chunk replication in RPGs [9,31]. To address this, we propose a solution called deduplicated primary – semi deduplicated replica. In this approach, the full deduplication is applied only to the primary data object, while the replica object is semi-deduplicated. The proposed system writes the primary object to the storage cluster, replicates a single replica object, and then deduplicates the primary object. The deduplicated chunks are not replicated, reducing the overall write amplification. Fault tolerance is handled by an asynchronous process that manages synchronization between the deduplicated chunks and the semi-deduplicated replica, as discussed in detail in Section 3.3. For example, in a conventional deduplication system, writing a 4MB data object could result in 12MB being written to storage due to replication. In contrast, our proposed system reduces this to a worst-case scenario of 8MB (4MB for the data chunks and 4MB for the semi-deduplicated replica).
- Replacing strong consistency with eventual consistency: The strong consistency model used in conventional deduplication storage systems introduces significant performance overhead, particularly in terms of latency and throughput. To improve performance, we propose replacing the strong consistency model with an eventual consistency model. This involves eliminating the use of Write-Ahead Log (WAL) journaling. Instead, we adopt a binary flag-assisted consistency mechanism to manage incoming write requests. A binary flag indicates the state of each write operation, changing from "0" to "1" upon successful write to storage. If a write fails, the flag remains with state "0". Future write requests for duplicate data can eventually correct the consistency by flipping the flag to "1", indicating a successful write. If no future duplicates are written, we adopt a periodic garbage collection thread [37] which invalidates all objects and chunks with a flag status of "0". This mechanism improves system performance by avoiding the overhead of WAL journaling. We detail this eventual consistency model in Section 3.2.1.
- Fault tolerance retention in proposed system: In our proposed system, we retain the fault tolerance and self-healing capabilities of conventional deduplication systems while introducing modifications meant to improve the deduplication system efficiency. Specifically, we ensure that fault tolerance is achieved between the deduplicated primary object and the semi-deduplicated replica object through new mechanisms. If a replica object becomes unreachable, a periodic Object Replica Check (ORC) thread is initiated to rehash the primary object and store it at a new location in the cluster. Similarly, if any chunk of the deduplicated primary object is unavailable, a periodic Chunk Availability Check (CAC) thread synchronizes with the semi-deduplicated replica and performs background chunking and hashing of the replica object. The background CAC resultant chunks are compared with the chunks of the deduplicated primary object. Missing chunks are then rehashed and stored in a new location. Both the ORC and CAC threads run periodically to ensure the availability and integrity of the data. Full details of the ORC and ARC are discussed in Section 3.3
3. Proposed Speed-Dedup Architecture
3.0.1. Deduplicated primary - semi-deduplicated replica model
- Distributed metadata partitioning: When a client writes an object to storage, a Dynamic Hash Table (DHT) algorithm is employed to determine the specific server responsible for handling the write request. Once the data reaches the assigned storage server, it undergoes two primary operations: (i) Replication within the Replication Placement Group (RPG) for fault tolerance (semi deduplicated replica). (ii) Deduplication of primary objects to identify and store only unique data chunks.
- Object mapping table (Object MAP) : During the deduplication process, the metadata of the incoming objects is stored in the Object Mapping Table (Object MAP), which is part of the DM-Shard. The Object MAP records all the essential information required to reconstruct the deduplicated object, including: Object ID (obj_ID): A unique identifier for the object, Object Hash Value (objhash): Hash value for the entire object, Chunk List (cnk_List): A list of the deduplicated object’s chunk hashes, Object reference count (obj_RefCont): which stores the reference counts of the semi deduplicated replicas and a Binary Flag which indicates whether the object, along with all its chunks, has been successfully written to storage. It also reflects the status of the replica. The flag is initially set to "0" and is flipped to "1" once both the deduplicated primary object and its semi-deduplicated replica have been successfully written.
- Chunk Information Table (CIT): For the deduplicated primary object, each chunk undergoes a secondary DHT algorithm to calculate its final placement location in the storage system. Once the chunk reaches its designated storage location, its metadata is recorded in the Chunk Information Table (CIT) within the DM-Shard. The CIT contains critical chunk-level information, including chunk IDs, chunk hash values, and and a consistent flag. Upon successfully writing the chunks to their storage locations, the flag in the CIT is flipped from "0" to "1".
3.0.2. Modified Replication Placement Group
3.1. Decoupling of Read and Write (R/W) Operations
- Read operation: In our design, all read operations are served via the semi-deduplicated replica object, ensuring non-blocking access to data even during write updates. In traditional deduplication systems, read operations issued during an object write update are often blocked. This happens because the system locks the RPG (Replication Placement Group) to prevent simultaneous read and write I/O operations on the same object, maintaining strong consistency but at the cost of I/O performance degradation. To overcome this limitation, Speed-Dedup implements a non-blocking read I/O thread. This thread is invoked through the semi-deduplicated replica object, allowing read operations to proceed independently from ongoing write updates to the primary object. By separating the replica from the deduplication process, we ensure faster and more efficient read operations, improving the overall responsiveness of the system.
- Write update operation: The write update operation is handled through the deduplicated primary object. When a write update is triggered, the system retrieves the relevant data chunks using metadata stored in the MD-Shards, such as the object ID (obj_ID), object hash (objhash), and the chunk list (chkList). If the object requiring the update exists, the updated object undergoes the following processes: Chunking: The updated object is divided into chunks. Hashing: Each chunk is hashed via hashing algorithm. Duplicate checks: The system verifies whether the updated chunks already exist in storage. For any new chunks, the system hashes them to their final storage locations, updating the metadata in the MD-Shards accordingly. The old metadata records of the object are then invalidated to reflect the update.
- Addressing inconsistencies between primary and replica objects: A potential challenge of this decoupled R/W model is the risk of inconsistencies between the deduplicated primary object and the semi-deduplicated replica object. Since the write update is applied only to the primary object, the replica may not immediately reflect the changes made to the primary object. To resolve this, we implement a periodic replication thread that runs in the background. This thread periodically reconciles the primary object with its replica by: (i) Replicating the updated data from the deduplicated primary object to the semi-deduplicated replica. Invalidating the old replica copy to ensure that the replica is always up-to-date with the primary object. This periodic synchronization ensures that the system maintains consistency between the primary and replica objects without sacrificing the performance benefits of decoupled read and write operations.
3.2. Write and Read I/O Acknowledgement
3.2.1. Eventual Consistency Model Implementation
- The object is first hashed to identify its initial storage location.
- In this initial location, the object undergoes replication across its RPG, ensuring redundancy, and the primary object is also chunked and hashed as part of the deduplication process.
- The system then updates the Object MAP table with relevant metadata for the object, including the object ID (obj_ID), object hash (objhash), chunk list (chkList), and a consistency flag. This flag is initially set to "0" and is changed to "1" only after a successful commit of both the replica object and all deduplicated chunks.
- The deduplicated chunks are then processed through a secondary hashing algorithm, which calculates their final storage locations.
- Upon arrival at the final storage location, the Chunk Information Table (CIT) of the DM-Shard is updated with chunk metadata, including chunk hashes, reference counts, and consistency flags. Similar to the Object MAP table, the consistency flag in the CIT is initialized to "0" and flips to "1" upon a successful commit to storage of the data chunk.
- Handling inconsistencies with duplicate data: Our system resolves inconsistencies by leveraging future data writes. If a duplicate data chunk (matching an earlier failed chunk) is written to the system at any point, the consistency flag in the CIT will be updated from "0" to "1", and the reference count for the chunk will be incremented. This mechanism allows the system to self-heal by correcting inconsistencies over time as duplicate data naturally enters the system.
- Garbage collection for inconsistent chunks: In the worst-case scenario, where no future duplicate chunk is written to the system, a periodic garbage collection thread will scan through the storage system. It will invalidate any objects or chunks with a consistency flag still set to "0", thereby ensuring that inconsistent or incomplete data is removed. This thread prevents long-term accumulation of inconsistent data and maintains the integrity of the storage system.
3.3. Fault Tolerance handling in the Proposed System
- In this scenario, the distributed storage system automatically recalculates the new Replica Placement Groups (RPGs) (Step 1 in Figure 4).
- Next, the ORC thread retrieves metadata about the failed replica from the Object MAP of the primary object, locates the object chunks using the secondary DHT algorithm (Step 2), and reconstructs the primary object based on this data (Step 3).
- The reconstructed primary object is then replicated to a new replica location in the updated RPG (Step 4).
- This means that the replica object may be corrupted.
- The CAC thread first performs a shadow deduplication process on the replica object, Step 1 in Figure 5.
- It then compares the chunk hashes obtained from the shadow deduplication process of the replica with those in the Object MAP of the primary object (Step 2). If any chunk hashes match those stored in reachable locations, the matching chunks are discarded.
- For matching chunks that are not reachable, the CAC thread rehashes the new chunk location and copies the unreachable chunk from the shadow deduplication to the newly identified storage location (Step 3).
- This scenario indicates that the chunk may be corrupted. In this case, the CAC thread updates the Chunk Information Table (CIT) by changing the flag for the corrupted chunk to "0" and decrements the reference count, Step 0 in Figure 5.
- The CAC thread then proceeds with shadow deduplication on the replica object (Step 1).
- Next CAC compares the chunk hashes from the shadow deduplication with the corrupted chunk hash in the CIT table. If a match is found, the shadow deduplicated chunk is copied into the original storage location, the reference count is incremented, and the flag status in the CIT is reset to "1" (Step 3b).
4. Evaluation
4.1. Testbed and Experimental Setup
4.2. Performance Evaluation
4.3. Write Amplification Evaluation
- Lower bound deduplication ratio (20%): 80% of the data is duplicate.
- upper bound deduplication ratio (80%): 20% of the data is duplicate.
- At the lower bound (20% deduplication ratio), Speed-Dedup wrote 2.4TB of data to storage (314,573 replica objects [4MB each] and 5,033,168 deduplicated chunks [256KB each]), compared to 3.6TB for GRATE and CAO (5,033,168 chunks replicated by a factor of 3 = 15,099,504 chunks of 256KB each).
- At the upper bound (80% deduplication ratio), Speed-Dedup wrote 9.6TB (1,258,292 replica objects [4MB each] and 20,132,672 deduplicated chunks [256KB each]) to storage, while GRATE and CAO wrote 14.4TB (20,132,672 replicated by a factor of 3 = 60,398,016 replicated chunks of size 256KB each).
4.4. Fault Tolerance and Failure Recovery
5. Conclusions
Author Contributions
Funding
Informed Consent Statement
Data Availability Statement
Conflicts of Interest
References
- Seagate, IDC. Data Age 2025: The Digitization of the World From Edge to Core.https://www.seagate.com/files/www-content/our-story/trends/files/dataage-idc-report-final.pdf.
- Cisco. Cisco Global Cloud Index: Forecast and Methodology, 2016–2021. https://virtualization network/Resources/Whitepapers/0b75cf2e-0c53-4891-918e-b542a5d364c5_white-paper-c11-738085.pdf.
- NetApp. Use Deduplication, Data Compression, and Data Compaction to Increase Storage Efficiency Overview, 2021.
- Ahmed, S.T.; George, L.E. Lightweight hash-based de-duplication system using the self detection of most repeated patterns as chunks divisors. Journal of King Saud University - Computer and Information Sciences 2022, 34, 4669–4678. [Google Scholar] [CrossRef]
- Fekry, A. Big Data Gets Bigger: What about Data Cleaning Analytics as a Storage Service? 9th USENIX Workshop on Hot Topics in Storage and File Systems (HotStorage 17); USENIX Association: Santa Clara, CA, 2017. [Google Scholar]
- Kaur, R.; Chana, I.; Bhattacharya, J. Data deduplication techniques for efficient cloud storage management: a systematic review. The Journal of Supercomputing 2018, 74, 2035–2085. [Google Scholar] [CrossRef]
- Zhang, D.; Le, J.; Mu, N.; Wu, J.; Liao, X. Secure and Efficient Data Deduplication in JointCloud Storage. IEEE Transactions on Cloud Computing 2023, 11, 156–167. [Google Scholar] [CrossRef]
- Yu, X.; Bai, H.; Yan, Z.; Zhang, R. VeriDedup: A Verifiable Cloud Data Deduplication Scheme With Integrity and Duplication Proof. IEEE Transactions on Dependable and Secure Computing 2023, 20, 680–694. [Google Scholar] [CrossRef]
- Oh, M.; Park, S.; Yoon, J.; Kim, S.; Lee, K.; Weil, S.; Yeom, H.Y.; Jung, M. Design of Global Data Deduplication for a Scale-Out Distributed Storage System. 2018 IEEE 38th International Conference on Distributed Computing Systems (ICDCS), 2018, pp. 1063–1073. [CrossRef]
- Khan, A.; Hamandawana, P.; Kim, Y. A Content Fingerprint-based Cluster-wide Inline Deduplication for Shared-Nothing Storage Systems. IEEE Access. 2020, pp.1–1. [CrossRef]
- Hamandawana, P.; Khan, A.; Kim, J.; Chung, T.S. Accelerating ML/DL Applications With Hierarchical Caching on Deduplication Storage Clusters. IEEE Transactions on Big Data 2022, 8, 1622–1636. [Google Scholar] [CrossRef]
- Goel, A.; Prabha, C. A Detailed Review of Data Deduplication Approaches in the Cloud and Key Challenges. 2023 4th International Conference on Smart Electronics and Communication (ICOSEC), 2023, pp. 1771–1779. [CrossRef]
- Oh, M.; Lee, S.; Just, S.; Yu, Y.J.; Bae, D.H.; Weil, S.; Cho, S.; Yeom, H.Y. TiDedup: A New Distributed Deduplication Architecture for Ceph. 2023 USENIX Annual Technical Conference (USENIX ATC 23); USENIX Association: Boston, MA, 2023; pp. 117–131. [Google Scholar]
- Wang, J.; Wang, Y.; Wang, H.; Ye, K.; Xu, C.; He, S.; Zeng, L. Towards Cluster-wide Deduplication Based on Ceph. 2019 IEEE International Conference on Networking, Architecture and Storage (NAS), 2019, pp. 1–8. [CrossRef]
- Weil, S.A.; Brandt, S.A.; Miller, E.L.; Maltzahn, C. CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data. Proceedings of the 2006 ACM/IEEE Conference on Supercomputing; ACM: New York, NY, USA. 2006;SC’06. [CrossRef]
- Sun, Z.; Xiao, N.; Liu, F.; Fu, Y. DS-Dedupe: A Scalable, Low Network Overhead Data Routing Algorithm for Inline Cluster Deduplication System. Proceedings of the 2014 International Conference on Computing, Networking and Communications (ICNC); IEEE: Honolulu, HI, Hawaii, 2014; pp. 895–899. [Google Scholar] [CrossRef]
- Meister, D.; Kaiser, J.; Brinkmann, A.; Cortes, T.; Kuhn, M.; Kunkel, J. A Study on Data Deduplication in HPC Storage Systems. Proceedings of the International Conference on High Performance Computing, Networking, Storage and Analysis; IEEE Computer Society Press: Los Alamitos, CA, USA. 2012 ;SC’12, pp. 7:1–7:11.
- Kwon, H.; Cho, Y.; Khan, A.; Park, Y.; Kim, Y. DENOVA: Deduplication Extended NOVA File System. 2022 IEEE International Parallel and Distributed Processing Symposium (IPDPS), 2022, pp. 1360–1371. [CrossRef]
- Jackowski, A.; Ślusarczyk, L.; Lichota, K.; Wełnicki, M.; Wijata, R.; Kielar, M.; Kopeć, T.; Dubnicki, C.; Iwanicki, K. ObjDedup: High-Throughput Object Storage Layer for Backup Systems With Block-Level Deduplication. IEEE Transactions on Parallel and Distributed Systems 2023, 34, 2180–2197. [Google Scholar] [CrossRef]
- zhichao Cao. ; Wen, H.; Wu, F., Ed.; Du, D.H. ALACC: Accelerating Restore Performance of Data Deduplication Systems Using Adaptive Look-Ahead Window Assisted Chunk Caching. 16th USENIX Conference on File and Storage Technologies (FAST 18); USENIX Association: Oakland, CA, 2018; pp. 309–324. [Google Scholar]
- Park, D.; Fan, Z.; Nam, Y.J.; Du, D.H. A Lookahead Read Cache: Improving Read Performance for Deduplication Backup Storage. Computer Science and Technology 2017, 32, 26–40. [Google Scholar] [CrossRef]
- Du, C.; Lin, Z.; Wu, S.; Chen, Y.; Wu, J.; Wang, S.; Wang, W.; Wu, Q.; Mao, B. FSDedup: Feature-Aware and Selective Deduplication for Improving Performance of Encrypted Non-Volatile Main Memory. ACM Trans. Storage 2024, 20. [Google Scholar] [CrossRef]
- Dubnicki, C.; Gryz, L.; Heldt, L.; Kaczmarczyk, M.; Kilian, W.; Strzelczak, P.; Szczepkowski, J.; Ungureanu, C.; Welnicki, M. HYDRAstor: A Scalable Secondary Storage. 7th USENIX Conference on File and Storage Technologies (FAST 09); USENIX Association: San Francisco, CA, 2009. [Google Scholar]
- Weil, S.A.; Brandt, S.A.; Miller, E.L.; Long, D.D.E.; Maltzahn, C. Ceph: A Scalable, High-performance Distributed File System. Proceedings of the 7th Symposium on Operating Systems Design and Implementation, 2006, OSDI ’06.
- Li, Z.; Wang, Y. An adaptive read/write optimized algorithm for Ceph heterogeneous systems via performance prediction and multi-attribute decision making. Cluster Computing 2023, 26, 1573–7543. [Google Scholar] [CrossRef]
- Zhu, L.; Hu, Y.; Wang, C. Asynchronous and Adaptive Checkpoint for WAL-based Data Storage Systems. 2023 IEEE 29th International Conference on Parallel and Distributed Systems (ICPADS), 2023, pp. 1238–1245. [CrossRef]
- Jin, S.; Yan, Q.; Zhang, Y.; Yang, J. A High-Performance Distributed File System for Mass Data. 2020 IEEE 6th International Conference on Computer and Communications (ICCC), 2020, pp. 1804–1809. [CrossRef]
- Debnath, B.K.; Sengupta, S.; Li, J. ChunkStash: Speeding Up Inline Storage Deduplication Using Flash Memory. USENIX Annual Technical Conference, 2010.
- Feng, D.,Chunking Algorithms.In Data Deduplication for High Performance Storage System; Springer Nature Singapore: Singapore, 2022; pp.25–51. [CrossRef]
- Zou, X.; Yuan, J.; Shilane, P.; Xia, W.; Zhang, H.; Wang, X. The Dilemma between Deduplication and Locality: Can Both be Achieved? 19th USENIX Conference on File and Storage Technologies (FAST 21). USENIX Association, 2021, pp. 171–185.
- Cheng, G.; Luo, L.; Xia, J.; Guo, D.; Sun, Y. When Deduplication Meets Migration: An Efficient and Adaptive Strategy in Distributed Storage Systems. IEEE Transactions on Parallel and Distributed Systems 2023, 34, 2749–2766. [Google Scholar] [CrossRef]
- How many objects will move when changing a crushmap ?. https://ceph.com/planet/how-many-objects-will-move-when-changing-a-crushmap/. (Accessed on 03/09/2018).
- Ahmed, S.; Nahiduzzaman, M.; Islam, T.; Bappy, F.H.; Zaman, T.S.; Hasan, R. FASTEN: Towards a FAult-tolerant and STorage EfficieNt Cloud: Balancing Between Replication and Deduplication, 2023, [arXiv:cs.DC/2312.08309]. arXiv:cs.DC/2312.08309].
- Hwang, J.Y.; Cho, S. Behaviors of Storage Backends in Ceph Object Store. 2017.
- Jeong, B.; Khan, A.; Park, S. Async-LCAM: a lock contention aware messenger for Ceph distributed storage system. Cluster Computing 2018, 22, 373–384. [Google Scholar] [CrossRef]
- Wang, F.; Nelson, M.; Oral, S.; Atchley, S.; Weil, S.; Settlemyer, B.W.; Caldwell, B.; Hill, J. Performance and Scalability Evaluation of the Ceph Parallel File System. Proceedings of the 8th Parallel Data Storage Workshop, 2013, PDSW ’13.
- Ceph Architecture. Ceph Scrubbing Documentation. http://docs.ceph.com/docs/master/ architecture/?highlight=scrub.
- AWS. Amazon EC2. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html, 2024.
- Axboe, J. Flexible I/O Tester. https://github.com/axboe/fio.






| Storage Nodes | Management / client node | |||
| Attribute | Description | Attribute | Description | |
| EC2 VM type | c5.2xlarge: 8 vCPUs; x86_64 arch; 16GB RAM | EC2 VM type | c5.2xlarge: 8 vCPUs; x86_64 arch; 16GB RAM | |
| VM Image | Ubuntu Server 22.04 (LTS) | VM image | Ubuntu Server 22.04 (LTS) | |
| Storage type | SSDs = 2 x 512 GB per server | Storage | Used for hosting OS and Ceph manager services only | |
| # Number of storage servers | Total 8 servers in the storage cluster | Object-Size | 4 MB | |
|
Total Data written from client |
Total Duplicate Data |
Total objects stored to storage |
Total chunks chunks stored to storage |
Total replica objects stored to storage |
Final Data committed to storage |
|
| Speed-Dedup 20% dedup ratio | 6TB | 1.2TB | 314,573 | 5,033,168 | 314,573 | 2.4TB |
| GRATE 20% dedup ratio | 6TB | 1.2TB | 314,573 | 15,099,504 | 0 | 3.6TB |
| CAO 20% dedup ratio | 6TB | 1.2TB | 314,573 | 15,099,504 | 0 | 3.6TB |
| Speed-Dedup 80% dedup ratio | 6TB | 4.8TB | 1,258,292 | 20,132,672 | 1,258,292 | 9.6TB |
| GRATE 80% dedup ratio | 6TB | 4.8TB | 1,258,292 | 60,398,016 | 0 | 14.4TB |
| GRATE 80% dedup ratio | 6TB | 4.8TB | 1,258,292 | 60,398,016 | 0 | 14.4TB |
|
Recovery Time (sec) 1 Failed OSD |
Recovery Time (sec) 2 Failed OSDs |
Recovery Time (sec) 3 Failed OSDs |
Recovery Time (sec) 4 Failed OSDs |
|
| 1|c|Speed-Dedup | 48.23 | 49.56 | 51.34 | 54.35 |
| 1|c|GRATE | 53.21 | 56.23 | 59.74 | 62.13 |
| 1|c|CAO | 53.29 | 56.28 | 60.02 | 62.13 |
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content. |
© 2024 by the authors. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license (http://creativecommons.org/licenses/by/4.0/).