Skip to content

Instantly share code, notes, and snippets.

@Malix-Labs
Last active March 1, 2026 00:27
Show Gist options
  • Select an option

  • Save Malix-Labs/f0325731b45cbd6482b9f399bb3c0f91 to your computer and use it in GitHub Desktop.

Select an option

Save Malix-Labs/f0325731b45cbd6482b9f399bb3c0f91 to your computer and use it in GitHub Desktop.
Efrei 2026 Cloud Computing Lab 4
use flake
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
package main
import (
"flag"
"fmt"
"net/http"
"os"
"time"
)
func main() {
// Define the -port flag with a default of 8082
port := flag.String("port", "8082", "port to run the service on")
flag.Parse()
http.HandleFunc("/book", func(w http.ResponseWriter, r *http.Request) {
event := r.URL.Query().Get("event")
// Create a client with a strict 2-second timeout
client := http.Client{
Timeout: 2 * time.Second,
}
// Synchronous critical dependency
// Use the custom client instead of the default http.Get
// Ask Event Service to decrement the slot
resp, err := client.Get("http://localhost:8081/decrement?event=" + event)
if err != nil || resp.StatusCode != http.StatusOK {
http.Error(w, "Event sold out or unavailable", http.StatusConflict)
return
}
if resp != nil {
defer resp.Body.Close()
}
// Asynchronous non-critical dependency (Goroutine)
go func(e string) {
// We intentionally ignore errors here to prevent cascading failures
_, _ = http.Get("http://localhost:8084/notify?event=" + e)
}(event)
// Include the port in the response to prove which instance handled it
fmt.Fprintf(w, "Booking confirmed for %s (handled by port %s)\n", event, *port)
})
fmt.Printf("Booking Service running on :%s\n", *port)
if err := http.ListenAndServe(":"+*port, nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
package main
import (
"fmt"
"net/http"
"time"
)
// We start with exactly 1 slot
var events = map[string]int{"efrei-gala": 1}
func main() {
http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
for k, v := range events {
fmt.Fprintf(w, "%s: %d slots\n", k, v)
}
})
http.HandleFunc("/decrement", func(w http.ResponseWriter, r *http.Request) {
event := r.URL.Query().Get("event")
slots := events[event]
// 1. Validation check
if slots <= 0 {
http.Error(w, "Sold out", http.StatusConflict)
return
}
// 2. The Vulnerability: We sleep while holding the validated state,
// allowing other parallel requests to also pass the check above.
time.Sleep(500 * time.Millisecond)
// 3. State mutation
events[event]--
w.WriteHeader(http.StatusOK)
})
fmt.Println("Event Service running on :8081")
http.ListenAndServe(":8081", nil)
}
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nix-systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1771848320,
"narHash": "sha256-2j5NAPU1sTVgnrj1FLxkBABP63q2DiRU2eIfEG56kPk=",
"rev": "2fc6539b481e1d2569f25f8799236694180c0993",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre953160.2fc6539b481e/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1769909678,
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "72716169fe93074c333e8d0173151350670b824c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nix-systems": "nix-systems",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
{
description = "Efrei 2026 Cloud Computing Lab 4";
inputs = {
nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
flake-parts.url = "github:hercules-ci/flake-parts";
nix-systems.url = "github:nix-systems/default";
};
outputs =
inputs@{
self,
nixpkgs,
flake-parts,
nix-systems,
...
}:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = import nix-systems;
perSystem =
{
system,
pkgs,
...
}:
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
go
];
};
};
};
}
package main
import (
"fmt"
"io"
"net/http"
"os"
)
var bookingInstances = []string{"http://localhost:8082", "http://localhost:8083"}
var requestCount = 0
// Helper to forward the request and return an error if the target is dead
func proxy(w http.ResponseWriter, target string) error {
resp, err := http.Get(target)
if err != nil {
return err
}
defer resp.Body.Close()
// Copy the status code and body to the client
w.WriteHeader(resp.StatusCode)
_, err = io.Copy(w, resp.Body)
return err
}
func main() {
http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
if err := proxy(w, "http://localhost:8081/events"); err != nil {
http.Error(w, "Event Service unreachable", http.StatusServiceUnavailable)
}
})
http.HandleFunc("/book", func(w http.ResponseWriter, r *http.Request) {
// Round-robin selection
primaryIdx := requestCount % len(bookingInstances)
fallbackIdx := (requestCount + 1) % len(bookingInstances)
requestCount++
target := bookingInstances[primaryIdx] + "/book?" + r.URL.RawQuery
// Attempt primary instance
if err := proxy(w, target); err != nil {
fmt.Printf("Instance %s failed, falling back to %s\n", bookingInstances[primaryIdx], bookingInstances[fallbackIdx])
// Attempt fallback instance
fallbackTarget := bookingInstances[fallbackIdx] + "/book?" + r.URL.RawQuery
if err2 := proxy(w, fallbackTarget); err2 != nil {
http.Error(w, "All Booking Services are unreachable", http.StatusServiceUnavailable)
}
}
})
fmt.Println("API Gateway running on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
module gist.github.com/Malix-Labs/f0325731b45cbd6482b9f399bb3c0f91
go 1.25.7
package main
import (
"fmt"
"net/http"
"time"
)
// In-memory state. No mutexes to intentionally allow logical race conditions later.
var events = map[string]int{"efrei-gala": 2}
var bookings = 0
func eventsHandler(w http.ResponseWriter, r *http.Request) {
for k, v := range events {
fmt.Fprintf(w, "%s: %d slots\n", k, v)
}
}
func bookHandler(w http.ResponseWriter, r *http.Request) {
event := r.URL.Query().Get("event")
slots, exists := events[event]
if !exists || slots <= 0 {
http.Error(w, "sold out or not found", http.StatusBadRequest)
return
}
// Artificial delay to widen the race condition window for Task 8
time.Sleep(50 * time.Millisecond)
events[event] = slots - 1
bookings++
fmt.Fprintf(w, "booked %s. Total bookings: %d\n", event, bookings)
}
func main() {
http.HandleFunc("/events", eventsHandler)
http.HandleFunc("/book", bookHandler)
fmt.Println("Monolith running on :8080")
http.ListenAndServe(":8080", nil)
}
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
http.HandleFunc("/notify", func(w http.ResponseWriter, r *http.Request) {
event := r.URL.Query().Get("event")
fmt.Printf("Notification triggered: Confirmation email sent for %s\n", event)
w.WriteHeader(http.StatusOK)
})
fmt.Println("Notification Service running on :8084")
if err := http.ListenAndServe(":8084", nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment