Breve guía de entrevista para Senior PHP/GO Backend / Sudo Null IT News

¿Estás buscando un trabajo de desarrollador backend y quieres destacar en las entrevistas?

Prepararse para las preguntas de los desarrolladores backend en PHP y Go puede ser un desafío. En este artículo, repasaremos algunas preguntas sobre desarrolladores backend sénior que le ayudarán a aprobar la entrevista e impresionar a un posible empleador.

¿Por qué es importante prepararse para las preguntas de los desarrolladores de backend?

La entrevista para un desarrollador backend incluye pruebas de conocimientos técnicos, habilidades para resolver problemas y capacidad para trabajar en equipo. Las respuestas preparadas previamente a preguntas populares lo ayudarán a sentirse más seguro y a mostrar sus mejores lados.

Pregunta: Concurrencia y paralelismo, ¿cuál es mejor y por qué es necesario?

Buen artículo sobre esto tema, y a continuación está mi explicación:

Concurrencia y Concurrencia – dos enfoques diferentes pero relacionados para resolver muchos problemas. Veamos qué significan, cuáles son sus ventajas y cuándo son necesarios. Un ejemplo sobre hámsters y más.

Competencia

Competencia

Paralelismo.  PD: Además, el paralelismo puede contener competencia, por ejemplo, los hámsteres del mismo color harán fila para recibir una bellota destinada específicamente a hámsteres de un determinado color.

Paralelismo. PD: Además, el paralelismo puede contener competencia, por ejemplo, los hámsteres del mismo color harán fila para recibir una bellota destinada específicamente a hámsteres de un determinado color.

Competencia

La concurrencia es la capacidad de un sistema para realizar varias tareas en el tiempo asignado, intercambiando recursos informáticos entre ellas.

El objetivo de la competitividad – evitar interbloqueos de tareas cambiando entre ellas cuando una de las tareas se ve obligada a esperar por un recurso externo. Un ejemplo típico es el procesamiento de múltiples solicitudes de red.

Ventajas competitivas:

  1. Aumento de la productividad: Los programas competitivos pueden completar tareas más rápido haciendo un mejor uso de los recursos.

  2. Sensibilidad: La interfaz de usuario puede seguir respondiendo incluso cuando se realizan cálculos prolongados.

  3. Uso eficiente de los recursos: El tiempo de inactividad del procesador se reduce porque las tareas pueden ejecutarse mientras otras esperan a que se complete la E/S.

Defectos:

  1. Complejidad del desarrollo: Los programas concurrentes son más difíciles de desarrollar y depurar debido a posibles condiciones de carrera y puntos muertos.

  2. Imprevisibilidad: Los resultados de la ejecución pueden variar cada vez que se ejecuta.

Paralelismo

El paralelismo es la ejecución simultánea de varias tareas o partes de una tarea en diferentes núcleos de procesador. A diferencia de la concurrencia, el paralelismo implica una ejecución simultánea real.

Ventajas del paralelismo:

  1. Mayor velocidad de ejecución: La ejecución paralela le permite acelerar significativamente la ejecución de tareas divididas en partes independientes.

  2. Escalabilidad: Los programas paralelos pueden utilizar más procesadores para completar tareas.

Defectos:

  1. Requisitos de hardware: Para implementar el paralelismo se requieren sistemas multiprocesador o multinúcleo.

  2. Dificultad de sincronización: Al igual que ocurre con la concurrencia, la sincronización de tareas paralelas puede resultar compleja y requerir un esfuerzo adicional.

¿Cuándo y por qué se necesitan la concurrencia y el paralelismo?

  1. Competitividad:

    • Adecuado para tareas que requieren multitarea, como aplicaciones de servidor donde es necesario procesar muchas solicitudes simultáneamente.

    • Útil en aplicaciones donde la interfaz de usuario debe responder.

  2. Paralelismo:

    • Efectivo para tareas computacionales intensivas como procesamiento de big data, computación científica y renderizado de gráficos.

    • Adecuado para tareas que se pueden dividir fácilmente en partes independientes que se pueden ejecutar en paralelo.

La concurrencia y la concurrencia no son conceptos mutuamente excluyentes.y, a menudo, se pueden utilizar juntos. La elección entre ellos depende de la tarea específica y de los recursos disponibles. La concurrencia es más adecuada para mejorar la capacidad de respuesta y gestionar la multitarea, mientras que el paralelismo es más adecuado para maximizar el rendimiento mediante el uso de múltiples procesadores o núcleos.

Concurrencia y concurrencia en idioma GO

En el lenguaje de programación Go, la concurrencia y la concurrencia son conceptos clave que lo han hecho conocido por su eficiencia y facilidad para manejar la multitarea. Veamos cómo se implementan en Go, qué es mejor usar en varios escenarios y por qué son necesarios.

Competencia en Go

Go fue diseñado teniendo en mente la necesidad de una implementación sencilla de programas concurrentes. Los principales medios para esto:

  1. Gorrutinas:

    • Hilos ligeros administrados por el programador Go que permiten ejecutar funciones simultáneamente.

    • Creado usando una palabra clave go. Ejemplo:

      go func() {
          // код, выполняемый конкурентно
      }()
      
  2. Canales:

    • Colas sincronizadas para transferir datos entre gorutinas.

    • Creado usando una función make. Ejemplo:

      ch := make(chan int)

Beneficios de la concurrencia en Go:

  • Facilidad de uso: Go ofrece mecanismos simples e intuitivos para crear programas concurrentes.

  • Ligero: Las gorutinas son significativamente más ligeras que los subprocesos del sistema operativo, lo que le permite ejecutar miles o millones de gorutinas en un solo programa.

  • Seguridad: Los canales permiten la transferencia segura de datos entre gorutinas, evitando las complejidades de los datos compartidos y los bloqueos.

Concurrencia en Go

El paralelismo en Go se logra mediante el uso de múltiples procesadores o núcleos para ejecutar gorutinas simultáneamente. Go utiliza su propio programador para gestionar la concurrencia.

Configuración de paralelismo:

  • El número de procesadores utilizados se puede configurar mediante la función runtime.GOMAXPROCS. Ejemplo:

    runtime.GOMAXPROCS(4) // Использовать 4 процессора

Beneficios del paralelismo en Go:

  • Aumento de la productividad: La ejecución paralela de tareas puede acelerar significativamente su ejecución en sistemas multiprocesador.

  • Uso eficiente de los recursos: El programador Go distribuye eficientemente gorutinas entre los procesadores disponibles.

