<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Eugene Kozlov</title><generator>teletype.in</generator><description><![CDATA[Eugene Kozlov]]></description><image><url>https://img2.teletype.in/files/10/c5/10c59c16-6847-415e-8145-c09c275c07ad.png</url><title>Eugene Kozlov</title><link>https://teletype.in/@abstractart</link></image><link>https://teletype.in/@abstractart?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/abstractart?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/abstractart?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 01 Oct 2026 00:25:42 GMT</pubDate><lastBuildDate>Thu, 01 Oct 2026 00:25:42 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@abstractart/WuOMYqOTnWB</guid><link>https://teletype.in/@abstractart/WuOMYqOTnWB?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><comments>https://teletype.in/@abstractart/WuOMYqOTnWB?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart#comments</comments><dc:creator>abstractart</dc:creator><title>Структура данных Queue: от наивного алгоритма к lock-free реализации</title><pubDate>Sat, 12 Sep 2026 07:04:13 GMT</pubDate><description><![CDATA[В прошлом посте мы разобрались что такое lock-free алгоритм на примере задачи TransferMoney. В заметке я обмолвился что идеи из предложенного алгоритма имеют место быть в реальном мире.]]></description><content:encoded><![CDATA[
  <nav>
    <ul>
      <li class="m_level_1"><a href="#7C6b">Классические реализации</a></li>
      <li class="m_level_1"><a href="#nWWA">Two Lock Queue</a></li>
      <li class="m_level_1"><a href="#pOpa">Отказываемся от Mutex совсем</a></li>
      <li class="m_level_1"><a href="#7vvv">Где можно встретить подобный код?</a></li>
    </ul>
  </nav>
  <p id="UOj4">В <a href="https://teletype.in/@abstractart/dMuRypJ1cgO" target="_blank">прошлом посте</a> мы разобрались что такое lock-free алгоритм на примере задачи TransferMoney. В заметке я обмолвился что идеи из предложенного алгоритма имеют место быть в реальном мире. </p>
  <p id="i4c8">Сегодня хочу это продемонстрировать на примере реализации структуры данных Queue. Пройдем путь от наивной реализации до очереди Майкла-Скотта.</p>
  <h2 id="7C6b">Классические реализации</h2>
  <blockquote id="vBNa">Очередь — это линейная структура данных, в которой элементы обрабатываются по принципу <strong>FIFO</strong> (<em>First In, First Out</em> — «первым пришел — первым вышел»)</blockquote>
  <p id="j1jC">Классическая реализация на языке Go:</p>
  <pre id="j6h0" data-lang="go">type Node struct {
  val  int
  next *Node
}

type Queue struct {
  head *Node
  tail *Node
}

func NewQueue() *Queue {
  sentinel := &amp;Node{}
  return &amp;Queue{head: sentinel, tail: sentinel}
}

func (q *Queue) Push(val int) {
  node := &amp;Node{val: val}
  q.tail.next = node
  q.tail = node
}

func (q *Queue) Pop() *int {
  next := q.head.next
  if next == nil {
    return nil
  }
  q.head = next
  val := next.val
  return &amp;val
}</pre>
  <p id="Dcbw">Если нам нужно работать из нескольких потоков:</p>
  <pre id="Mier" data-lang="go">type Node struct {
  val  int
  next *Node
}

type MutexQueue struct {
  mu   sync.Mutex
  head *Node
  tail *Node
}

func NewMutexQueue() *MutexQueue {
  sentinel := &amp;Node{}
  return &amp;MutexQueue{head: sentinel, tail: sentinel}
}

func (q *MutexQueue) Push(val int) {
  q.mu.Lock()
  defer q.mu.Unlock()

  node := &amp;Node{val: val}
  q.tail.next = node
  q.tail = node
}

func (q *MutexQueue) Pop() *int {
  q.mu.Lock()
  defer q.mu.Unlock()

  next := q.head.next
  if next == nil {
    return nil
  }
  q.head = next
  val := next.val
  return &amp;val
}</pre>
  <p id="W8WG">Реализация простая и понятная. Но у нее есть важный минус - она сериализует чтения и записи в один поток исполнения. А хочется чтобы читатели сами по себе и писатели тоже.</p>
  <h2 id="nWWA">Two Lock Queue</h2>
  <p id="lKLT">В статье <a href="https://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf?" target="_blank">Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms</a> от Maged M. Michael и Michael L. Scott был предложен вариант реализации очереди не на одном мьютексе а на двух - для головы и хвоста соответственно. </p>
  <pre id="ntJJ" data-lang="go">type Node struct {
  val  int
  next atomic.Pointer[Node]
}

type TwoLockQueue struct {
  head     *Node
  headLock sync.Mutex

  tail     *Node
  tailLock sync.Mutex
}

func NewTwoLockQueue() *TwoLockQueue {
  sentinel := &amp;Node{}
  return &amp;TwoLockQueue{head: sentinel, tail: sentinel}
}

func (q *TwoLockQueue) Push(val int) {
  node := &amp;Node{val: val}

  q.tailLock.Lock()
  defer q.tailLock.Unlock()

  q.tail.next.Store(node)
  q.tail = node
}

func (q *TwoLockQueue) Pop() *int {
  q.headLock.Lock()
  defer q.headLock.Unlock()

  next := q.head.next.Load()
  if next == nil {
    return nil
  }
  q.head = next
  val := next.val
  return &amp;val
}</pre>
  <p id="fj3R">Код совсем не отличается от того что мы рассмотрели ранее. Кроме одной вещи - в структуре Node атрибут next теперь не просто Node, а atomic.Pointer. Нужно это для того чтобы получить синхронизацию когда в очереди 0 и 1 элемент. Без атомика одновременные push и pop будут бить в одну и ту же область памяти.</p>
  <p id="vbuF">Такую реализацию очереди можно встретить в Java - класс LinkedBlockingQueue</p>
  <p id="ruZd">Итог - мы добились того что чтения и записи у нас не зависят друг от друга.</p>
  <h2 id="pOpa">Отказываемся от Mutex совсем</h2>
  <p id="GbLO">А что насчет Lock-free реализации? Она существует и ее предложили в той же статье что я упомянул выше. </p>
  <pre id="ut8t" data-lang="go">type AtomicNode struct {
  val  int
  next atomic.Pointer[AtomicNode]
}

type LockFreeQueue struct {
  head atomic.Pointer[AtomicNode]
  tail atomic.Pointer[AtomicNode]
}

func NewLockFreeQueue() *LockFreeQueue {
  q := &amp;LockFreeQueue{}
  q.head = atomic.Pointer[AtomicNode]{}
  node := &amp;AtomicNode{}

  q.head.Store(node)
  q.tail.Store(node)

  return q
}

func (q *LockFreeQueue) Push(val int) {
  node := AtomicNode{val: val}

  for {
    curr := q.tail.Load()
    currNext := curr.next.Load()

    // поймали незавершенный push, 
    // пытаемся завершить операцию &quot;за того парня&quot;
    if currNext != nil {
      q.tail.CompareAndSwap(curr, currNext)
      continue
    }

    // подвешиваемся на то место где раньше был nil
    if curr.next.CompareAndSwap(nil, &amp;node) {
      q.tail.CompareAndSwap(curr, &amp;node)
      return
    }
  }
}

