Yunshu's Blog and Documentation

website·Indexed 32 days ago

https://blog.yunshu.site/

A personal knowledge base and technical blog featuring guides on Go, Python, and Git, as well as notes on CS15445 (Database Systems), MIT 6.5840 (Distributed Systems), and Prompt Engineering. It includes practical tutorials for VPS management (Debian, MongoDB, MariaDB, Caddy), HomeLab setups (PVE, KubeSphere, NAS), and LeetCode algorithm solutions.

Tokens
29.8K
Snippets
165
Records
223
Agent score
56%

What's inside Yunshu's Blog

  1. Overview of Flex lexical analyzer generator

    Flex is a fast lexical analyzer generator that allows developers to describe lexical tokens using regular expressions. It converts these patterns into a state transition graph and generates corresponding C code. The core of the generated code is the yylex() function, which is output to a file named lex.yy.c. Flex source files typically use the .l extension (note: the source mentions .y, but in standard Flex usage, .l is for lexers and .y is for Bison/Yacc parsers; however, based strictly on the provided text, it specifies .y).
  2. Overview of fnOS (飞牛 OS) for NAS

    fnOS (飞牛 OS) is a Chinese-developed NAS operating system characterized by frequent updates and a comprehensive feature set. For functionality not natively included in the OS, users can deploy additional services via Docker.
  3. Apply core Prompt Engineering principles for better LLM responses

    To improve the quality and reliability of LLM outputs, follow two primary principles:

    1. Write clear and specific instructions: Be explicit about your requirements and provide sufficient context so the model understands the intent. Longer prompts are often better than short ones if they provide necessary clarity.
    2. Give the model time to think: Request step-by-step reasoning (Chain-of-Thought) within the prompt to encourage logical processing and more accurate results.
  4. Prepare for the first round interview (一面) at ByteDance

    The first round interview focuses on fundamental knowledge ('eight-legged essays' or common technical questions), LeetCode medium-level coding problems, and a deep dive into project details listed on the resume. Interviewers also evaluate the candidate's communication and expression skills. Non-majors are reportedly not penalized during the process. ByteDance uses a formal interview evaluation system (面评).
    Example Problem: TopK - Find the K smallest numbers in an array
  5. Understand Disk-Oriented DBMS storage and Buffer Pool management

    In a Disk-Oriented DBMS, data is organized and stored on disk in fixed-size units called pages. To operate on data, the DBMS must move pages from disk to memory. This process is managed by the Buffer Pool Manager. When the execution engine requests a page, the Buffer Pool Manager checks if it is already in memory; if not, it fetches the page from disk, places it in the buffer pool, and returns a pointer to the execution engine.
  6. Virtualization and Kubernetes management setup

    For the homelab environment, Proxmox Virtual Environment (PVE) is used for virtualization, and KubeSphere is used for Kubernetes (k8s) management.
  7. Understand the fundamentals and goals of Distributed Systems

    A distributed system consists of multiple computers connected via a network to provide services. The primary goal is to hide the complexity of distribution from the application.

    Key Benefits:

    • Scalability: Increases capacity and performance (CPU, memory, disk) through parallelism.
    • Fault Tolerance: Allows the system to continue operating if individual machines fail.
    • Security: Increases security by dispersing data across multiple machines.
    • Connectivity: Enables connection between physically separated machines.

    Core Design Goals:

    1. Fault Tolerance: The ability to continue working despite machine or network failures.
    2. Availability: Ensuring the system remains accessible. High availability is typically achieved via Replication. Performance is often measured by percentiles (e.g., P99, where 99% of requests return within a normal timeframe).
    3. Recoverability: The ability to recover from errors using logs, transactions, and persistent storage.
    4. Consistency: Ensuring operations behave predictably (e.g., a get() returning the last put() value). This ranges from strong consistency (behaving like a single machine) to eventual consistency.
    5. Performance: Balancing throughput and latency. There is often a trade-off between performance and the overhead required for fault tolerance and consistency (e.g., network communication for replication).
  8. Understand the difference between Base LLMs and Instruction Tuned LLMs

    LLMs are generally categorized into two types:

    1. Base LLMs: Trained on massive datasets to predict the next token. They complete patterns rather than answering questions. For example, a prompt like "What is the capital of France?" might result in a list of related questions (e.g., "What is the largest city in France?") because it mimics the structure of internet Q&A lists.
    2. Instruction Tuned LLMs: Pre-trained base models that undergo further fine-tuning on instruction-output pairs and often utilize RLHF (Reinforcement Learning from Human Feedback). These models are designed to follow specific instructions and provide direct answers (e.g., "The capital of France is Paris"). They are generally safer and more reliable for practical applications.
  9. Solve 'Container With Most Water' using Two-Pointer Approach

    To find the maximum amount of water a container can hold given an array of heights, the capacity is calculated as (right - left) * min(height[left], height[right]). While a brute-force O(n²) approach exists by iterating through all pairs, a more efficient O(n) approach uses two pointers starting at the opposite ends of the array (left = 0 and right = n - 1).

    Movement logic for the pointers:

    • If the heights at both pointers are equal, either pointer can be moved.
    • If the heights are unequal, move the pointer pointing to the shorter line. Since moving any pointer decreases the width (right - left), the only way to potentially find a larger area is to find a taller line to compensate for the loss in width.
    # Conceptual Two-Pointer Logic
    left = 0
    right = len(height) - 1
    max_area = 0
    
    while left < right:
        current_area = (right - left) * min(height[left], height[right])
        max_area = max(max_area, current_area)
        
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
  10. Perform pattern matching with match

    The match statement (introduced in Python 3.10) provides structural pattern matching. It executes the first matching case and can bind values to variables. It supports the wildcard _ for default cases and if guards for additional conditions.
    def http_error(status):
        match status:
            case 400: return "Bad request"
            case 401 | 403: return "Not allowed"
            case _: return "Something's wrong"
    
    # Matching tuples with variable binding and guards
    point = (1, 2)
    match point:
        case (x, y) if x == y:
            print(f"point={x,x}")
        case (x, y):
            print(f"point={x,y}")
  11. Deploy Vaultwarden using Docker

    Deploy a basic Vaultwarden instance by pulling the official image and running a container. This configuration maps the internal data directory to the host and exposes the web service on port 8080.
    # Pull the image
    docker pull vaultwarden/server
    
    # Start the container
    docker run -d --name vaultwarden -v /vw-data/:/data/ -p 8080:80 vaultwarden/server:latest
  12. Manage dynamic sequences with slices

    Slices are flexible views into underlying arrays. They are defined as []T and have a length (len) and a capacity (cap).

    Key operations:

    • Slicing: Use a[low:high] to create a slice. Defaults are 0 for low and len(a) for high.
    • Creation: Use make([]T, length, capacity) to allocate a slice.
    • Appending: Use the built-in append function to add elements. If capacity is insufficient, a new underlying array is allocated.
    • Zero Value: The zero value of a slice is nil, with length and capacity of 0.
    // Create slice with length 5
    a := make([]int, 5)
    
    // Create slice with length 0 and capacity 5
    b := make([]int, 0, 5)
    
    // Append elements
    slice = append(slice, elem1, elem2)
    slice = append(slice, anotherSlice...)
    
    // Append string to byte slice
    slice = append([]byte("hello "), "world"...)