¿Qué es mejor usar en Go y por qué?

  1. Competitividad:

    • Adecuado para tareas en las que la capacidad de respuesta y la gestión multitarea son importantes, por ejemplo, para servidores de red donde es necesario procesar muchas solicitudes simultáneamente.

    • Útil para operaciones asincrónicas, como realizar operaciones de E/S de larga duración, sin bloquear el hilo principal de ejecución.

  2. Paralelismo:

    • Efectivo para tareas computacionalmente intensivas que se pueden dividir en partes independientes.

    • Adecuado para procesar grandes cantidades de datos, representar gráficos y realizar cálculos matemáticos complejos.

Ejemplos de uso

Competitividad:

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        fmt.Println(s)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go say("world")
    say("hello")
}

Paralelismo:

package main

import (
    "fmt"
    "runtime"
)

func fib(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        x, y = y, x+y
    }
    c <- x
}

func main() {
    runtime.GOMAXPROCS(4) // Используем 4 процессора

    c := make(chan int)
    for i := 0; i < 10; i++ {
        go fib(i, c)
    }

    for i := 0; i < 10; i++ {
        fmt.Println(<-c)
    }
}

Línea de fondo

La concurrencia y la concurrencia de Go proporcionan herramientas poderosas para crear programas eficientes y multitarea. La concurrencia se utiliza mejor para tareas que requieren ejecución asincrónica y alta capacidad de respuesta, mientras que el paralelismo es adecuado para tareas computacionalmente intensivas que se pueden ejecutar simultáneamente en múltiples procesadores.

Pregunta: ¿Herencia o composición? ¿Qué soluciona? ¿Qué prefieres?

Gran artículo sobre este tema

  • La herencia y la composición son las dos formas principales de organizar el código en la programación orientada a objetos (POO). Ambos enfoques tienen sus pros y sus contras y se aplican según el contexto y los requisitos del proyecto.

    Herencia

    La herencia se utiliza cuando existe una jerarquía de objetos y las subclases pueden lógicamente tratarse como tipos de su superclase. Esto permite que las subclases hereden las propiedades y métodos de la superclase, así como también agreguen los suyos propios.

    Cuándo utilizar la herencia:

    1. Cuando tienes una jerarquía clara “es-a”.

    2. Cuando una subclase necesita utilizar la mayor parte de la funcionalidad de la superclase.

    3. Cuando desee polimorfismo (la capacidad de utilizar objetos de subclase como objetos de superclase).

    Ejemplo PHP:

    <?php
    
    class Animal {
        public function makeSound() {
            echo "Some generic animal sound\n";
        }
    }
    
    class Dog extends Animal {
        public function makeSound() {
            echo "Woof! Woof!\n";
        }
    }
    
    class Cat extends Animal {
        public function makeSound() {
            echo "Meow! Meow!\n";
        }
    }
    
    $dog = new Dog();
    $dog->makeSound(); // Woof! Woof!
    
    $cat = new Cat();
    $cat->makeSound(); // Meow! Meow!
    ?>
    

    Composición

    La composición se utiliza cuando los objetos se componen de otros objetos sin requerir una jerarquía rígida “es-un”. La composición le permite combinar de manera flexible diferentes objetos para crear funciones complejas.

    Cuándo utilizar la composición:

    1. Cuando los objetos pueden estar compuestos de muchos componentes diferentes.

    2. Cuando se debe agregar funcionalidad a una clase sin cambiar su naturaleza básica.

    3. Cuando necesita cambiar el comportamiento del tiempo de ejecución sustituyendo componentes.

    Ejemplo PHP:

    <?php
    
    class Engine {
        public function start() {
            echo "Engine started\n";
        }
    }
    
    class Transmission {
        public function engage() {
            echo "Transmission engaged\n";
        }
    }
    
    class Car {
        private $engine;
        private $transmission;
    
        public function __construct(Engine $engine, Transmission $transmission) {
            $this->engine = $engine;
            $this->transmission = $transmission;
        }
    
        public function drive() {
            $this->engine->start();
            $this->transmission->engage();
            echo "Car is driving\n";
        }
    }
    
    $engine = new Engine();
    $transmission = new Transmission();
    $car = new Car($engine, $transmission);
    $car->drive(); // Engine started, Transmission engaged, Car is driving
    ?>
    

    conclusiones

    • Usar herenciacuando tiene una jerarquía clara y cuando los objetos pueden tratarse lógicamente como tipos de superclase.

    • Ambas clases son de la misma materia.

    • El código de antepasado es necesario o adecuado para el heredero

    • Heredero básicamente agrega lógica a la clase.

    • Usar composicióncuando necesita combinar componentes de manera flexible y cuando necesita evitar una jerarquía rígida.

    • Si necesita cambiar la lógica de la clase

Cada uno de estos enfoques tiene sus propias fortalezas y debilidades. La herencia puede conducir a jerarquías rígidas que son difíciles de cambiar, mientras que la composición permite una mayor flexibilidad para cambiar y ampliar la funcionalidad de los objetos.

Pregunta: arquitectura limpia

