Selectores en Plastic SCM: Bienvenidos al lado oscuro III

17:20 0 Comments

Después de posts previos describiendo los selectores al detalle, ahora es el momento de pasar la última frontera de los selectores: selectores con múltiples repositorios.

Como ya sabéis plastic puede gestionar múltiples repositorios. Se puede mapear cada uno de los proyectos en un repositorio de plastic o utilizar prácticas más avanzadas como desarrollo orientado a componentes.

Los repositorios pueden ser totalmente independientes unos de otros, pero tambien hay situaciones en las que se pueden relacionar. Por ejemplo, podemos tener librerías compartidas que se han desarrollado y reutilizado entre diversos proyectos. Si este fuera el caso podría ser muy útil el tener un repositorios para cada proyecto y otro repositorio para las librerías.

Pero entonces, ¿cómo pueden utilizar los desarrolladores el código de las librerías y del proyecto a la vez?


Veamos un repositorio muy simple como el de la imagen. Tiene un par de ficheros y un directorio vacío. Probablemente ninguno de los proyectos que utilize sea tan simple. Supongamos que este es el repositorio denominado “proj00”.




Entonces tendremos otro repositorio que contenga la librería de código. Tiene la apariencia de la siguiente figura. Este repositorio se llama “lib_repos”.



Ahora necesitamos que el repositorio lib_repos esté disponible para los desarrolladores “proj00”.
Por favor tenga en cuenta que hemos creado un directorio vacío “lib” que se utilizará como punto de montaje en “proj00” para unir “lib_repos”.

Veamos el siguiente selector:

repository "lib_repos" mount "/lib"

path "/"

branch "/main"

checkout "/main"

repository "proj00"

path "/"

branch "/main"

checkout "/main"


Recordemos cómo funcionan las reglas del selector:
de arriba hacia abajo verá como le decimos a plastic: coge todo de lib_repos en la rama principal, pero móntalo en /lib. Después necesitará resolver la ruta /lib que se utilizará utilizando la siguiente regla del repositorio (proj00).

Si ejecuta el siguiente ls (con un modificador de formato para mostrar la información del repositorio), verá lo siguiente.

>cm ls

br:/main#1@rep:proj00@local:8084 .

br:/main#0@rep:proj00@local:8084 file00.txt

br:/main#0@rep:proj00@local:8084 file01.txt

br:/main#1@rep:lib_repos@local:8084 lib



Nota:
Utilizamos la siguiente variable de entorno PLASTIC_LS_FORMAT:
LS_FORMAT="{3}@{8,-26} {4,-5} {5}"

Bien, el directorio de la librería se está cargando desde el repositorio lib_repos.
Claro que si nos movemos dentro del directorio comprobaremos que todo lo que hay dentro está en el mismo repositorio. Mire la siguiente captura de plastic que muestra los detalles de los ficheros y directorios del repositorio.

Implementar un entorno de trabajo real
El selector de arriba mostraba cómo ir desde el desarrollo en la rama principal a un escenario de múltiples repositorios. Pero probablemente necesitará implementar toda una estrategia de ramas. Si es así debería de considerar el siguiente selector:

repository "lib_repos" mount "/lib"

path "/"

label “lib_00”

repository "proj00"

path "/"

branch "/main/task001" label “BL010”

checkout "/main/task001"



Este patrón se parece al patrón de rama por tarea en un escenario de múltiples repositorios.

Por favor tenga en cuenta que ahora estamos montando “lib_repos” como sólo lectura ya que estamos especificando una etiqueta y no una regla de desprotección.

El modo de utilizar el repositorio montado variará dependiendo de las necesidades de su proyecto. Puede ocurrir que un grupo diferente de desarrollo gestione completamente “lib_repos”, entonces se podría montar cómo de sólo lectura ya que los desarrolladores del proyecto “proj00” sólo lo utilizarán como “librería”. Lib_repos irá a través de su propio ciclo de versionado y el equipo de proj00 solo tendrá que ocuparse de cambiar la etiqueta del repositorio montado cada vez que haya una nueva versión disponible y aprobada para su proyecto.

También puede ocurrir que su equipo sea responsable de ambos repositorios. Ha decidido separarlos por estar trabajando claramente con diferentes componenetes pero sólo tienen un ciclo de versionado.

