10 Kasım 2026 Salı

ACID - Write Skew

Write Skew Nedir ?
İki farkı satırım değiştirilmesi sonucunda olmaması gereken bir noktaya varmaktir.

Açıklaması şöyle
Imagine you’re building a hospital management system.

The hospital has two doctors on call. Either doctor can request time off, but the system must always ensure at least one doctor remains available.

The business rule sounds simple: At least one doctor must remain on call at all times.

Now imagine both doctors request time off at almost exactly the same moment. Doctor A checks the database and sees two active doctors. Doctor B performs the same check and also sees two active doctors. Both conclude that taking time off is safe. Doctor A updates their status to OFF_DUTY. Doctor B does the same.

Both transactions commit successfully.

What does the database contain now?

Zero doctors are on call.

Neither transaction violated the rule as conflict isn’t between two writes to the same row. Both transactions executed successfully. Yet the final database state violates an important business constraint.

This is called Write Skew, and it exposes an important limitation of Snapshot Isolation.
Örnek - Postgres
Açıklaması şöyle
PostgreSQL's SERIALIZABLE isolation level prevents write skew.

PostgreSQL's Serializable Snapshot Isolation (SSI) detects dangerous dependency cycles that could produce non-serializable results.

For example:
1. Two doctors are on call.
2. Each transaction checks that at least two doctors are on call.
3. Transaction A takes doctor 1 off call.
4. Transaction B takes doctor 2 off call.
5. Both transactions attempt to commit.
Under REPEATABLE READ, both transactions can commit, leaving zero doctors on call.

Under PostgreSQL SERIALIZABLE, PostgreSQL detects the conflict and aborts at least one transaction.

Important: You must retry the entire aborted transaction when PostgreSQL returns SQLSTATE 40001.

So, if you need protection against write skew, PostgreSQL SERIALIZABLE is the appropriate isolation level.
Örnek - SqlServer
Açıklaması şöyle
In SQL Server, SERIALIZABLE prevents write skew.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

-- Read and update data

COMMIT;


Unlike PostgreSQL SSI, SQL Server primarily achieves this through locking, including key-range locks.
Database Serializable implementation
PostgreSQL SSI (conflict detection)
SQL Server Locking and key-range locks

Important: SQL Server's SNAPSHOT isolation does not prevent write skew. It can allow two transactions to update different rows based on the same initial state.

For your SQL Server applications, use SERIALIZABLE when you need this guarantee, or consider explicit UPDLOCK, HOLDLOCK when you want more targeted locking.

Lost Update vs Write Skew
Örnek
Bir örnek şöyle
Case 3: Consider your team is on production support for a week. The requirement is at least 1 person should always be on support. Is it possible that if you and teammate try to unassign themselves (assuming the other one is still available) then no one is on support ?
Hatalı kod şöyle
## Psuedo code
if(totalMembersOnSupport() > 1) {
  removeFromSupport(user_id);
}
totalMembersOnSupport() -> Select count(user_id) from support where on_support = 't';
removeFromSupport() -> update support set on_support = 'f' where user_id = '234';
Açıklaması şöyle. Yani her iki kullanıcı bir sayaca bakarak kendi satırlarına güncelleme yapıyorlar.
One difference between this case and the previously discussed cases is that in the previous cases concurrent transactions were trying to update the same row. But in this case, they are updating two different rows.
This is known as Write Skew


13 Ekim 2026 Salı

SSRF - Server-Side Request Forgery

Giriş
Dışarıya bakan bir REST noktamız var. Bu nokta şöyle olsum
@RestController
@RequestMapping("/preview")
class PreviewController {

    private final RestClient restClient;

    PreviewController(RestClient.Builder builder) {
        this.restClient = builder.build();
    }

    @GetMapping
    String preview(@RequestParam URI url) {
        return restClient.get()
                .uri(url)
                .retrieve()
                .body(String.class);
    }
}
Eğer saldırgan URL olarak içsel/dahili bir adres verirse, butün güvenlik önlemlerini aşabilir. 

Spring Boot 4.1.1 bunu önlemek için org.springframework.boot.http.client.InetAddressFilter; sınıfını sağlıyor. Böylece restClient başka bir adrese bağlanamaz.

CRDTs - Conflict-Free Replicated Data Types

Giriş
Conflict-free replicated data types( CRDTs) are a family of data structures for sets, maps, ordered lists, counters, etc that can be concurrently edited by multiple users. It uses two-way merges.
İlgili bir yazı burada
Örnek
Şöyle olsun
import java.util.Objects;