Lea más en este artículo

  • Arquitectura limpia Clean Architecture es un conjunto de principios y patrones de diseño de software propuestos por Robert C. Martin, también conocido como el tío Bob. El objetivo principal de Clean Architecture es crear un sistema que sea flexible, fácilmente mantenible y comprobable. Los principios fundamentales de esta arquitectura incluyen:

    1. Independencia del marco: La arquitectura no debe depender de bibliotecas o marcos específicos.

    2. Capacidad de prueba: El código debería ser fácil de probar.

    3. IU independiente: La interfaz de usuario puede cambiar sin tener que cambiar la lógica empresarial o la base de datos.

    4. Independiente de la base de datos: El sistema no debe depender de una tecnología de almacenamiento específica.

    5. Independencia de agencias externas: Los servicios y herramientas externos pueden cambiar sin cambiar la lógica empresarial.

    Capas básicas de arquitectura limpia

    1. Entidades: Contiene reglas y lógica empresarial. Son independientes de cualquier otra parte del sistema.

    2. Casos de uso: Coordinar el flujo de datos hacia y desde entidades. Definen acciones que se pueden realizar en entidades.

    3. Adaptadores de interfaz: Contiene detalles de la transformación de datos para que puedan pasarse desde y hacia casos de uso y entidades.

    4. Marcos y controladores: Contiene herramientas y bibliotecas externas, como bases de datos, interfaz de usuario, marcos web, etc.

    Ejemplo PHP

    Veamos una aplicación de gestión de tareas de ejemplo.

    1. Entidades

    <?
    // Task.php
    class Task {
        private $id;
        private $title;
        private $description;
        private $status;
    
        public function __construct($id, $title, $description, $status) {
            $this->id = $id;
            $this->title = $title;
            $this->description = $description;
            $this->status = $status;
        }
    
        // Геттеры и сеттеры
        public function getId() {
            return $this->id;
        }
    
        public function getTitle() {
            return $this->title;
        }
    
        public function getDescription() {
            return $this->description;
        }
    
        public function getStatus() {
            return $this->status;
        }
    
        public function setStatus($status) {
            $this->status = $status;
        }
    }
    

    2. Casos de uso

    <?
    // TaskRepositoryInterface.php
    interface TaskRepositoryInterface {
        public function findById($id);
        public function save(Task $task);
    }
    
    // CreateTaskUseCase.php
    class CreateTaskUseCase {
        private $taskRepository;
    
        public function __construct(TaskRepositoryInterface $taskRepository) {
            $this->taskRepository = $taskRepository;
        }
    
        public function execute($title, $description) {
            $task = new Task(null, $title, $description, 'pending');
            $this->taskRepository->save($task);
        }
    }
    
    // CompleteTaskUseCase.php
    class CompleteTaskUseCase {
        private $taskRepository;
    
        public function __construct(TaskRepositoryInterface $taskRepository) {
            $this->taskRepository = $taskRepository;
        }
    
        public function execute($id) {
            $task = $this->taskRepository->findById($id);
            $task->setStatus('completed');
            $this->taskRepository->save($task);
        }
    }
    

    3. Adaptadores de interfaz

    <?
    // InMemoryTaskRepository.php
    class InMemoryTaskRepository implements TaskRepositoryInterface {
        private $tasks = ();
    
        public function findById($id) {
            foreach ($this->tasks as $task) {
                if ($task->getId() == $id) {
                    return $task;
                }
            }
            return null;
        }
    
        public function save(Task $task) {
            if ($task->getId() === null) {
                $taskId = count($this->tasks) + 1;
                $reflection = new ReflectionClass($task);
                $idProperty = $reflection->getProperty('id');
                $idProperty->setAccessible(true);
                $idProperty->setValue($task, $taskId);
            }
            $this->tasks($task->getId()) = $task;
        }
    }
    

    4. Marcos y controladores

    <?
    // index.php
    require 'Task.php';
    require 'TaskRepositoryInterface.php';
    require 'CreateTaskUseCase.php';
    require 'CompleteTaskUseCase.php';
    require 'InMemoryTaskRepository.php';
    
    $taskRepository = new InMemoryTaskRepository();
    $createTaskUseCase = new CreateTaskUseCase($taskRepository);
    $completeTaskUseCase = new CompleteTaskUseCase($taskRepository);
    
    // Создаем задачу
    $createTaskUseCase->execute('Изучить Чистую архитектуру', 'Изучить принципы и примеры Чистой архитектуры');
    $task = $taskRepository->findById(1);
    echo 'Задача создана: ' . $task->getTitle() . PHP_EOL;
    
    // Завершаем задачу
    $completeTaskUseCase->execute(1);
    $task = $taskRepository->findById(1);
    echo 'Статус задачи: ' . $task->getStatus() . PHP_EOL;
    

    Este ejemplo demuestra los principios básicos de la arquitectura limpia al proporcionar separación de lógica, casos de uso e infraestructura, lo que hace que el código sea más flexible, comprobable y mantenible.

Pregunta: Descifrado SÓLIDO. Lo que importa aquí es cómo lo entiendes, por qué necesitas explicarlo con ejemplos.

  • SOLID es un acrónimo que significa cinco principios de programación orientada a objetos destinados a mejorar la estructura y la calidad del código. Estos principios fueron formulados por Robert C. Martin e incluyen lo siguiente:

    1. Principio de Responsabilidad Única (SRP) – Principio de Responsabilidad Única:
      Cada clase debe tener un y sólo un motivo de cambio, es decir, una sola responsabilidad.

    2. Principio abierto/cerrado (OCP): el principio de apertura/cerramiento:
      Las entidades de software deben estar abiertas a la extensión, pero cerradas a la modificación.

    3. Principio de sustitución de Liskov (LSP): principio de sustitución de Barbara Liskov:
      Los objetos de un programa deben ser reemplazados por instancias de sus subtipos sin afectar la correcta ejecución del programa.

    4. Principio de segregación de interfaces (ISP): principio de separación de interfaces:
      Los clientes no deberían depender de interfaces que no utilizan. Muchas interfaces especializadas son mejores que una general.

    5. Principio de inversión de dependencia (DIP) – Principio de inversión de dependencia:
      Las dependencias deben basarse en abstracciones, no en clases concretas.

    Veamos un ejemplo en PHP donde se aplican estos principios.

    Ejemplo de uso de SOLID en PHP

    Principio de Responsabilidad Única (SRP)

    Cada clase tiene una única responsabilidad.

    <?
    class Report {
        public function generate() {
            // Генерация отчета
        }
    }
    
    class ReportPrinter {
        public function print(Report $report) {
            // Печать отчета
        }
    }
    

    Principio abierto/cerrado (OCP)

    Las clases deben estar abiertas para extensión, pero cerradas para modificación.

    <?
    abstract class Shape {
        abstract public function area();
    }
    
    class Circle extends Shape {
        private $radius;
    
        public function __construct($radius) {
            $this->radius = $radius;
        }
    
        public function area() {
            return pi() * $this->radius * $this->radius;
        }
    }
    
    class Rectangle extends Shape {
        private $width;
        private $height;
    
        public function __construct($width, $height) {
            $this->width = $width;
            $this->height = $height;
        }
    
        public function area() {
            return $this->width * $this->height;
        }
    }
    

    Principio de sustitución de Liskov (LSP)

    Los subtipos deben reemplazar a los tipos base.

    <?
    class Bird {
        public function fly() {
            // Птица летает
        }
    }
    
    class Sparrow extends Bird {
        public function fly() {
            // Воробей летает
        }
    }
    
    class Ostrich extends Bird {
        public function fly() {
            throw new Exception("Страус не умеет летать");
        }
    }
    
    // Лучше сделать базовый класс FlyableBird и наследовать его тем, кто может летать
    class FlyableBird extends Bird {
        public function fly() {
            // Летает
        }
    }
    
    class Sparrow extends FlyableBird {
        public function fly() {
            // Воробей летает
        }
    }
    
    class Ostrich extends Bird {
        // Страус не летает, нет метода fly
    }
    

    Principio de segregación de interfaz (ISP)

    Las interfaces deben ser especializadas.

    <?
    interface Worker {
        public function work();
    }
    
    interface Eater {
        public function eat();
    }
    
    class Human implements Worker, Eater {
        public function work() {
            // Человек работает
        }
    
        public function eat() {
            // Человек ест
        }
    }
    
    class Robot implements Worker {
        public function work() {
            // Робот работает
        }
    }
    

    Principio de inversión de dependencia (DIP)

    Las dependencias deben construirse sobre abstracciones.

    <?
    interface DatabaseConnection {
        public function connect();
    }
    
    class MySQLConnection implements DatabaseConnection {
        public function connect() {
            // Подключение к MySQL
        }
    }
    
    class PasswordReminder {
        private $dbConnection;
    
        public function __construct(DatabaseConnection $dbConnection) {
            $this->dbConnection = $dbConnection;
        }
    }
    

    En este ejemplo PasswordReminder no depende de una implementación específica MySQLConnectiony desde la abstracción DatabaseConnectionlo que facilita cambiar el tipo de conexión a la base de datos.

Pregunta: DDD, ¿a qué capa pertenece el repositorio?

El diseño basado en dominios (DDD) es un enfoque de desarrollo de software que se centra en dominios empresariales complejos y su lógica. DDD tiene como objetivo crear un modelo de dominio que se utilizará en todas las partes del sistema, garantizando la coherencia y comprensibilidad del código. Los principios básicos de DDD incluyen:

  • modelo de dominio: Representa entidades y sus interacciones.

    1. Contextos gestionados (Contextos acotados): demarcación de áreas de responsabilidad para evitar la mezcla de lógica de diferentes partes del sistema.

    2. Idioma del dominio (Lenguaje ubicuo): lenguaje común utilizado por desarrolladores y expertos en negocios para describir un sistema.

    3. Agregados: Grupos de objetos relacionados que se tratan como una sola unidad.

    4. Eventos de dominio: Cambios en el estado del sistema que pueden afectar otras partes del sistema.

    Capas en DDD

    DDD a menudo implica dividir una aplicación en varias capas, cada una de las cuales realiza sus propias funciones:

    1. Interfaz de usuario (UI): Responsable de la interacción del usuario.

    2. Capa de aplicación: Coordina la ejecución de tareas, delegándolas en las partes apropiadas del sistema.

    3. Capa de dominio: Contiene lógica de negocios y modelos de dominio.

    4. Capa de infraestructura: Proporciona detalles técnicos como la interacción con la base de datos y los sistemas externos.

    Repositorio en DDD

    El repositorio pertenece a la capa de infraestructura. Su trabajo es proporcionar métodos para acceder a los datos y gestionar su estado. Los repositorios encapsulan la lógica de la base de datos proporcionando una interfaz a la capa de dominio.

    Ejemplo PHP

    Veamos un ejemplo de cómo puedes organizar un repositorio dentro de DDD en PHP.

    modelo de dominio

    <?
    class User {
        private $id;
        private $name;
        private $email;
    
        public function __construct($id, $name, $email) {
            $this->id = $id;
            $this->name = $name;
            $this->email = $email;
        }
    
        public function getId() {
            return $this->id;
        }
    
        public function getName() {
            return $this->name;
        }
    
        public function getEmail() {
            return $this->email;
        }
    }
    

    Interfaz del repositorio

    <?
    interface UserRepository {
        public function findById($id);
        public function save(User $user);
        public function delete(User $user);
    }
    

    Repositorio de implementación

    <?
    class MySQLUserRepository implements UserRepository {
        private $connection;
    
        public function __construct(PDO $connection) {
            $this->connection = $connection;
        }
    
        public function findById($id) {
            $stmt = $this->connection->prepare('SELECT * FROM users WHERE id = :id');
            $stmt->bindParam(':id', $id);
            $stmt->execute();
            $data = $stmt->fetch(PDO::FETCH_ASSOC);
    
            if ($data) {
                return new User($data('id'), $data('name'), $data('email'));
            }
    
            return null;
        }
    
        public function save(User $user) {
            if ($this->findById($user->getId())) {
                $stmt = $this->connection->prepare('UPDATE users SET name = :name, email = :email WHERE id = :id');
            } else {
                $stmt = $this->connection->prepare('INSERT INTO users (id, name, email) VALUES (:id, :name, :email)');
            }
    
            $stmt->bindParam(':id', $user->getId());
            $stmt->bindParam(':name', $user->getName());
            $stmt->bindParam(':email', $user->getEmail());
            $stmt->execute();
        }
    
        public function delete(User $user) {
            $stmt = $this->connection->prepare('DELETE FROM users WHERE id = :id');
            $stmt->bindParam(':id', $user->getId());
            $stmt->execute();
        }
    }
    

    Usando el repositorio

    <?
    $pdo = new PDO('mysql:host=localhost;dbname=mydatabase', 'username', 'password');
    $userRepository = new MySQLUserRepository($pdo);
    
    // Создание нового пользователя
    $newUser = new User(1, 'John Doe', 'john.doe@example.com');
    $userRepository->save($newUser);
    
    // Получение пользователя по ID
    $user = $userRepository->findById(1);
    echo $user->getName(); // Выведет "John Doe"
    
    // Удаление пользователя
    $userRepository->delete($user);
    

    Este ejemplo demuestra cómo se puede organizar un repositorio para administrar datos utilizando el enfoque DDD en PHP. El repositorio encapsula todos los detalles de la interacción con la base de datos, proporcionando una interfaz sencilla para la capa de dominio.

