visual update studio remove net mvc migrations framework force first existing enable code automatic asp asp.net .net sql-server release-management

asp.net - update - remove migration



Release Management(administración de versiones), que se entrega a un subconjunto de usuarios, cómo funcionaría para un sitio web público (7)

El enfoque más simple de IMO sería separar las características por conjuntos de páginas que utilizan la seguridad para determinar qué página recibe un usuario. Más allá de la seguridad y otros medios para garantizar que un usuario no pueda acceder directamente a una página a la que no tiene acceso (mapas del sitio, etc.), puede almacenar en la base de datos la lista de características y qué usuarios o roles tienen acceso a esa característica:

Create Table Features ( Code varchar(10) not null Primary Key , StartPage nvarchar(max) not null , Description nvarchar(max) not null ) Create Table UserFeatures ( UserId ... not null , FeatureCode varchar(10) References Features ( Code ) )

Primero, la razón por la que usaría un código de texto para la clave principal de la característica y no una clave sustituta como una columna IDENTITY o guid es que solo el sistema consultará las características. Los usuarios nunca tendrán la posibilidad de agregar arbitrariamente una función. Por lo tanto, hace que su código sea mucho más limpio y fácil de leer para consultar ...Where FeatureCode = ''AdvancedEntry'' que ...Where FeatureId = 13 .

Segundo, en este enfoque, el código de las propias páginas determinará a qué procedimiento llamar. Sin embargo, eso puede significar un poco de duplicación si las características simplemente involucran campos adicionales de información.

Por lo tanto, si las funciones se integran estrechamente en la base de código y la capa de presentación existentes (lo que, por cierto, es la razón por la que el control de versiones es tan difícil), un enfoque alternativo sería almacenar el nombre del procedimiento almacenado que se debe usar en la tabla de Features . Su código consultará unirse a las tablas anteriores y devolverá el nombre del procedimiento almacenado que debe usarse. Para los argumentos, puede almacenar una llamada parametrizada en la base de datos (por ejemplo, exec Schema.Foo @bar, @gamma, @beta ) y al ejecutar la consulta, simplemente verifique si esa cadena contiene un parámetro determinado y si lo hace, agregue ese parámetro valor:

