Category Archives: .NET / SQL Server Code & Tools

A follow-up on my unlikely Doomsday Scenario for SQL Server On Premises

A few days ago I published a post that conjectured about a rather unlikely scenario regarding SQL Server On Premises.

I was basing my conjecture on one single fact: that native client connectivity to SQL Server On Premises through SNAC is no longer supported, and that from now on, the only supported way to connect from client apps will be through an expanded ODBC driver.

Between August and September 2016, the Microsoft SQLNCli team blog published the following posts:

ODBC Driver 13.1 for SQL Server Released

Preview Release of the SQL Server [C/C++] ODBC Driver 13 for Linux

These two posts open the door to a different kind of scenario:

SQL Server has become a multi-platform technology, and it makes more sense for the team in charge of connectitivy tools to offer the best tools for all platforms, but at the same time, this team has to strive for as much maintainability for these tools as it is possible. One way to achieve that is to look for an API that can connect to all platforms, and that clearly is ODBC.
The SQLNCli team is working on the double to include in these ODBC drivers support for all relevant technologies previously available through SNAC and OleDb.

Kind regards, GEN

An Investigation on Database Id Generation Strategies (Part I)

The World ID-10092667

In this post, I will Investigate how Id generation strategies chosen for primary keys of business entities at the database level may impact the entire solution. Databases that are the repository of a transactional application do not work in a vaccum: They are a part of a larger solution.

So, design decisions for the database should not be made away from the context of the design decisions of the entire solution. On the contrary, all main design decisions regarding the database of any transactional application should be made considering the impact they may have on the solution as a whole.

So, proper trade-offs may apply regarding all main design decisions in a database that is the repository of a transactional application, including Id Generation Strategies for all business entities that are persisted by the application in the database.

Regarding the architecture of any given solution, we could say that it is all about the design decisions that we make and the consequences of such decisions.

Proper trade-offs are those that could strike the best possible balance of the consequences (both positive and negative) that may impact the solution in the short, mid and long term.

To conduct this investigation, I will choose an Ad-Hoc approach:
I will first navigate the problem, so we all could grasp a better understanding of what is at stake when we make isolated design decisions regarding Id Generation Strategies.

Once we have navigated the problem, so that we have some good understanding of the pros &cons of such a way of making design decisions, we will be in a much better position to explore possible solutions to this problem.

We could not argue that we fully understand any given solution if we do not have a deep understanding of the problem that such a solution is meant to solve.

Since my interest is to investigate this general problem from an architectural perspective, I  will use common design patterns and tools, like Separation of Concerns, Model-View-Mediator (this is a generic way to refer to patterns like Model-View-Controller or MVC, Model-View-Presenter or MVP, or Model-View-ViewModel or MVVM) and Object Relational Mapping (ORM).

When I say “MVC” I mean any tool that implements the MVC form of the general Model-View-Mediator pattern, and not just ASP.NET MVC, as it just as well applies to Spring.NET, or to any other MVC-based tool.

Why should we care to use these patterns and tools?

Mainly, because they are useful to us in a very practical way: they allow us to achieve our development goals with the least amount of effort from our part, if we make proper use of them.

The principle of Separation of Concerns is a very pervasive principle in Software Architecture, since it is applied in just about any architectural tool that we could consider, like for instance, when we use any Model-View-Mediator based tool, or when we use any ORM tool.

The principle of Separation of Concerns (SoC) states that we will organize our code in chunks in such a way that any given chunk of code will have a single and well-defined purpose, and it does not assume any superfluous responsibilities.

It means that if we choose to have an n-tier (or multiple layer) architecture, one of the main reasons behind this decision is the SoC principle.

It also means that if we choose to use some kind of Model-View-Mediator approach (like say, MVC, or MVVM, or MVP), one of the main reasons behind this decision is the SoC principle.

It would also mean that if we choose to use an ORM tool (like say, NHibernate, or EF), one of the main reasons behind this decision is the SoC principle.

With any of these tools and patterns, we use the concept of Model.

The Model is a software representation of a solution to a known problem.

The Model includes all the entities or business objects that are required by the solution to solve such a known problem.

Following the SoC principle, some chunk of code at some layer or tier will use these entities to apply the necessary logic that solves the business problem at hand.

By the same token, some other chunk of code at some other layer or tier will use these entities to persist their changes of state at the proper time and at the proper data repository.

The focus of my investigation will be at the level of this particular responsibility: how the different database id generation strategies affect the CRUD operations of business objects, and I will use an ORM tool as a helper for my analysis.

Speaking of ORM tools: why do we use them? what kind of problem do they solve for us?

As I have already said, the Model is a software representation of a solution to a known problem.

If we use an object-oriented representation of a given solution, such representation is aptly named the Object Model (OM) of said solution.

If we use an entity-relationship representation of a given solution, such representation is aptly named the Data Model (DM) of said solution.

For any given solution, its Object Model is very different from its Data Model.

If your team has to implement a solution with an OOP language like C# and a database like MS-SQL Server, such difference between the two representations of the solution poses a very serious problem to the software development effort of your team.

The formal name for this problem (the wide gap between the OM and the DM of a given solution) is Object-Relational Impedance Mismatch (ORIM).

It has been proven that a certain set of patterns is effective in the solution of the ORIM problem.

ORM tools are practical implementations of these patterns.

All ORM tools use a technique known as Mapping to bridge the gap of the ORIM problem.

