From 66d8dce2d6eb36d419c4f9ca5d361a66b985f254 Mon Sep 17 00:00:00 2001 From: Ishaan Kesarwani Date: Tue, 17 Dec 2024 14:52:39 +0530 Subject: [PATCH 1/5] Issue#1 solved- rename the file --- mermoria.go => memoria.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mermoria.go => memoria.go (100%) diff --git a/mermoria.go b/memoria.go similarity index 100% rename from mermoria.go rename to memoria.go From d05bc7b1cd0a0f8cab6ac9ce89ac5c443bb6dd84 Mon Sep 17 00:00:00 2001 From: Ishaan Kesarwani Date: Tue, 17 Dec 2024 16:12:01 +0530 Subject: [PATCH 2/5] Issue#8 solved - Learn and Teach: Mutexes --- docs/MUTEX.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 docs/MUTEX.md diff --git a/docs/MUTEX.md b/docs/MUTEX.md new file mode 100644 index 0000000..b36e40d --- /dev/null +++ b/docs/MUTEX.md @@ -0,0 +1,74 @@ +# **MUTEXes Explained Like You're Five** 🚦 + +## **What’s a Mutex?** +Imagine you’re in a kitchen with one cookie jar, and all your friends want to grab a cookie at the same time. What would happen? LADAAI! Everyone’s hands get in the way, and high chances that the jar would fall and break. + +A **mutex** (mutual exclusion) is like a **key to the cookie jar**. Only one person can hold the key at a time, which means only they can access the cookies. And when they’re done, they hand the key to the next person. This way, the cookie jar stays safe, and everyone gets their turn. + +--- + +## **Mutex vs Semaphore: What’s the Difference?** +Let’s say you have two scenarios: + +1. **The Cookie Jar (Mutex):** + Only one person can take cookies at a time. This is what a **mutex** does — it ensures **exclusive access** to shared resources. + +2. **The Playground Swing (Semaphore):** + Imagine there are three swings, and a group of kids waiting to play. Up to three kids can swing at the same time, but no more. This is a **semaphore**, which allows multiple threads to access a resource, but only up to a fixed limit. + +--- + + +# **Understanding Mutexes in Go** 🚦 + +Mutexes are an essential tool in programming to avoid chaos when multiple threads or goroutines need access to shared resources. Think of them as the "key" that ensures only one thread can use a resource at a time. This README will walk you through how mutexes work, when to use them, and when to avoid them. + +--- + +## **Core Operations?** 🤔 + +Here’s a breakdown of the core operations when using a mutex in Go: + +| **Operation** | **Description** | +|--------------------------|------------------------------------------------------------------------------------------------------| +| `mutex.Lock()` | Locks the shared resource (e.g., `counter`), ensuring only one goroutine can update it at a time. | +| `mutex.Unlock()` | Unlocks the resource, allowing other goroutines to access it. | +| `sync.WaitGroup` | Helps synchronize the goroutines, ensuring the main program waits for all to finish before proceeding.| + + +## **Mutex Example in Go** 🚀 + +Here’s a practical example to demonstrate mutex usage: + +```go +package main + +import ( + "fmt" + "sync" +) + +var ( + counter int // A shared resource + mutex sync.Mutex // A mutex to protect the resource +) + +func increment(wg *sync.WaitGroup) { + mutex.Lock() // Acquire the lock + counter++ // Safely update the counter + mutex.Unlock() // Release the lock + wg.Done() // Mark this goroutine as done +} + +func main() { + var wg sync.WaitGroup + wg.Add(3) // We’ll use three goroutines + + for i := 0; i < 3; i++ { + go increment(&wg) // Start a goroutine + } + + wg.Wait() // Wait for all goroutines to finish + fmt.Println("Final Counter:", counter) +} +``` \ No newline at end of file From f02eaca47348cccf7c32a84d14509d60f5c06c3a Mon Sep 17 00:00:00 2001 From: Ishaan Kesarwani Date: Tue, 17 Dec 2024 16:32:46 +0530 Subject: [PATCH 3/5] Issue#8 Solved Learn and Teach: Mutexes --- docs/MUTEX.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/MUTEX.md b/docs/MUTEX.md index b36e40d..d08e6e2 100644 --- a/docs/MUTEX.md +++ b/docs/MUTEX.md @@ -10,6 +10,7 @@ A **mutex** (mutual exclusion) is like a **key to the cookie jar**. Only one per ## **Mutex vs Semaphore: What’s the Difference?** Let’s say you have two scenarios: + 1. **The Cookie Jar (Mutex):** Only one person can take cookies at a time. This is what a **mutex** does — it ensures **exclusive access** to shared resources. From 2f693a94c692c0c8db43870d4dbd9df9f35c02f8 Mon Sep 17 00:00:00 2001 From: Ishaan Kesarwani Date: Tue, 17 Dec 2024 16:57:29 +0530 Subject: [PATCH 4/5] Issue 8 fixed - Write Buffer to temporary directory first to enable atomicity --- memoria.go | 65 +++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/memoria.go b/memoria.go index 8be0d6e..2b1a094 100644 --- a/memoria.go +++ b/memoria.go @@ -103,16 +103,14 @@ func (m *Memoria) Write(key string, val []byte) error { // writes the data given by the io.reader performs explicit sync if mentioned otherwise // depedning on the physical media it sync -func (m *Memoria) WriteStream(key string, r io.Reader, sync bool) error { +func (m *Memoria) WriteStream(key string, r io.Reader, sync bool) error { if len(key) <= 0 { return fmt.Errorf("Empty key") } pathKey := m.transform(key) - //TODO: check for bad paths check if any part contains / after being transformed - m.mu.Lock() defer m.mu.Unlock() @@ -120,47 +118,48 @@ func (m *Memoria) WriteStream(key string, r io.Reader, sync bool) error { return fmt.Errorf("Cannot create directory: %s", err) } - f, err := m.createKeyFile(pathKey) - + // created a temp file + tmpFile, err := os.CreateTemp(m.pathFor(pathKey), "tmp-*") if err != nil { - return fmt.Errorf("cannot create key file: %s", err) + return fmt.Errorf("Cannot create temporary file: %s", err) } + defer func() { + if _, err := os.Stat(tmpFile.Name()); err == nil { + os.Remove(tmpFile.Name()) // Cleanup temporary file + } + }() - wc := io.WriteCloser(&nopWriteCloser{f}) - - //TODO: replace wc with compression writer when implementing compression - - // this is the place where data transfers actually happens when - // we transfer a read buffer to a writer - if _, err := io.Copy(wc, r); err != nil { - return cleanUp(f, fmt.Errorf("Cannot copy from read buffer %s", err)) + // wrote data(in temp file) + if _, err := io.Copy(tmpFile, r); err != nil { + return cleanUp(tmpFile, fmt.Errorf("Cannot copy from read buffer: %s", err)) } - if err := wc.Close(); err != nil { - return cleanUp(f, fmt.Errorf("Cannot close compression error %s", err)) + // close the temporary file + if err := tmpFile.Close(); err != nil { + return fmt.Errorf("Cannot close temporary file: %s", err) } - // if sync { - // if err := f.Sync(); err != nil { - // cleanUp(f, fmt.Errorf("Cannot Sync: %s", err)) - // } - // } - if err := f.Close(); err != nil { - return fmt.Errorf("Cannot close file: %s", err) + // renamed the temporary file to the target file + fullPath := m.completePath(pathKey) + if err := os.Rename(tmpFile.Name(), fullPath); err != nil { + return fmt.Errorf("Cannot rename temporary file to target file: %s", err) } - //Atomic Writes: uncomment the following code when implemented atomic writes - // fullPath := m.completePath(pathKey) + // optional sync to ensure data is written to disk + if sync { + f, err := os.Open(fullPath) + if err != nil { + return fmt.Errorf("Cannot open file for syncing: %s", err) + } + defer f.Close() - // if f.Name() != fullPath { - // if err := os.Rename(f.Name(), fullPath); err != nil { - // os.Remove(f.Name()) - // return fmt.Errorf("Cannot rename files: %s", err) - // } - // } + if err := f.Sync(); err != nil { + return fmt.Errorf("Cannot sync file: %s", err) + } + } - // empty the cache for original key - m.emptyCacheFor(pathKey.originalKey) // cache is read only + // Clear the cache for the original key + m.emptyCacheFor(pathKey.originalKey) return nil } From dc811ce6ebed39c248eb32cb985b5a9cc10a6596 Mon Sep 17 00:00:00 2001 From: Ishaan Kesarwani Date: Tue, 17 Dec 2024 18:01:17 +0530 Subject: [PATCH 5/5] solved #18, made chnages in the readme.md --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/README.md b/README.md index aa463f5..ceff97d 100644 --- a/README.md +++ b/README.md @@ -150,3 +150,94 @@ More operations which will be added like `PeekMax`,`PopMax`,`PopMin` etc. --- +## Some simple primitive methods : + + - [Read Operation](#) + - [Write Operation](#write-operation) + - [Keys Operation](#keys-operation) + - [Erase Operation](#erase-operation) + - [Put Operation](#put-operation) +--- +1) Read Operation +--- +``` +package main + +import "fmt" + +func main() { + // Example data + data := map[string]string{ + "key1": "value1", + "key2": "value2", + } + + // Reading a value by key + value, exists := data["key1"] + if exists { + fmt.Println("Read value:", value) // Output: value1 + } else { + fmt.Println("Key not found") + } +} +``` +2) Wrire Operation +``` +package main + +import "fmt" + +func main() { + // Example data + data := map[string]string{ + "key1": "value1", + "key2": "value2", + } + + // Reading a value by key + value, exists := data["key1"] + if exists { + fmt.Println("Read value:", value) // Output: value1 + } else { + fmt.Println("Key not found") + } +} +``` +3) Write Operation +``` +package main + +import "fmt" + +func main() { + // Creating an empty map + data := make(map[string]string) + + // Writing data to the map + data["key1"] = "value1" + fmt.Println("Data after write:", data) // Output: map[key1:value1] +} +``` +4) Keys Operation +``` +package main + +import "fmt" + +func main() { + // Example data + data := map[string]string{ + "key1": "value1", + "key2": "value2", + } + + // Retrieving all keys + fmt.Println("Keys in the map:") + for key := range data { + fmt.Println(key) + } + // Output: + // key1 + // key2 +} +```