if ( ProcTemplate.Contains( "@bar") commandInstance.Parameters.AddWithValue( "@bar", barValue ); if ( ProcTemplate.Contains( "@gamma") commandInstance.Parameters.AddWithValue( "@gamma", gammaValue ); ...

Si asigna funciones a roles o grupos de usuarios, entonces deberá diseñar reglas de "eliminación" que determinen qué función se debe usar en el caso de que se devuelvan múltiples. En este enfoque, dejaría solo los procedimientos almacenados existentes si no se realizara un cambio en el esquema que requirió un cambio en el procedimiento almacenado (por ejemplo, la eliminación de una columna). Un resultado adicional de este enfoque es que podría agregar una fecha a la tabla de Features para determinar cuándo una nueva característica debería estar en línea.

Leí en algún lugar (lo siento, no recuerdo exactamente la fuente) que Facebook lanzó los martes. Lanzan las nuevas funciones a sus empleados internos primero, luego a un pequeño grupo de usuarios externos y luego a todo el mundo. Creo que Google también hace algo similar.

Trabajo principalmente con Microsoft stack (TFS para el control de código fuente, IIS, asp.net, servidor SQL con datos enormes). Los sitios que se enfrentan al público, por supuesto, tienen que estar abiertos 24x7x365. Aunque puedo imaginar liberar mi api / dll solo en uno de los servidores (en la webfarm) y probarlo, ¿cómo lo haría si hubiera DB (firmas de procesos almacenados, cambios de esquema de tabla)? Actualmente estamos versionando SPs (los nuevos serán mySPNameV2, donde como el anterior será mySPNameV1 - ambos tomarán un conjunto diferente de parámetros, por lo tanto el cambio de nombre) y las nuevas API usarán SP-V2, mientras que la antigua API continuará con SP -V1.

Veo un olor a diseño, pero ¿hay una mejor manera de hacerlo?

Edición: lanzamos el nuevo código a un solo servidor y lo probamos, lo que es difícil es cómo abstraería (puede ser que la palabra correcta no sea abstracta, pero se entiende) el esquema de la base de datos cambia de varias versiones simultáneas de la aplicación


En mi empresa casi siempre hacemos un lanzamiento importante de manera escalonada. Esto lo logramos agregando una marca para cada nueva característica en la tabla de usuarios. Por defecto, esta bandera se establece en falso; y mientras estamos implementando la función para más usuarios, simplemente cambiamos la bandera en la tabla de DB.

Esto significa que, en el nivel de la aplicación, debemos asegurarnos de que en todos los lugares verificamos esta bandera. El código push va a todos los servidores. Se realizan cambios en la base de datos; pero todavía solo algunos usuarios ven la nueva característica.

En el nivel de la base de datos, nos aseguramos de que cualquier cambio en los SP sea "compatible con versiones anteriores". Esto se hace siguiendo algunas reglas simples:

  1. Cualquier parámetro nuevo agregado al SP debe ir al final de la lista de parámetros.
  2. Los nuevos parámetros deben tener un valor predeterminado. Esto se hace para que las llamadas existentes al SP no se rompan.
  3. Si los parámetros existentes a SP tienen que cambiarse (o si el orden de los parámetros tiene que cambiar), entonces obviamente se cambian todas las llamadas al SP. Pero el SP está codificado de tal manera que admite a los usuarios que tienen la función habilitada, así como a los usuarios que no la tienen habilitada.
  4. La mayoría de los cambios en la tabla están relacionados con la adición de nuevas columnas. Es realmente raro cuando tenemos que modificar una columna existente. Todas las columnas nuevas se agregan con un valor predeterminado o permiten NULL.

En cuanto a la API, la mayoría de nuestros parámetros se pasan como objetos personalizados (estructuras). De esta manera podemos agregar un nuevo parámetro a los métodos de la API y aun así evitar que las llamadas existentes a la API se rompan.

Además, para cada usuario almacenamos la versión de API que están utilizando. Dependiendo de la versión, los usuarios van a una URL de API diferente. Básicamente, cuando los usuarios realizan la primera llamada a la API para autenticarse, pasamos una nueva URL de API (basada en la versión de API para el usuario). Para todas las llamadas subsiguientes, se supone que deben llamar a la nueva URL. Salesforce.com también sigue esto para sus llamadas a la API.


Estuve en QCon el año pasado, cuando un tipo del Equipo web de Facebook hablaba de este enfoque. Usted es 100% correcto de que habrá olores de código para implementar eso. Pero toma mucho más que solo un código huele (en realidad ellos llaman a esos olores de código Gate Keepers :).

En primer lugar, su forma de representar y almacenar datos es mucho más sofisticada, no están usando claves foráneas, ni ninguna otra característica "avanzada" de las bases de datos :), según recuerdo, sus datos no son eso "relacional" ya que todos queremos mantener el nuestro :).

Lo que puede hacer es agregar guardianes de puerta (si (el usuario es de Nueva Zelanda) {use la nueva versión con las nuevas clases} de lo contrario {se queda con la anterior}).

Probablemente pueda imaginar a qué tipo de duplicación de código llevará este líder en el caso de un modelo estructurado (por ejemplo, deberá tener 2 usuarios, 2 pedidos, 2 detalles de pedido). Dejando eso a un lado, creo que mucha regresión también tendrá lugar si no eres extremadamente cuidadoso.

Por lo que recuerdo, están lanzando mucho más a menudo que solo los martes. Creo que tienen un despliegue continuo y el código se pone en marcha todos los días, solo que limpian los antiguos controladores de la puerta / los cambios de esquema los martes, ya que cambiaron a todos los usuarios a la nueva funcionalidad para entonces.

Así que básicamente:

  1. Usted agrega portacontenedores en todas partes donde se está llevando a cabo una nueva funcionalidad.
  2. Mantienes 2 versiones del esquema.
  3. Usted agrega más y más usuarios para cambiar al nuevo.
  4. Tú limpias el viejo.
  5. Vas al punto 1 (si otro equipo no está ya allí :).

Me doy cuenta de que esta respuesta puede no ser lo suficientemente completa como para calificar para una respuesta, pero escuché esto de su tipo y pensé que vale la pena compartirla.