ORM tools allow us to use a default set of Mapping rules and conventions, and they also allow us to customize the rules and conventions to be used by our implementation.

The simplest way to use any ORM tool is with the default set of Mapping rules and conventions.

In this post I will use NHibernate as a reference model for an ORM tool.

I will present and use concepts that are relevant for any given mainstream ORM tool, but I will use the names of those concepts as they are referred by NHibernate.

I will start with the simplest of examples, and I will gradually move on to more complex examples.

Since I want to explore how the different database id generation strategies may affect the CRUD operations of business objects, in my first example I will let the ORM tool choose the database id generation strategy by letting it use its default behaviour, then do some basic CRUD operations and use the debugging tools from the ORM engine to obtain useful information to analyze how good (or bad) is the default Id generation strategy from the perspective of the system as a whole.

To do this, I have chosen to use the approach commonly known as “Code First”, and let the ORM tool generate the database schema source code for the Model used in my first example.

I will use some POCO classes as the entities of my Model.

But before I go on, it would be useful to explore a little deeper into the Model and how it is used by the different layers or tiers.

When it comes to solving a given kind of problem, it is at the level of the Business Logic Layer where the “actual” solving of the problem happens.

When it comes to persisting and retrieving the state of business objects, it is at the level of the Data Access Layer where those kinds of operations happen.

At the level of the Business Logic Layer (BLL), all business objects (instances) of all business entities (entity classes) participate.

At the level of the Data Access Layer (DAL), only instances of persistent business entities (persistent entity classes) participate.

For many kinds of businesses, there is a subset of business entities that are non-persistent: that is, instances of such non-persistent entity classes are required and used at the BLL level, but none of such instances of such classes exist at the DAL level, which means that the database schema has no tables to represent the non-persistent entity classes.

At this point, it is very useful to present an example of such kind of scenario.

Let’s consider the following business example: A company has an customer loyalty program as part of their CRM business processes.

Some of the business processes involved in the customer loyalty program apply certain business rules based on algorithms that calculate metrics as a function of the “age” of a given customer in the customer loyalty program.

Let’s suppose that, for any given order, there are 10 different algorithms that use this “age” of the customer to calculate these metrics.

The “age” of a given customer in the customer loyalty program is the number of days, expressed in years (as a real number) between the start date when such a customer joined the program, and today’s date.

We should all realize  that the start date when any given customer joins the customer loyalty program has to be a public property of some business entity that has to be a persistent entity class.

The “age” of a customer in the program, as a property, it is a function of the start date and today’s date, so, it is not an independent property, so, it should not be persisted.

Regarding the aforementioned algorithms (we have supposed that there are 10 different calculations for each new order), we could just as well use the persistent start date as parameter with each one of them. But if we did so, it would mean that for each order, we would have to calculate the very same subtraction ten times in a row, which is a clear waste of resources.

So, why not use some non-persistent business entities at the Business Logic Layer when it seems to be useful and it makes a lot of sense from many perspectives?

Now that we have gone through the rationale behind non-persistent business entities, let’s delve into a simple Object Model that could solve the “Tango with Persistent & Non-Persistent classes”:

EntityHierarchy

Now, we can get back to the simplest way to use the “Code First” approach so that our choice of ORM tool, using defaults, generates the source code for the database schema of our Model.As we are using NHibernate as a reference model for any ORM tool, the simplest way to achieve what we need is with Automapping. What Automapping really means is that we will use the default set of rules and conventions with very little customizing.

With Automapping we can tell our ORM tool to generate the source code of the database schema that corresponds to our Model, that is, the object model that represents the business entities of the domain of our solution.

Since the domain of our solution is comprised of two subsets, a subset of persistent business entities, and a subset of non-persistent business entities, we need to tell our ORM tool to generate a database schema that only includes the persistent business entities.

The code for the base classes that we need to solve the “Tango” are these:

namespace SimpleAutoMap.Domain
{

public abstract class EntityBase
{}

}

namespace SimpleAutoMap.Domain
{

public abstract class NonPersistentEntityBase : EntityBase
{}

}

namespace SimpleAutoMap.Domain
{
public abstract class PersistentEntityBase : EntityBase
{
public virtual int Id { get; set; }
}

}

It is interesting to note that, in our model, the base class for all persistent business entity classes already has the Id property included: in this case, we are using implementation inheritance so as to save code!
It is also very important to note that so far, we have only dealt with the “Tango” of Persistent and Non-Persistent classes strictly from the perspective of pure implementation inheritance, and we still need to do some more work so that our ORM tool will work with the business entities as we expect it to do.
Now that we have our base classes in place, we can move on to the main classes of our (rather simple) model:
namespace SimpleAutoMap.Domain
{
public class Product : PersistentEntityBase
{
public virtual string ProductName { get; set; }
}

}

namespace SimpleAutoMap.Domain
{
public class Customer : PersistentEntityBase
{
public virtual string CustomerName { get; set; }
public virtual DateTime InceptionDate { get; set; }
public virtual DateTime ClpStartDate { get; set; }
}

}

namespace SimpleAutoMap.Domain
{
public class LineItem : PersistentEntityBase
{
public virtual int Quantity { get; set; }
public virtual decimal UnitPrice { get; set; }
public virtual Product Product { get; set; }
}

}

namespace SimpleAutoMap.Domain
{
public class Order : PersistentEntityBase
{
public virtual DateTime OrderDate { get; set; }
public virtual Customer Customer { get; set; }
public virtual IList LineItems { get; set; }
}

}

namespace SimpleAutoMap.Domain
{
class ClpProcessingOptions : NonPersistentEntityBase
{
public double Age { get; set; }
}

}

(NOTE: in the original post, I forgot to include the properties InceptionDate and ClpStartDate to Customer. Now it is fixed!)

Before we go any further, it would be very useful to say a word about why all the properties of the persistent entities have the modifier virtual, while at the same time, the properties of the non-persistent entities do not have the modifier virtual?

At this point I do not want to distract the attention away from the main goal of this post, but nonetheless I will give a short but proper answer to this valid and important question.

From the perspective of the engine of any ORM tool, the model is an atomic unit, in the sense that each and every entity class that is a part of the persistent subset of the model (the part of the model that is relevant to the ORM engine) is “created equal”.

Unless we say otherwise, when we tell the ORM engine to “load”, it will try to load to memory each and every instance of each and every entity class (which happens to be a real waste of resources!).

This funny way to behave (the default behaviour) is apty named eager loading. But if any ORM tool would only support eager loading, it would be useless to us.

So, in order to be useful, all ORM tools also support another behaviour, apty named lazy loading.

With lazy loading, we have complete programmatic control over when and how any give set of instances of any given entity class is loaded to memory by the ORM engine.

To be able to support lazy loading, all entity classes (so as to be able to be handled by the ORM tool in this way), MUST have all of its public properties declared as virtual.

Well, now that we can get back to own main interest, we have to figure out a way to tell the ORM engine to include into the Data Model only the entity classes that inherit from the class PersistentEntityBase.

With NHibernate this goal is very simple to achieve: the default set of rules and conventions is controlled by the class DefaultAutomappingConfiguration.

All we have to do is create a subclass of DefaultAutomappingConfiguration with the proper behaviour and use it in our implementation.

The class DefaultAutomappingConfiguration has a very useful method that will help us in what we want to achieve: the method ShouldMap.

The overload of this method that is interesting to our investigation has the following signature:

public virtual bool ShouldMap(Type type)
This overload in particular is very useful, indeed, for it is virtual (which means that we can override it with our own specialized logic), and it receives as parameter any object of the class Type.
This is simple and wonderful at the same time, as we can figure out how the ORM engine uses this overload: it iterates over the entire set of entity classes of the model, and for each given entity class, it passes it to this method and uses its outcome to determine if said entity class of the model has to be mapped or not.
This is exactly what we need to tell the ORM engine to map only those entity classes that inherit from the base class PersistentEntityBase.
So, our subclass of the base class  DefaultAutomappingConfiguration looks like this:
namespace SimpleAutoMapping
{

public class SimpleAutoMappingConfiguration
: DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.IsSubclassOf(typeof(PersistentEntityBase));
}
}

}

Finally, we are ready to tell our ORM tool to follow its default behaviour (with just a very simple customizing), and generate the database schema for the subset of the persistent entity classes of our model.

With a powerful ORM tool (like for instance, NHibernate!), we need a very simple routine to do this:

class Program
{

static void Main(string[] args)
{
string outputFileName = ConfigurationManager.AppSettings[“OutputFileName”];
var cfg = new SimpleAutoMapConfiguration();var configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008)
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Customer>(cfg)))
.BuildConfiguration();
var exporter = new SchemaExport(configuration);
exporter.SetOutputFile(outputFileName);exporter.Execute(false, false, false);
Console.WriteLine(“\n\nDB schema source code.”);
Console.ReadLine();
}

}

This routine generates a database schema that looks like this:

if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FKDDD0206ACBEF7F6]’) AND parent_object_id = OBJECT_ID(‘[LineItem]’))
alter table [LineItem] drop constraint FKDDD0206ACBEF7F6

if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FKDDD0206A75BA3E60]’) AND parent_object_id = OBJECT_ID(‘[LineItem]’))
alter table [LineItem] drop constraint FKDDD0206A75BA3E60

if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3117099B4095694A]’) AND parent_object_id = OBJECT_ID(‘[Order]’))
alter table [Order] drop constraint FK3117099B4095694A

