Cloud Tech by Victor

Search

30 results for “ubuntu

Search results

Blog

Linux Core Operations: Full Hands‑On Practical Labs for Users, Permissions, sudo, Packages & Services

Linux Core Operations: Full Practical Labs (Beginner → Intermediate)

Blog

Linux Beginner Labs: Foundations (Understand Linux, Not Just Commands)

This lab helps you understand how Linux works internally, not just type commands. By the end of these labs, Linux will feel logical, not intimidating. Before You Start The Lab (Very Important). What You Need You can use any Linux environment : Ubuntu Desktop / Server Linux VM Cloud VM WSL (Windows…

SQL Joins & Query Planning

Why would a hash join beat a nested loop join for two large, unindexed tables, but lose to a nested loop join when one table is tiny?

A nested loop join's cost scales with (outer rows) × (cost of an inner-side lookup per row), so with two large unindexed tables, the inner-side scan is expensive and gets repeated for every single outer row, making the total cost grow multiplicatively. A hash join instead pays a roughly one-time cost to build a hash table from one side, then does a cheap lookup per row from the other side, additive rather than multiplicative, which wins decisively at scale. But when one table is tiny, the nested loop's "repeat the inner scan per outer row" cost is trivial regardless, and it avoids the hash table's build overhead entirely, so the simpler algorithm wins for small inputs specifically.

Database Locking & Deadlocks

Two transactions each update two of the same two accounts, but in opposite order, and deadlock. What's the actual fix, not just for this pair of transactions, but for the application generally?

The deadlock happens because Transaction 1 locks account A then waits for account B, while Transaction 2 locks account B then waits for account A, a circular wait. The general fix isn't retry logic alone, retries only paper over deadlocks that keep recurring, it's acquiring locks on multiple objects in the same, consistent order everywhere in the application (for example, always locking accounts in ascending id order), which makes the circular-wait pattern structurally impossible rather than merely less frequent. Retry logic is still worth having as a safety net, but consistent lock ordering is what actually eliminates this class of deadlock.

Rate Limiting Algorithms

A client sends a sudden spike of requests that exceeds the configured rate, but the API doesn't immediately reject any of them. Why not, and when does rejection actually start?

The token bucket has accumulated capacity up to the burst limit, if the client had been under the rate limit recently, unused tokens built up in the bucket, and that reserve absorbs a short spike without any request failing, exactly the point of separating burst capacity from steady-state rate. Rejection (a 429 response) only starts once the bucket is actually empty, every accumulated token has been consumed and the spike is sustained long enough that the rate of token consumption keeps exceeding the rate of token replenishment. This is why a token bucket, unlike a hard per-second cap, tolerates brief bursts gracefully while still enforcing a real steady-state ceiling.

Blog

How to Configure Desktop Backgrounds, Power Settings, and Legal Notices Using Group Policy

In this lab, I implemented key Group Policy configurations to standardize system behavior, improve user experience, and strengthen security awareness across all domain joined devices. The configuration focuses on: Enforcing a consistent and professional desktop environment Preventing unauthorized o…

Rate Limiting Algorithms

In the token bucket algorithm, what do the "rate" and "burst" settings each actually control?

The rate is how many tokens get added to the bucket per second, the steady-state throughput the API is designed to sustain indefinitely. The burst is the bucket's total capacity, the maximum number of tokens that can accumulate and therefore the largest spike of concurrent requests the API will accept in a single instant before it starts rejecting anything further. A request consumes one token; as long as the bucket has a token available, the request proceeds immediately, rate and burst are two independent numbers, not one combined setting, because sustained throughput and tolerance for short spikes are genuinely different things to configure.

Web Accessibility Fundamentals

A custom dropdown built entirely from styled divs passes a visual design review. What is likely still broken for a keyboard-only or screen-reader user, and why doesn't looking right catch it?

Without the correct ARIA roles/states (or, better, a native `<select>`), a div-based dropdown typically has no way to be reached or operated via keyboard alone (no built-in Tab/Enter/Arrow-key handling), and a screen reader has no semantic information telling it "this is a dropdown, it is currently closed, here are its options," so it may announce nothing meaningful at all. A purely visual review can't catch this because the div looks and behaves correctly with a mouse, the missing behavior only surfaces via keyboard navigation or assistive technology, which is exactly why accessibility has to be tested directly with a keyboard and a screen reader, not inferred from how a component looks.

Binary Search

Why does binary search require the input to already be sorted, and what actually happens if you run it on unsorted data?

Binary search's core logic is comparing the target against a midpoint and eliminating the entire half that can't possibly contain it, an elimination step that's only valid if elements are ordered, so everything below the midpoint really is smaller and everything above really is larger. Run it on unsorted data and there's no error or exception, the algorithm has no way to detect the invariant is broken, it just keeps halving the search space based on comparisons that no longer imply anything real, and returns an insertion point or "not found" result that has no actual relationship to whether or where the target exists in the list.

Database Transactions & Isolation Levels

A transaction under Repeatable Read isolation fails with "could not serialize access due to concurrent update." What actually happened, and what is the application expected to do?

Repeatable Read uses snapshot isolation, the transaction sees a consistent snapshot from its own start, but if it then tries to update a row that another, concurrently-committed transaction already modified, PostgreSQL detects the conflict and aborts the transaction with a serialization failure rather than silently applying an update based on stale data. This is not an application bug, it is Repeatable Read (and Serializable) working as designed, both isolation levels explicitly require the application to catch this specific error and retry the transaction from the beginning, trading the guarantee of not overwriting concurrent changes for the operational cost of occasional automatic retries.

Hash Tables & the Hash/Equality Contract

What is the required contract between equality and hashing for an object used as a dictionary key, and why does the hash table need it specifically?

The contract is one-directional but strict: if two objects compare equal, they must produce the same hash value. A hash table uses an object's hash to pick which bucket to look in, then uses equality only to confirm the exact match within that bucket, so if two equal objects hashed differently, a lookup for one would search the wrong bucket entirely and never even reach the equality check that would have confirmed the match. The hash doesn't have to be unique across unequal objects (collisions are expected and handled), it just has to agree for anything that compares equal, that's the one property the whole lookup mechanism depends on.

LLM Fundamentals: Tokens, Context Windows & Sampling

A request's input already exceeds the model's context window before generation even starts. What happens, versus a request that only exceeds the limit once output is generated?

If the input alone already exceeds the context window, the API rejects the request upfront with a 400 error, generation never starts at all. If the input fits but input tokens plus the requested max output tokens could exceed the window, current models accept the request and generate as far as they can; if generation actually reaches the window limit before finishing, it stops early with a specific stop reason indicating the context window was exhausted, rather than silently truncating or erroring out mid-response. The distinction matters operationally: the first case is a fixable request-construction bug, the second is a signal to reduce the requested output length or the accumulated conversation history.

Recursion & the Call Stack

Python's own documentation warns that raising the recursion limit "should be done with care, because a too-high limit can lead to a crash." Why doesn't raising the limit simply allow deeper, safe recursion?

The recursion limit is a proxy for the real constraint, actual available C stack space, which is platform-dependent and finite regardless of what the configured limit says. Setting the limit higher than the platform's actual available stack can support doesn't create more stack space, it just removes the early warning that would have raised a clean `RecursionError`, so recursion can now run deep enough to exhaust the real stack and crash the process with a low-level segmentation fault instead, a worse failure mode than the exception the limit was preventing in the first place.

Blog

Linux Foundations (Beginner): How Linux Really Works - Not Just Commands

Most beginners learn Linux backwards.

Linux Filesystem Hierarchy & Permissions

A process creates a file requesting mode 0666, but the file ends up with permissions 0644. What decided that, and would the outcome change if the parent directory had a default ACL?

The process's umask is what changed the requested mode: umask 022 turns off the write bit for group and others from any requested mode, so 0666 (rw-rw-rw-) becomes 0666 & ~022 = 0644 (rw-r--r--). The umask is applied by the kernel at file/directory creation time, not by the application deciding to be conservative. If the parent directory has a default ACL set, that changes the outcome: default ACL inheritance takes precedence over the umask entirely, so the new file's permissions would instead be derived from the ACL, not from applying umask to the requested mode.

Linux Networking

A socket shows up in the LISTEN state versus ESTABLISHED. What is actually different about what that socket is doing?

LISTEN means a server-side socket is bound to a port and passively waiting to accept incoming connection requests, it isn't attached to any specific remote peer yet. ESTABLISHED means a full connection has completed its handshake and is actively associated with a specific remote address and port, data can flow in both directions. Seeing a service you expect to be running not show a LISTEN socket on its expected port is usually the very first, most direct signal that the service isn't actually up or isn't bound where you think it is.

Message Queues & Event-Driven Architecture

A consumer receives a message, starts processing, but crashes before deleting it. What happens to that message, and why is this actually the desired behavior?

Once the visibility timeout expires without the message being deleted, it automatically becomes visible again in the queue and can be picked up by the same or a different consumer for another processing attempt. This is deliberate, not a bug: the alternative (a crashed consumer's message vanishing permanently) would silently lose data, whereas reappearing after timeout guarantees eventual processing at the cost of a possible duplicate attempt, which is exactly the trade-off "at-least-once delivery" describes. Setting the visibility timeout too short causes premature, unnecessary reprocessing of messages still legitimately being worked on; too long delays legitimate retries after a real crash.

PostgreSQL Fundamentals

What is the difference between GROUP BY aggregation and a window function like `ROW_NUMBER() OVER (...)`?

`GROUP BY` collapses every row in a group into a single output row per group, you lose access to the individual rows once you've aggregated. A window function computes its result (a rank, a running total, a row number) per row while keeping every individual row in the output, `PARTITION BY` defines the grouping for that calculation, but nothing is collapsed. This is why "give me each order plus that customer's running total" needs a window function, while "give me one row per customer with their total" is a plain `GROUP BY`.

Python Syntax Fundamentals

Why is using a mutable object (like a list) as a default argument value a common bug in Python?

Default argument values are evaluated exactly once, when the function is defined, not on every call, so a mutable default like `def f(items=[])` creates one list object that is shared across every call that doesn't explicitly pass its own `items`. Appending to it in one call leaves those items present the next time the function is called with the default, which looks like inexplicable state leaking between unrelated calls. The fix is defaulting to `None` and creating a new list inside the function body when `items is None`, so every call that needs the default gets its own fresh object.

Web Accessibility Fundamentals

What is the "first rule of ARIA," and why does a native <button> beat a <div role="button"> even though both can be made accessible?"

The W3C's own first rule of ARIA use is direct: if a native HTML element or attribute already provides the semantics and behavior you need, use it instead of re-purposing a different element with ARIA. A native `<button>` comes with keyboard interaction (Enter/Space activates it, Tab reaches it), focus management, and correct semantics built into the browser for free. A `<div role="button">` requires manually replicating every one of those behaviors with JavaScript and additional ARIA attributes, tabindex, keydown handlers for both Enter and Space, and it is easy to miss an edge case a real button never had in the first place. ARIA can make a div accessible in theory; a native element already is, with less code and less risk.

Web Performance & Core Web Vitals

A page has a fast average LCP but users still frequently report the page feeling slow. What could the percentile-based measurement reveal that an average wouldn't?

If a substantial slice of real page loads, say the slowest 25%, badly miss the 2.5 second LCP threshold (a slow connection, a busy device, a cold cache), the average can still look fine because it's dominated by the faster majority, while a real, sizable group of users are having a genuinely bad experience the average is actively hiding. Checking the 75th-percentile value directly (rather than the mean) surfaces that gap: if the 75th percentile is well above 2.5 seconds even though the average looks fine, that's a concrete signal that meaningful numbers of real users are missing the threshold, not a false alarm.

Database Partitioning

A query filters WHERE logdate >= 2008-01-01 against a table range-partitioned by logdate across dozens of monthly partitions. What does partition pruning actually do, and what does it depend on?

Partition pruning lets the planner prove, from the query's WHERE clause and each partition's declared bounds, that some partitions cannot possibly contain a matching row, and it excludes them from the plan entirely rather than scanning and filtering every partition. In this example, `logdate >= 2008-01-01` has no upper bound, so it prunes only the partitions entirely before 2008-01, decades of older monthly partitions are eliminated before execution, while every partition from 2008-01 onward, including all of them up to the present, is still considered and scanned. Pruning down to a single partition would need a bounded predicate on both ends, for example `logdate >= 2008-01-01 AND logdate < 2008-02-01`. This depends entirely on the partition bounds themselves, not on any index, a partitioned table with no indexes at all still benefits from pruning, and pruning specifically requires the WHERE clause to reference the partition key directly with values (or parameters) the planner can actually compare against those bounds.

Database Connection Pooling

An application uses PREPARE to create a reusable prepared statement, then relies on it across multiple requests. What happens if it's deployed behind a transaction-pooled PgBouncer, and why?

It breaks. A SQL PREPARE statement is a session-level feature, it lives on whatever specific server connection issued the PREPARE, but transaction pooling reassigns the underlying server connection to a different client (or the same client's next transaction) as soon as each transaction ends, so there's no guarantee a later request lands on that same server connection where the prepared statement actually exists. This is explicitly documented as one of the session-based features transaction pooling breaks, along with SET/RESET, LISTEN, WITH HOLD cursors, and session-level advisory locks, all of which depend on state tied to one specific, persistent server connection. This is distinct from protocol-level named prepared statements issued via the extended query protocol (what most driver-level "prepared statements" actually are), which PgBouncer can support under transaction pooling when `max_prepared_statements` is set to a non-zero value.

Hash Tables & the Hash/Equality Contract

A custom class defines __eq__ based on a value field but leaves __hash__ using default identity-based hashing. What actually goes wrong when you use an instance as a dict key?

In Python 3, simply defining `__eq__` without touching `__hash__` doesn't leave the old identity-based hash in place, Python automatically sets `__hash__` to `None` on that class, making instances unhashable, so using one as a dict key raises `TypeError` immediately rather than corrupting anything. This happens even if a parent class defines `__hash__`: overriding `__eq__` in a subclass sets that subclass's `__hash__` to `None` regardless of what the parent provides, ordinary inheritance does not carry the parent's hash forward. The silent, no-exception version of this bug only happens if the class explicitly keeps or re-supplies an identity-based `__hash__` alongside the value-based `__eq__` (e.g. `__hash__ = object.__hash__`, or, to retain a parent's hash on purpose, `__hash__ = Parent.__hash__`). In that case, two instances with the same value compare equal (`a == b` is `True`) but hash differently, and inserting under key `a` then looking up with an equal-but-distinct key `b` lands in the wrong hash bucket and returns nothing, because the hash mismatch never gave the lookup a chance to even check equality against the right entry.

LLM Evaluation & Reducing Hallucinations

Why is an evaluation suite necessary before shipping a prompt change, instead of just testing it manually on a few examples?

A prompt change can improve one success dimension while quietly breaking another, better accuracy but worse consistency, or improved tone at the cost of latency, and manually eyeballing a handful of examples won't reliably catch a regression on a dimension you weren't specifically looking at. An evaluation suite tests against a defined, ideally large set of cases, including deliberately hard edge cases like sarcasm or mixed sentiment, and scores every dimension that actually matters, which turns "it feels better" into a measurable, defensible, trackable claim, and catches regressions before they reach production rather than after.

SQL Joins & Query Planning

PostgreSQL's documentation says it's "impossible to suppress nested-loop joins entirely" even with enable_nestloop off. What does that tell you about relying on planner hints to force a specific join algorithm?

That specific guarantee is documented only for `enable_nestloop`: turning it off only discourages the planner by making nested loops look artificially expensive in cost estimation, it can't hard-disable them, because for some queries a nested loop is the only viable plan at all (for example, certain correlated subquery shapes), so the planner will still use one if it must. `enable_hashjoin` and `enable_mergejoin` don't carry that same caveat, disabling either one actually can prevent the planner from choosing that join type, since a nested loop (or the other remaining method) is always available as a fallback plan. In practice, though, all three settings are best treated as a debugging/diagnostic tool for understanding planner behavior, not a reliable production mechanism for forcing a specific join algorithm, the actual fix for a bad plan is almost always better statistics (via `ANALYZE`) or a better index, not overriding the planner's method choice.

Dynamic Programming

Why does a naive recursive Fibonacci function run in exponential time, and how does memoization fix that specifically?

Naive recursive `fib(n) = fib(n-1) + fib(n-2)` recomputes the exact same subproblem enormous numbers of times, `fib(n-2)` gets computed once directly and once again inside the `fib(n-1)` call, and this duplication compounds recursively, producing roughly 2^n total calls. Memoization caches each `fib(k)` result the first time it's computed, so every subsequent call with the same `k` becomes an O(1) cache lookup instead of a full recursive recomputation, collapsing the total distinct work down to O(n), one computation per distinct subproblem instead of an exponential number of repeated ones.

Kubernetes Security

How does Kubernetes RBAC decide whether a request is allowed, and can you write an explicit deny rule?

RBAC is default-deny and purely additive: a request is denied unless some Role/RoleBinding or ClusterRole/ClusterRoleBinding explicitly grants it, and there is no such thing as an explicit deny rule. Every applicable binding's permissions are unioned together, so restricting access means removing a grant (or removing the subject from a binding), not adding a deny statement on top of an existing grant, an approach that works cleanly for grant-based access but has no mechanism for "allow everything except X" within RBAC itself.

Blog

Azure Web App Zero-Downtime Deployment: A Hands-On Guide to Deployment Slots, Auto Scaling, and Load Testing

A practical Azure App Service lab covering staging slots, slot swaps, autoscaling, and traffic testing.

Azure Compute

What does AKS (Azure Kubernetes Service) manage for you compared to running Kubernetes yourself on VMs?

AKS manages the Kubernetes control plane (API server, etcd, scheduler) at no direct cost for the control plane itself; you only pay for the worker nodes, which still run as VMs you have some visibility into but don't have to manually install or upgrade Kubernetes onto. Running Kubernetes yourself on plain VMs means standing up and maintaining the entire control plane, including its high availability and upgrade process, which is a substantial and ongoing operational burden. AKS trades some control-plane visibility for removing that burden, which is why it's the default choice for running Kubernetes on Azure unless there's a specific reason to self-manage.

Search results for “ubuntu” | Cloud Tech by Victor