Pregunta: Cuéntenos sobre el patrón de la Bandeja de salida

  • Patrón de bandeja de salida transaccional

    El patrón Transactional Outbox está diseñado para permitir la transferencia confiable de mensajes entre microservicios en un sistema distribuido. Se utiliza para garantizar que los eventos o mensajes se envíen exactamente una vez y sólo después de una transacción exitosa en la base de datos.

    Conceptos básicos

    1. Tabla de transacciones de la bandeja de salida:

      • Todos los mensajes que deben enviarse se escriben primero en una tabla de base de datos, generalmente llamada outbox.

      • Escribir un mensaje en una mesa outbox se ejecuta dentro de la misma transacción que la transacción comercial para la cual se genera el mensaje.

    2. Bandeja de salida periódica del procesador:

      • Un proceso o subproceso independiente comprueba periódicamente la tabla outbox para mensajes nuevos.

      • Si encuentra mensajes, los procesa y los envía al sistema de destino o al intermediario de mensajes (por ejemplo, Kafka).

      • Después de un envío exitoso, los mensajes se marcan como enviados o se eliminan de la tabla. outbox.

    Implementación de ejemplo en Go

    Veamos un ejemplo del uso del patrón Transactional Outbox en Go usando una base de datos PostgreSQL y Kafka como intermediario de mensajes.

    Estructura del proyecto

    go-transactional-outbox/
    ├── main.go
    ├── outbox_processor.go
    ├── models/
    │   └── outbox.go
    ├── database/
    │   └── database.go
    └── kafka/
        └── kafka.go
    

    Archivo principal main.go

    package main
    
    import (
        "context"
        "fmt"
        "log"
        "time"
    
        "github.com/jmoiron/sqlx"
        _ "github.com/lib/pq"
    
        "go-transactional-outbox/database"
        "go-transactional-outbox/models"
        "go-transactional-outbox/outbox_processor"
    )
    
    func main() {
        db, err := database.ConnectDB()
        if err != nil {
            log.Fatal(err)
        }
    
        // Пример бизнес-логики с использованием транзакционной outbox
        ctx := context.Background()
        tx, err := db.BeginTx(ctx, nil)
        if err != nil {
            log.Fatal(err)
        }
    
        // Выполнение бизнес-операции
        _, err = tx.ExecContext(ctx, "INSERT INTO users (name) VALUES ($1)", "John Doe")
        if err != nil {
            tx.Rollback()
            log.Fatal(err)
        }
    
        // Запись события в таблицу outbox в рамках той же транзакции
        outboxEntry := models.OutboxEntry{
            ID:        "1",
            Payload:   `{"event":"UserCreated","name":"John Doe"}`,
            Status:    "pending",
            CreatedAt: time.Now(),
        }
        _, err = tx.NamedExecContext(ctx, `INSERT INTO outbox (id, payload, status, created_at) VALUES (:id, :payload, :status, :created_at)`, &outboxEntry)
        if err != nil {
            tx.Rollback()
            log.Fatal(err)
        }
    
        err = tx.Commit()
        if err != nil {
            log.Fatal(err)
        }
    
        // Запуск процессора outbox
        go outbox_processor.Start(db)
    
        // Запуск основного приложения
        fmt.Println("Application started. Press Ctrl+C to exit.")
        select {}
    }
    

    Modelos de datos models/outbox.go

    package models
    
    import "time"
    
    type OutboxEntry struct {
        ID        string    `db:"id"`
        Payload   string    `db:"payload"`
        Status    string    `db:"status"`
        CreatedAt time.Time `db:"created_at"`
        UpdatedAt time.Time `db:"updated_at"`
    }
    

    Conexión de base de datos base de datos/database.go

    package database
    
    import (
        "github.com/jmoiron/sqlx"
        _ "github.com/lib/pq"
    )
    
    func ConnectDB() (*sqlx.DB, error) {
        db, err := sqlx.Connect("postgres", "user=youruser dbname=yourdb sslmode=disable password=yourpassword")
        if err != nil {
            return nil, err
        }
        return db, nil
    }
    

    Bandeja de salida del procesador outbox_processor.go

    package outbox_processor
    
    import (
        "context"
        "log"
        "time"
    
        "github.com/jmoiron/sqlx"
        "go-transactional-outbox/models"
        "go-transactional-outbox/kafka"
    )
    
    func Start(db *sqlx.DB) {
        for {
            processOutboxEntries(db)
            time.Sleep(10 * time.Second) // Периодическое выполнение каждые 10 секунд
        }
    }
    
    func processOutboxEntries(db *sqlx.DB) {
        var entries ()models.OutboxEntry
        err := db.Select(&entries, "SELECT * FROM outbox WHERE status="pending"")
        if err != nil {
            log.Println("Error fetching outbox entries:", err)
            return
        }
    
        for _, entry := range entries {
            err = kafka.SendMessage(entry.Payload)
            if err != nil {
                log.Println("Error sending message to Kafka:", err)
                continue
            }
    
            _, err = db.Exec("UPDATE outbox SET status="processed", updated_at = $1 WHERE id = $2", time.Now(), entry.ID)
            if err != nil {
                log.Println("Error updating outbox entry:", err)
            }
        }
    }
    

    Envío de mensajes a Kafka kafka/kafka.go

    package kafka
    
    import (
        "log"
    
        "github.com/Shopify/sarama"
    )
    
    func SendMessage(message string) error {
        producer, err := sarama.NewSyncProducer(()string{"localhost:9092"}, nil)
        if err != nil {
            log.Println("Error creating Kafka producer:", err)
            return err
        }
        defer producer.Close()
    
        msg := &sarama.ProducerMessage{
            Topic: "your_topic",
            Value: sarama.StringEncoder(message),
        }
    
        _, _, err = producer.SendMessage(msg)
        if err != nil {
            log.Println("Error sending message to Kafka:", err)
            return err
        }
    
        return nil
    }
    

    Conclusión

    Este ejemplo demuestra los pasos básicos para implementar el patrón Transactional Outbox en Go usando PostgreSQL y Kafka. La implementación del patrón garantiza que los mensajes se envíen solo después de que la operación comercial principal se haya completado con éxito, garantizando así la integridad de los datos en un sistema distribuido.