if exists (select * from dbo.sysobjects where id = object_id(N'[Customer]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table [Customer]

if exists (select * from dbo.sysobjects where id = object_id(N'[LineItem]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table [LineItem]

if exists (select * from dbo.sysobjects where id = object_id(N'[Order]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table [Order]

if exists (select * from dbo.sysobjects where id = object_id(N'[Product]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table [Product]

create table [Customer] (
Id INT IDENTITY NOT NULL,
CustomerName NVARCHAR(255) null,
InceptionDate DATETIME null,
ClpStartDate DATETIME null,
primary key (Id)
)

create table [LineItem] (
Id INT IDENTITY NOT NULL,
Quantity INT null,
UnitPrice DECIMAL(19,5) null,
Product_id INT null,
Order_id INT null,
primary key (Id)
)

create table [Order] (
Id INT IDENTITY NOT NULL,
OrderDate DATETIME null,
Customer_id INT null,
primary key (Id)
)

create table [Product] (
Id INT IDENTITY NOT NULL,
ProductName NVARCHAR(255) null,
primary key (Id)
)

alter table [LineItem]
add constraint FKDDD0206ACBEF7F6
foreign key (Product_id)
references [Product]

alter table [LineItem]
add constraint FKDDD0206A75BA3E60
foreign key (Order_id)
references [Order]

alter table [Order]
add constraint FK3117099B4095694A
foreign key (Customer_id)
references [Customer]

We can check that the ORM tool, with the small set of constraints that we have given it and its own default behaviour, has generated a database schema that uses IDENTITY-based primary keys on all entities.

How good (or bad) is this decision from the perspective of the entire solution (and not just from the perspective of the database itself)?
We will explore this in my next blog post (Part II of this investigation).

To download the code sample, click here

Kind regards, GEN

XAML y el desarrollo cross-platform de la UI de smartphone apps

Blue Wave

En este post revisaremos brevemente algunas de las razones por las cuales el uso de XAML en el desarrollo cross-platform de la UI de smartphone apps es una decisión de diseño muy importante, lo que habla muy bien de aquellas empresas, como Xamarin, que han tomado tal decisión.

Contexto de hardware gráfico previo al lanzamiento de Windows Vista

Previo al lanzamiento de Windows Vista, un porcentaje de las PCs de ese entonces contaban con hardware acelerador de gráficos, tales como tarjetas aceleradoras dedicadas, en el caso de usuarios asiduos de los video games de PCs, o chipsets con capacidades básicas de aceleración integradas en el motherboard.

Previo al lanzamiento de Vista, para poder trabajar con dicho hardware gráfico especializado, el software debía hacer uso de APIs complejos como Direct3D, mientras que la funcionalidad gráfica básica de Windows se basa en el uso de otros APIs más sencillos pero con capacidades más básicas, como es el caso de GDI+.

En base a este panorama, la gente de Microsoft vió propicio incorporar a las tools de desarrollo de .NET, a través de nuevos componentes del framework, un nuevo API integrador de manejo gráfico que evitase la complejidad para los desarrolladores de tener que lidiar con diversos APIs, de acuerdo con el hardware con el que disponga la PC en cuestión.

Este nuevo API integrador es Windows Presentation Foundation, que detecta que hardware gráfico está disponible en la PC y lo aprovecha para obtener el mejor resultado visual posible, como por ejemplo, aplicaciones cuya interfase de usuario presente ventanas con algún porcentaje de transparencia.

Esto puede parecer algo superfluo, pero la presión competitiva de las interfases de usuario con efectos visuales atractivos que ofrecían las aplicaciones en Mac era muy fuerte y llevó a Microsoft a pensar como incorporar al desarrollo .NET un API con una curva de aprendizaje relativamente baja (en comparación con la necesaria para aprender a usar Direct3D) que permita que muchos desarrolladores puedan implementar rapidamente aplicaciones visualmente muy atractivas.

Breve revisión de la arquitectura de Windows Presentation Foundation

Para poder comprender el aporte crucial que representa usar XAML en el desarrollo cross-platform de la Interfase de Usuario (UI) de smartphone apps, es conveniente revisar brevemente la arquitectura de Windows Presentation Foundation, que es el área del framework de .NET donde se usa XAML vinculado con el desarrollo de la interfase de usuario de una aplicación.

WPF Arch

WPF Architecture

Como se puede apreciar, un único y mismo API que permite describir los elementos de interfase de usuario en WPF puede usar el hardware gráfico disponible, a fin de aplicar los efectos visuales solicitados con la mejor performance que el equipo permita.

Es importante destacar el punto mas relevante del diagrama de arquitectura: el mismo API de elementos de interfase de usuario puede ser ejecutado por hardware diverso, en base a lo que este disponible en el equipo en cuestión, y es el mismo framework el que aisla al programador de la decisión de que clases dentro del árbol de herencia es necesario invocar para tener acceso al hardware a usar.

Ventajas de XAML/WPF para el desarrollo Cross-Platform de Smartphone Apps

Dadas las características de WPF mencionadas previamente, empresas como Xamarin vieron las ventajas implícitas en la arquitectura original de WPF para extenderla, de tal forma de ser aprovechada para el desarrollo cross-platform de smartphone apps de diversos vendors, que han de ejecutar en forma nativa en diferentes sistemas operativos y con diferente hardware.

La amplia experiencia previa de la gente de Mono (nucleo de desarrollo del que se derivó el equipo de desarrollo de Xamarin) en la extensión del framework de .NET y de la implementación del CLR para otros sistemas operativos y plataformas de hardware les ha permitido realizar este tipo de adaptaciones en forma exitosa y ofrecer estas ventajas a los desarrolladores.

Saludos, GEN

Escalabilidad Infinita y tablas Memory-Optimized

Clock

 

 

En este post voy a analizar algunos escenarios de escalabilidad de aplicaciones, en particular, aquellos escenarios que pueden verse beneficiados con el uso de tablas memory-optimized.

Los puntos principales del post son los siguientes:

  • Rangos de escalabilidad de aplicaciones
  • Algunas restricciones a la escalabilidad infinita impuestas por la arquitectura tradicional de base de datos relacionales
  • Ventajas de las tablas memory-optimized para sistemas con escalabilidad infinita

Rangos de escalabilidad de aplicaciones

Cuando hablamos de escalabilidad de aplicaciones, nos referimos a la capacidad que tiene una aplicación dada de mantener el mismo nivel de performance ante cantidades crecientes de usuarios concurrentes que la utilizan.

La escalabilidad de aplicaciones puede agruparse en diversos rangos, según el punto de vista del análisis, pero una forma típica es la siguiente:

  • Escalabilidad básica
  • Escalabilidad intermedia
  • Escalabilidad ilimitada o infinita

Escalabilidad básica es el rango mínimo de escalabilidad, que abarca entre uno y unos pocos miles de usuarios concurrentes.

En este rango se ubican aplicaciones de PyMEs y aplicaciones departamentales de grandes empresas.

La edición Express de SQL Server puede atender este rango de escalabilidad.

Este rango no es el foco de este post.

Escalabilidad Intermedia es un rango que abarca entre unos pocos miles y varias decenas de miles a varios cientos de miles (para muchos escenarios) de usuarios concurrentes. Este es el rango de escalabilidad que las ediciones de SQL Server a partir de la Standard pueden proveer (con los recursos apropiados de hardware del server).

En este rango se ubican aplicaciones corporativas del estilo de ERP, portales corporativos, aplicaciones de Helpdesk y otras similares.

En este rango de escalabilidad, las ediciones de SQL Server mencionadas (con los recursos de hardware apropiados), son capaces de atender con alta performance a todo tipo de escenarios. También es importante destacar que a partir de este rango de escalabilidad, es imprescindible usar versiones 64 bits de SQL Server con múltiples procesadores de múltiples núcleos, y con un dimensionamiento adecuado de memoria.

Escalabilidad ilimitada o infinita es el rango que se corresponde con aplicaciones web de uso masivo, a partir de varias decenas de miles de usuarios concurrentes en adelante, sin cota superior definida.

Este rango puede ser atendido por las mismas ediciones ya mencionadas de SQL Server, con los adecuados recursos de hardware en el server, sin embargo, es importante destacar que con cantidades muy grandes de usuarios concurrentes, existen diversos escenarios donde puede aumentar la latencia en las transacciones hasta niveles no deseables: este es el tipo de escenarios que se puede beneficiar con las ventajas de las tablas memory-optimized.

Para poder analizar las ventajas que las tablas memory-optimized ofrecen para este tipo de requerimiento (escalabilidad infinita), es necesario analizar con más detalle algunas de las restricciones a la escalabilidad infinita que imponen algunos elementos de la arquitectura tradicional de los engines de bases de datos relacionales.

Algunas restricciones a la escalabilidad infinita impuestas por la arquitectura tradicional de base de datos relacionales

Vamos a repasar brevemente la secuencia de pasos que ocurren en un server de base de datos SQL Server desde que recibe una solicitud de ejecutar un proceso hasta que el resultado del proceso es retornado al proceso cliente en un contexto de escalabilidad infinita.

Para realizar este breve análisis, me focalizaré en un concepto que es útil para este propósito: el concepto de recurso limitante.

En un contexto de alta escalabilidad, el server de base de datos ha de recibir una cantidad muy alta de Remote Procedure Calls concurrentes, provenientes de procesos cliente.

Para poder atender a todos estas invocaciones concurrentes sin tener que encolarlas, es necesario que el server tenga multiples procesadores con múltiples núcleos.

Desde este punto de vista, el procesamiento paralelo es favorable para cubrir el requerimiento de escalabilidad infinita.

Para poder ejecutar el proceso solicitado, el server ha de verificar si los datos necesarios para dicho proceso están disponibles en el Data Caché.

Este es el primer recurso limitante relevante que se presenta en los diversos pasos a seguir para ejecutar el proceso solicitado.

Si en el Buffer Pool no están todos los datos necesarios, el server debe leer los datos faltantes en los files que los contienen y ubicarlos en el Data Caché para poder ejecutar el proceso solicitado.

Al querer leer los datos faltantes en los files que los contienen, aparece el segundo recurso limitante relevante: que el disco que contiene los files donde se alojan los datos requeridos esté ocupado realizando otra tarea previa.

Esto hace que se debe encolar la tarea de lectura y esperar su turno hasta que pueda ser realizada: esta espera es la causante de los wait types “infames” del tipo PageIOLatch*.

Es importante destacar que tener múltiples procesadores no ayuda para reducir ambos recursos limitantes: es más, cuantos más procesadores tenga el server, es más probable que múltiples invocaciones concurrentes estén “peleando” entre sí por los recursos limitantes mencionados.

También es importante destacar que el aumento de memoria permite tener un Data Caché más grande, y por lo tanto, bajar un poco la probabilidad que el server no encuentre los datos necesarios para ejecutar el proceso solicitado y por lo tanto, tenga que leer dichos datos de los files que los contienen.

En cuanto al recurso limitante que el disco esté ocupado con otra tarea previa, el elemento crucial es la velocidad del disco, y tener más procesadores o más memoria en el server no resuelve  en forma significativa este problema (toda vez que es necesario ir a leer los datos faltantes).

En resumen, las restricciones a la escalabilidad infinita surgen por el tiempo total necesario para ejecutar el proceso solicitado, y dicho tiempo se ve afectado por la espera para leer los datos faltantes, y esto ocurre porque dichos datos no están disponibles en el Data Caché al momento de ejecutar el proceso solicitado.

Por lo tanto, si se puede garantizar que los datos necesarios para un proceso en particular estén “siempre” en memoria, se eliminan las restricciones mencionadas a la escalabilidad infinita.

Justamente, esto es lo que aporta el uso de tablas memory-optimized a los escenarios de escalabilidad infinita.

Ventajas de las tablas memory-optimized para sistemas con escalabilidad infinita

Al eliminar la necesidad de tener que ir a leer al disco los datos faltantes para ejecutar un proceso en particular, el uso de tablas memory-optimized puede mejorar en forma significativa la performance de los procesos que operan con dichas tablas, donde el factor de mejora típicamente es por lo menos un orden de magnitud, y en muchos casos, dos órdenes de magnitud: justamente esto justificó que el codename de esta tecnología sea Hekaton, que literalmente es “cien” en griego.

Este nivel de mejora de performance es crucial para aplicaciones tanto Internet Facing como Cloud.

Para aplicaciones Web Internet Facing, se tiene disponible las tablas memory-optimized en SQL Server 2014 Enterprise (On Premises), mientras que para Cloud-based web applications, se tiene disponible tablas memory-optimized en SQL Azure.

Saludos, GEN

 

Cómo integrar los requerimientos de DBAs y Admins a las decisiones de diseño de una solución

En este post voy a tratar brevemente sobre qué podemos hacer para asegurarnos que los requerimientos de DBAs y Admins están adecuadamente cubiertos por las decisiones de diseño de una solución, en equilibrio con una adecuada cobertura de los requerimientos de los demás stakeholders de dicha solución.

Los puntos principales de este post son los siguientes:

  • Los DBAs y Admins también son stakeholders de primer nivel de una solución
  • Los requerimientos de DBAs y Admins suelen “asomar” a través de los requerimientos no funcionales de la solución
  • Pensar en cómo automatizar el deployment de la solución desde el comienzo
  • Incorporar metodologías como DevOps para mejorar la integración con DBAs y Admins en los deployments

Los DBAs y Admins también son stakeholders de primer nivel de una solución

Un problema frecuente que se presenta en la documentación de requerimientos es que resulta poco probable que los usuarios clave entrevistados por los analistas funcionales tengan presentes los requerimientos de los DBAs y de los Admins y los describan con suficiente detalle.

En suma, los requerimientos de los DBAs y Admins pasan a engrosar la lista de los requerimientos “invisibles” de la solución en cuestión: son todos aquellos requerimientos que no tienen un “vocero” que los saque del anonimato, a tiempo.

Lamentablemente, este es un hecho muy frecuente, y un abrumador porcentaje de las soluciones existentes tiene su lista de requerimientos “invisibles”, que típicamente incluye a los requerimientos de los DBAs y Admins.

Un elemento que contribuye a que los requerimientos de los DBAs y Admins sean “invisibles” es que la gran mayoría de los usuarios no comprende de qué forma puede comprometer seriamente la correcta operación de la solución si los requerimientos de los DBAs y Admins no están adecuadamente cubiertos.

Qué podríamos hacer para mejorar esto ?

Existe una alternativa de resolución muy simple: que el arquitecto sea el que “saque del anonimato” a los requerimientos de DBAs y Admins, junto con otros requerimientos “invisibles” importantes.

Una forma apropiada para hacer esto es que el arquitecto le sugiera a los analistas funcionales como orientar las preguntas de requerimientos en general, y en particular, sobre los no funcionales.

Los requerimientos de DBAs y Admins suelen “asomar” a través de los requerimientos no funcionales de la solución

Ciertamente, los requerimientos de los DBAs y los Admins no son “completamente invisibles”, más bien, son altamente “transparentes”, ya que estos requerimientos suelen “asomar” en forma velada o indirecta a través de los requerimientos no funcionales que los usuarios normalmente describen.

Por diversas razones, tanto prácticas como técnicas, los arquitectos preferimos agrupar los requerimientos de una solución en categorías que se denominan Quality Attributes.

Consideremos brevemente algunos Quality Attributes (QAs) relevantes que suelen “hacer visibles” a algunos de los requerimientos de DBAs y Admins:

  • Performance
  • Escalabilidad
  • Seguridad
  • Alta Disponibilidad/Disaster Recovery (HA/DR)
  • Mantenibilidad
  • Administrabilidad

Veamos un ejemplo sobre como algunos requerimientos de seguridad pueden “hacer visibles” a requerimientos importantes de DBAs, Admins y otras áreas.

Supongamos que un documento de requerimientos de una solución dice algo similar a lo siguiente:

“Esta solución Web debe ser Internet Facing, dado que debe soportar tanto a usuarios de Intranet como a usuarios de Extranet”

Esta sola frase remite a requerimientos de los DBAs, de los Admins y de Seguridad Informática.

Repasemos brevemente las implicancias que esta frase tiene en las decisiones de diseño para que la solución cubra adecuadamente los requerimientos de DBAs, Admins y Seguridad Informática que “asoman”.

Los Web Front End servers de esta Web Application deben estar en la DMZ o red perimetral, dado que la Web Application en cuestión es Internet Facing.

Dado que la DMZ es una “zona no segura”, la DMZ no puede alojar ni a la capa de lógica de negocios ni a la capa de acceso a datos: solamente debería estar la capa de presentación de la Web Application en la DMZ.

Los requests y responses de los RPCs desde el código de la capa de presentación de la Web Application a la capa de lógica de negocios deben pasar a través de un firewall, por lo tanto, dichas invocaciones deberían estar “montadas” sobre el protocolo HTTP/HTTPS para ser viables.

Queda claro que la arquitectura típica presente en una cantidad importante de Web Applications existentes no es capaz de cumplir con las condiciones y requerimientos mencionados.

Por lo tanto, tal vez sea necesario revisar algunas de las decisiones de diseño que comúnmente se toman en Web Applications para que puedan cubrir estos requerimientos importantes de seguridad en forma adecuada.

En cuanto a los QAs de Mantenibilidad y Administrabilidad, podemos repasar brevemente como la forma en que registramos las excepciones arrojadas por la solución tienen implicancias cruzadas en ambos QAs.

Para los DBAs y Admins, sería conveniente que la solución registre las excepciones en el event log de cada server donde las mismas emergen, mientras que para el equipo de production support que ha de dar soporte (bug fixing e implementación de cambios de requerimientos) a la solución en producción, sería conveniente que la solución registre las excepciones en archivos de log en directorios de red a los que ellos tengan acceso.

Es importante destacar que el equipo de production support tal vez necesite que estos archivos de log registren detalles adicionales de las excepciones que serían un “exceso de información” para los DBAs y Admins, por lo tanto, las decisiones de diseño deberían cubrir en forma adecuada y equilibrada las diferencias en los detalles de información de excepciones a registrar para estos dos grupos.

En relación con el QA de Alta Disponibilidad/Disaster Recovery (HA/DR), debemos contemplar que si bien como primera aproximación es aceptable decir que las implementaciones de alta disponibilidad que soportan medidas de failover automático (como Failover Clustering y Database Mirroring, o las diversas configuraciones de AlwaysOn, en SQL Server) son transparentes a la lógica de los sistemas de información que las usan, lo correcto sería verificar con pruebas de carga con volúmenes y picos de transacciones comparables a los reales el grado de aumento de la latencia en las transacciones, debido a los procesos adicionales necesarios para el soporte de Alta Disponibilidad.

En suma, podemos encontrar que los requerimientos de todos los QAs mencionados tienen diversas y serias implicancias a nivel de las decisiones de diseño que permitan dar una cobertura apropiada a las requerimientos de DBAs y Admins que se desprenden (se “hacen visibles” a través) de dichos QAs.

Tarde o temprano, las decisiones de diseño que debemos tomar para dar adecuada y balanceada cobertura a todos los requerimientos derivan en una arquitectura distribuida, como una diversa cantidad de componentes a instalar en diversos servers.

No hay nada de malo en una arquitectura distribuida con diversos componentes a ser instalados en una serie de servers: es cierto, en sí mismo, no tiene nada de malo, pero debemos contemplar algunas de sus consecuencias.

Por ejemplo, un proceso manual de instalación y configuración de estos diversos componentes en diversos servers no es lo más recomendable, dado que podría ser propenso a diversos errores de instalación y/o configuración no evidentes (errores de instalación y/o configuración que no arrojan mensajes de error durante el proceso manual de instalación, configuración y prueba).

Para eliminar o mitigar la ocurrencia de este tipo de errores, es conveniente que contemos con procesos automáticos de deployment de los diversos componentes de la solución.

Pensar en cómo automatizar el deployment de la solución desde el comienzo

Ya hemos visto muy brevemente que debemos considerar con mucho detenimiento las decisiones de diseño que tomamos para asegurar una adecuada y equilibrada cobertura de todos los requerimientos de una solución, y además, hemos visto que las decisiones de diseño que tomamos tienen implicancias que afectan a estos requerimientos (por ejemplo, ya vimos como una arquitectura distribuida tiene implicancias en el deployment de la misma).

Por lo tanto, es necesario que empecemos a pensar en los procesos automáticos de deployment de la solución desde las etapas tempranas del diseño y del desarrollo, dado que si dejamos esto para el final, corremos el riesgo de descubrir (tarde) que algunas decisiones de diseño no “suman” a la automatización del deployment.

Es importante destacar que esto no implica trabajo adicional, sino que tan solo cambiar la decisión de que tan temprano se empieza a diseñar y desarrollar el proceso de deployment automático de la solución.

Para un equipo de desarrollo que utiliza Agile, debería ser algo bastante natural usar métodos automáticos de deployment.

Una de las prácticas de Agile es tener builds frecuentes durante la ejecución de una iteración o Sprint, como mínimo, en la medida de lo posible, un build diario, aunque sería mucho mejor si el equipo pudiese implementar un proceso de continuous delivery: poder realizar un build a pedido en cualquier momento, y si el mismo es exitoso, a continuación hacer un deployment automático a un entorno en particular de todos los componentes actualizados, recién generados en el build.

En sí mismo, para poder realizar un build a pedido, es necesario automatizar una serie compleja y larga de tareas, que incluyen procesos automáticos de copia coordinada de archivos a determinados directorios de red, junto con la invocación automática (y también coordinada) de determinados procesos (ejecución de procesos de verificación de estilos y standards de programación, ejecución de procesos automáticos de unit testing, ejecución de procesos automáticos de compilación y linking, etc.)

Los equipos de desarrollo Agile suelen usar determinadas tools open source para automatizar builds e integrations, tales como Ant o NAnt.

Por su parte, los DBAs y los Admins suelen usar tools como Perl, Python o PowerShell para automatizar procesos de administración.

Es altamente recomendable que los procesos automáticos de deployment, que han de ser ejecutados por los DBAs y Admins, estén desarrollados con las tools que los DBAs y Admins acostumbrar usar.

Por lo tanto, es altamente recomendable que el equipo de desarrollo adquiera la experiencia necesaria para desarrollar con fluidez en Perl, Python, PowerShell u otra tool, según corresponda.

Queda claro que tanto para el equipo de desarrollo como para el equipo de DBAs y Admins será de mucho valor agregado buscar formas de integrar el trabajo en conjunto que deben realizar.

Incorporar metodologías como DevOps para mejorar la integración con DBAs y Admins en los deployments

Qué podemos hacer para mejorar la integración del equipo de desarrollo con el equipo de DBAs y Admins?

A priori, esto parecería ser algo complicado de lograr, dado que, típicamente, el equipo de desarrollo usa procesos como Agile, por ejemplo, mientras de los DBAs y los Admins usan procesos como ITIL, por ejemplo.

A pesar que estos dos tipos de procesos parecerían presentar dificultades de reconciliación para poder pensar que sería posible tener procesos integrados, en realidad, es una tarea menos complicada de lo que parece, e inclusive, existen metodologías exitosas que permiten integrar los procesos de ambos equipos para aquellas tareas que deben diseñar e implementar en forma conjunta y coordinada.

Una de tales metodologías es DevOps

(para más detalles, ver definiciones y tools)

Como cierre, les dejo dos ideas para reflexionar:

“Los arquitectos debemos entablar un diálogo fluido, cordial y frecuente con los DBAs y Admins, de tal forma que seamos ‘todos juntos contra el problema’, en vez de ‘estar enfrentados por el problema’”.

“Si hemos de desarrollar soluciones que tengan a SQL Server como repositorio de persistencia, los arquitectos debemos ampliar tanto el alcance como la profundidad de los conocimientos que tenemos sobre las diversas capacidades que soporta esta herramienta”

Saludos, GEN

If SOAP Services are a standard and REST services are not, why bother with REST services?

This seems to be a very powerful statement: why waste any time at all with REST?

With REST, all parties involved (providers and consumers) have to agree on most aspects of the design, as there are no standards beyond the actual (HTTP) GET, POST, PUT and DELETE.

What benefit could come out of REST?

Well, I think that to be able to offer a good operational answer to this question, we should picture a simple scenario.

Let’s suppose that you have an information system that maintains weather information for many locations, and you update that information into the system on a timely basis (like say, every 15 minutes).

As any system, it should support the usual CRUD functions: CREATE (INSERT), READ (SELECT), UPDATE (UPDATE), DELETE (DELETE).

You have many distributed weather data capture stations that send data to a centralized service following the aforementioned update period.

You need to offer a scalable and simple service façade to both the data providers (data capture stations) and the data consumers (other systems that query for weather info).

SOAP web services at first seem to be fine with this requirement (why shouldn’t they?).

But when you start thinking about quality attributes and architectural decisions that would better give support to those attributes, you start wondering…

One characteristic of the entire operation is a thorny issue: within each and every 15-minute period, data does not change!

So, all consumers of data for any given location (with the location ID being in itself a parameter of the query) would get always the same result within one given 15 minute period.

SOAP web services are great for highly transactional systems, that is, systems that are affected by high rates of data changes per unit of time.

For the kind of scenario that we are conceiving, SOAP web services are not necessarily that great, as we would benefit from a service that could make good use of web page caching mechanisms (that are very common on most web servers).

Well, the good thing about REST services is that, from the perspective of the web server where they are hosted, they appear and behave as simple web pages (to the server).

As that is the case, we can actually make good use of standard page caching mechanisms with REST services, in particular, with GET calls.

In WCF REST services, we can use standard Output Cache mechanisms (including cache profiles), starting with VaryByParam, VaryByParams and VaryByCustom.

So now, we have a better understanding of when and how to take advantage of REST services.

KR, GEN

Useful Query to retrieve Index Stats / Query util para stats de indices

 
SQL Server 2005 has a very useful function that returns a dynamic view of index stats.
 
(this is sys.dm_db_index_physical_stats )
 
Even though this function is very powerful, I have found that some additional data would make it even more useful.
 
So after some trial and error work, I found the following query to be more suitable.
 
(See Code Sample 1)
 
This query joins the return of of the function with an inner join to sysobjects (to retrieve the object name and object type) and an outer join to sysindexes (to retrieve the index name, when appropiate)
 
In the function, the only parameter that I usually use is the one that specifies the database name (in the code sample it is the AdventureWorks sample database).
 
 
 
SQL Server 2005 incluye una función muy util que retorna una vista dinámica de las estadísticas de índices.
 
(Esta función es sys.dm_db_index_physical_stats )
 
Si bien esta función es muy poderosa, he encontrado que sería mucho más util si contase con alguna información adicional.
 
Luego de un poco de ensayo y error, he encontrado que el siguiente query es más adecuado.
 
 
(Ver Code Sample 1)
 
Este query hace un inner join entre el retorno de la funcíón y sysobjects (para recuperar el nombre del objeto y el tipo de objeto) y un outer join con sysindexes (para recuperar el nombre del índice, cuando corresponda)
 
En la función, el único parámetro que suelo usar es el que especifica el nombre de la base de datos (en el ejemplo de código es el de la base de datos de ejemplo AdventureWorks).
 
 
 
 
Code Sample 1
 
SELECT sysobj.name object_name,
       sysobj.xtype object_type,
       indexes.name index_name,
       index_data.*
 
FROM sys.dm_db_index_physical_stats (DB_ID(N’AdventureWorks’), NULL, NULL, NULL, NULL) index_data
 
inner join
 
 sys.sysobjects sysobj
 
on index_data.object_id = sysobj.id
 
left outer join sys.sysindexes indexes
 
on index_data.index_id = indexes.indid
 
and index_data.object_id = indexes.id