Entonces tendría sentido seguir un "ciclo de ramificación y merge" combinado para ambos repositorios. Si está trabajando en la tarea task001 entonces puede ocurrir que necesite cambiar código tanto en both lib_repos como en proj00. Estará probablemente utilizando un selector como el siguiente:


repository "lib_repos" mount "/lib"

path "/"

branch "/main/task001" label “BL010”

checkout "/main/task001"

repository "proj00"

path "/"

branch "/main/task001" label “BL010”

checkout "/main/task001"



Por favor tenga en cuenta que:
Hay dos ramas /main/task001, una en cada uno de los repositorios, y lo mismo ocurre con las etiquetas, pero se puede utilizar un sistema de nombre (utilizando el mismo nombre que yo estoy usando aquí) para reforzar su relación.

Avanzando aún más, configuracion del sistema montado
Hasta ahora hemos visto escenarios típicos. Pero, ¿qué pasa si necesitaramos montar dentro del directorio “/lib” en “proj00” algo que no está en la raíz de “lib_repos”?

Entonces utilizaremos la potencia de herencia de ramas de plastic para conseguir el resultado deseados.

Supongamos que queremos montar dentro de “lib” el contenido del directorio “/bin”. Así que queremos utilizar “/bin” como la raíz del repositorio lib_repos repository. Para facilitar las cosas asumimos que hay un subdirectorio versionado dentro de “bin”. Mire la siguiente figura.



Vamos al repositorio lib_repos y creamos una rama denominada “/main/mount-point”.
Entonces utilizamos los comandos rm y mv para configurar correctamente la rama "mount-point" como necesitamos.


>cm co .
Checking out . ... Done

>cm rm doc src
Item doc has been removed.
Item src has been removed.

>cm co bin
Checking out bin ... Done

>cm mv bin\release .
bin\release has been moved to .

>cm ls
0 07/04/08 dir br:/main/mount-point#CO CO .
0 07/04/08 dir br:/main/mount-point#CO CO bin
0 07/04/08 dir br:/main#0 release

>cm ci bin
Checking in bin ... Done
Created changeset
cs:3@rep:lib_repos@repserver:CONRAD:8084

>cm rm bin
Item bin has been removed.

>cm ls
0 07/04/08 dir br:/main/mount-point#CO CO .
0 07/04/08 dir br:/main#0 release

>cm ci .
Checking in . ... Done
Created changeset
cs:4@rep:lib_repos@repserver:CONRAD:8084

Entonces podemos configurar el siguiente selector:

repository "lib_repos" mount "/lib"
path "/?"
branch "/main"
checkout "/main"
path "/" norecursive
branch "/main/mount-point"
repository "proj00"
path "/"

branch "/main"
checkout "/main"

Por favor, tenga en cuenta lo siguiente:
Estamos utilizando la rama /main/mount-point sólo como “refactor”, pero el contenido se cargará en la rama principal del repositorio “lib_repos”. Claro que en vez de la rama principal podríamos estar utilizando otra rama.
El propósito de la rama /main/mount-point no es el de volver a integrarla en “/main” sino realizar una reorganización del proyecto. De hecho podemos incluso evitar que se integre denegando el permiso de integración.


Configuremos el siguiente selector:

repository "lib_repos" mount "/lib"

path "/?"

branch "/main/task001" label “BL010”

checkout "/main/task001"

path "/" norecursive

branch "/main/mount-point"

repository "proj00"

path "/"

branch "/main/task001" label “BL010”

checkout "/main/task001"



Y una pequeña explicación:

Cuando haga cambios en el código dentro del directorio “lib” estará colocando los cambios directamente en “/main/task001”, pero utilizando la reorganización del directorio de “/main/mount-point”.

Al volver a integrar /main/task001 en lib_repos, sólo obtendrá los cambios realizados en la tarea, y no toda la reorganización realizada dentro de “mount-point”.
Esto es por lo que la herencia de ramas de plastic es tan potente y permite implementar diversos escenarios.

Trabajo futuro
Actualmente estamos trabajando en el diseño de nuevas reglas del selector para permitir montar diversos repositorios directamente en diferentes localizaciones. Hay quien considerará la solución de la rama “mount-point” útil pero otros preferirán poder hacer algo equivalente utilizando las reglas del selector.

Estamos incluyendo nuevas reglas del selector para poder especificar cuál es el ítem raíz que se utilizará . De este modo se podría especificar que /bin es ahora raíz de lib_repos.

Además estamos trabajando en crear “directorios de selector del espacio de trabajo”, que son directorios locales que no están bajo el control de código fuente sino gestionados por la herramienta (creados en el proceso de update) y pueden contener código controlado…

Así que estamos abiertos a nuevas sugerencias… podéis contactarnos si tenéis ideas sobre la posible evolución del selector.

0 comentarios:

Estrategias de integración

10:15 0 Comments

En anteriores ocasiones hemos hablado del futuro de la integración continua, teniendo en cuenta tanto del libre de Duvall como otras posibles alternativas.

Hoy me voy a centrar precisamente en este tema: diferentes alternativas para gestionar la fase de integración.

Algunos preferirán seguir con el estilo de desarrollo en la línea principal mientras que otros optarán por una integración más controlada (y posiblemente menos ágil).

Además de la estrategia de manejo de ramas seleccionada habrá que tener una estrategia de integración.
Comencemos con el desarrollo de la línea principal. Esta es, probablemente, la técnica más conocida y extendida en control de versiones. ¿Cómo funciona? Sencillo, todas las protecciones van a la rama principal, como se puede observar en la siguiente figura:


Por supuesto que tiene sus propias ventajas y desventajas.

Ventajas de la línea principal
  • Es simple de configurar y fácil de usar
  • Cualquier sistema de control de versiones es capaz de manejar esta estrategia (al menos todos soportan el trabajo en la línea principal)

    Desventajas de la línea principal
  • Puede llevar a la inestabilidad del proyecto.
  • Los usuarios tienen la última versión después de cada commit, así que se pueden ver fácilmente infectados por un "error en movimiento".
  • Para prevenir los problemas anteriores los desarrolladores deben de hacer commit de sus cambios sólo cuando hayan comprobado el código. Esto puede llevar a que el código esté fuera del control de versiones mucho tiempo. Además los desarrolladores comienzan a utilizar el control de versiones como su único mecanismo de entrega, ya no es una herramienta para desarrollar, no pueden proteger sus cambios cada cinco minutos a no ser que estén totalmente seguros de que los nuevos cambios no romperán la estabilidad de la versión...Entonces se pierde la capacidad de utilizar el control de versiones para saber por qué el código estaba funcionando antes de un pequeño cambio que se hizo...



  • Las prácticas estándar de Integración Continua intentan resolver todos los problemas en base a dos principios:

  • Hacer commit con la mayor frecuencia posible, incluso varias veces al día, (por favor tener en cuenta: varias veces al día es menos de lo que protegerías tu código si tuvieses una rama en la que incluir incluso el código en el que no están trabajando en ese momento, simplemente para ayudarte en tu desarrollo).
  • Hay que asegurarse siempre de no romper la versión estable. Será necesario el utilizar un potente conjunto de tests para comprobar los cambios junto con los anteriores. Si se sigue un desarrollo basado en testing entonces se puede lograr este objetivo de manera más fácil.
  • Lo que obtienes por contrapartida es un proyecto que evoluciona de manera muy rápida, lo cual es genial. El problema que generalmente encuentro es que los equipos tienen dificultades por estar previniendo que no se destruya lo ya creado, y normalmente prefieren cambiar evolución por estabilidad. Por supuesto esto no es siempre así.

    Algunas alternativas
    La alternativa de la que voy a hablar es el patrón de rama por tarea. Algunos de sus aliados son: Ramificación activa, ramificación por tareas, ramificación lateral o ramificación transitoria. Es probablemente mi patrón favorito por su flexibilidad, facilidad de uso y sus beneficios asociados. Además sienta las bases de nuevas tendencias en control de versiones tales como la gestión de streams.

    ¿Qué aspecto tiene el patrón de rama por tarea? Mira la siguiente figura:

    Está la línea principal, que se ha etiquetado como “BL00” y hay una rama para una tarea denomiada task001.
    Aquí hay tres importantes asuntos a tener en cuenta:
  • Cada tarea comienza desde una línea base conocida: se elimina el problema de errores que siguen apareciendo.
  • La línea base no sólo recibirá los cambios estables. Se refuerza el principio de "nunca corromper la línea principal”.
  • Se crea un enlace explícito entre las tareas creadas en el sistema de gestión de tareas (como Jira, DevTrack, VersionOne, OnTime, Mantis, Bugzilla o un mecanismo interno). Aquí hay algo realmente importante: un desarrollador trabaja en una rama que sabe que está enlazada a una tarea, ya que tiene el nombre de esa tarea!. Así que los desarrolladores saben exactamente en que ítem están trabajando. Esto quiere decir que tanto los responsables de proyecto como los desarrolladores por fin hablan el mismo idioma!

    Aquí desaparecen todos los problemas del trabajo en la línea principal. El punto negativo es que es un poco más complicado de entender (sólo un poco...) y no lo soportan todos los controles de versiones (por eso hemos desarrollado Plastic!).

    Rápidamente estarás creando más ramas para implementar más cambios, como se puede ver en la siguiente imágen:



    Pero la cuestión no es sólo cómo y cuándo crear tus ramas, sino cómo, cuándo y quién las va a volver a integrar en la línea principal.

  • Abordaré este tema desde dos enfoques diferentes.

    Ejecutar mini-bigbangs
    Has decidido usar rama por tarea y tus colegas llevan varios días creando ramas. ¿Cuándo deberías integrarlas dentro de la línea principal?

    Yo, normalmente, te diría: no esperes más de una semana. De hecho si dejas que transcurra más de una semana entre integraciones seguramente te encuentres con uno de los mayores problemas del control de versiones: la integración big-bag.


    La integración big bang integration es un problema muy conocido y documentado, una de las “raíces de la maldad” en desarrollo de software, pero ahí sigue esperando nuevas víctimas.

    ¿Cómo funciona? Digamos que estás trabajando en un proyecto de 6 meses. Entonces planeas 2 fases y divides el equipo de subequipos. Trabajan en características diferentes e integrarán su trabajo una semana antes de cada fase. ¿Suena familiar? Bien, espero que no ya que es una receta estupenda para el desastre!

    Si sigues este enfoque en lugar de una semana, probablemente tu primera “integración” tardará mucho más, y no es necesario decir que la segunda no será mejor… Obtendrás una gran cantidad de software que nunca antes se había integrado y necesitarás estar seguro de que funciona… en una semana! Una locura.

    Esto responde al porqué es una buena idea reducir el tiempo entre integraciones. Yo diría que la frecuencia de integración tiene que ser inversamente proporcional a la cantidad de trabajo que tu equipo pueda realizar. Me explico, si estás trabajando en un pequeño grupo de 5 desarrolladores, puede que te vaya bien ejecutando una integración a la semana, pero tan pronto como vayas creciendo, tal vez tengas que integrar con más frecuencia, incluso más de una vez al día.


    Lo que se intenta evitar son los problemas de integración. Es exactamente la misma regla que introduce la integración continua: si algo puede dar errores...¡hacerlo con la mayor frecuencia posible para reducir el riesgo!

    Y recuerda que el problema de la integración no debería ser el integrar ficheros y directorios, si esto es así: ¡cambia a otro control de versiones! El problema es, que incluso cuando el código se compila correctamente, puede destruir muchos tests o esconder un inesperado número de errores críticos. Como he mencionado, el problema no debería ser la integración. Estuvimos trabajando con una empresa que ejecutaba integraciones semanales con CVS. . Ellos necesitaban muchas horas para integrar el código. Entonces cambiaron a Plastic y emplean el tiempo que ahorraron en ejecutar un test suite completo. Esta es la fortaleza de la integración, el estar seguro de que tu código funciona como se espera, y no estando preocupado de cómo integrarlo.


    Habiendo dicho esto veamos una pequeña iteración de integración:



    Yo las llamo “mini-bigbangs” porque en realidad son integraciones big bang: coges un número de cambios separados del código desarrollado y los integras. La clave es que son tan pequeños que el germen del “big-bang” no se muestra.

    ¿Entonces por qué ejecutamos aún este enfoque, por qué no vamos directamente a la pura integración continua en la línea principal? Bien, el tener tu propia rama para cada tarea es una gran idea, te permite tener un espacio para hacer cambios y previene que la línea principal se corrompa, aparte de todas las demás ventajas del modelo rama por tarea.

    Entonces continúas trabajando y tu desarrollo tendrá el aspecto de la siguiente figura:



    Hasta que realizas otra integración y:


    ¿Está ahora más claro? Recuerda que los tests son la piedra angular de este enfoque de integración. Debes verificar que se cumple un subset del test completo para ser ejecutado y terminar así la tarea (puedes instalar un servidor para hacerlo, buscando nuevas tareas acabadas, descargando, compilando y comprobándolas unitariamente una vez que están marcadas y terminadas).

    Existen algunas advertencias a tener en cuenta: he estado ejecutando este tipo de integración con gran éxito en diferentes proyectos, pero puede pasar que, por alguna razón, la integración llegue a ser un auténtico infierno. En mi caso ocurrió, fue a causa de los tests: se hicieron tan grandes que la comprobación de cada tarea en la integración (es algo que se tiene que hacer!) era demasiado costosa. Entonces la integración empezó a crecer más y más, y se convirtió en un auténtico dolor, como dije antes.


    Por rfavor ten en cuenta que el verdadero problema no está en el campo del control de versiones sino en el del test: puede que los test sean frágiles o que te lleven mucho tiempo, y se tienen que fijar de alguna manera. Intentemos buscar algunas alternativas.

    La primera se puede conocer como integración escalonada: el desarrollador, ejecutando el “mini big-bang”, decide agrupar las tareas juntas, integrarlas en diferentes ramas de integración intermedias, testarlas en paralelo e integrarlas todas juntas en la rama principal.



    Recuerda que aquí la clave está en no apresurarse demasiado con la integración, la cual es ya muy rápida de por si, pero hay que ser capaz de ejecutar el mayor número posible de test en paralelo mientras se integran las ramas.

    Integración rama por tarea e integración continua
    Si estuvieras en la situación de tener una enorme batería de tests y necesitaras impedir cualquier retraso en las integraciones, o quisieras evitar el papel del integrador (en el enfoque más ágil) o buscar un ciclo de trabajo más rápido, podrías combinar la técnica de rama por tarea con el enfoque de integración continua.



    Con este enfoque cada desarrollador integra su propia rama en la línea principal. Recordemos que primero debe (como se muestra en la segunda rama) integrar hacia abajo, desde la rama principal, para coger los últimos cambios, después se deben ejecutar los test (usando un servidor de integración contínua, por ejemplo) y sólo cuando estos se hayan pasado, integrar los cambios en la principal (será sólo un “copiar integración”).

    Entonces se lanzará una versión en la rama principal, incluso una larga, y todo va bien, con lo que se crea una nueva versión del software.



    Integración continua con ramas de versiones

    Hay más opciones. Supongamos que cada desarrollador integra sus cambios en una "rama de integración" como muestra la siguiente figura:



    La rama de versión se puede utilizar para realizar integraciones durante el día y luego por la noche dejar funcionando un proceso de testing automatizado. Si todo funciona como debería la rama de la integración con los cambios realizados durante el último día se puede integrar automáticamente en la rama principal, se crea una nueva versión y se utiliza como línea base de desarrollo durante el siguiente día.



    En resumen
    Como podéis ver hay varias alternativas al realizar integraciones, desde las más simples a las más sofisticadas. Yo siempre sigo el principio de hacer las cosas del modo más sencillo posible. Por supuesto que esta no es siempre la mejor opción así que hay que ver cual es la mejor alternativa para cada equipo.

    ¿Quieres intentarlo tú mismo
    Si quieres intentarlo puedes descargar Plastic desde este enlace? También disponible en versión Linux.

    0 comentarios:

    Ocultar ramas utilizando la seguridad

    9:18 0 Comments

    ¿Cómo puedo instalar Plastic con seguridad para que los desarrolladores puedan ver solamente sus propias ramas además de la principal y la de mantenimiento?
    Esto fue lo que me preguntaron hace un par de días y ahora intentaré dar una respuesta detallada y explicar paso a paso cómo instalarlo con Plastic.
    Asegura tu servidor de repositorios
    El primer paso será asegurar el servidor repositorio. ¿Sabes lo que es el servidor repositorio? Bien, es el servidor que se ocupa de todos los repositorios, y como sabrás, los repositorios son los únicos que en realidad contienen los datos (ramas, revisiones, objetos, etiquetas…).¿Por qué es una buena idea asegurar el servidor repositorio? Plastic implementa una completa jerarquia de seguridad. Todo se hereda del servidor de repositorios, después del repositorio, y después de los objetos que hay dentro de cada repositorio. Puedes verlo en el siguiente diagrama:






    Lo primero que haremos será permitir el acceso al servidor de repositorios solamente a nuestros usuarios con permiso. En nuestro caso daré permisos a gestión, desarrollo y los usuarios especiales llamados OWNER. Para configurar los permisos del servidor de repositorios desde la interfaz gráfica GUI tenemos que ir a vista de repositorios, hacer click con el botón derecho sobre un repositorio e ir a permisos del servidor de repositorios....



    Por favor tenga en cuenta que tanto owner como el grupo de gestión tienen todos los permisos concedidos, y los desarrolladores no tienen el permiso view. Vea la siguiente figura:



    De esta manera los desarrolladores no podrán ver ningún objeto que ellos no hayan creado, pero nosotros podremos concederles acceso a las ramas, objetos y etiquetas que necesiten para que puedan trabajar correctamente. Los permisos concedidos a owner aseguran que pueden trabajar en las ramas creadas por ellos.

    Nota: comprueba la configuración de permisos con un usuario que pertenezca al grupo de administradores pertinente, gestión (management) en mi caso.
    ¡Asegura el acceso al código!
    Ahora mismo ningún desarrollador puede acceder al código ya que todos los ítems heredan del repositorio, y este hereda del servidor de repositorios, y el servidor de repositorios no tiene acceso de vista configurado para los desarrolladores.
    Así que si como administrador vas a la vista de objetos, pinchas con el botón derecho sobre el ítem raíz y vas a los permisos de ese ítem como muestra la siguiente figura:



    Una vez que estás aquí debes de asegurarte de dar el permiso de vista (view) a los desarrolladores



    Asegurando las ramas
    Ahora es el turno de las ramas. En el ejemplo queremos que todos los desarrolladores puedan ver la rama principal y las ramas creadas por ellos. El segundo requisito se logra gracias a los permisos de owner en el nivel del servidor de repositorios.

    En realidad, si queremos conceder permiso a los desarrolladores para ver la rama principal y la de mantenimiento (y cualquiera de las otras ramas que queremos que estén disponibles) tenemos que ir a permisos de rama y habilitar el permiso de vista.



    Comprobar que todo funciona
    Compruebe la siguiente figura. La instancia de plastic de la izquierda está funcionando como usuario privilegiado, mientras que la de la derecha corresponde a un desarrollador. Hay que tener en cuenta que el desarrollador sólo puede obtener el listado de las ramas principales y de mantenimiento y las que él mismo haya creadom, pero no las creadas por el usuario administrador (/main/task001pablo) en el ejemplo.



    Colocar todo sin olvidarse de las etiquetas
    Si te acuerdas de la jerarquía que se incluye arriba verás que las etiquetas no tienen permiso de vista tampoco. Así que después de crear una nueva etiqueta hay que asegurarse de proporcionar acceso.



    El mecanismo de seguridad de Plastic otorga a los usuarios una enorme flexibilidad. El propósito de establecer permisos no siempre esta relacionado necesariamente con la prevención de accesos no deseados sino que también se emplea para hacer cumplir ciertas políticas de desarrollo. Hablaremos de esto en un futuro post.

    0 comentarios:

    La manera más rápida de insertar grabaciones de 100K

    16:46 0 Comments

    Estoy haciendo hoy algunos tests de rendimiento con nuestras tres diferentes backends de bases de datos: MySql, Firebird y SQL Server.

    Mi objetivo es encontrar la manera más rápida de insertar un gran número de grabaciones en una tabla teniendo en cuenta los diferentes backends.

    Mi tabla de test es la siguiente en las tres bases de datos:

    CREATE TABLE testtable (
    iobjid BIGINT NOT NULL,
    ifield0 BIGINT,
    ifield1 BIGINT);

    iobjid es la clave primaria y los otros dos campos también están indexados.

    Así que vamos con el primer bucle:

    IDbConnection conn = // grab a connection somehow
    conn.Open();
    try
    {
    IDbCommand command = conn.CreateCommand();
    command.CommandText = "delete from testtable";
    command.ExecuteNonQuery();
    int initTime = Environment.TickCount;

    IDbTransaction t = conn.BeginTransaction(
    IsolationLevel.RepeatableRead);
    command.Transaction = t;
    for( int i = 1; i < 100000; i++)
    {
    command.CommandText = string.Format(
    "INSERT INTO testtable "+
    " (iobjid, ifield0, ifield1)"+
    " VALUES ( {0}, {1}, {2} )",
    i, 300000-i, 50000 + i);
    command.ExecuteNonQuery();
    }
    t.Commit();
    Console.WriteLine("{0} ms", Environment.TickCount - initTime);
    }
    finally
    {
    conn.Close();
    }

    ¿Cuánto tiempo me ha llevado insertar grabaciones de 100k en mi antiguo portátil?
    • Firebird 2.0.1 (embebido) -> 38s
    • SQL Server 2005 -> 28s
    • MySql 5.0.1 -> 40s
    He repetido el test con todos los posibles valores en niveles de aislamiento y no encuentro ninguna diferencia.


    Insertar con parámetros

    Mi segundo test intenta obtener un mejor resultado usando parámetros en los comandos…Aquí está el código:

    IDbConnection conn = //get your connection
    conn.Open();
    try
    {
    IDbCommand command = conn.CreateCommand();
    command.CommandText = "delete from testtable";
    command.ExecuteNonQuery();
    int initTime = Environment.TickCount;
    IDbTransaction t = conn.BeginTransaction(
    IsolationLevel.RepeatableRead);
    command.Transaction = t;
    // sqlserver and firebird use ‘@’ but mysql uses ‘?’
    string indexParamName =
    GetParametersNamePrefix() + "pk";
    string field0ParamName =
    GetParametersNamePrefix() + "field0";
    string field1ParamName =
    GetParametersNamePrefix() + "field1";
    command.CommandText = string.Format(
    "INSERT INTO testtable "+
    " (iobjid, ifield0, field1) "+
    "VALUES ( {0}, {1}, {2} )",
    indexParamName,
    markerParamName,
    revisionParamName);
    IDbDataParameter paramIndex = command.CreateParameter();
    paramIndex.ParameterName = indexParamName;
    command.Parameters.Add(paramIndex);
    IDbDataParameter paramField0 = command.CreateParameter();
    paramField0.ParameterName = field0ParamName;
    command.Parameters.Add(paramField0);
    IDbDataParameter paramField1 = command.CreateParameter();
    paramField1.ParameterName = field1ParamName;
    command.Parameters.Add(paramField1);
    for( int i = 0; i < 100000; i++)
    {
    paramIndex.Value = i;
    paramField0.Value = 300000 -i;
    paramField1.Value = 50000 + i;
    command.ExecuteNonQuery();
    }
    t.Commit();
    Console.WriteLine("{0} ms",
    Environment.TickCount - initTime);
    }
    finally
    {
    conn.Close();
    }

    ¿Cuánto tiempo tarda ahora?
    • Firebird -> 19s
    • SQL Server-> 20s
    • MySql -> 40s
    Parece que MySQL no resulta afectado por parámetros, ¡pero los otros dos realmente obtienen un rendimiento muy superior!

    Una inserción para todos

    Intentemos ahora una última opción: ¿qué pasaría si insertáramos todos los valores en una sola operación? Desafortunadamente ni el servidor SQL ni el de Firebird soportan múltiples filas en la parte de valores de una inserción. Sabemos que sí pueden usar algún tipo de cláusula de unión para hacer algo similar, pero el rendimiento no es superior.

    Intentémoslo con MySQL:

    IDbConnection conn = // grab your conn
    conn.Open();
    try
    {
    IDbCommand command = conn.CreateCommand();
    command.CommandText = "delete from testtable";
    command.ExecuteNonQuery();
    int initTime = Environment.TickCount;

    IDbTransaction t = conn.BeginTransaction(
    IsolationLevel.RepeatableRead);
    command.Transaction = t;
    StringBuilder builder = new StringBuilder();
    builder.Append(string.Format(
    "INSERT INTO testtable "+
    " (iobjid, ifield0, ifield1) "+
    "VALUES ( {0}, {1}, {2} )",
    0, 300000, 50000));
    for( int i = 1; i < 100000; i++)
    {
    builder.Append(string.Format(
    ", ( {0}, {1}, {2} )",
    i, 300000-i, 50000 + i));
    }
    command.CommandText = builder.ToString();
    command.ExecuteNonQuery();
    t.Commit();
    Console.WriteLine("{0} ms",
    Environment.TickCount - initTime);
    }
    finally
    {
    conn.Close();
    }

    Y el ganador es… MySQL tarda sólo 9 segundos en insertar las grabaciones de 100K… pero únicamente usando la operación de múltiples valores.

    ¡Disfrutad!

    0 comentarios:

    ¡Ya están listas las smart branches de Plastic SCM!

    9:56 0 Comments

    Estamos muy orgullosos de anunciar la primera versión del soporte para smart branches o ramas inteligentes de Plastic. Esta versión no es aún oficial (así que no la uséis para producción sin habernos contactado antes) y se puede descargar directamenete desde aquí, sin necesidad de registrarse.




    ¿Qué son exactamente las smart branches? Son la evolución de la funcionalidad básica de manejo de ramas de Plastic en respuesta a las demandas de algunos de nuestros usuarios: pueden recordar su punto de partida por lo que al volver a cambiar a una rama el usuario no tiene que recordar cuál es su punto de partida. Esta característica es muy positiva para diversos patrones de ramas muy utilizados, y sobre todo ayudará a los desarrolladores que utilizan el patrón de rama por tarea a recuperar viejas ramas.

    Una rama inteligente es conceptualmente muy parecida a una "stream" pero hemos preferido quedarnos con el nombre más tradicional de branches/ramas.

    Con las ramas inteligentes Plastic recuerda cuál es el punto de partida de una rama en un punto en el tiempo. El diálogo de propiedades de la rama (también una novedad en la versión BL101) mostrará cuál es el punto de partida actual de la rama y además permitirá que se modifique creando uno nuevo (lo cual es muy útil para las operaciones de rebase por ejemplo).

    El nuevo diálogo de propiedades además permite a los usuarios modificar el nombre de la rama y especialmente sus comentarios, algo que nuestros clientes nos habían pedido desde que sacamos las versión 2.0.



    Con esta nueva versión también se da mayor visibilidad a los changesets: están presentes en Plastic desde el primer día, pero ahora las ramas no se crean tan sólo desde una etiqueta o línea base sino también desde un changeset en concreto, lo cual facilita el mantenimiento.

    El mecanismo de herencia de ramas de Plastic es lo bastante flexible para poder definir diversas estrategias de ramificación y ahora se pueden combinar (de manera más sencilla que antes) con el uso de las smart branches.

    Una rama inteligente es simplemente una rama de Plastic con un enlace a un punto de partida. Este punto de partida puede ser:

  • otra rama (en cuyo caso heredarará de lo ultimo de esta rama, implementando la herencia actualizada de manera dinámica)
  • un changeset concreto en una rama (que especifica herencia fija desde un punto de partida bien definido)
  • una etiqueta (que es el caso normal que se utiliza como buena práctica en el manejo de ramas).

    Se crea un nuevo changeset cada vez que se configura una nueva rama como base así que los usuarios pueden encontrar fácilmente un punto de partida para utilizar más tarde, si fuera necesario, para recuperar esta configuración en concreto.



    Viendo la figura de arriba se observa que si un desarrollador selecciona ir al changeset 99 la rama /main/task001 se utilizará como label00 de base, pero si se selecciona el changeset 100 será la etiqueta label01.

    Los cambios también se introducen en la definición del selector por lo que ahora se pueden utilizar reglas como la siguiente:


    rep “codicetest”
    path “/”
    smartbranch “/main/task001”

    Y se configurarán los detalles de herencia de ramas que sean necesarios.

    La versión BL101 incluye toda esta nueva funcionalidad desde la interfaz gráfica y en nuestra próxima versión también se incluirá soporte para ramas inteligentes desde el explorador de ramas.

    Esperamos que las ramas inteligentes hagan que el manejo de ramas en Plastic sea aún más sencillo de utilizar tanto para nuestros clientes nuevos como para los actuales, y además incluyen más escenarios de manejo de ramas avanzado cuando sea necesario.

    ¡Esperamos que os guste!
  • 0 comentarios: