Chris Padilla/Blog

My passion project! Posts spanning music, art, software, books, and more
You can follow by Newsletter or RSS! (What's RSS?) Full archive here.

    An Intro to Redis

    I had the pleasure of taking a look at the Redis server at work this week! So here are some notes from my own exploration ā€”

    What is Redis?

    Redis is a fully fledged database for storing and persisting various types and shapes of data. The name is short for "Remote Dictionary Server"

    A few key features:

    • Redis stores data in-memory. RAM is used instead of disc space to store data
    • It is a NoSQL Key/Value Store
    • Redis has a built in master/replica pattern. Replica servers can defer to changes in master

    The in-memory approach to storing data, in addition to its ability to maintain a master/replica patterns makes it a great fit for being the cache server for applications.

    Redis can support multiple database forms by expanding on the "Core" format of key-value pairs. Plugins such as RediSearch, RediGraph, RedisJSON, and RedisTimeseries can support other database models like those used by Elastisearch, Graph based such as Neo4J, and MongoDB.

    In a microservice environment, Redis can shine as a cache for multiple databases that have varied data models. Latency is often introduced when multiple services rely on multiple connections to different datasources on top of communicating with one another. Using Redis as a cache on request, then updating the cache if the data differs, can keep a group of microservices lean and quick.

    How Does Data Persist?

    If you opt to use Redis as a primary database, it begs the question: What happens when the server fails?

    The safest way is through replicas. Similar to using Redis as a distributed cache, you can use replicas as back ups. But then again, what if ALL of these fail?

    There are a couple of ways this is mitigated:

    Snapshotting (dump.rdb files)

    • Can be stored on a disc at intervals of 5 minutes or an hour
    • These are good for backups and disaster recovery
    • You would lose an data stored between the last backup and the failure

    Append Only File

    • Logs every write operation continuously to the disk
    • When restarting, it will use the AOF to rebuild the DB
    • Slower restarts
    • Potential for bugs in the AOF engine.

    The best approach is to use both. Redis recommends a mixed approach and goes into great detail on persistence in their documentation.

    From here, you can store those persisted files in a cloud environment separate from Redis. The multiple-location approach firms up the reliability of the system.

    Setting Values With an Expiration

    Most of the juicy details are in how Redis is implemented in your application or cloud environment. The actually interface from the application is pretty simple.

    Here's an example of setting a value with an expiration of 6 hours in Python:

    import redis
    
    redis_client = redis.Redis(...options)
    
    namespace = "comments"
    user = "Chris"
    message = "I love Redis!"
    target = f'{namespace}:{user}'
    
    r.set(target, value= message, ex=60*60*6)
    
    # Do more work...
    
    user_comment = r.get(target) # "I love Redis!"

    After 6 hours, if we were to try and get the value, there would be none since it would be cleared from the cache.

    Someday My Prince Will Come - Churchill

    Listen on Youtube

    šŸ‰ šŸ° šŸŒŒ

    Reefapalooza!

    Sketches from this week!

    Went to Reef-a-palooza with my folks this last weekend! Like anime conventions, but for coral and clownfish (and there was a cosplayer!)

    My dad said this made it look like he was walking the fish

    Started watching an MST3K classic ā€” Cave Dwellers!

    "I'm huge!"

    Still doing loads of gestures. This new-fangled digital pen takes some wrangling!

    Chameleon Twist Vibes

    And, lastly, I'm braving my own album art illustration! This guy will be making an appearance:

    Forest Frog sends his greetings from space!

    Goroutines, Structs, and Pointers in Go

    A potpourri of Go features I dove into this week!

    Structs

    Last week I looked at the Maps data type. It works similar to a JS Object, except all values must be the same type.

    Structs in Go are a data structure for saving mixed Data Type values. Here's what they look like:

    // Type Declaration
    type UserData struct {
        firstName string
        lastName string
        email string
        numberOfTickets uint
    }
    
    // Can then be used in array or slice
    var sales = make([]UserData, 0)
    
    var userData = UserData {
        firstName: "Chris",
        lastName: "Padilla,
        email: "hey@chris.com",
        numberOfTickets: 2,
    }
    
    
    // accessing values:
    
    booking.firstName
    
    // in a map, you would use bracket syntax
    // booking["firstName"]

    This is comparable to a lightweight class in languages like Java and C#. And, of course, the type declaration will look familiar to any TypeScript users.

    Goroutines

    This is what I've been waiting for!

    Goroutines are the key ingredient for Go's main benefit: Lightweight and easy to use concurrency.

    The strengths of Go over other languages:

    • Less overhead structurally
    • Less complex to write and manage
    • Threads in other language are more expensive as far as memory used compared to Go

    Goroutines are an abstraction of an actual OS thread. They're cheaper and lightweight, meaning you can run hundreds of thousands or even millions without affecting the performance of your application.

    Java uses OS threads, takes a longer startup time. Additionally, those threads don't have an easy means of communicating with each other. That's made possible through channels in Go, a topic for another day!

    Sample Code

    The syntax for firing off a concurrent function is pretty simple:

    // Do something expensive and Block the thred
    sendTickets()
    
    // Now try this non-blocking approach, creating a second goroutine
    go sendTickets()

    There's a bit of extra work needed to align the separate Goroutine with your main function. If the above code was all that was in our program, the execution would exit before the result from sendTickets() was reached.

    Go includes sync in it's standard library to help with this:

    import (
        "sync"
        "time"
    )
    
    var wg = sync.WaitGroup{}
    
    wg.Add(1)
    go sendTickets()
    
    wg.Wait()
    
    func sendTickets() {
        // Do something that takes a while.
        wg.Done()
    }

    When we create a Wait Group, we're essentially creating a counter that will keep track of how many concurrent methods have fired. We add to that counter before starting the routine with wg.Add(1) and then note when we want to hold for those goroutines to end with wg.Wait()

    Already, with this, interesting possibilities are now available! Just like in JavaScript, you can set up a Promise.all() situation on the server to send off multiple requests and wait for them all to return.

    Pointers

    Pointers are the address in memory of a variable. Pointers are used to reduce memory usage and increase performance.

    The main use case is in passing large variables to functions. Just like in JavaScript, when you pass an argument to a function, a copy is made that is then used within that function. If you were to pass the pointer instead here, that would be far less strain on the app's memory

    func bookTickets(largeTicketObject TicketObj) string {
        // Do stuff with this large object
        
        return "All set!"
    }

    Also, if we made changes to this object and wanted the side effect of adjusting that object outside the function, they wouldn't take effect. We could make those changes with pointers, though.

    To store the pointer in a var, prepend & to the variable name:

    x := 5
    xPointer = &x
    fmt.Println(xPointer)
    
    // Set val through pointer
    
    *xPointer = 64
    
    fmt.Println(i) // 64

    Faber - Fantasia Con Spirito

    Listen on Youtube

    Spooky music month! This one feels very "Cave of Wonders" from Aladdin. šŸ§žā€ā™‚ļø