func (q *LockFreeQueue) Pop() *int {
  for {
    curr := q.head.Load()
    currNext := curr.next.Load()
    if currNext == nil {
      return nil
    }
    val := currNext.val
    if ok := q.head.CompareAndSwap(curr, currNext); ok {
      return &amp;val
    }
  }
}</pre>
  <p id="pxbg">В чем основные отличия?</p>
  <ul id="tFHJ">
    <li id="iNF0">Каждый узел очереди - обязательно спрятан за atomic.</li>
    <li id="1KpJ">Операции реализованы через бесконечный цикл (классика lock-free).</li>
    <li id="qZIv">У нас есть dummy узел. Если для классической реализации и версии с мьютексом это скорее украшательство для удобства и простоты кода. То для lock-free очереди это обязательно.</li>
    <li id="EFnb">В функции Push спрятана логика взаимопомощи потоков. Если при вставке мы видим что у tail есть ненулевой tail.next то в первую очередь мы протолкнем его, и уйдем на следующую итерацию цикла.</li>
    <li id="U7L8">В случае когда у нас честный tail и tail.next нам нужно обновить 2 атомика. И как вы помните, тут нужно быть осторожными. Поэтому мы в обязательном порядке линкуем к хвосту наш новый узел и Если операция прошла успешно, то уже по принципу best effort пытаемся подвинуть и сам tail, но уже не проверяем успешность - ведь если что, за нас это сделает другой поток.</li>
  </ul>
  <p id="Diw9">Как итог - мы снова добились того что чтения и записи у нас не зависят друг от друга. На этот раз без мьютексов вообще.</p>
  <p id="02ct">P.S. Реализация подобная этой есть в Java - ConcurrentLinkedQueue.</p>
  <h2 id="7vvv">Где можно встретить подобный код?</h2>
  <p id="2oQ2">Код который мы рассмотрели в заметке корректный, пару десятилетий назад только такие реализации и можно было встретить в реальном коде. Но время идет и не стоит на месте и сейчас уже такой код встретить практически невозможно.</p>
  <p id="ZGFC">Почему спросите вы? Дело в том что для настоящего хайлоад прода мало взять  lock-free, нужно еще и оптимизироваться под железо (которое стало и мощнее и сложнее). А это отдельный вид трюков, для каждого можно писать отдельную заметку. В своем же цикле заметок я стараюсь идти поступательно, от простого (насколько это возможно) к сложному. </p>
  <p id="hOfq">В любом случае начало положено, мы уже научились писать код без мьютексов, в следующей заметке добьем последний кусочек теории - wait free алгоритмы и структуры данных.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@abstractart/dMuRypJ1cgO</guid><link>https://teletype.in/@abstractart/dMuRypJ1cgO?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><comments>https://teletype.in/@abstractart/dMuRypJ1cgO?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart#comments</comments><dc:creator>abstractart</dc:creator><title>Пишем ThreadSafe код без Mutex или что такое этот ваш Lock-free?</title><pubDate>Thu, 03 Sep 2026 16:26:17 GMT</pubDate><description><![CDATA[В прошлых заметках мы прошлись по синтетической задаче TransferMoney вдоль и поперек. Сошлись на том что код с Mutex самая простая и понятная реализация. Но есть ли у нее альтернативы? Мы попытались написать код на атомиках, он оказался сложнее и имел баги. В этой заметке я попробую написать код без недостатков.]]></description><content:encoded><![CDATA[
  <nav>
    <ul>
      <li class="m_level_1"><a href="#YyEo">Multi-Word Compare-And-Swap</a></li>
      <li class="m_level_1"><a href="#6C0T">Show me your code</a></li>
      <li class="m_level_1"><a href="#qpok">Выводы или "Зачем так сложно?"</a></li>
    </ul>
  </nav>
  <p id="8AyH">В <a href="https://t.me/careerunderhood/458" target="_blank">прошлых заметках</a> мы прошлись по синтетической задаче TransferMoney вдоль и поперек. Сошлись на том что код с Mutex - самая простая и понятная реализация. Но есть ли у нее альтернативы? Мы попытались написать код на атомиках, он оказался сложнее и имел баги. В этой заметке я попробую написать код без найденных недостатков.</p>
  <p id="CYqQ">Ахиллесова пята нашего первого &quot;блина комом&quot; -  наличие двух атомиков. Достичь атомарности и предсказуемости над двумя участками памяти в лоб невозможно, наше текущее железо к такому просто не приспособлено. Но потребность писать код подобный нашему синтетическому примеру была есть и будет. И раз уж железяка нам не может помочь - будем что-то придумывать на уровне алгоритма самой программы. </p>
  <h2 id="YyEo">Multi-Word Compare-And-Swap</h2>
  <p id="BDMz">В 2002м году вышла статья <a href="https://timharris.uk/papers/2002-disc.pdf" target="_blank">A Practical Multi-Word Compare-and-Swap Operation by Timothy L. Harris, Keir Fraser and Ian A. Pratt. </a>В ней был предложена реализация алгоритма CASN -  гарантированное атомарное изменение N atomic переменных. Я не буду ее пересказывать, авторы постарались на славу и не только предложили алгоритм но и доказали что он рабочий и корректный (а это в теме concurrency самое главное). </p>
  <p id="JsN9">Главные мысли которые нам нужны чтобы приступить к кодированию:</p>
  <ul id="u87R">
    <li id="FQH5">Вводим примитив &quot;дескриптор&quot; - он содержит внутри себя операции которые нужно применить атомарно.</li>
    <li id="aG6D">Вместо мьютексов мы рядом с нашими атомиками хранящими ценные данные храним еще и ссылку на дескриптор - это сигнал программе что над этим кусочком данных кто-то уже начал работу.</li>
  </ul>
  <p id="0COY">Вместе эти два фактора дают нам возможность внедрить логику &quot;доталкивания&quot;. Пример: если поток №1 начал операцию и его внезапно усыпил ОС то поток №2 которому нужна та же ячейка памяти для своей операции вместо того чтобы блокироваться и засыпать поток №2 пытается применить ту самую операцию. Вспоминаем определение lock-free - <strong>код гарантирует что в случае конкурентного исполнения кто-то обязательно достигнет прогресса. </strong></p>
  <p id="ew25">Похоже что авторам удалось придумать то что мы так долго искали. Давайте попробуем приземлить идеи на практику.</p>
  <h2 id="6C0T">Show me your code</h2>
  <p id="iiUt">TLDR: Весь код в формате Go Playground доступен <a href="https://go.dev/play/p/R9tzP2Y-KvW" target="_blank">здесь</a>. Переходите и экспериментируйте. Перейдем к рассмотрению кода, он будет состоять из нескольких примитивов:</p>
  <ul id="4utE">
    <li id="oexX">Аккаунт - место где мы храним денежку.</li>
    <li id="Lqe6">Транзакция - абстракция хранящая наши операции, гарантирующиая детерминированное исполнение.</li>
    <li id="e20p">Операция - примитив хранящий данные о том в каком состоянии данные сейчас, и какой инкремент нужно произвести.</li>
  </ul>
  <p id="xhmC">Начнем со структуры account</p>
  <pre id="dqO8" data-lang="go">type Account struct {
	balance atomic.Pointer[balance]
}

