Biblioteca Online : ¡La Suscripción ENI por 9,90 € el primer mes!, con el código PRIMER9. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Scripting Python en Linux
  3. Simulación de la actividad
Extrait - Scripting Python en Linux Desarrolle sus herramientas de sistema
Extractos del libro
Scripting Python en Linux Desarrolle sus herramientas de sistema Volver a la página de compra del libro

Simulación de la actividad

Introducción

Durante la entrada en funcionamiento de cualquier sistema informático, el aumento de carga sigue siendo un paso fundamental. Lo bueno en la informática es que se puede simular la realidad. Y se vuelve realmente interesante cuando es posible simular docenas o incluso cientos de usuarios. Si bien estos se comportan de manera ideal, sin cometer errores (al fin y al cabo, son autómatas), al menos permite validar herramientas en un contexto óptimo. Recuerde, todo es cuestión de volumen y frecuencia.

En este capítulo, vamos a ver cómo simular una actividad informática de gestión con una pseudo empresa que practica el trading. Es un caso fácil y clásico, pero permite generar actividad en un servidor y volumen de datos.

Originalmente, este pequeño proyecto me permitiría probar las copias de seguridad RMAN de la base de datos de Oracle, sin embargo, hacer una copia de seguridad de una base de datos sin volumen no tiene es casi ningún interés.

Hacer una copia de seguridad siempre es la parte más fácil. Lo que es complicado y difícil es la restauración. Además, la recuperación de datos suele realizarse en un entorno tenso y estresante con usuarios ansiosos, de ahí el valor de entrenarse en la recuperación de bases de datos.

Además, necesitábamos simular una pequeña actividad y generar...

Descripción

La empresa "ACME Corporation" (el nombre no importa), vende productos a sus clientes y, dependiendo de las cantidades solicitadas, compra a sus proveedores el volumen necesario para cumplir con los pedidos.

Esto se conoce comúnmente como comercio.

Aquí hay tres entidades principales: Cliente/Producto/Proveedor, que se convertirán en objetos y tablas en nuestra base de datos.

El cliente realiza pedidos de cliente que, una vez entregados en su totalidad, se pueden facturar. Esto nos da un objeto "pedido cliente" que, a nivel de la base de datos, se convertirá en una tabla de encabezados y líneas de pedidos.

La empresa realiza los pedidos de los proveedores que se deben recibir. Esto da otro objeto: líneas de pedido de proveedores.

Y para saber dónde estamos para cada producto, gestionaremos stock, de ahí el último objeto: el stock.

La estructura de los datos

A continuación puede ver una descripción de la estructura de los datos.

En primer lugar, en forma de esquema:

images/Imagen_17.png

Estructura de datos

Ahora más detallado:

Las tablas Cliente/Producto/Proveedor:

Tabla

Campo

Tipo

Observación

CLIENTE

Nombre

Char(20)

Clave única

PRODUCTO

Desc

Char(30)

Clave única/Descripción del producto 

PRODUCTO

Precio

Flotante

El precio del producto

PROVDR

Nombre

Char(20)

Clave única

PROVDR

Plazo

Entero

Plazo de reaprovisionamiento

La Tabla Stock:

Tabla

Campo

Tipo

Observación

STOCK

Almacen

Char(10)

Código del almacén de mercancías

STOCK

PRO_ID

 

Identificador producto/clave extranjera

STOCK

CSTOCK

Entero

Cantidad en stock

Los pedidos de los clientes: las tablas PEDCLI y LINCLI.

Tabla PEDCLI (Pedidos de cliente):

Tabla

Campo

Tipo

Observación

PEDCLI

NUMPED

Entero

Clave única

PEDCLI

FECPED

Fecha

Por defecto fecha de creación

PEDCLI

CLI_ID

 

Identificador Cliente/Clave extranjera

PEDCLI

FACTURA

Entero

N° de factura

Tabla LINCLI (Líneas de pedido cliente):

Tabla

Campo

Tipo

Observación

LINCLI

NUMPED

Entero

N° de pedido cliente

LINCLI

NUMLIN

Entero

N° de línea

LINCLI

PRO_ID

 

Identificador producto/clave extranjera

LINCLI

NPED

Entero

Cantidad pedida

LINCLI

PRECIO

Flotante

Precio del producto

LINCLI

CENTREG

 

Cantidad entregada

LINCLI

CFAC

Entero

Cantidad facturada

La tabla LINPROV de los pedidos proveedores:

Tabla

Campo

Tipo

Observación

LINPROV

NUMPED

Entero

N° de pedido cliente

LINPROV

NUMLIN

Entero

N° de línea

LINPROV

PROV_ID

 

Identificador proveedor/clave extranjera

LINPROV

PRO_ID

 

Identificador producto/clave extranjera

LINPROV

NPED

Entero

Cantidad pedida

LINPROV

PRECIO

Flotante

Precio del producto

LINPROV

FPREVI

Fecha

Fecha de entrega prevista

LINPROV

CREC

Entero

Cantidad recibida

La gestión de los contadores requiere la gestión de una tabla adicional. Se utiliza un contador para gestionar los números de pedido de cliente, de pedidos a proveedores, de factura, etc.

Es necesario codificar una gestión de contadores, para que sea independiente de la base de datos.

Tabla

Campo

Tipo

Observación

CONTADOR

NOM

Char(10)

Nombre del contador

CONTADOR

VAL

Entero

Valor del contador

Esta estructura de datos es muy clásica para la informática...

La inicialización de la base de datos

SQLAlchemy ya se ha tratado más atrás en este libro y por esta razón, solo describiremos lo esencial.

1. definitions.py

Lo primero que se debe hacer, es describir la estructura de los datos como un objeto Python.

Entonces el primer script se llamará definitions.py y tendrá que definir la base de datos que queremos crear.

En primer lugar, es necesario realizar las importaciones de los módulos correctos. SQLAlchemy es una gran librería y una importación global (import SQLAlchemy.*) puede ser costosa en términos de memoria.

Es necesario importar:

La definición de columnas y claves extranjeras: Column ForeignKey.

Los tipos de datos: Integer, String, DateTime, Date, Float y BigInteger.

Métodos y funciones como relationship, declarative_base y create_engine.

ForeignKey: una clave extranjera, en una base de datos relacional, es una restricción que garantiza la integridad referencial entre dos tablas. La clave extranjera identifica una columna o conjunto de columnas de una tabla que se reflere a una columna o conjunto de columnas en otra tabla.

Fuente Wikipedia: https://es.wikipedia.org/wiki/Clave_for%C3%A1nea

En el lado más "práctico" aquí: la declaración de una clave extranjera de una tabla a otra garantiza que, al introducir un pedido para un cliente, este cliente existe en la tabla de clientes, de lo contrario el pedido es rechazado.

El módulo de definiciones se debe diseñar para ser importado por los otros scripts; las clases descritas necesitan ser representadas específicamente, de ahí el uso del método __repr__.

Y dado que estas son clases de Python, es muy posible definir una clase base que contenga los campos relacionados con cada clase.

A continuación el archivo definiciones.py:

#archivo: db-activity-gestion/definitions.py  
## -------------------------------------  
## DEFINICIONES DE LA BASE DE DATOS  
## -------------------------------------  
  
import os  
import sys  
from sqlalchemy import Column, ForeignKey  
from sqlalchemy import Integer, String, DateTime, Date, Float, 
BigInteger  
from sqlalchemy.ext.declarative import declarative_base  
from sqlalchemy.orm import relationship  
from sqlalchemy import create_engine  ...

La conexión a la base de datos

El script anterior solo necesita ejecutarse una vez para inicializar la base de datos.

Para el resto de la simulación, cada script será autónomo, pero funcionará de la misma forma:

  1. Conexión a la base de datos

  2. Ejecución de la función principal

Cada script necesitará una función connect() que devolverá un objeto session.

Este es el objetivo del script base.py que se potencia con una función simple para recuperar el catálogo de productos para probar:

#archivo: db-activity-gestion/base.py  
  
from sqlalchemy import create_engine, select  
from sqlalchemy.orm import sessionmaker  
  
from definitions import Base, BASE_NAME  
from definitions import Cliente, Producto, Proveed, Stock,  
PedCli, LinCli, LinProv  
  
def connect():  
   engine = create_engine(BASE_NAME, echo=False)  
   Base.metadata.bind = engine  
   DBSession = sessionmaker(bind=engine)  
   session = DBSession()  
   return session  
  
def get_catalog():  
   session = connect()  
   s = select([Producto.id])  
   prods = session.execute(s)  
   p = [ x[0] for x in prods ]  
   session.close()  ...

Los contadores

Inicialmente, esta simulación fue diseñada para la base de datos Oracle, que tiene de base una gestión de secuencia. Estas secuencias permiten obtener una serie de números únicos que se suceden, pero sin garantizar la continuidad.

Desafortunadamente, este objeto de base de datos no existe en algunas bases de datos, incluida SQLite. Sin embargo, es bastante fácil de codificar, especialmente con Python.

Para un contador, es solo un nombre y un valor; solo necesitamos una función para crear un contador, otra para obtener el siguiente valor y, dado el caso, una función para obtener el valor actual.

Cada función será autónoma y creará su propia sesión.

A continuación se muestra el script:

# archivo:  db-activity-gestion/secuencia.py  
  
## -------------------------  
## Librería de las secuencias  
## -------------------------  
  