Pregunta: ¿Qué es CQRS?

  • Segregación de responsabilidades de consultas de comandos (CQRS) Es un patrón arquitectónico que separa las operaciones de modificación de datos (comandos) y las operaciones de lectura de datos (consultas) en diferentes modelos. La idea principal es optimizar el rendimiento, la escalabilidad y la seguridad procesando comandos y solicitudes por separado.

    Principios básicos de CQRS:

    1. Separación de comandos y solicitudes:

      • equipos: cambios de estado del sistema (creación, actualización, eliminación de datos).

      • Peticiones: Recibe datos sin cambiar de estado.

    2. Separación del modelo de datos:

    3. Asincronía:

      • En la mayoría de los casos, los comandos y solicitudes se procesan de forma asincrónica, lo que mejora el rendimiento y la escalabilidad.

    4. Gestión de datos aislados:

    Ejemplo PHP

    Para simplificar, veremos una implementación simplificada de CQRS en PHP.

    1. Comando: Crear un nuevo usuario

    Dominio:

    <?
    // CreateUserCommand.php
    class CreateUserCommand {
        private string $name;
        private string $email;
    
        public function __construct(string $name, string $email) {
            $this->name = $name;
            $this->email = $email;
        }
    
        public function getName(): string {
            return $this->name;
        }
    
        public function getEmail(): string {
            return $this->email;
        }
    }
    

    Manipulador:

    <?
    // CreateUserHandler.php
    class CreateUserHandler {
        private UserRepository $userRepository;
    
        public function __construct(UserRepository $userRepository) {
            $this->userRepository = $userRepository;
        }
    
        public function handle(CreateUserCommand $command): void {
            $user = new User($command->getName(), $command->getEmail());
            $this->userRepository->save($user);
        }
    }
    

    2. Consulta: Obtener una lista de usuarios

    Consulta:

    <?
    // GetUsersQuery.php
    class GetUsersQuery {
        // Параметры запроса (если нужны)
    }
    

    Manipulador:

    <?
    // GetUsersHandler.php
    class GetUsersHandler {
        private UserRepository $userRepository;
    
        public function __construct(UserRepository $userRepository) {
            $this->userRepository = $userRepository;
        }
    
        public function handle(GetUsersQuery $query): array {
            return $this->userRepository->findAll();
        }
    }
    

    3. Repositorio: trabajar con datos

    Repositorio de usuarios:

    <?
    // UserRepository.php
    class UserRepository {
        private PDO $dbConnection;
    
        public function __construct(PDO $dbConnection) {
            $this->dbConnection = $dbConnection;
        }
    
        public function save(User $user): void {
            $stmt = $this->dbConnection->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
            $stmt->execute(('name' => $user->getName(), 'email' => $user->getEmail()));
        }
    
        public function findAll(): array {
            $stmt = $this->dbConnection->query("SELECT * FROM users");
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
    }
    

    4. modelo de usuario

    Usuario:

    <?
    // User.php
    class User {
        private string $name;
        private string $email;
    
        public function __construct(string $name, string $email) {
            $this->name = $name;
            $this->email = $email;
        }
    
        public function getName(): string {
            return $this->name;
        }
    
        public function getEmail(): string {
            return $this->email;
        }
    }
    

    5. Ejemplo de uso

    Ejemplo:

    <?
    // index.php
    
    // Подключение к базе данных
    $dbConnection = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    
    // Репозиторий
    $userRepository = new UserRepository($dbConnection);
    
    // Обработчик команды
    $createUserHandler = new CreateUserHandler($userRepository);
    $createUserCommand = new CreateUserCommand('John Doe', 'john@example.com');
    $createUserHandler->handle($createUserCommand);
    
    // Обработчик запроса
    $getUsersHandler = new GetUsersHandler($userRepository);
    $getUsersQuery = new GetUsersQuery();
    $users = $getUsersHandler->handle($getUsersQuery);
    
    print_r($users);
    

    Este sencillo ejemplo demuestra los principios básicos de CQRS. En un sistema real, es posible que se requieran más componentes, como gestión de eventos, procesamiento de comandos asincrónicos, varias bases de datos de comandos y consultas, etc.

Temas: Mapeo relacional de objetos (ORM), ActiveRecord, Doctrine

  • Mapeo relacional de objetos (ORM)

    ¿Qué es ORM?

    El mapeo relacional de objetos (ORM) es una técnica de programación que se utiliza para transformar datos entre tipos de sistemas incompatibles, como sistemas de programación orientados a objetos y bases de datos relacionales. ORM permite a los desarrolladores trabajar con bases de datos en términos de objetos, en lugar de directamente con tablas y consultas SQL.

    Principales ventajas de ORM:

    • Abstracción de base de datos: ORM proporciona una capa de abstracción entre la aplicación y la base de datos, lo que facilita el trabajo con datos.

    • Acelerar el desarrollo: Reduce la cantidad de código escrito, especialmente SQL repetitivo.

    • Soporte de múltiples bases de datos: Un ORM normalmente admite diferentes DBMS, lo que facilita la transferencia del código entre diferentes sistemas de bases de datos.

    Registro activo

    ¿Qué es ActiveRecord?

    ActiveRecord es un patrón de diseño utilizado en ORM para trabajar con bases de datos. En este patrón, cada tabla de base de datos está representada por una clase y las filas de la tabla están representadas por objetos de esa clase. ActiveRecord es responsable de gestionar el acceso a los datos y realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) básicas.

    Ventajas de ActiveRecord:

    1. Facilidad de uso: Los modelos ActiveRecord son intuitivos y fáciles de usar, especialmente para desarrolladores que recién comienzan con ORM (Mapeo relacional de objetos).

    2. Menos código: Se requiere menos código para operaciones CRUD básicas (crear, leer, actualizar, eliminar).

    3. Responsabilidad única: Los modelos son responsables de sus datos, lo que hace que el código sea más fácil de entender.

    4. Integración con marcos: Muchos marcos web (como Ruby on Rails) utilizan ActiveRecord, lo que facilita el desarrollo de aplicaciones.

    Desventajas de ActiveRecord:

    1. Violación del principio de responsabilidad única: Los modelos pueden sobrecargarse tanto con lógica empresarial como con lógica de acceso a datos.

    2. Escala de dificultad: En proyectos grandes, puede resultar difícil gestionar consultas complejas y lógica empresarial en un solo modelo.

    3. Soporte débil para reglas comerciales complejas: Los modelos ActiveRecord pueden dificultar la implementación de reglas comerciales complejas que no se ajustan a las operaciones CRUD estándar.

    Ejemplo de uso de ActiveRecord en PHP (usando el framework Laravel):

    <?
    // Создание новой записи в таблице users
    $user = new User;
    $user->name="John Doe";
    $user->email="john@example.com";
    $user->save();
    
    // Получение всех записей из таблицы users
    $users = User::all();
    
    // Обновление записи в таблице users
    $user = User::find(1);
    $user->email="newemail@example.com";
    $user->save();
    
    // Удаление записи из таблицы users
    $user = User::find(1);
    $user->delete();
    

    Mapeador de datos

    Data Mapper es un patrón de diseño en el que un objeto separado (mapeador) es responsable de traducir datos entre objetos en la memoria y la base de datos. Los modelos en este caso no conocen la base de datos y no contienen consultas SQL. En cambio, el mapeador gestiona esta tarea asegurando la separación de la lógica empresarial y de acceso a los datos.

    Ventajas del mapeador de datos:

    1. Clara división de responsabilidades.: Los modelos y los mapeadores tienen responsabilidades claramente separadas, lo que hace que el código sea más fácil de mantener y escalar.

    2. Flexibilidad y capacidad de prueba: El código se vuelve más flexible y fácil de probar, ya que los modelos no dependen de la base de datos.

    3. Soporte para reglas comerciales complejas: Más fácil de implementar y mantener lógica y reglas comerciales complejas.

    4. Soporte de múltiples bases de datos: Facilita el trabajo con múltiples bases de datos o almacenes de datos no estándar.

    Desventajas de Data Mapper:

    1. Dificultad creciente: El desarrollo basado en Data Mapper requiere más código y una estructura más compleja en comparación con ActiveRecord.

    2. Más tiempo de desarrollo: Se requiere más tiempo para el desarrollo y la configuración, ya que es necesario escribir y mantener los mapeadores.

    3. Umbral de entrada: A los desarrolladores que no estén familiarizados con el patrón puede resultarles difícil dominarlo.

    Ejemplo de uso de Data Mapper (Doctrine) en PHP:

    <?
    // Определение сущности (Entity)
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity
     * @ORM\Table(name="users")
     */
    class User
    {
        /** 
         * @ORM\Id
         * @ORM\Column(type="integer") 
         * @ORM\GeneratedValue 
         */
        protected $id;
    
        /** @ORM\Column(type="string") */
        protected $name;
    
        /** @ORM\Column(type="string") */
        protected $email;
    
        // Геттеры и сеттеры
        public function getId()
        {
            return $this->id;
        }
    
        public function getName()
        {
            return $this->name;
        }
    
        public function setName($name)
        {
            $this->name = $name;
        }
    
        public function getEmail()
        {
            return $this->email;
        }
    
        public function setEmail($email)
        {
            $this->email = $email;
        }
    }
    
    // Создание нового пользователя
    $user = new User();
    $user->setName('John Doe');
    $user->setEmail('john@example.com');
    
    $entityManager->persist($user);
    $entityManager->flush();
    
    // Получение всех пользователей
    $userRepository = $entityManager->getRepository(User::class);
    $users = $userRepository->findAll();
    
    // Обновление пользователя
    $user = $userRepository->find(1);
    $user->setEmail('newemail@example.com');
    $entityManager->flush();
    
    // Удаление пользователя
    $user = $userRepository->find(1);
    $entityManager->remove($user);
    $entityManager->flush();
    

    Comparación de ActiveRecord y Doctrine

    1. Enfoque de la responsabilidad:

      • Registro activo: Los modelos son responsables de los datos y la lógica empresarial.

      • Mapeador de datos: Los modelos están limpios y no contienen lógica de acceso a datos; esto lo realiza el asignador.

    2. Estructura del código:

      • Registro activo: Menos código, más simple e intuitivo, pero menos escalable.

      • Mapeador de datos: Estructura de código más compleja, pero enfoque más flexible y escalable.

    3. Escalabilidad y soporte para lógica compleja:

      • Registro activo: Más adecuado para aplicaciones simples con lógica simple.

      • Mapeador de datos: Más fácil de escalar y admitir reglas comerciales complejas.

    Donde usado:

    • Registro activo: Ruby on Rails Active Record, Laravel Eloquent.

    • Mapeador de datos: Doctrina (PHP), Hibernación (Java).

    Dependiendo de las especificaciones y requisitos del proyecto, puede elegir el patrón de diseño adecuado que mejor se adapte a sus necesidades.