type balance struct {
    amount int64
    tx *tx
}

func (b *balance) isInFlight() bool { return b.tx != nil }</pre>
  <pre id="RkUb" data-lang="go">func NewAccount(amount int64) *Account {
	a := &amp;Account{}
	a.balance.Store(&amp;balance{amount: amount})
	return a
}

func (a *Account) Balance() int64 {
	return a.readBalance().amount
}

// readBalance возвращает реальное значение ячейки,
// помогая завершить чужую транзакцию если ячейка захвачена.
func (a *Account) readBalance() *balance {
	for {
		b := a.balance.Load()
		if !b.isInFlight() {
			return b
		}
		b.tx.commit()
	}
}

// Transfer — lock-free перевод через Multi-word CAS.
func (from *Account) Transfer(to *Account, amount int64) {
	for {
		t := &amp;tx{}
		t.firstOp = newOperation(from, t, -amount)
		t.secondOp = newOperation(to, t, +amount)

		// Фиксируем порядок операций по адресу, чтобы &quot;доталкивание&quot; работало корректно:
		// без порядка A помогает B, B помогает A → бесконечная рекурсия.
		if uintptr(unsafe.Pointer(from)) &gt; uintptr(unsafe.Pointer(to)) {
			t.firstOp, t.secondOp = t.secondOp, t.firstOp
		}

		if t.commit(); t.succeeded() {
			return
		}
	}
}</pre>
  <p id="Nm0v">Что мы видим:</p>
  <ul id="taSm">
    <li id="C3UA">Бесконечный цикл внутри функции Transfer, классика non-blocking алгоритмов. Пробуем сделать что-то полезнон пока не достигнем успеха.</li>
    <li id="z7hF">Чтение баланса - не просто возвращает нам цифру - под капотом идет проверка на наличии inFlight операции, и если есть - мы ее &quot;доталкиваем&quot; как писали в статье.</li>
  </ul>
  <p id="4J1G">Перейдем теперь на уровень ниже - реализация примитива транзакция:</p>
  <pre id="VSM4" data-lang="go">type txStatus int32

const (
	txUnresolved txStatus = iota
	txSucceeded
	txFailed
)

// tx — дескриптор атомарной операции над двумя счетами.
type tx struct {
	done     atomic.Int32
	firstOp  operation
	secondOp operation
}

func (t *tx) status() txStatus { return txStatus(t.done.Load()) }
func (t *tx) resolved() bool   { return t.status() != txUnresolved }
func (t *tx) succeeded() bool  { return t.status() == txSucceeded }
func (t *tx) tryResolve(ok bool) {
	if ok {
		t.done.CompareAndSwap(int32(txUnresolved), int32(txSucceeded))
	} else {
		t.done.CompareAndSwap(int32(txUnresolved), int32(txFailed))
	}
}

func (t *tx) commit() {
	if !t.resolved() {
		t.tryResolve(t.prepare())
	}
	if t.succeeded() {
		t.applyProgress()
	} else {
		t.applyRollback()
	}
}

func (t *tx) prepare() bool {
	return t.firstOp.prepare() &amp;&amp; t.secondOp.prepare()
}

func (t *tx) applyProgress() {
	t.firstOp.tryFinalize()
	t.secondOp.tryFinalize()
}

func (t *tx) applyRollback() {
	t.firstOp.tryRestore()
	t.secondOp.tryRestore()
}</pre>
  <p id="3MhV">Что мы видим:</p>
  <ul id="7FwR">
    <li id="Fydb">Транзакция может быт в  статусе - unresolved, success, failure</li>
    <li id="72sN">Транзация состоит из двух фаз - подготовка (prepare) и фиксация (commit)</li>
    <li id="4C9q">если по каким то причинам не удалось успешно подготовить все вложенные в транзакцию операции то мы падаем и пробуем заново (см код выше)</li>
  </ul>
  <p id="mv0R">Перейдем к заключительной детали паззла - примитив операция:</p>
  <pre id="IOas" data-lang="go"> type operation struct {
	acc    *Account
	before *balance
	after  *balance
}

func newOperation(acct *Account, t *tx, delta int64) operation {
	current := acct.readBalance()
	return operation{
		acc:    acct,
		before: current,
		after:  &amp;balance{amount: current.amount + delta, tx: t},
	}
}

func (op *operation) tx() *tx                     { return op.after.tx }
func (op *operation) current() *balance           { return op.acc.balance.Load() }
func (op *operation) isClaimed(cur *balance) bool { return cur == op.after }
func (op *operation) isStale(cur *balance) bool   { return cur != op.before }
func (op *operation) tryClaim() bool {
	return op.acc.balance.CompareAndSwap(op.before, op.after)
}

func (op *operation) tryFinalize() {
	op.acc.balance.CompareAndSwap(
	    op.after, 
	    &amp;balance{amount: op.after.amount},
	)
}

func (op *operation) tryRestore() {
	op.acc.balance.CompareAndSwap(op.after, op.before)
}