from sqlalchemy import create_engine, select  
from sqlalchemy.orm import sessionmaker  
  
from base import connect  
from definitions import Contador  
  
def cr_Contador(nombre):  
   session = connect()  
   c = Contador()  
   c.nombre = nombre  
   c.val = 1  
   session.add(c)  
   session.commit()  ...

Los pedidos de clientes

Una vez que se inicializa la base de datos, es posible centrarse en el aspecto básico del negocio, es decir, el pedido del cliente.

Para esta simulación, este script deberá generar un pedido de cliente a partir del catálogo completo de los productos y para un cliente elegido al azar.

El script comienza recuperando el catálogo de los productos, y especialmente la información interesante, a saber, el identificador del producto y su precio.

A continuación, debe buscar un cliente y para eso, extraer los identificadores de los clientes y usar la función random.choice().

Queda por seleccionar los productos pedidos. Para hacer esto, la función random.sample() le permite extraer una muestra significativa.

En el estado actual de las cosas, el máximo de productos pedidos se calcula en función del número de productos; esto es adecuado cuando hay pocos productos. Pero si queremos un archivo de productos más imponente, será necesario limitar el número de productos pedidos, para no tener pedidos surrealistas de varios miles de líneas (aunque esto realmente exista).

Cuando tengamos el cliente y la lista de productos solicitados, podemos generar un pedido. Nos encargaremos de supervisar la escritura en la base de datos mediante una gestión de excepciones y de hacer commit solo al final. También puede hacer todo lo posible para tratar de mantener...

La entrega de los pedidos de cliente

Si disponemos de pedidos y de stock, podemos proceder a la entrega de los pedidos del cliente.

No es mucho más complicado que el resto. Hay que buscar las líneas de pedido cuya cantidad pedida menos la cantidad entregada sea mayor que cero.

La consulta SQL es mucho más sucinta que en español, porque no es ni más ni menos que:

select numped, numlin, producto_id, cped, centreg  
from LINCLI  
where cped-centreg > 0  
order by numped, numlin 

SQLAlchemy permite usar consultas directamente con el método execute(), de la sesión actual.

A partir del resultado de esta consulta, podemos trabajar en cada pedido y en cada línea de cada pedido.

Empezamos recuperando el stock de cada producto y, si el stock es mayor a la cantidad necesaria (cped - centreg), entonces podemos asignar nuestra línea y disminuir el stock.

Tenga en cuenta la solución encontrada por los desarrolladores de SQLAlchemy para expresar múltiples pruebas, en la función get_stock() acumulando los ".where".

No hay demasiado control a este nivel para el stock negativo, ni demasiado riesgo siempre que este script se ejecute de manera unitaria y con regularidad.

A continuación se muestra el script:

# archivo: db-activity-gestion/entrega_cliente.py  
  
from sqlalchemy import select  
  
from base import connect  ...

La facturación de los pedidos entregados

Los pedidos están entregados, por lo que es posible facturarlos. De hecho, se trata más de un proceso de reflexión sobre la cuestión.

El principio de este script es buscar pedidos de clientes no facturados (campo FACTURA == 0) y comprobar si se han introducido todas las líneas.

Esto es lo que hace la función get_pcl_a_facturar(); devuelve una lista de pedidos a facturar.

Esta lista permite lanzar, por cada pedido a facturar, la función factura_pcl(). Su función es crear un número de factura, lanzar un comando SQL update para actualizar el pedido y actualizar las líneas del pedido, colocando la cantidad facturada en la cantidad entregada.

A continuación se muestra el script:

#archivo: db-activity-gestion/fact_cliente.py  
  
from sqlalchemy import select  
  
from base import connect  
from definitions import Cliente, Producto, Proveed, Stock, PedCli, 
LinCli, LinProv  
  
import sequence as seq  
  
import datetime  
import random  
  
# -------------------------------------  
# Búsqueda de los pedidos facturables  
# -------------------------------------  
def get_pcl_a_facturar():  
   pcl_a_facturar = []  
   session = connect()  
   pcl...

El reaprovisionamiento del stock

Después de un tiempo, no habrá más stock y tendremos que ir a alguna parte. Aquí es donde entra el reaprovisionamiento.

En esta simulación, cada proveedor puede entregar todos los productos. En la vida real, es mucho más complejo.

Al principio, para realizar las pruebas, el tiempo de reabastecimiento se establece en 0 días para evitar esperas. En un contexto de simulación más largo, es deseable modificarlo para reproducir los flujos de un entorno en explotación.

¿Cómo se repone un stock?

Calculando la necesidad de cada producto. Esto se hace en dos pasadas:

La primera busca en los pedidos del cliente el volumen de stock necesario para cada producto, con una consulta como:

select producto_id, sum(cped) 'cnt'  
from LINCLI  
where cped-centreg > 0  
group by producto_id  
order by producto_id 

Esto significa que queremos la lista de identificadores de producto con la suma de las cantidades pedidas para las líneas de pedidos no entregadas (cped-centreg> 0) y, para hacer la vida más fácil, ordenamos y agrupamos por identificador de producto.

El resultado es la lista ordenada de necesidades (la suma de las cantidades a entregar), para cada producto.

Una vez establecida esta lista de necesidades, aún es necesario comprobar si no hay ningún pedido de proveedor activo, antes de generar...

La recepción de pedidos de proveedores

Último script para la gestión de esta pequeña comercializadora: la recepción de los pedidos de proveedores.

Solo hay que buscar las líneas de pedidos a proveedores, cuya cantidad pedida menos la cantidad recibida, sea mayor que cero (cped - crec> 0) y cuya fecha esperada sea menor o igual a la fecha actual.

De ser así, se recupera el registro de stock correspondiente y el stock se incrementa con la cantidad recibida.

En la misma transacción, actualizamos la línea de pedido de proveedor, aumentando también la cantidad recibida.

A continuación se muestra el script:

# archivo: db-activity-gestion/recept_prov.py  
  
from sqlalchemy import select  
  
from base import connect  
from definitions import Cliente, Producto, Proveed, Stock, PedCli, 
LinCli, LinProv  
  
import sequence as seq  
  
import datetime  
import random  
  
def get_cprov_ar(fecha_actual):  
   session = connect()  
   r = select([LinProv]).\  
       where(LinProv.cped-LinProv.crec > 0).\  
       where(LinProv.fprevi <= fecha_actual)  
   cprovar = session.execute(r).fetchall()  
   c = [ x for x in cprovar]  
 ...

Utilización

A continuación, un ejemplo de uso de estos scripts.

Lo primero que se debe hacer es elegir la base de datos que se desea probar.

Estos scripts se adaptan fácilmente a otra base de datos, gracias al uso de SQLAlchemy.

El segundo punto es adaptar el script populate.py al volumen deseado; por defecto, este script crea 10 clientes, productos y proveedores, lo que hace posible probar los scripts, pero incluso SQLite no responde demasiado mal en estos volúmenes.

El último punto es elegir un período: hora, día, 30 minutos. Todo depende del tiempo y del volumen que quiera generar.

A continuación, la tabla resumen de los scripts descritos anteriormente:

Scripts

Frecuencia

Observación

Populate.py

1 sola vez

Inicializa la base de datos

Cliente.py

Varias veces/periodo

La frecuencia se tiene que adaptar en función del volumen de flujo deseado

entrega_cliente.py

2 veces por periodo

 

fact_cliente.py

1 vez por periodo

Fuera del periodo de explotación

reappro_prov.py

1 vez por periodo

Fuera del periodo de explotación

recept_prov.py

1 a 2 veces por periodo

 

El período puede corresponder a un día, una hora o un minuto. Todo depende del tipo de simulación que se desee.

Una continuación se muestra una tabla de activación de los scripts, que podría corresponder a una explotación tradicional y que además, permite secuenciar bien las operaciones para no molestar demasiado al servidor.

Todas las operaciones se debían completar a las 22 h, hora a la que habíamos programado una copia de seguridad global de la base de datos.

Horario

  

  

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

  

  

scripts

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

cliente.py

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

entrega_cliente.py

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

reappro_prov.py

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

recepe_prov.py

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 ...

Resumen

Originalmente, este proyecto debería permitir probar durante un largo período de tiempo, los scripts de copia de seguridad y purgado vinculados a los mecanismos de archivado de los archivos de log de Oracle, más conocidos con los nombres de RMAN y ARCHIVELOG.

De hecho, es difícil probar este tipo de cosas sobre una base de datos estática y queríamos un entorno de laboratorio para comprender un poco mejor cómo evolucionaba el consumo de recursos y especialmente, el recurso disco.

Después de unos días, esto superó nuestras expectativas, porque también pudimos estudiar el volumen de cómo se comportaba la base de datos en función de parámetros deliberadamente reducidos, como la memoria caché asignada (SGA, System GlobalArea).

Pero esto se puede trasponer a otra base de datos como MySQL o PostgreSQL, y de esta manera permitir ver cómo reacciona con un volumen que hemos decidido y que controlamos.

Estos scripts son intencionalmente simples, incluso simplistas. Cuando haya escrito código equivalente más complejo en diferentes lenguajes, no le llevará mucho tiempo crear estos seis scripts.

Sin embargo, solo se trata de una simulación, ciertamente imperfecta, y no hay que presionar demasiado esta simulación para que se desencadenen errores o inconsistencias (como stock negativo, por ejemplo).

Por otro lado, gracias...