public record ReplicaId(String value) {
    public ReplicaId {
        if (value == null || value.isBlank()) {
            throw new IllegalArgumentException("Replica ID must not be blank");
        }
    }
}
Şöyle olsun
public sealed interface CounterCommand
        permits CounterCommand.Increment, CounterCommand.Decrement {
    ReplicaId replica();
    record Increment(ReplicaId replica) implements CounterCommand {
        public Increment {
            java.util.Objects.requireNonNull(replica);
        }
    }
    record Decrement(ReplicaId replica) implements CounterCommand {
        public Decrement {
            java.util.Objects.requireNonNull(replica);
        }
    }
}
Gerçekleştirim şöyle.
Instead of storing one mutable total, it stores two maps:
- positive: the cumulative increments made by each replica.
- negative: the cumulative decrements made by each replica.
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public record PnCounter(
        Map<ReplicaId, Long> positive,
        Map<ReplicaId, Long> negative) {

    public PnCounter {
        positive = immutableNonNegative(positive);
        negative = immutableNonNegative(negative);
    }

    public static PnCounter empty() {
        return new PnCounter(Map.of(), Map.of());
    }

    public PnCounter apply(CounterCommand command) {
        Objects.requireNonNull(command);

        return switch (command) {
            case CounterCommand.Increment inc ->
                    new PnCounter(bump(positive, inc.replica()), negative);
            case CounterCommand.Decrement dec ->
                    new PnCounter(positive, bump(negative, dec.replica()));
        };
    }

    public PnCounter merge(PnCounter other) {
        Objects.requireNonNull(other);
        return new PnCounter(
                joinByMaximum(positive, other.positive),
                joinByMaximum(negative, other.negative));
    }

    public long value() {
        return Math.subtractExact(sum(positive), sum(negative));
    }

    private static Map<ReplicaId, Long> bump(
            Map<ReplicaId, Long> current,
            ReplicaId replica) {

        var next = new HashMap<>(current);
        next.merge(replica, 1L, Math::addExact);
        return Map.copyOf(next);
    }

    private static Map<ReplicaId, Long> joinByMaximum(
            Map<ReplicaId, Long> left,
            Map<ReplicaId, Long> right) {

        var merged = new HashMap<>(left);
        right.forEach((replica, count) ->
                merged.merge(replica, count, Math::max));
        return Map.copyOf(merged);
    }

    private static long sum(Map<ReplicaId, Long> components) {
        return components.values().stream()
                .mapToLong(Long::longValue)
                .reduce(0L, Math::addExact);
    }

    private static Map<ReplicaId, Long> immutableNonNegative(
            Map<ReplicaId, Long> source) {

        Objects.requireNonNull(source);
        Map<ReplicaId, Long> copy = Map.copyOf(source);
        copy.forEach((replica, count) -> {
            Objects.requireNonNull(replica);
            if (count < 0) {
                throw new IllegalArgumentException("Components must be non-negative");
            }
        });
        return copy;
    }
}


9 Ekim 2026 Cuma

Metamorphic Testing

Giriş
Konuyu ilk defa burada gördüm.
Traditional testing assumes you know the expected output.

assertEquals(4, calculator.add(2, 2));

But how do you test an LLM that generates open-ended answers when there is no single correct output?

This is called the oracle problem: determining whether a program's output is correct when the expected answer is difficult or impossible to establish.

Metamorphic testing addresses this by checking relationships between multiple executions instead of checking each output against a known answer.
Örnek
How metamorphic testing works

The key concept is a metamorphic relation (MR): a property that should remain true when you transform an input and execute the system again.

1. Original input

A woman is sitting on a park bench with her dog.

Paraphrase without changing meaning

2. Transformed input

A woman sits outdoors with her pet nearby.

Run both inputs through the LLM

Same classification : Relation holds

Different classification : Potential fault

The important point is that you don't need to know the correct classification in advance. If two semantically equivalent inputs produce contradictory classifications, the model may be behaving inconsistently. 

8 Ekim 2026 Perşembe

TCP Bottleneck Bandwidth and Round-trip propagation time (BBR)

1. Flow control — protects the receiver
Flow control = don't overwhelm the receiver.

2. Congestion control — protects the network
Even if the receiver can accept 1 GB/s, the network path might only handle 100 MB/s.

3. BBR — a different approach to congestion control
BBR changes the question.

Instead of primarily asking:
"Did I lose packets?"

it measures:
- Bandwidth
- RTT (latency)

and estimates the network's BDP (Bandwidth × RTT).

Then it tries to keep approximately the right amount of data in flight to utilize the available bandwidth without unnecessarily filling network buffers.

CUBIC ve BBR

CUBIC:
send faster
   ↓
packet loss
   ↓
slow down
   ↓
increase again
   ↓
loss
   ↓
slow down
BBR:
measure bandwidth + RTT
          ↓
estimate capacity
          ↓
send near that rate
          ↓
continuously measure again

ETL (Extract Transform Load) Pipeline

1. ETL Pipeline ne zaman lazım olur ?
- Bir projeyi ilk defa ayağa kaldırırken mevcut veriyi alıp kendi veri modelimize çevirirken kullanırız.

- Çalışan bir başka sistemin değişen deltasını alıp kendi sistemimize ekleyerek, senkronizasyonu sağlamak amaçlı kullanabiliriz.

- Data warehouse sistemini çeşitli kaynakları kullanarak beslemek için kullanabiliriz.

2. ELED Extract–Load–Eligibility-Deliver Pipeline
Aslında delivery pipeline demek daha doğru. ELED benim kendi uydurmam. Açıklaması şöyle
An ELD (Extract–Load–Deliver) delivery pipeline with eligibility, acknowledgment, retry, and penalty processing.
Burada
  • Extract = obtain candidate work from the source.
    • Read pending transmission records from DB
    • Receive new transmission requests
    • Fetch configuration/parameters needed to decide eligibility
  • Load = bring that work into the dispatcher's working set.
    • Put records into the actor's queue/buffer
    • Restore pending/in-flight state
    • Build the in-memory transmission items
  • Eligibility = decide what can be transmitted now.
    • Is it enabled?
    • Is the penalty expired?
    • Is the destination available?
    • Is the item within its transmission window?
    • Has it already been sent/in-flight?
  • Deliver = actually perform reliable transmission.
    • Send
    • Wait for ACK
    • Retry
    • Apply penalty
    • Complete/remove the item


    6 Ekim 2026 Salı

    Invariant nedir

    Giriş
    Açıklaması şöyle
    An invariant is not documentation describing what usually happens. It is a condition that must remain true across every valid transition in the system.
    Örnek
    Available + reserved= total inventory