func (op *operation) prepare() bool {
	for {
		if op.tx().resolved() {
			return op.tx().succeeded()
		}
		cur := op.current()
		switch {
		case op.isClaimed(cur):
			return true
		case cur.isInFlight():
			cur.tx.commit()
			continue
		case op.isStale(cur):
			return false
		default:
			op.tryClaim()
		}
	}
}</pre>
  <p id="Ymts">Что мы видим:</p>
  <ul id="sO8N">
    <li id="fyCw">Бесконечный цикл, крутимся пока либо сами не допушим транзакцию либо нам не поможет сосед. Ну или мы настолько отстали что нужно падать с false чтобы управляющий код сделал ретрай.</li>
  </ul>
  <p id="d0eR">Опытные ребята наверняка сейчас поймают эффект легкого дежавю. Код что-то напоминает. Действительно, то что у нас получилось это оптимистичный 2 phase commit. Еще раз убеждаемся что Concurrency и Distributed Systems идут рука об руку.</p>
  <p id="rvF0">Завершаем разбор исходного кода, функция main:</p>
  <pre id="akdU" data-lang="go">func main() {
    // простой пример, без concurrency
	a := NewAccount(1000)
	b := NewAccount(0)
	fmt.Printf(&quot;before: a=%d, b=%d\n&quot;, a.Balance(), b.Balance())
	a.Transfer(b, 300)
	fmt.Printf(&quot;after:  a=%d, b=%d\n&quot;, a.Balance(), b.Balance())

	// много параллельных операций, убедимся что 
	// не теряем и не делаем леньги из воздуха :)
	const (
		numAccounts   = 10
		initialFunds  = 1000
		numGoroutines = 50
		numTransfers  = 200
	)

	accounts := make([]*Account, numAccounts)
	for i := range accounts {
		accounts[i] = NewAccount(initialFunds)
	}
	wantTotal := int64(numAccounts * initialFunds)

	var wg sync.WaitGroup
	for range numGoroutines {
		wg.Add(1)
		go func() {
			defer wg.Done()
			rng := rand.New(rand.NewSource(rand.Int63()))
			for range numTransfers {
				from := accounts[rng.Intn(numAccounts)]
				to := accounts[rng.Intn(numAccounts)]
				if from == to {
					continue
				}
				from.Transfer(to, int64(rng.Intn(10)+1))
			}
		}()
	}
	wg.Wait()

	var total int64
	for _, acc := range accounts {
		total += acc.Balance()
	}
	fmt.Printf(&quot;conservation check: want=%d got=%d ok=%v\n&quot;, wantTotal, total, total == wantTotal)
}</pre>
  <h2 id="qpok">Выводы или &quot;Зачем так сложно?&quot;</h2>
  <p id="tz7E">Нам удалось достичь поставленной цели -  реализовать алгоритм без мьютексов в котором разделяемая память представлена сложнее чем единственный atomic.</p>
  <p id="prf6">Если вы дочитали до этого места у вас наверняка возник вопрос вынесенный в заголовок. И он абсолютно справедливый и здравый. Скорее всего в продакшне напрямую такой никто из нас никогда не писал и даже не видел. Скажу больше - именно в таком виде встретить реализацию структуры данных и алгоритма практически невозможно - слишком наивно и дорого по перформансу. </p>
  <p id="Z3KP">Но так или иначе идеи которые мы рассмотрели это фундамент на основе которого построены например:</p>
  <ul id="5FgM">
    <li id="Tib9">Software Transactional Memory подход который очень классно себя показал в функциональных языках программирования. Про него сделаю отдельный пост в канале или заметку.</li>
    <li id="JOQE">Lock-free структуры данных (идеи MCAS нашли отклик в реализации дереьев поиска, а идея &quot;доталкивания&quot; присутствует дефакто стандартной lock-free реализации очереди - Michael-Scott lock-free queue.</li>
  </ul>
  <p id="TWQC">На этом всё, надеюсь вам было интересно и не скучно. В следующих постах мы будем уходить подальше от академических изысков и будем искать ответы на вопросы вида:</p>
  <ul id="EgZS">
    <li id="qmbj">А что по перформансу? Lock-Free уделывает мьютексы?</li>
    <li id="9PCN">Почему мы все еще пользуемся мьютексами если можно без них?</li>
  </ul>
  <p id="8KQb">Спасибо, что дочитали, до встречи в новых постах!</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@abstractart/careerunderhood_map</guid><link>https://teletype.in/@abstractart/careerunderhood_map?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><comments>https://teletype.in/@abstractart/careerunderhood_map?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart#comments</comments><dc:creator>abstractart</dc:creator><title>🗺 Карта канала &quot;Евгений Козлов пишет про IT&quot;</title><pubDate>Mon, 17 Nov 2025 07:40:04 GMT</pubDate><description><![CDATA[Здесь ссылки на посты, статьи и Github репозитории созданные и найденные мной за время существования канала.]]></description><content:encoded><![CDATA[
  <p id="zV02">Здесь ссылки на посты, статьи и Github репозитории созданные и найденные мной за время существования <a href="https://t.me/careerunderhood" target="_blank">канала</a>.</p>
  <h2 id="WGrk"><strong>🟢 Backend, Software Engineering</strong></h2>
  <ul id="OvJZ">
    <li id="8Qw0"><a href="https://t.me/careerunderhood/211" target="_blank">Проблема развития  Junior / Middle Backend разработчиков</a></li>
    <li id="encg"><strong>Цикл</strong> постов о ресурсах по прокачке в Backend <a href="https://t.me/careerunderhood/211" target="_blank">#0</a> <a href="https://t.me/careerunderhood/212" target="_blank">#1</a> <a href="https://t.me/careerunderhood/218" target="_blank">#2</a> <a href="https://t.me/careerunderhood/220" target="_blank">#3</a></li>
    <li id="6sPv"><strong><a href="https://github.com/beagreatengineer/how-to-develop-perfect-crud" target="_blank">Хорошие практики разработки типичных back-end приложений</a></strong></li>
    <li id="aydX"><strong><a href="https://t.me/careerunderhood/145" target="_blank">Как практиковаться начинающему разработчику?</a></strong></li>
    <li id="MGAu"><a href="https://t.me/careerunderhood/175" target="_blank">Подборка ресурсов по Git</a></li>
    <li id="isNA"><a href="https://t.me/careerunderhood/151" target="_blank">Полезные технические ресурсы программисту-стартаперу</a></li>
    <li id="UavV"><a href="https://t.me/careerunderhood/173" target="_blank">Научные статьи по Computer Science</a></li>
    <li id="SYEA"><a href="https://t.me/careerunderhood/386" target="_blank">Саморазвитие в Computer Science через практику языка Си</a></li>
  </ul>
  <h2 id="t1N6"><strong>🟢 Docker, K8s, Microservices</strong></h2>
  <ul id="FCO2">
    <li id="Nw5G">Цикл постов о виртуализации и контейнерах <a href="https://t.me/careerunderhood/181" target="_blank">#1 </a><a href="https://t.me/careerunderhood/182" target="_blank">#2 </a><a href="https://t.me/careerunderhood/183" target="_blank">#3 </a><a href="https://t.me/careerunderhood/185" target="_blank">#4</a></li>
    <li id="DDH7"><strong><a href="https://t.me/careerunderhood/196" target="_blank">TOP 3 ресурса по погружению в Docker, Kubernetes, Microservices, Cloud Native.</a></strong> </li>
    <li id="q4Eh"><a href="https://t.me/careerunderhood/195" target="_blank">Подборка для изучения и практики Kubernetes</a></li>
    <li id="McOt"><a href="https://t.me/careerunderhood/282" target="_blank">What happens when ... Kubernetes edition!</a></li>
    <li id="WxB2"><a href="https://t.me/careerunderhood/240" target="_blank">Создать K8s кластер в одну команду</a></li>
    <li id="UeA1"><a href="https://t.me/careerunderhood/190" target="_blank">Монолитная архитектура</a></li>
    <li id="f9KV"><a href="https://t.me/careerunderhood/194" target="_blank">Прагматичный взгляд на Микросервисную архитектуру.</a></li>
  </ul>
  <h2 id="H2cu"><strong>🟢 Linux, DevOps, SRE</strong></h2>
  <ul id="rn5v">
    <li id="X3tJ">Подборка бесплатных ресурсов по изучению командной строки и утилит Linux <a href="https://t.me/careerunderhood/128" target="_blank">#1</a> <a href="https://t.me/careerunderhood/131" target="_blank">#2</a> <a href="https://t.me/careerunderhood/132" target="_blank">#3</a> </li>
    <li id="Qqq3"><a href="https://t.me/careerunderhood/39" target="_blank">Изучение DevOps через упражнения</a></li>
    <li id="9pzX"><a href="https://t.me/careerunderhood/142" target="_blank">Бесплатный видеокурс по основам SRE от инженеров Google Cloud</a></li>
    <li id="DdM7"><a href="https://github.com/mxssl/sre-interview-prep-guide" target="_blank">SRE Interview Preparation Guide</a></li>
    <li id="pxPx"><a href="https://t.me/careerunderhood/217" target="_blank">Обзор сетевых очередей Linux</a></li>
  </ul>
  <h2 id="1Fhc"><strong>🟢 </strong>Concurrency, Performance</h2>
  <ul id="wGEi">
    <li id="8dLt"><a href="https://t.me/careerunderhood/335" target="_blank">Concurrency. Multiprocess, Multithreading и Async</a></li>
    <li id="jYQg"><a href="https://t.me/careerunderhood/403" target="_blank">Concurrency. Synchronization и Consistency</a></li>
    <li id="s0Nl"><a href="https://t.me/careerunderhood/55" target="_blank">Продуманная оптимизация</a></li>
    <li id="V5l6"><a href="https://t.me/careerunderhood/419" target="_blank">SIMD</a></li>
  </ul>
  <h2 id="VaT6">🟢 Языки программирования</h2>
  <h3 id="g78W">🔵 Golang</h3>
  <ul id="mcDy">
    <li id="x52j"><a href="https://t.me/careerunderhood/210" target="_blank">Как я изучал язык Go</a></li>
    <li id="oxJN"><a href="https://t.me/careerunderhood/207" target="_blank">Тулинг для Go разработчика</a></li>
  </ul>
  <h3 id="FbSd">🔵 Ruby</h3>
  <ul id="IFrC">
    <li id="jZtF"><a href="https://t.me/careerunderhood/258" target="_blank">💎 Learn Ruby. My way</a></li>
    <li id="g2MF"><a href="https://teletype.in/@abstractart/learn-rails-senior" target="_blank">💎 Learn Rails. My way</a></li>
    <li id="wM3G"><a href="https://dev.to/abstractart/easy-way-to-setup-debugger-and-autocomplete-for-ruby-in-visual-studio-code-2gcc" target="_blank">💎Complete Guide to setup VS Code for Ruby on Rails (Debugger, Linter, Completion, Formatting)</a></li>
  </ul>
  <h3 id="fhgY">🔵 Python</h3>
  <ul id="EqRt">
    <li id="72nI"><a href="https://t.me/careerunderhood/261" target="_blank">🐍 Как я изучал Python. Step 1 - Изучение языка.</a></li>
    <li id="zxZd"><a href="https://t.me/careerunderhood/262" target="_blank">🐍 Как я изучал Python. Step 2 - Инструменты и библиотеки.</a></li>
    <li id="H0ZK"><a href="https://t.me/careerunderhood/267" target="_blank">🐍 Как я изучал Python. Step 3 - Concurrency</a></li>
    <li id="85vH"><a href="https://habr.com/ru/articles/773376/" target="_blank"><strong>🐍Практический гайд по процессам и потокам (и не только) в Python</strong></a></li>
    <li id="WGLX"><a href="https://t.me/careerunderhood/261" target="_blank">🐍</a> <a href="https://habr.com/ru/articles/764420/" target="_blank">Многопоточность в Python: очевидное и невероятное</a></li>
  </ul>
  <h2 id="41Ik"><strong>🟢 OLTP СУБД</strong></h2>
  <ul id="canb">
    <li id="5JZ2"><strong><a href="https://t.me/careerunderhood/160" target="_blank">Подборка книг по СУБД. От новичка до эксперта.</a></strong></li>
    <li id="PP6l"><a href="https://t.me/careerunderhood/295" target="_blank"><strong>Путеводитель по базам данных</strong></a></li>
    <li id="i2Mz"><a href="https://t.me/careerunderhood/161" target="_blank">Use The Index, Luke</a></li>
    <li id="soU6"><a href="https://t.me/careerunderhood/114" target="_blank">Подборка классных и бесплатных ресурсов по PostgreSQL</a> </li>
    <li id="BxuW"><a href="https://t.me/careerunderhood/167" target="_blank">Партиционирование в PostgreSQL</a></li>
    <li id="nFex"><a href="https://t.me/careerunderhood/284" target="_blank">Пагинация на примере PostgreSQL. От простого к сложному</a></li>
  </ul>
  <h2 id="ZL11"><strong>🟢 OLAP. Data Engineering</strong></h2>
  <ul id="83zC">
    <li id="hsgO"><a href="https://t.me/careerunderhood/379" target="_blank">Fundamentals of Data Engineering</a></li>
    <li id="ANd2"><a href="https://t.me/careerunderhood/322" target="_blank">7 баз данных за 7 недель, Clickhouse</a></li>
  </ul>
  <h2 id="7FNQ"><strong>🟢 </strong>Kafka</h2>
  <ul id="BGz4">
    <li id="W0ar"><strong><a href="https://teletype.in/@abstractart/kafka-for-novices" target="_blank">Простым языком об Apache Kafka, как, зачем и почему</a></strong></li>
    <li id="Y9Vy"><a href="https://t.me/careerunderhood/241" target="_blank">Kafka Connect Tutorial</a></li>
  </ul>
  <h2 id="KhbP"><strong>🟢 Big Tech Interview</strong></h2>
  <ul id="xgxp">
    <li id="J6jF"><a href="https://github.com/beagreatengineer/learn-system-design" target="_blank">Всё, что нужно знать о System Design для прохождения интервью и не только</a></li>
    <li id="nJQk"><a href="https://github.com/beagreatengineer/algo-interview" target="_blank">Бесплатный, минимально необходимый план подготовки к алгоритмическому интервью в компанию мечты.</a></li>
  </ul>
  <h2 id="56CO"><strong>🟢 Soft Skills, Mindset</strong></h2>
  <ul id="qExq">
    <li id="hnYG"><a href="https://t.me/careerunderhood/194" target="_blank">1 to 1 - что это, зачем, как подготовиться?</a></li>
    <li id="suZA">Какие вопросы стоит задать перед тем как принять оффер? <a href="https://t.me/careerunderhood/115" target="_blank">#1</a><a href="https://t.me/careerunderhood/51" target="_blank"> #2</a></li>
    <li id="6cOi"><a href="https://t.me/careerunderhood/90" target="_blank">Зарплатные переговоры</a></li>
    <li id="rH7f"><strong><a href="https://t.me/careerunderhood/47" target="_blank">Как правильно задавать вопросы, если ты начинающий айтишник</a></strong></li>
    <li id="vkMT"><strong><a href="https://t.me/careerunderhood/127" target="_blank">Наглядный жизненный урок</a></strong></li>
    <li id="FZG8"><a href="https://t.me/careerunderhood/237" target="_blank">Почему обучение — это новая прокрастинация</a></li>
  </ul>
  <h2 id="hPS9">🟢 Личное</h2>
  <ul id="Aj8J">
    <li id="vpaU"><a href="https://t.me/careerunderhood/257" target="_blank">Первые шаги в профессии. Ruby</a></li>
    <li id="7jSo"><a href="https://t.me/careerunderhood/266" target="_blank">Рост в Ruby до уровня Senior</a></li>
    <li id="caps"><a href="https://t.me/careerunderhood/271" target="_blank">Как я в первый раз попробовал покорить Big Tech</a></li>
    <li id="Njus"><a href="https://t.me/careerunderhood/301" target="_blank">Вторая попытка свичнуться из Ruby или как уволиться отработав 2х недели и научиться слышать себя.</a></li>
    <li id="jc0M"><a href="https://t.me/careerunderhood/303" target="_blank">Как я менял стек. Success story после череды неудач.</a></li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@abstractart/org-structure</guid><link>https://teletype.in/@abstractart/org-structure?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><comments>https://teletype.in/@abstractart/org-structure?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart#comments</comments><dc:creator>abstractart</dc:creator><title>Про оргструктуры компаний</title><pubDate>Tue, 08 Apr 2025 11:51:35 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d7/53/d7534ed2-2e11-47ee-9f30-a0dd5cf512db.png"></media:content><description><![CDATA[<img src="https://media.lpgenerator.ru/uploads/2015/09/19/image07.jpg"></img>В рамках прохождения курса &quot;Школа Технического директора&quot; от Стратоплана собрал конспект с кратким обзором основных видов организационных структур компаний.]]></description><content:encoded><![CDATA[
  <p id="5FKQ">В рамках прохождения курса &quot;Школа Технического директора&quot; от Стратоплана собрал конспект с кратким обзором основных видов организационных структур компаний.</p>
  <p id="dAul"><strong>Плоская оргструктура</strong> - хороша тем что все решения принимаются быстро а минус в том что руководитель в такой системе станет бас фактором. Так как руководить в одиночку всеми специалистами напрямую задачка для человека с  высоким уровнем компетенций.</p>
  <p id="S8Nz"><strong>Линейная / иерархическая оргструктура</strong> -  добавляет иерархии, появляется несколько уровней управления, руководители взаимозаменяемы  и все довольно прозрачно с точки зрения полномочий и ответственности. К минусам стоит отнести что скорость принятия решений низкая.</p>
  <p id="TtBk"><strong>Функциональная оргструктура</strong> - если дробить департаменты по исполняемой функции (например разработка, тестирование и эсплуатация) то получается что внутри юнита принимаются качественные решения, все сфокусированы на своей функции которую выполняют отлично, но такой подход сложно масштабировать + достижение бизнес целей с такой оргструктурой может быть затруднено, так как каждый юнит неявно работает на себя и глубокий фокус становится минусом.</p>
  <figure id="OSqW" class="m_original">
    <img src="https://media.lpgenerator.ru/uploads/2015/09/19/image07.jpg" width="600" />
  </figure>
  <p id="jwUy"><strong>Проектная оргструктура</strong>. Хороша тем, что каждый департамент сфокусирован на своем проекте и приносит конкретную измеримую пользу бизнесу. К минусам можно отнести проблему что в такой оргструктуре с ростом компании будет проблемой &quot;выравнивание&quot; культуры между проектами, выравнивание экспертизы специалистов. Сфера применимости - outsource компании.</p>
  <p id="VEEO"><strong>Матричная оргструктура</strong> - одна из самых популярных оргструктур, к ее плюсам можно отнести высокую гибкость, а также то что менеджмент уделяет внимание всем возможным аспектам (delivery / people management). Основные минусы такой оргструктуры - двоевластие и размытие ответственности.</p>
  <figure id="51vL" class="m_original">
    <img src="https://media.lpgenerator.ru/uploads/2015/09/19/image21.jpg" width="600" />
  </figure>
  <p id="bqes"><strong>Дивизионная оргструктура</strong>. Плюсы: масштабируемся быстро, просто создаем новый дивизион. Четкие уровни управления. К минусам можно отнести высокий уровень дезинтеграции и дублирование функций в каждом дивизионе.</p>
  <p id="Ry8d"></p>
  <figure id="6CKV" class="m_original">
    <img src="https://media.lpgenerator.ru/uploads/2015/09/19/image13.jpg" width="600" />
    <figcaption>Дивизионная оргструктура с делением на продукты</figcaption>
  </figure>
  <p id="jFs4"></p>
  <figure id="UeRc" class="m_original">
    <img src="https://media.lpgenerator.ru/uploads/2015/09/19/image18.jpg" width="600" />
    <figcaption>Дивизионная оргструктура с делением на рынки</figcaption>
  </figure>
  <p id="LdFS"></p>
  <figure id="BNnZ" class="m_original">
    <img src="https://media.lpgenerator.ru/uploads/2015/09/19/image17.jpg" width="600" />
    <figcaption>Дивизионная оргструктура с делением по локации</figcaption>
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@abstractart/learn-rails-senior</guid><link>https://teletype.in/@abstractart/learn-rails-senior?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><comments>https://teletype.in/@abstractart/learn-rails-senior?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart#comments</comments><dc:creator>abstractart</dc:creator><title>💎 Learn Rails from Junior to Senior. My way</title><pubDate>Tue, 23 Jan 2024 13:57:51 GMT</pubDate><description><![CDATA[<img src="https://img1.teletype.in/files/c9/a1/c9a1b36a-b8df-4a74-be1b-e3df29c6e7ee.png"></img>Продолжаю рассказывать по каким материалам учился и прокачивался в первом профессиональном стеке - Ruby &amp; Rails. Далее вы найдете огромную кучу ресурсов, которые помогли мне овладеть основными компонентами фреймворка, углубиться в них и решать задачи эффективно с точки зрения времени + писать производительный код.]]></description><content:encoded><![CDATA[
  <p id="Rp9F">Продолжаю рассказывать по каким материалам учился и прокачивался в первом профессиональном стеке - Ruby &amp; Rails. Далее вы найдете огромную кучу ресурсов, которые помогли мне </p>
  <ul id="xJEG">
    <li id="0Zf9">овладеть основными компонентами фреймворка.</li>
    <li id="YSEd">углубиться в них и решать задачи эффективно с точки зрения времени.</li>
    <li id="tvcz">писать производительный код.</li>
    <li id="ri07">вырости до уровня Senior и решать сложные задачи. </li>
  </ul>
  <p id="eDWr">Аналогичный пост по Ruby - <a href="https://t.me/careerunderhood/258" target="_blank">здесь</a>.</p>
  <h2 id="VOgS">💎Getting Started</h2>
  <p id="qBuY">У Rails просто офигенная документация, я очень часто использовал ее для решения повседневных задач. Для меня это один из примеров того как должна выглядеть дружелюбная к разработчику дока.</p>
  <ul id="gSaA">
    <li id="UxVP"><a href="http://guides.rubyonrails.org/" target="_blank">https://rusrails.ru/</a></li>
    <li id="3HKY"><a href="http://guides.rubyonrails.org/" target="_blank">http://guides.rubyonrails.org/</a></li>
  </ul>
  <p id="yEVK">Ну и само собой я не могу не отметить классику - <a href="https://www.railstutorial.org" target="_blank">туториал Майкла Хартла</a> по написанию клона твиттера</p>
  <h3 id="Ffkr"><strong>Книги</strong></h3>
  <ul id="pyyT">
    <li id="7p02"><strong><u>Agile Web Development with Rails 7</u></strong> - тот самый туториал про интернет магазин</li>
    <li id="2OfR"><u><strong>The Rails 7 Way </strong></u>- энциклопедия по Ruby on Rails</li>
  </ul>
  <h2 id="l3rW">💎 Best practices</h2>
  <p id="a1GZ">Репозитории компании разрабатывающих на Ruby в которых они ведут свои соглашения и правила по написанию кода на Ruby on Rails. </p>
  <p id="UWYi">Если вы хотите максимально быстро прокачаться лучше ресурса не найти - учитесь на чужих ошибках и впитывайте чужой опыт.</p>
  <ul id="ykr6">
    <li id="6vvY"><a href="https://github.com/ankane/production_rails" target="_blank"><strong>Best practices for running Rails in production</strong></a></li>
    <li id="JPE3"><a href="https://github.com/infinum/rails-handbook" target="_blank">Describing the development process used by the Infinum Rails Team.</a></li>
    <li id="8aYH"><a href="https://github.com/thoughtbot/guides" target="_blank">A guide for programming in style.</a></li>
  </ul>
  <h2 id="6WWB">💎 Templates</h2>
  <p id="IrN6">Для того чтобы с нуля стартовать с крутым набором инструментом и не тратить время на настройку локального окружения можно воспользоваться готовыми сборками:</p>
  <ul id="lLg8">
    <li id="9bZf"><a href="https://github.com/ledermann/docker-rails" target="_blank"><strong>https://github.com/ledermann/docker-rails</strong></a></li>
    <li id="DAx2"><a href="https://github.com/bullet-train-co/bullet_train" target="_blank">https://github.com/bullet-train-co/bullet_train</a></li>
  </ul>
  <p id="cVvH">Статья о том из каких библиотек строят проекты Evil Martians - <a href="https://evilmartians.com/chronicles/gemfile-of-dreams-libraries-we-use-to-build-rails-apps" target="_blank">Gemfile of dreams: the libraries we use to build Rails apps</a></p>
  <h1 id="8TI0">💎 Performance</h1>
  <p id="AMiF">Статьи в которых авторы ныряют вглубь фреймворка и разбираются какие же места в нем медленные и как их можно оптимизировать и ускорить.</p>
  <ul id="AaTM">
    <li id="Sr21"><a href="https://jtway.co/design-rails-json-api-with-performance-in-mind-427e0f0e6f04?gi=425ac75ebcf9" target="_blank"><strong>Design Rails JSON API with performance in mind - JTWay</strong></a></li>
    <li id="El9p"><a href="https://buttercms.com/blog/json-serialization-in-rails-a-complete-guide" target="_blank"><strong>JSON Serialization in Rails: A Complete Guide</strong></a></li>
    <li id="njSV"><a href="https://github.com/puma/puma/blob/master/docs/architecture.md" target="_blank"><strong>Архитектура сервера PUMA</strong></a></li>
    <li id="92ez"><a href="https://www.speedshop.co/2017/10/12/appserver.html" target="_blank"><strong>Configuring Puma, Unicorn and Passenger for Maximum Efficiency</strong></a></li>
    <li id="ITrJ"><a href="https://www.youtube.com/watch?v=F07xFtorXDA" target="_blank">Threads are evil. Async Ruby in action - Подробный гайд о тредах, процессах и асинхронности</a></li>
    <li id="u0y0"><a href="https://www.youtube.com/watch?v=eYhJWoxgoKA" target="_blank">Ruby CPU and Memory - Pareto principle - как профилировать производительность приложений</a></li>
  </ul>
  <h2 id="iihw"><strong>💎 Active Record</strong></h2>
  <p id="QTno">Всё что нужно знать чтобы хорошо уметь готовить ActiveRecord  и выжимать из него максимум</p>
  <ul id="LUt2">
    <li id="Hi4A">самый популярный вопрос на собеседовании  - <a href="https://scoutapm.com/blog/activerecord-includes-vs-joins-vs-preload-vs-eager_load-when-and-where" target="_blank"><strong>Making sense of ActiveRecord joins, includes, preload, and eager_load </strong></a></li>
    <li id="a2XR"><a href="https://blog.carbonfive.com/2016/11/16/rails-database-best-practices/" target="_blank">Rails Database Best Practices <strong>(About queries)</strong></a></li>
    <li id="jRlh"><a href="https://blog.cloud66.com/getting-the-most-out-of-your-database-with-ruby-on-rails/" target="_blank">Getting the Most out of your Database with Ruby on Rails <strong>(About configuration)</strong></a></li>
    <li id="ZIjE"><a href="http://thelazylog.com/understanding-locking-in-rails-activerecord/" target="_blank">Understanding<strong> Locking</strong> in Rails ActiveRecord</a></li>
  </ul>
  <p id="v25O">PostgreSQL</p>
  <ul id="fsuK">
    <li id="iCq7"><a href="https://karolgalanciak.com/blog/2018/08/19/indexes-on-rails-how-to-make-the-most-of-your-postgres-database/" target="_blank">Indexes on Rails: How to Make the Most of Your Postgres Database</a></li>
    <li id="elcX"><a href="https://pgday.ru/files/papers/27/rails.postgresql_pgday_2015.pdf" target="_blank">Как правильно готовить Ruby on Rails для работы с PostgreSQL</a></li>
  </ul>
  <h2 id="gDk3">💎 Архитектура / Паттерны</h2>
  <p id="gKpF">Rails довольно гибкий и мощный фреймворк в котором многое держится на соглашениях, и если бездумно ими пользоваться то проект может превратиться в запутанное спагетти</p>
  <ul id="tjFq">
    <li id="cWma"><a href="https://speakerdeck.com/inem/rails-hurts-because-were-using-it-wrong-lets-fix-that?slide=26" target="_blank"><strong>Rails hurts, because we&#x27;re using it wrong. Let&#x27;s fix that!</strong> - в чём проблема с архитектурой Rails?</a></li>
    <li id="REwa"><a href="https://rubyhero.dev/rails-design-patterns-the-big-picture" target="_blank"><strong>Rails design patterns</strong> - простые варианты как улучшить кодовую базу</a></li>
  </ul>
  <h2 id="N3i5">💎 Rails Security</h2>
  <p id="3lVu">Работая в production важно не только делать фичу но и писать безопасный код и защищать данные клиентов от вредоносного вмешательства.</p>
  <ul id="aQ2Z">
    <li id="QW8W"><a href="https://github.com/brunofacca/zen-rails-security-checklist" target="_blank"><strong>Checklist of security precautions for Ruby on Rails applications.</strong></a></li>
    <li id="ktja"><a href="https://rails-sqli.org/" target="_blank"><strong>SQL Injections Guide</strong></a></li>
  </ul>
  <h2 id="aYAc">💎 Блоги компаний</h2>
  <ul id="7wUn">
    <li id="zI7j"><a href="https://thoughtbot.com/blog" target="_blank">https://thoughtbot.com/blog</a></li>
    <li id="c52w"><a href="https://evilmartians.com/chronicles" target="_blank">https://evilmartians.com/chronicles</a></li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@abstractart/kafka-for-novices</guid><link>https://teletype.in/@abstractart/kafka-for-novices?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart</link><comments>https://teletype.in/@abstractart/kafka-for-novices?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=abstractart#comments</comments><dc:creator>abstractart</dc:creator><title>Простым языком об Apache Kafka, как, зачем и почему</title><pubDate>Sun, 13 Aug 2023 10:50:59 GMT</pubDate><description><![CDATA[В данной статье я хочу попытаться рассказать максимально доступно и просто о такой непростой технологии как Apache Kafka. Расскажу о том:]]></description><content:encoded><![CDATA[
  <p id="docs-internal-guid-148588fb-7fff-7854-cdf2-3d3dc9c9ddfa">В данной статье я хочу попытаться рассказать максимально доступно и просто о такой непростой технологии как Apache Kafka. Расскажу о том:</p>
  <ul id="NXrt">
    <li id="wtuc">Кем и зачем она была создана.</li>
    <li id="hKpe">Доступно рассмотрим из каких абстракций и примитивов состоит.</li>
    <li id="nPDG">За счет чего достигается высокая пропускная способность.</li>
    <li id="aCD5">Поговорим про отказоустойчивость и за счет чего она достигается.</li>
  </ul>
  <p id="RCAV">Многие новички часто задают вопрос &quot;Почему?&quot; касаемо любой новой технологии. Надеюсь что этой статьей я смогу ответить на большинство “Почему?” связанных с Apache Kafka.</p>
  <h2 id="hpeS">Краткий экскурс в историю. Причины появления Kafka</h2>
  <p id="NoLN">Apache Kafka — распределенный, реплицируемый журнал с открытым исходным кодом, разрабатываемый в рамках фонда Apache. Написан на Java и Scala. Сложно, непонятно, но пойдем дальше.</p>
  <p id="syKB">Разработан в компании LinkedIn в начале 2010х, далее был передан в фонд Apache. Сейчас авторы проекта работают в компании Confluent которую сами и основали.</p>
  <p id="sYsa">Kafka зародилась в LinkedIn как решение для задач про передаче больших объемов данных между сервисами, в частности для построения моделей машинного обучения. Классические брокеры сообщений не могли обеспечить нужную пропускную способность (throughput) и LinkedIn решил создать “свой велосипед”</p>
  <h2 id="rcTU">Из чего состоит Apache Kafka?</h2>
  <p id="kH9q">Что же придумали разработчики Kafka чтобы достигнуть высокой скорости передачи данных? Ответ: сделали брокер максимально простым и топорным: он хранит все сообщения на диске и позволяет делать с ними только 2 простых но очень эффективных с точки зрения железа операции:</p>
  <ul id="rbIr">
    <li id="4qZ3">добавить запись в конец файла</li>
    <li id="bnr2">прочитать файл с позиции X до позиции Y</li>
  </ul>
  <p id="nJVb">Как следствие, нельзя:</p>
  <ul id="raYI">
    <li id="kySy">искать, фильтровать данные на стороне кластера, только на стороне клиента</li>
    <li id="fysv">удалять данные  (за удаление данных отвечает Kafka)</li>
  </ul>
  <p id="fg7V">За чтение сообщений отвечают потребители (consumers).За отправку сообщений в Kafka отвечают производители (producers). Писатель и читатель при взаимодействии с брокером указывают обязательный параметр - topic или тема. За счет него логически идет разделение потоков данных на стороне kafka.</p>
  <h3 id="jspk">Чуть подробнее о потребителях</h3>
  <p id="Vq2z">Потребитель по порядку читает сообщения из темы и направляет брокеру Kafka специальные сообщения о том что он прочитал. В этом самом сообщении брокеру передается специальный идентификатор offset - позиция на которой остановился читатель.</p>
  <p id="tQjB">Если читатель перезапускается то при повторном подключении к брокеру он получит последнее актуальное состояние offset и продолжит читать сообщения с того места на котором остановился. Offset в каком то смысле похож на индекс в массиве. Зная индекс мы можем сразу обратиться к нужному элементу и продолжить обработку всех последующих за ним.</p>
  <h2 id="MUSY">И это всё? Просто файлы?</h2>
  <p id="wGBV">На самом деле нет. Представим, у нас есть топик X, в который пишется очень много сообщений. Мы понимаем что в одного потребителя не справляемся и хотим распаралелить процесс. Если наш topic это просто файл то читать из него несколькими потребителями становится проблематично, так как нужно потребителей синхронизировать между собой, чтобы не получилось так что каждый потребитель обрабатывает одно и то же. Синхронизация это время, и оно очень ценно, с ростом количества потребителей время синхронизации будет линейно расти.</p>
  <p id="DXpF">Что же делать? Разработчики Kafka решили разделить топик на N независимых сущностей (по факту это те же файлы) назвав партициями. Теперь нас ничто не останавливает от того чтобы создать N потребителей и быстро вычитывать данные, так как потребителей не нужно синхронизировать, каждый читает только свой кусочек, свою партицию.</p>
  <p id="uzQz">Мы достигли цели, пропускная способность за счет деления топика на партиции способна расти линейно, успевай добавлять партицию и потребителей.</p>
  <p id="iJ9n">Набор потребителей в Kafka именуется Consumer Group (группа потребителей). При добавлении или выходе потребителей из группы kafka требуется ребалансировка, чтобы либо выделить новому потребителю свою партицию либо назначить уже работающему консьюмеру партицию которую читал умерший консьюмер.</p>
  <h2 id="J3tR">Что насчет отказоустойчивости?</h2>
  <h3 id="600N">Репликация</h3>
  <p id="rdct">Что может пойти не так с Kafka брокером? Да всё что угодно, например сервер на котором он развернут может выйти из строя. В таком случае мы не сможем ни писать сообщения ни читать их, а это грусть печаль, бизнес стопорится, деньги не зарабатываются. Что спасает в таких случаях? Правильно, избыточность.</p>
  <p id="IFmB">Если базы данных умеют восстанавливаться от сбоев за счет репликации то почему бы и Kafka не перенять эту механику? Так посчитали и разработчики Kafka и добавили механизм репликации данных между узлами. Теперь вместо одного брокера получаем кластер брокеров.</p>
  <p id="D612">Если с одним из узлов что-то пойдет не так, то не только система продолжит свою работу но и потребители и производители за счет переключения чтений и записей на партиции из других реплик кластера.</p>
  <p id="l9jk">Репликация конфигурируется для каждого топика через переменную Replication Factor - сколько копий для партиции нужно иметь в кластере.</p>
  <h3 id="Bfc9">Controller</h3>
  <p id="cPKC">У одного узла в кластере Kafka есть специальная роль - controller, в его обязанности входит как раз таки на основании состояния кластера назначать лидеры для партиций топиков. Лидер партиция - партиция с которой взаимодействуют потребители и произовдители.  Аналог мастера в мире БД. Остальные копии лидер-партиции являются ведомыми и их обязанность - участвовать в репликации.</p>
  <p id="7D7l">Для того чтобы гарантировать в кластере уникальность роли controller а также хранить и отслеживать состояние брокеров кластера Kafka использует kraft / Apache Zookeper.</p>
  <h2 id="yamB">Итог</h2>
  <p id="lhCp">В данной статье я постарался описать максимально простым и понятным языком как устроен брокер Apache Kafka и почему именно так а не как иначе. Многие вещи в этой статье не удалось рассмотреть, например семантики доставки, это было сделано осознанно чтобы не растягивать статью.</p>
  <p id="kd4x">Надеюсь у меня получилось доступно донести концепции и теперь вас не испугать популяным определением которую дают Kafka на просторах сети - распределенный, реплицируемый журнал</p>

]]></content:encoded></item></channel></rss>