Pregunta: ¿Qué es el ÁCIDO?

ACID es un acrónimo que describe cuatro propiedades fundamentales de las transacciones de bases de datos: atomicidad, coherencia, aislamiento y durabilidad. Veamos cada una de estas propiedades con más detalle y demos ejemplos en MySQL.

Atomicidad

La atomicidad garantiza que todas las operaciones dentro de una transacción se completen por completo o no se completen en absoluto. Si la transacción se cancela a mitad de camino, se descartan todos los cambios.

Ejemplo de MySQL:

  • START TRANSACTION;
    UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
    COMMIT;

    Si ocurre un error después de la primera actualización, la transacción no se completará y los cambios no se guardarán.

    Consistencia

    La coherencia garantiza que una transacción lleve la base de datos de un estado coherente a otro. Esto significa que cualquier dato escrito en la base de datos debe ser válido de acuerdo con todas las reglas definidas, incluidas restricciones, operaciones en cascada y activadores.

    Ejemplo de MySQL:

    CREATE TABLE accounts (
        account_id INT PRIMARY KEY,
        balance DECIMAL(10, 2) CHECK (balance >= 0)
    );
    START TRANSACTION;
    UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
    COMMIT;
    

    La coherencia garantiza que el saldo de la cuenta no sea negativo.

    Aislamiento

    El aislamiento garantiza que los resultados de una transacción sean invisibles para otras transacciones hasta que se complete. El nivel de aislamiento determina cómo y cuándo los cambios realizados por una transacción se vuelven visibles para otras transacciones.

    Ejemplo de MySQL:

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    START TRANSACTION;
    SELECT balance FROM accounts WHERE account_id = 1;
    /* Другие транзакции не могут изменить баланс account_id = 1 до завершения текущей транзакции /
    UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
    COMMIT;
    

    El nivel de aislamiento SERIALIZABLE proporciona el máximo aislamiento pero puede reducir el rendimiento.

    Durabilidad

    La durabilidad garantiza que los resultados de una transacción se conserven en la base de datos incluso si el sistema falla.

    Ejemplo de MySQL:

    START TRANSACTION;
    UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
    COMMIT;
    / Изменения записываются на диск и сохраняются даже в случае сбоя */
    

    Después de ejecutar COMMIT, los cambios se escribirán en el disco e incluso una falla del sistema no hará que se pierdan.

    Estos ejemplos demuestran cómo las propiedades ACID garantizan la confiabilidad y la integridad de los datos en las bases de datos MySQL.

Pregunta: ¿Qué es la idempotencia?

Idempotencia (del latín idem – lo mismo y potentia – fuerza, poder) en programación y matemáticas significa la propiedad de una operación en la que la aplicación repetida de esta operación equivale a una sola aplicación. En pocas palabras, es cuando realizar una operación varias veces seguidas no cambia el resultado en comparación con realizar la operación una vez.

En programación, la idempotencia a menudo se analiza en el contexto de las bases de datos y los métodos HTTP. Ejemplos de operaciones idempotentes incluyen la lectura de datos (solicitudes GET) y la actualización de datos (como solicitudes PUT), siempre que no cambien el estado del servidor cuando se ejecuten nuevamente.

Ejemplos de operaciones idempotentes en PHP

  1. Lectura de datos (solicitud HTTP GET)

    <?php
    // Пример GET запроса, который является идемпотентным
    $url = "
    $response = file_get_contents($url);
    $data = json_decode($response, true);
    
    print_r($data);

    En este ejemplo, realizar una solicitud a la misma URL varias veces devuelve el mismo estado de los datos sin cambiarlos.

  2. Actualización de datos (solicitud HTTP PUT)

    <?php
    $url = "
    $data = array("name" => "New Name");
    
    $options = array(
        'http' => array(
            'header'  => "Content-type: application/json\r\n",
            'method'  => 'PUT',
            'content' => json_encode($data),
        ),
    );
    
    $context  = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    
    echo $result;

    En este ejemplo, enviar una solicitud PUT con los mismos datos a la misma URL actualiza el recurso, pero ejecutar la solicitud nuevamente no cambiará el resultado porque los datos ya se actualizaron a los valores especificados.

  3. Establecer el valor de una variable

    <?php
    // Пример идемпотентного действия в PHP
    $value = 42;
    
    function setValue(&$var, $newValue) {
        $var = $newValue;
    }
    
    setValue($value, 100);
    setValue($value, 100);
    setValue($value, 100);
    
    echo $value; // 100

    En este ejemplo, ejecutar la función varias veces setValue con los mismos argumentos no cambia el resultado final después de la primera llamada.

    Las operaciones idempotentes son importantes en sistemas que necesitan ser resistentes a fallas y solicitudes de reproducción, como servicios web, API REST y sistemas transaccionales.