Realmente pensé que esta pregunta habría recibido mucha más atención. Teniendo en cuenta lo que dijo y las respuestas existentes, creo que su solución existente es la más sencilla y fácil de administrar. Como dijiste, hay algo de "olor a diseño" (me gusta esa frase) pero tiene más sentido.

Quizás vaya un paso más allá y combine algunas modificaciones leves de las sugerencias con las suyas:

  • Mantenga su convención de versión de base de datos existente
  • Libere versiones candidatas específicas para un subdominio candidato01.yoururl.com o un servidor específico si tiene una granja de servidores
  • Use una marca en su tabla de usuario / miembro para indicar a qué servidor de producción o subdominio debe dirigirse el usuario
    • Proporciona la capacidad de dirigir a algunos usuarios a los servidores candidatos de lanzamiento
    • No requiere la cantidad de código que la opción de codificación por característica mencionada en la respuesta de DK (podría ir de cualquier manera, dependiendo de qué tan específico sea el destino que quiera ser, pero creo que la ruta menos complicada sería la mejor aquí y dirigiría a los usuarios a versiones de la aplicación en lugar de intentar activar / desactivar funciones individuales por usuario)

Aparte de eso, gran pregunta! Ah, sí, y cuando todo esté listo, solo tienes que girar el interruptor del interruptor para activar el mecanismo de doble búfer y todo está listo.


Tal vez estoy simplificando aquí, pero ¿por qué no simplemente ejemplificar más la base de datos?

Cuando está haciendo un subconjunto de usuarios para "probar" ya sea interno, subconjunto de externo o el mundo, ¿no está de hecho realizando algo parecido a una prueba beta? ¿No requeriría esto una instancia diferente de la base de datos que tiene la versión beta de los procesos almacenados frente a la versión / instancia actual? Solo es cuestión de señalar cadenas de conexión en configuraciones web en ese punto, ¿no es así?

Tal vez la restricción aquí es el costo de los servidores y la "gran cantidad de datos" que hay en ellos, lo cual admito que estoy echando un vistazo o si la pregunta es realmente acerca de la versión de los procesos almacenados, etc. ¿No pueden ser controlados por la fuente y los cambios de esquema?

Probablemente he preguntado más que contestado.


Opción 1:

Un (sub) dominio para el (semi) (¿público?) Devsite. Ciertos usuarios son aceptados en el sitio web basado en una cookie de configuración manual (empleados, etc.) <pruebas privadas

El dominio principal que establece la cookie para ciertos usuarios (la cookie se establece cuando el tiempo es entre el tiempo X y el tiempo Y) <dependiendo de su tráfico. Si recibe 1000 visitantes (únicos) cada hora y desea un 10% en el dominio dev, asegúrese de que el tiempo delta sea de 6 minutos. Simplemente puede eliminar las cookies cuando desee que los usuarios sean redirigidos al sitio normal (asegúrese de redireccionar toda la URL entrante para evitar los marcadores rotos).

Opcion 2:

Balanceo de carga de un cierto porcentaje del tráfico a servidores que ejecutan una aplicación nueva.

Base de datos

1: Interactúe con la base de datos en vivo mientras desarrolla <copias de seguridad regulares + desarrolladores expertos regulares = seguro

2: observe la replicación del esclavo maestro para crear una instantánea "en vivo" de su base de datos


Si te entendí correctamente, lo que quieres es tener dos mecanismos que utilicen los mismos datos en vivo, pero diferentes versiones de API. Ahora, asumiendo que ya está trabajando con un mecanismo de doble búfer, supongo que su problema real es usar tablas en vivo durante esa transición.

La solución es que sus tablas incluyan columnas V1 y V2 (por ejemplo, una tabla de usuarios que incluirá campos de ambas API). Nota: Todos los campos no comunes deben tener valores predeterminados.

Para que esto funcione sin problemas, debe crear vistas para V1 y V2, exponiendo solo los campos relevantes para cada versión de la API, y debe desarrollar para vistas en lugar de tablas (similar al concepto de desarrollo para interfaces en lugar de implementación).

Lo mismo ocurre con los procedimientos almacenados: todos los parámetros no comunes deben tener valores predeterminados, etc.