Pregunta: ¿Qué es una condición de carrera, usando GO como ejemplo?

Una condición de carrera en el lenguaje de programación Go es una situación en la que varias gorutinas acceden simultáneamente a datos compartidos y al menos una de ellas modifica esos datos. Esto puede provocar un comportamiento impredecible del programa, errores y resultados incorrectos.

Ejemplo de condición de carrera

Veamos un ejemplo de código donde varias gorutinas incrementan una variable común:

package main

import (
	"fmt"
	"sync"
)

var counter int

func increment(wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		counter++
	}
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(&wg)
	}

	wg.Wait()
	fmt.Println("Final Counter:", counter)
}

En este ejemplo, varias gorutinas llaman a una función. incrementlo que aumenta el valor de la variable común counter. Sin una sincronización adecuada, el resultado puede ser incorrecto debido a una condición de carrera.

Detección de condiciones de carrera

Puede utilizar una herramienta incorporada para detectar condiciones de carrera en Go race detectorque se activa al compilar o ejecutar un programa con la bandera -race.

go run -race main.go

Resolver el problema de las condiciones de carrera

Se pueden utilizar mecanismos de sincronización como mutex o canales para evitar condiciones de carrera.

Usando un mutex

Ejemplo de uso de un mutex para sincronizar el acceso a una variable:

package main

import (
	"fmt"
	"sync"
)

var counter int
var mu sync.Mutex

func increment(wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		mu.Lock()
		counter++
		mu.Unlock()
	}
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(&wg)
	}

	wg.Wait()
	fmt.Println("Final Counter:", counter)
}

En este ejemplo el mutex mu bloquea el acceso a una variable counterasegurando la atomicidad de la operación de incremento.

Usando canales

Ejemplo de uso de canales para sincronizar el acceso a una variable:

package main

import (
	"fmt"
	"sync"
)

func increment(counter chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		value := <-counter
		value++
		counter <- value
	}
}

func main() {
	var wg sync.WaitGroup
	counter := make(chan int, 1)
	counter <- 0

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(counter, &wg)
	}

	wg.Wait()
	fmt.Println("Final Counter:", <-counter)
}

En este ejemplo el canal counter Se utiliza para sincronizar el acceso a una variable. Un canal garantiza que solo una rutina acceda a una variable a la vez.

Condición de carrera en Go es un problema común en la programación multiproceso que puede provocar un comportamiento impredecible del programa. Para evitarlo es necesario utilizar mecanismos de sincronización como mutexes o canales, así como una herramienta race detector para detectar posibles condiciones de carrera.

Pregunta: SELECCIONE… PARA ACTUALIZAR

El comando MySQL SELECT ... FOR UPDATE se utiliza para recuperar filas y bloquearlas para su posterior actualización. Esto es útil en transacciones en las que desea evitar una condición de carrera asegurándose de que otras transacciones no modifiquen las filas seleccionadas antes de que se complete la transacción actual. Aquí están los detalles y ejemplos de uso:

Principio de funcionamiento

  1. Recuperar y bloquear: SELECT ... FOR UPDATE selecciona filas y establece un bloqueo en ellas, evitando que otras transacciones las cambien hasta que se complete la actual.

  2. Uso en transacciones: normalmente se utiliza dentro de una transacción para garantizar la integridad de los datos.

Ejemplo de uso

Digamos que tenemos una mesa. accounts con parlantes id, name y balance.

CREATE TABLE accounts (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    balance DECIMAL(10,2)
);
  1. Inicio de la transacción:

    START TRANSACTION;
  2. Obteniendo una fila con un candado:

    SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
    
  3. Actualización de fila:

    UPDATE accounts SET balance = balance + 100 WHERE id = 1;
    
  4. Completando la transacción:

    COMMIT;

Ejemplo completo con código

-- Начало транзакции
START TRANSACTION;

-- Выборка и блокировка строки
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;

-- Логика проверки и обновления баланса
UPDATE accounts SET balance = balance + 100 WHERE id = 1;

-- Завершение транзакции
COMMIT;

Importante a considerar

  • Cerraduras: Filas seleccionadas con SELECT ... FOR UPDATEse bloquean hasta que se completa la transacción (COMMIT o ROLLBACK).

  • Aislamiento de transacciones: El nivel de aislamiento de transacciones afecta el comportamiento de los bloqueos. Por defecto, MySQL usa el nivel de aislamiento. REPEATABLE READlo que garantiza que los datos no serán modificados por otras transacciones antes de que se complete la actual.

  • Usar con precaución: Transacciones largas con SELECT ... FOR UPDATEya que esto puede provocar bloqueos y un rendimiento deficiente.

Alternativas

  • BLOQUEAR EN MODO COMPARTIR: Un método alternativo de bloqueo de filas que permite que otras transacciones lean los datos pero no los modifiquen.

    SELECT balance FROM accounts WHERE id = 1 LOCK IN SHARE MODE;

SELECT ... FOR UPDATE es una herramienta poderosa para administrar el acceso simultáneo a los datos y su uso adecuado ayuda a evitar muchos problemas de integridad de los datos en sistemas multiusuario.

Pregunta: hay una lista interminable, cómo encontrar el valor deseado en esta lista, sin complicar el código. Escribir código en php

En PHP puedes crear un generador infinito usando generadores y buscar el valor deseado, por ejemplo así:

<?php
function infiniteGenerator() {
    $i = 0;
    while (true) {
        yield $i++;
    }
}

function findValue($generator, $target) {
    foreach ($generator as $value) {
        if ($value === $target) {
            return $value;
        }
    }
}

$targetValue = 1000;
$generator = infiniteGenerator();
$foundValue = findValue($generator, $targetValue);

echo "Found value: $foundValue\n";

En este código:

  1. Función infiniteGenerator crea un generador de números infinitos a partir de 0.

  2. Función findValue revisa los valores del generador y los compara con el valor objetivo. Una vez que se encuentra el valor, la función lo devuelve.

Este enfoque no complica el código y le permite buscar un valor en una lista infinita.

Conclusión

Un empleo exitoso como desarrollador senior de PHP y Go requiere una preparación cuidadosa y un enfoque estratégico. Actualice su currículum y cartera, amplíe sus redes profesionales, prepárese para entrevistas, investigue el mercado laboral y postule con un enfoque personalizado. Continúe aprendiendo y desarrollándose y definitivamente encontrará el trabajo de sus sueños.

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *