Shared posts

20 Nov 23:06

Internal value representation in PHP 7 - Part 1

by nikic@php.net (NikiC)

My last article described the improvements to the hashtable implementation that were introduced in PHP 7. This followup will take a look at the new representation of PHP values in general.

Due to the amount of material to cover, the article is split in two parts: This part will describe how the zval (Zend value) implementation differs between PHP 5 and PHP 7, and also discuss the implementation of references. The second part will investigate the realization of individual types like strings or objects in more detail.

Zvals in PHP 5

In PHP 5 the zval struct is defined as follows:

typedef struct _zval_struct {
    zvalue_value value;
    zend_uint refcount__gc;
    zend_uchar type;
    zend_uchar is_ref__gc;
} zval;

As you can see, a zval consists of a value, a type and some additional __gc information, which we’ll talk about in a moment. The value member is a union of different possible values that a zval can store:

typedef union _zvalue_value {
    long lval;                 // For booleans, integers and resources
    double dval;               // For floating point numbers
    struct {                   // For strings
        char *val;
        int len;
    } str;
    HashTable *ht;             // For arrays
    zend_object_value obj;     // For objects
    zend_ast *ast;             // For constant expressions
} zvalue_value;

A C union is a structure in which only one member can be active at a time and those size matches the size of its largest member. All members of the union will be stored in the same place in memory and will be interpreted differently depending on which one you access. If you read the lval member of the above union, its value will be interpreted as a signed integer. If you read the dval member the value will be interpreted as a double-precision floating point number instead. And so on.

To figure out which of these union members is currently in use, the type property of a zval stores a type tag, which is simply an integer:

#define IS_NULL     0      /* Doesn't use value */
#define IS_LONG     1      /* Uses lval */
#define IS_DOUBLE   2      /* Uses dval */
#define IS_BOOL     3      /* Uses lval with values 0 and 1 */
#define IS_ARRAY    4      /* Uses ht */
#define IS_OBJECT   5      /* Uses obj */
#define IS_STRING   6      /* Uses str */
#define IS_RESOURCE 7      /* Uses lval, which is the resource ID */

/* Special types used for late-binding of constants */
#define IS_CONSTANT 8
#define IS_CONSTANT_AST 9

Reference counting in PHP 5

Zvals in PHP 5 are (with a few exceptions) allocated on the heap and PHP needs some way to keep track which zvals are currently in use and which should be freed. For this purpose reference counting is employed: The refcount__gc member of the zval structure stores how often a zval is currently “referenced”. For example in $a = $b = 42 the value 42 is referenced by two variables, so its refcount is 2. If the refcount reaches zero, it means a value is unused and can be freed.

Note that the references that the refcount refers to (how many times a value is currently used) have nothing to do with PHP references (using &). I will always using the terms “reference” and “PHP reference” to disambiguate both concepts in the following. For now we’ll ignore PHP references altogether.

A concept that is closely related to reference counting is “copy on write”: A zval can only be shared between multiple users as long as it isn’t modified. In order to change a shared zval it needs to be duplicated (“separated”) and the modification will happen only on the duplicated zval.

Lets look at an example that shows off both copy-on-write and zval destruction:

$a = 42;   // $a         -> zval_1(type=IS_LONG, value=42, refcount=1)
$b = $a;   // $a, $b     -> zval_1(type=IS_LONG, value=42, refcount=2)
$c = $b;   // $a, $b, $c -> zval_1(type=IS_LONG, value=42, refcount=3)

// The following line causes a zval separation
$a += 1;   // $b, $c -> zval_1(type=IS_LONG, value=42, refcount=2)
           // $a     -> zval_2(type=IS_LONG, value=43, refcount=1)

unset($b); // $c -> zval_1(type=IS_LONG, value=42, refcount=1)
           // $a -> zval_2(type=IS_LONG, value=43, refcount=1)

unset($c); // zval_1 is destroyed, because refcount=0
           // $a -> zval_2(type=IS_LONG, value=43, refcount=1)

Reference counting has one fatal flaw: It is not able to detect and release cyclic references. To handle this PHP uses an additional cycle collector. Whenever the refcount of a zval is decremented and there is a chance that this zval is part of a cycle, the zval is written into a “root buffer”. Once this root buffer is full, potential cycles will be collected using a mark and sweep garbage collection.

In order to support this additional cycle collector, the actually used zval structure is the following:

typedef struct _zval_gc_info {
    zval z;
    union {
        gc_root_buffer       *buffered;
        struct _zval_gc_info *next;
    } u;
} zval_gc_info;

The zval_gc_info structure embeds the normal zval, as well as one additional pointer - note that u is a union, so this is really just one pointer with two different types it may point to. The buffered pointer is used to store where in the root buffer this zval is referenced, so that it may be removed from it if it’s destroyed before the cycle collector runs (which is very likely). next is used when the collector destroys values, but I won’t go into that here.

Motivation for change

Let’s talk about sizes a bit (all sizes are for 64-bit systems): First of all, the zvalue_value union is 16 bytes large, because both the str and obj members have that size. The whole zval struct is 24 bytes (due to padding) and zval_gc_info is 32 bytes. On top of this, allocating the zval on the heap adds another 16 bytes of allocation overhead. So we end up using 48 bytes per zval - although this zval may be used by multiple places.

At this point we can start thinking about the (many) ways in which this zval implementation is inefficient. Consider the simple case of a zval storing an integer, which by itself is 8 bytes. Additionally the type-tag needs to be stored in any case, which is a single byte by itself, but due to padding needs another 8 bytes.

To these 16 bytes that we really “need” (in first approximation), we add another 16 bytes handling reference counting and cycle collection and another 16 bytes of allocation overhead. Not to mention that we actually have to perform that allocation and the subsequent free, both being quite expensive operations.

This raises the question: Does a simple integer value really need to be stored as a reference-counted, cycle-collectible, heap-allocated value? The answer to this question is of course, no, this doesn’t make sense.

Here is a summary of the primary problems with the PHP 5 zval implementation:

  • Zvals (nearly) always require a heap allocation.
  • Zvals are always reference counted and always have cycle collection information, even in cases where sharing the value is not worthwhile (an integer) and it can’t form cycles.
  • Directly refcounting the zvals leads to double refcounting in the case of objects and resources. The reasons behind this will be explained in the next part.
  • Some cases involve quite an awesome amount of indirection. For example to access the object stored in a variable, a total of four pointers need to be dereferenced (which means following a pointer chain of length four). Once again this will be discussed in the next part.
  • Directly refcounting the zvals also means that values can only be shared between zvals. For example it’s not possible to share a string between a zval and hashtable key (without storing the hashtable key as a zval as well).

Zvals in PHP 7

And this brings us to the new zval implementation in PHP 7. The fundamental change that was implemented, is that zvals are no longer individually heap-allocated and no longer store a refcount themselves. Instead any complex values they may point to (like strings, arrays or objects) will store the refcount themselves. This has the following advantages:

  • Simple values do not require allocation and don’t use refcounting.
  • There is no more double refcounting. In the object case, only the refcount in the object is used now.
  • Because the refcount is now stored in the value itself, the value can be shared independently of the zval structure. A string can be used both in a zval and a hashtable key.
  • There is a lot less indirection, i.e. the number of pointers you need to follow to get to a value is lower.

Now lets take a look at how the new zval is defined:

struct _zval_struct {
    zend_value value;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar type,
                zend_uchar type_flags,
                zend_uchar const_flags,
                zend_uchar reserved)
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t var_flags;
        uint32_t next;                 // hash collision chain
        uint32_t cache_slot;           // literal cache slot
        uint32_t lineno;               // line number (for ast nodes)
        uint32_t num_args;             // arguments number for EX(This)
        uint32_t fe_pos;               // foreach position
        uint32_t fe_iter_idx;          // foreach iterator index
    } u2;
};

The first member stays pretty similar, this is still a value union. The second member is an integer storing type information, which is further subdivided into individual bytes using a union (you can ignore the ZEND_ENDIAN_LOHI_4 macro, which just ensures a consistent layout across platforms with different endianness). The important parts of this substructure are the type (which is similar to what it was before) and the type_flags, which I’ll explain in a moment.

At this point there exists a small problem: The value member is 8 bytes large and due to struct padding adding even a single byte to that grows the zval size to 16 bytes. However we obviously don’t need 8 bytes just to store a type. This is why the zval contains the additional u2 union, which remains unused by default, but can be repurposed by the surrounding code to store 4 bytes of data. The different union members correspond to different usages of this extra data slot.

The value union looks slightly different in PHP 7:

typedef union _zend_value {
    zend_long         lval;
    double            dval;
    zend_refcounted  *counted;
    zend_string      *str;
    zend_array       *arr;
    zend_object      *obj;
    zend_resource    *res;
    zend_reference   *ref;
    zend_ast_ref     *ast;

    // Ignore these for now, they are special
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        ZEND_ENDIAN_LOHI(
            uint32_t w1,
            uint32_t w2)
    } ww;
} zend_value;

First of all, note that the value union is now 8 bytes instead of 16. It will only store integers (lval) and doubles (dval) directly, everything else is a pointer. All the pointer types (apart from those marked as special above) use refcounting and have a common header defined by zend_refcounted:

struct _zend_refcounted {
    uint32_t refcount;
    union {
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,
                zend_uchar    flags,
                uint16_t      gc_info)
        } v;
        uint32_t type_info;
    } u;
};

Of course the structure contains a refcount. Additionally it contains a type, some flags and gc_info. The type just duplicates the zval type and allows the GC to distinguish different refcounted structures without storing a zval. The flags are used for different purposes with different types and will be explained for each type separately in the next part.

The gc_info is the equivalent of the buffered entry in the old zvals. However instead of storing a pointer into the root buffer it now contains an index into it. Because the root buffer has a fixed size (10000 elements) it is enough to use a 16 bit number for this instead of a 64 bit pointer. The gc_info info also encodes the “color” of the node, which is used to mark nodes during collection.

Zval memory management

I’ve mentioned that zvals are no longer individually heap-allocated. However they obviously still need to be stored somewhere, so how does this work? While zvals are still mostly part of heap-allocated structures, they are directly embedded into them. E.g. a hashtable bucket will directly embed a zval instead of storing a pointer to a separate zval. The compiled variables table of a function or the property table of an object will be zval arrays that are allocated in one chunk, instead of storing pointers to separate zvals. As such zvals are now usually stored with one level of indirection less. What was previously a zval* is now a zval.

When a zval is used in a new place, previously this meant copying a zval* and incrementing its refcount. Now it means copying the contents of a zval (ignoring u2) instead and maybe incrementing the refcount of the value it points to, if said value uses refcounting.

How does PHP know whether a value is refcounted? This cannot be determined solely based on the type, because some types like strings and arrays are not always refcounted. Instead one bit of the zvals type_info member determines whether or not the zval is refcounted. There are a number of other bits encoding properties of the type:

#define IS_TYPE_CONSTANT            (1<<0)   /* special */
#define IS_TYPE_IMMUTABLE           (1<<1)   /* special */
#define IS_TYPE_REFCOUNTED          (1<<2)
#define IS_TYPE_COLLECTABLE         (1<<3)
#define IS_TYPE_COPYABLE            (1<<4)
#define IS_TYPE_SYMBOLTABLE         (1<<5)   /* special */

The three primary properties a type can have are “refcounted”, “collectable” and “copyable”. You already know what refcounted means. Collectable means that the zval can participate in a cycle. E.g. strings are (often) refcounted, but there’s no way you can create a cycle with a string in it.

Copyability determines whether the value needs to copied when a “duplication” is performed. A duplication is a hard copy, e.g. if you duplicate a zval that points to an array, this will not simply increase the refcount on the array. Instead a new and independent copy of the array will be created. However for some types like objects and resources even a duplication should only increment the refcount - such types are called non-copyable. This matches the passing semantics of objects and resources (which are, for the record, not passed by reference).

The following table shows the different types and what type flags they use. “Simple types” refers to types like integers or booleans that don’t use a pointer to a separate structure. A column for the “immutable” flag is also present, which is used to mark immutable arrays and will be discussed in more detail in the next part.

                | refcounted | collectable | copyable | immutable
----------------+------------+-------------+----------+----------
simple types    |            |             |          |
string          |      x     |             |     x    |
interned string |            |             |          |
array           |      x     |      x      |     x    |
immutable array |            |             |          |     x
object          |      x     |      x      |          |
resource        |      x     |             |          |
reference       |      x     |             |          |

At this point, lets take a look at two examples of how the zval management works in practice. First, an example using integers based off the PHP 5 example from above:

$a = 42;   // $a = zval_1(type=IS_LONG, value=42)

$b = $a;   // $a = zval_1(type=IS_LONG, value=42)
           // $b = zval_2(type=IS_LONG, value=42)

$a += 1;   // $a = zval_1(type=IS_LONG, value=43)
           // $b = zval_2(type=IS_LONG, value=42)

unset($a); // $a = zval_1(type=IS_UNDEF)
           // $b = zval_2(type=IS_LONG, value=42)

This is pretty boring. As integers are no longer shared, both variables will use separate zvals. Don’t forget that these are now embedded rather than allocated, which I try to signify by writing = instead of a -> pointer. Unsetting a variable will set the type of the corresponding zval to IS_UNDEF. Now consider a more interesting case where a complex value is involved:

$a = [];   // $a = zval_1(type=IS_ARRAY) -> zend_array_1(refcount=1, value=[])

$b = $a;   // $a = zval_1(type=IS_ARRAY) -> zend_array_1(refcount=2, value=[])
           // $b = zval_2(type=IS_ARRAY) ---^

// Zval separation occurs here
$a[] = 1   // $a = zval_1(type=IS_ARRAY) -> zend_array_2(refcount=1, value=[1])
           // $b = zval_2(type=IS_ARRAY) -> zend_array_1(refcount=1, value=[])

unset($a); // $a = zval_1(type=IS_UNDEF) and zend_array_2 is destroyed
           // $b = zval_2(type=IS_ARRAY) -> zend_array_1(refcount=1, value=[])

Here each variable still has a separate (embedded) zval, but both zvals point to the same (refcounted) zend_array structure. Once a modification is done the array needs to be duplicated. This case is similar to how things work in PHP 5.

Types

Lets take a closer look at what types are supported in PHP 7:

// regular data types
#define IS_UNDEF                    0
#define IS_NULL                     1
#define IS_FALSE                    2
#define IS_TRUE                     3
#define IS_LONG                     4
#define IS_DOUBLE                   5
#define IS_STRING                   6
#define IS_ARRAY                    7
#define IS_OBJECT                   8
#define IS_RESOURCE                 9
#define IS_REFERENCE                10

// constant expressions
#define IS_CONSTANT                 11
#define IS_CONSTANT_AST             12

// internal types
#define IS_INDIRECT                 15
#define IS_PTR                      17

This list is quite similar to what was used in PHP 5, however there are a few additions:

  • The IS_UNDEF type is used in places where previously a NULL zval pointer (not to be confused with an IS_NULL zval) was used. For example, in the refcounting examples above the IS_UNDEF type is set for variables that have been unset.
  • The IS_BOOL type has been split into IS_FALSE and IS_TRUE. As such the value of the boolean is now encoded in the type, which allows the optimization of a number of type-based checks. This change is transparent to userland, where this is still a single “boolean” type.
  • PHP references no longer use an is_ref flag on the zval and use a new IS_REFERENCE type instead. How this works will be described in the next section.
  • The IS_INDIRECT and IS_PTR types are special internal types.

The IS_LONG type now uses a zend_long value instead of an ordinary C long. The reason behind this is that on 64-bit Windows (LLP64) a long is only 32-bit wide, so PHP 5 ended up always using 32-bit numbers on Windows. PHP 7 will allow you to use 64-bit numbers if you’re on an 64-bit operating system, even if that operating system is Windows.

Details of the individual zend_refcounted types will be discussed in the next part. For now we’ll only look at the implementation of PHP references.

References

PHP 7 uses an entirely different approach to handling PHP & references than PHP 5 (and I can tell you that this change is one of the largest source of bugs in PHP 7). Lets start by taking a look at how PHP references used to work in PHP 5:

Normally, the copy-on-write principle says that before modifying a zval it needs to be separated, in order to make sure you don’t end up changing the value for every place sharing the zval. This matches by-value passing semantics.

For PHP references this does not apply. If a value is a PHP reference, you want it to change for every user of the value. The is_ref flag that was part of PHP 5 zvals determined whether a value is a PHP reference and as such whether it required separation before modification. An example:

$a = [];  // $a     -> zval_1(type=IS_ARRAY, refcount=1, is_ref=0) -> HashTable_1(value=[])
$b =& $a; // $a, $b -> zval_1(type=IS_ARRAY, refcount=2, is_ref=1) -> HashTable_1(value=[])

$b[] = 1; // $a = $b = zval_1(type=IS_ARRAY, refcount=2, is_ref=1) -> HashTable_1(value=[1])

One significant problem with this design is that it’s not possible to share a value between a variable that’s a PHP reference and one that isn’t. Consider the following example:

$a = [];  // $a         -> zval_1(type=IS_ARRAY, refcount=1, is_ref=0) -> HashTable_1(value=[])
$b = $a;  // $a, $b     -> zval_1(type=IS_ARRAY, refcount=2, is_ref=0) -> HashTable_1(value=[])
$c = $b   // $a, $b, $c -> zval_1(type=IS_ARRAY, refcount=3, is_ref=0) -> HashTable_1(value=[])

$d =& $c; // $a, $b -> zval_1(type=IS_ARRAY, refcount=2, is_ref=0) -> HashTable_1(value=[])
          // $c, $d -> zval_1(type=IS_ARRAY, refcount=2, is_ref=1) -> HashTable_2(value=[])
          // $d is a reference of $c, but *not* of $a and $b, so the zval needs to be copied
          // here. Now we have the same zval once with is_ref=0 and once with is_ref=1.

$d[] = 1; // $a, $b -> zval_1(type=IS_ARRAY, refcount=2, is_ref=0) -> HashTable_1(value=[])
          // $c, $d -> zval_1(type=IS_ARRAY, refcount=2, is_ref=1) -> HashTable_2(value=[1])
          // Because there are two separate zvals $d[] = 1 does not modify $a and $b.

This behavior of references is one of the reasons why using references in PHP will usually end up being slower than using normal values. To give a less-contrived example where this is a problem:

$array = range(0, 1000000);
$ref =& $array;
var_dump(count($array)); // <-- separation occurs here

Because count() accepts its value by-value, but $array is a PHP reference, a full copy of the array is done before passing it off to count(). If $array weren’t a reference, the value would be shared instead.

Now, let’s switch to the PHP 7 implementation of PHP references. Because zvals are no longer individually allocated, it is not possible to use the same approach that PHP 5 used. Instead a new IS_REFERENCE type is added, which uses the zend_reference structure as its value:

struct _zend_reference {
    zend_refcounted   gc;
    zval              val;
};

So essentially a zend_reference is simply a refcounted zval. All variables in a reference set will store a zval with type IS_REFERENCE pointing to the same zend_reference instance. The val zval behaves like any other zval, in particular it is possible to share a complex value it points to. E.g. an array can be shared between a variable that is a reference and another that is a value.

Lets go through the above code samples again, this time looking at the PHP 7 semantics. For the sake of brevity I will stop writing the individual zvals of the variables and only show what structure they point to.

$a = [];  // $a                                     -> zend_array_1(refcount=1, value=[])
$b =& $a; // $a, $b -> zend_reference_1(refcount=2) -> zend_array_1(refcount=1, value=[])

$b[] = 1; // $a, $b -> zend_reference_1(refcount=2) -> zend_array_1(refcount=1, value=[1])

The by-reference assignment created a new zend_reference. Note that the refcount is 2 on the reference (because two variables are part of the PHP reference set), but the value itself only has a refcount of 1 (because one zend_reference structure points to it). Now consider the case where references and non-references are mixed:

$a = [];  // $a         -> zend_array_1(refcount=1, value=[])
$b = $a;  // $a, $b,    -> zend_array_1(refcount=2, value=[])
$c = $b   // $a, $b, $c -> zend_array_1(refcount=3, value=[])

$d =& $c; // $a, $b                                 -> zend_array_1(refcount=3, value=[])
          // $c, $d -> zend_reference_1(refcount=2) ---^
          // Note that all variables share the same zend_array, even though some are
          // PHP references and some aren't.

$d[] = 1; // $a, $b                                 -> zend_array_1(refcount=2, value=[])
          // $c, $d -> zend_reference_1(refcount=2) -> zend_array_2(refcount=1, value=[1])
          // Only at this point, once an assignment occurs, the zend_array is duplicated.

The important difference to PHP 5 is that all variables were able to share the same array, even though some were PHP references and some weren’t. Only once some kind of modification is performed the array will be separated. This means that in PHP 7 it’s safe to pass a large, referenced array to count(), it is not going to be duplicated. References will still be slower than normal values, because they require allocation of the zend_reference structure (and indirection through it) and are usually not handled in the fast-path of engine code.

Wrapping up

To summarize, the primary change that was implemented in PHP 7 is that zvals are no longer individually heap-allocated and no longer store a refcount themselves. Instead any complex values they may point to (like strings, array or objects) will store the refcount themselves. This usually leads to less allocations, less indirection and less memory usage.

In the second part of this article the remaining complex types will be discussed.

16 May 21:45

Robins Tharakan: Basic OLAP Support in PostgreSQL

While reviewing an existing application, I thought it'd be worthwhile to review how good / bad PostgreSQL is in terms of OLAP. This (growing) post is going to be my (un)learning of how ready is PostgreSQL. Row Numbering Support: Yes.  Use: Row_Number() function numbers rows generated in a result-set. Example: SELECT   row_number() OVER (ORDER BY marks DESC) AS rn,  nameFROM x; Review: Some
16 May 21:38

Robins Tharakan: Postgres finally has CUBE / ROLLUP / GROUPING SETS !

Finally ! A *much* awaited feature, this attempt at adding the GROUPING SETS / ROLLUP / CUBE feature to PostgreSQL has been in the works for about a year (besides the so many in the past decade and a half that didn't get through), and thankfully this has finally got the approval of the powers that be, so the upcoming Postgres 9.5 would finally have this long pending SQL feature. MSSQL and
16 May 21:35

Joel Jacobson: Finding missing foreign keys

By coincidence I stumbled upon a table where one of its columns didn’t have a foreign key. I found this strange, since our convention is to always add foreign keys when appropriate.

But humans are humans, and will eventually forget to add a foreign key, so we better have both belt and suspenders and find a way to deal with this inevitable problem in an efficient way.

It would be a tedious job to manually look for missing foreign keys in all tables and columns.
But if you’re lucky enough to have the pleasure of working with a system where all tables and columns have been given their names by following a strict naming convention, it might be possible to fully automate the task.

This is the namning convention we use:

  1. Table names are always in plural form with a tailing “s”, e.g. users
  2. Primary key column names are always equal to the table name in singular form with a tailing id, e.g. userid.
  3. Foreign key columns are always equal to the primary key they are referecning, e.g. transactions.userid -> users.userid

This means you always know based on the column name alone, what table if any that column might be referencing.

Example: If table transactions have a column named userid, and there is a table where userid is also the primary key, but if there isn’t any foreign key on transactions.userid, then it’s a missing foreign key, or else someone has not followed the namning convention.

Thanks to PostgreSQL’s fantastic pg_catalog system tables, we can write a query which uses the rules from the naming convention and returns all the columns which appears to be missing foreign keys. It doesn’t support multi-column keys, but we don’t have many of those, so it’s not a problem in my case.

Thanks to the view below, I automatically found three more missing foreign keys of the same type, which saves me many hours of boring work today.

pg1:joel=#* SELECT * FROM view_missing_foreign_keys;
 nspname |      relname    | attname 
---------+-----------------+---------
 public  | transactions    | userid
 public  | someohtertable1 | userid
 public  | someothertable2 | userid
(5 rows)

I’m posting the view I wrote here in hope it might be useful for others with a similar naming convention, and/or to inspire others to include the table name in their column names used as primary/foreign keys.

CREATE OR REPLACE VIEW view_missing_foreign_keys AS
SELECT
    pg_catalog.pg_namespace.nspname,
    pg_catalog.pg_class.relname,
    pg_catalog.pg_attribute.attname
FROM pg_catalog.pg_namespace
INNER JOIN pg_catalog.pg_class ON (pg_catalog.pg_class.relnamespace = pg_catalog.pg_namespace.oid)
INNER JOIN pg_catalog.pg_attribute ON (pg_catalog.pg_attribute.attrelid = pg_catalog.pg_class.oid)
WHERE pg_catalog.pg_class.relkind = 'r'
AND pg_catalog.pg_attribute.attnum > 0
AND NOT pg_catalog.pg_attribute.attisdropped
AND pg_catalog.pg_namespace.nspname NOT IN ('pg_toast','information_schema','pg_catalog')
AND pg_catalog.pg_attribute.attname LIKE '%id'
AND EXISTS (
    -- The column is PRIMARY KEY in some table
    SELECT 1 FROM pg_catalog.pg_constraint
    WHERE pg_catalog.pg_constraint.contype = 'p'
    AND pg_catalog.pg_get_constraintdef(pg_catalog.pg_constraint.oid) = format('PRIMARY KEY (%s)',pg_catalog.pg_attribute.attname)
)
AND NOT EXISTS (
    -- There is no FOREIGN KEY on this column
    SELECT 1 FROM pg_catalog.pg_constraint
    WHERE pg_catalog.pg_constraint.contype = 'f'
    AND pg_catalog.pg_constraint.conrelid = pg_catalog.pg_class.oid
    AND pg_catalog.pg_get_constraintdef(pg_catalog.pg_constraint.oid) LIKE (format('FOREIGN KEY (%s)',pg_catalog.pg_attribute.attname) || '%')
)
AND NOT EXISTS (
    -- This column is not the PRIMARY KEY of it's own table,
    -- since if it was, we wouldn't require a FOREIGN KEY on it
    SELECT 1 FROM pg_catalog.pg_constraint
    WHERE pg_catalog.pg_constraint.contype = 'p'
    AND pg_catalog.pg_constraint.conrelid = pg_catalog.pg_class.oid
    AND pg_catalog.pg_get_constraintdef(pg_catalog.pg_constraint.oid) = format('PRIMARY KEY (%s)',pg_catalog.pg_attribute.attname)
)
ORDER BY
pg_catalog.pg_namespace.nspname,
pg_catalog.pg_class.relname,
pg_catalog.pg_attribute.attnum

16 May 21:20

mérito

by Francisco Nunes

Quase ninguém se apercebe, por si próprio, do mérito de outra pessoa.

(Jean de La Bruyère)

Send to Kindle
16 May 21:20

vida

by Francisco Nunes

A vida é uma pedra de amolar: desgasta-nos ou afia-nos, conforme o metal de que somos feitos.

(Bernard Shaw)

Send to Kindle
16 May 21:18

The Brief: May 11th, 2015

by The Brief

This week: Venom strikes, the House denies the NSA, and United Airlines tries to make nice with security researchers.

OpenDNS’s weekly Infographic of security related events

The post The Brief: May 11th, 2015 appeared first on OpenDNS Blog.

16 May 21:15

Shaun M. Thomas: PG Phriday: 10 Ways to Ruin Performance: Forgetting to EXPLAIN

Yesterday I gave the developers at my company what I call a DBA Chat. It’s something I try to do every month to keep them apprised on various features, caveats, performance considerations, and so on. I find that educating the folks who regularly work with the database does wonders for application performance and my sanity. The benefit of this long format is that I can go over more information than a time constrained set of slides.

This month we went over one of my slightly older talks, and even though the material was three years old from my perspective, it was all new to them. And why not? Developers have a job to do, and while they do work with the database, it generally isn’t their responsibility to research the voluminous minutia and quirks commonly associated with a specific platform. That’s my job.

So here is the first of a ten part series on anti-patterns that can annihilate PGDB (PostgreSQL) performance, some of which are outright insidious. This week we’ll start slow with a short discussion on the importance of EXPLAIN. This is PGDB’s first line of defense against bad queries, and it’s overlooked more than you might expect, as well as somewhat difficult to interpret for the uninformed. I’ll probably have a more in-depth version of this article in the future for truly interpreting EXPLAIN output, but this time we’ll be focusing on using it in general.

First, we need to set up a use case to illustrate a couple of variants. Two tables with a close relationship should do it:

CREATE TABLE sys_product
(
    product_id   SERIAL  PRIMARY KEY,
    prod_name    TEXT    NOT NULL,
    descr        TEXT    NOT NULL DEFAULT now()
);
 
INSERT INTO sys_product (prod_name, descr)
SELECT 'Product ' || a.id::TEXT, 'It does stuff.'
  FROM generate_series(1, 1000) a(id);
 
CREATE TABLE sys_order
(
    order_id     SERIAL       NOT NULL,
    product_id   INT          NOT NULL,
    item_count   INT          NOT NULL,
    order_dt     TIMESTAMPTZ  NOT NULL DEFAULT now()
);
 
INSERT INTO sys_order (product_id, item_count)
SELECT (a.id % 100) + 1, (random()*10)::INT + 1
  FROM generate_series(1, 1000000) a(id);
 
ALTER TABLE sys_order ADD CONSTRAINT pk_order_order_id
      PRIMARY KEY (order_id);
 
CREATE INDEX idx_order_product_id
    ON sys_order (product_id);

Now, EXPLAIN comes in many variants, but there are two that directly concern us: EXPLAIN and EXPLAIN ANALYZE. The first will list the estimated cost of the query, which indexes will be used, how many rows are estimated to match, and so on. The second will physically execute the query and obtain the actual match counts along with elapsed time for each step. Here’s what that looks like:

EXPLAIN SELECT * FROM sys_order;
 
                             QUERY PLAN                             
--------------------------------------------------------------------
 Seq Scan ON sys_order  (cost=0.00..16370.00 ROWS=1000000 width=20)
 
EXPLAIN ANALYZE * FROM sys_order;
 
                             QUERY PLAN
--------------------------------------------------------------------
 Seq Scan ON sys_order  (cost=0.00..16370.00 ROWS=1000000 width=20)
     (actual TIME=0.006..465.956 ROWS=1000000 loops=1)
 Planning TIME: 0.041 ms
 Execution TIME: 886.766 ms

I’ve reformatted the results a bit, but the difference in output should be readily apparent. Interpreting this isn’t even very difficult at a high level. Begin by examining the first set of parenthesis in both results, and ignore the cost for now; it’s essentially meaningless noise intended primarily for the query planner. What concerns us is the row count, which is one-million in this case. It’s how many rows PGDB estimated it would return.

The second set of parenthesis in the ANALYZE version gives the actual values obtained by executing the query. In this case, the query matched exactly one million rows, and took 465ms to do so. If you don’t know already, a Seq Scan is short for Sequence Scan, and means the database read the entire contents of the table. That should be expected since we didn’t provide any WHERE clause to restrict the results.

Pay special attention to the rows section, because it can be a major clue to ferret out performance problems. PGDB keeps statistics on table contents, but these are necessarily sparse and in aggregate form. There’s also currently no way to correlate statistics between tables, so a JOIN can drastically affect row estimates, and since it’s how cost is calculated, can result in wildly different execution plans than the optimal case.

So we know that a Sequence Scan will read the entire table. Hopefully that point by itself is an obvious indication that doing so is generally undesirable. If we are querying a table with 300-million rows, reading its entire contents is almost never the intended approach, nor should it be.

What other variants should we look for? How about an Index Scan:

EXPLAIN ANALYZE
 SELECT * FROM sys_order
  WHERE order_id BETWEEN 5 AND 10;
 
                             QUERY PLAN                             
--------------------------------------------------------------------
 INDEX Scan USING pk_order_order_id ON sys_order 
       (cost=0.42..8.53 ROWS=5 width=20)
       (actual TIME=0.008..0.012 ROWS=6 loops=1)
   INDEX Cond: ((order_id >= 5) AND (order_id <= 10))
 Planning TIME: 0.112 ms
 Execution TIME: 0.041 ms

We can see a few things from these results:

  • Estimated row count is not the same as how many actually matched.
  • The rows were found with an Index Scan.
  • Execution time was much faster than the Sequence Scan case.

It’s not critical for estimated and actual row counts to match, but they should be within an order of magnitude of each other. If this isn’t the case, it’s likely the planner has insufficient information about the data, or statistics are wrong, or data is more closely correlated than it assumed, resulting in less eliminations than it expected, and so on. This is one of the first ways to see that there could be a problem with a query’s execution plan.

Regarding the index scan, matching five or six values against a million isn’t a big deal, but there are limits. Indexes in PGDB work, in most cases, by fetching very quickly from the index file and then consulting the base table file to retrieve the matching row. This results in two random seeks to the underlying storage subsystem, and if you don’t know already, random reads are usually slower than a sequential read by an order of magnitude or more. This doesn’t apply for SSD devices, but most databases don’t reside entirely on such expensive hardware just yet.

That’s why the rows element of the EXPLAIN is important in respect to index scans; it’s entirely possible that reading the entire table would be faster than performing several hundred thousand index and table seeks. For the most part, the planner knows this, but calculating this relationship is still more of an art than a science. Consequentially, sometimes the planner guesses wrong, and query performance will suffer. By using EXPLAIN, we can see what happened, and find a way around the problem.

Let’s turn this into a JOIN to make it a little more fun:

EXPLAIN ANALYZE
 SELECT o.*
   FROM sys_order o
   JOIN sys_product p USING (product_id)
  WHERE p.product_id IN (5, 10, 20);
 
                             QUERY PLAN                             
--------------------------------------------------------------------
 Nested Loop  (cost=188.87..15715.29 ROWS=3614 width=20)
              (actual TIME=2.317..66.377 ROWS=30000 loops=1)
   ->  INDEX ONLY Scan USING sys_product_pkey ON sys_product p
             (cost=0.28..24.88 ROWS=3 width=4)
             (actual TIME=0.033..0.056 ROWS=3 loops=1)
         INDEX Cond: (product_id = ANY ('{5,10,20}'::INTEGER[]))
         Heap Fetches: 3
   ->  Bitmap Heap Scan ON sys_order o
              (cost=188.59..5130.14 ROWS=10000 width=20)
              (actual TIME=2.105..13.037 ROWS=10000 loops=3)
         Recheck Cond: (product_id = p.product_id)
         Heap Blocks: exact=19107
         ->  Bitmap INDEX Scan ON idx_order_product_id
               (cost=0.00..186.09 ROWS=10000 width=0)
               (actual TIME=1.170..1.170 ROWS=10000 loops=3)
               INDEX Cond: (product_id = p.product_id)
 Planning TIME: 0.274 ms
 Execution TIME: 78.834 ms

Wow! Luckily we can ignore most of this output by focusing on the Nested Loop at the top. This is really just a fancy way of saying “For Loop”. This is also the outermost level of the query plan, as EXPLAIN output is somewhat inverted. Each nesting level is a prerequisite step to construct the layer above it, somewhat like an onion.

If we look at the loops section in the actual execution information for each of these blocks, we can see that the loop really ran three times (the nested entries show loops=3), even though it says it only ran once. The distinction here is that the outermost loop ran once, and it is somewhat misleading.

But basically PGDB fetched three rows from the product table, and looped through memory using those values to find corresponding data in the order table. And as you might imagine, this is fine for small numbers of loops. There are occasions where PostgreSQL will underestimate row counts and opt for a Nested Loop, when really there are hundreds of thousands or millions of potential loop candidates, and it will use the same process as we saw above.

In such cases, execution time is extremely adversely impacted. Why? Nested loops and other types of loop are computationally expensive. As far as the database is concerned, it’s almost always faster to create an in-memory object of some kind and perform a bulk operation on all values at once, such as a comparison or a transformation. Nested loops make this impossible, and impose a series of calculations and function callbacks on each individual row set in O(n*m) scale. For small loop counts, this is often cheaper than building those afore-mentioned memory representations, but scale is vastly important.

Here’s an example of what I mean, and we don’t even need the whole EXPLAIN output to see the problem:

EXPLAIN ANALYZE
 SELECT o.*
   FROM sys_order o
   JOIN sys_product p USING (product_id)
  WHERE p.product_id BETWEEN 6 AND 10;
 
                             QUERY PLAN                             
--------------------------------------------------------------------
 Nested Loop  (cost=188.20..17588.55 ROWS=4000 width=20)
              (actual TIME=2.542..105.528 ROWS=50000 loops=1)

This should be ringing alarm bells. Note that PGDB expected 4000 rows, and actually matched 50,000. At this scale that isn’t really a major issue, but expand these tables by a couple orders of magnitude, and that can quickly change. I’ve seen several queries where the estimated row count is five orders of magnitude less than the actual results. Disk fetches are exceptionally expensive, and multiplying random read times by a few thousand adds up. If a query is using a nested loop is being slow, always check the statistics based on EXPLAIN output; you may need to revise your query to compensate.

Those are the Big Three in a nutshell. Even if you know nothing else about PGDB, how it plans or executes queries, or even databases in general, try to examine the execution path of your queries whenever possible. This is especially true if you have a query that’s unexpectedly slow. Being cognizant of potential issues prevents malformed queries or structures from worming their way permanently into a production application, and that’s something we can all celebrate.

Happy querying.

16 May 15:49

Autocomplete just got a whole lot better

by benogle

With the release of Atom 0.199.0, we're happy to be bundling the first community-developed package as part of the official Atom release: autocomplete-plus.

autocomplete-plus is the work of @joefitzgerald @saschagehlich, and @park9140, who improved on the default autocomplete with better suggestion quality, suggestions displayed as you type, and extensibility. autocomplete-plus spread like wildfire; users downloaded it 330,000 times, making it the most popular package in the entire Atom ecosystem. Now autocomplete-plus will replace the original autocomplete package.

What's new?

Out of the box, you will notice two large changes: suggestions show as you are typing, and there are colored icons next to each suggestion.

symbols

Symbols from all open documents will be suggested when typing — functions, variables, and even words in strings. The icons indicate the suggestion type: right arrow for a snippet, v for variable, c for class, f for function, etc.

Beyond symbol suggestions, Atom 0.199.0 includes deeper intelligence for HTML, CSS, the Atom API, and snippets.

HTML

There is now intelligent tag, attribute, and attribute-value suggestions in HTML and many HTML based templating languages, like PHP and ERB. Along with the suggestions are descriptions for tags and attributes, plus links to the MDN documentation.

ac-html-2

CSS

Writing CSS is a lot easier in 0.199.0 with suggestions for CSS properties and property values. The CSS suggestions also work in CSS-derived languages like LESS and SCSS. LESS and SCSS language support has improved as well, notably all builtin LESS and SCSS functions (e.g. darken or fadeout) are displayed along with their doc strings and links to the documentation.

ac-css-2

Atom API Suggetions

Atom now displays suggestions for the atom global. As with the HTML and CSS suggestions, each Atom API suggestion displays the description of the object with a link back to the atom.io documentation. Just type atom. in any JavaScript or CoffeeScript file in your Atom package to display the suggestions.

atom-api

Snippets

Snippets are useful shortcuts that generate common code like requires, functions, loops, etc. Without snippet suggestions, not even we on the Atom team would know about all the cool snippets available. Snippets are indicated by the right arrow tab-stop icon, and exist for many contexts. To get snippet suggestions, just type!

snip

Extensibility

Overall, the part of autocomplete-plus that has the core team most excited is its extensibility. autocomplete-plus is able to consume other Atom packages that define an autocomplete provider. Each provider can provide suggestions for a specific language, e.g. haskell, or a specific context, such as completing system paths when importing a module. All of the intelligent behavior for HTML, CSS, Atom API, and Snippets now bundled with the official Atom release were written as providers: autocomplete-html, autocomplete-css, autocomplete-atom-api, and autocomplete-snippets.

In addition to the bundled providers, one of the many community-built provider packages can bring greater intelligence to your favorite language: JavaScript, TypeScript, python, go lang, C#, and others. See the full listing of available providers on the autocomplete-plus wiki.

The atom-ternjs provider:

tern

The autocomplete-plus-python-jedi provider:

jedi

Writing your own provider

Check out the provider API documentation to get started and use one of the many providers listed above as an example.

There are static analysis tools and autocomplete systems for many languages that can be integrated into an autocomplete-plus provider: ternjs for JavaScript, jedi for python, rsense for ruby, racer for rust, clang for c/c++ and objective-c, etc. With systems like these, the autocomplete-plus provider becomes the glue code between Atom and the deeper analysis system.

Improving language support

If writing a provider seems like too much, the suggestions from the default provider can be improved by tweaking settings in each language's package. Check out the SymbolProvider API documentation, and a language package example for more info.

Onward

We're excited about autocomplete-plus not just for the improved experience it will provide, but also for the process by which it was developed. Our philosophy with Atom has been to optimize for evolution, and the transformation of autocomplete-plus from a community fork of a simple package to the full-featured, extensible system we're bundling today is a model for how we see Atom advancing. We believe that extensibility and modularity are not just nice-to-haves, but are actually essential to the development of complex shared infrastructure like Atom. The strength of Atom depends on the strength of Atom's community, and we think autocomplete-plus is a fantastic example of what a smart, engaged community can do.

Again, a big thank you to @joefitzgerald @saschagehlich, and @park9140 for creating autocomplete-plus. And thank you to all the provider authors out there, specifically @basarat, @tststs, @tinloaf, @eqot, @ctolkien for help with the API and keeping their providers up to date given all the recent movement!

15 May 18:07

New in the Wolfram Language: Cryptography

by Christopher Wolfram

Cryptography has existed for thousands of years, but before serious computers came around, only specific kinds of messages were worth encrypting. Now that computers routinely manage a huge amount of communication, there is little downside to invisibly applying cryptography to almost everything, from verifying where information comes from to exchanging information securely. Because of cryptography’s widespread use, we added the basic building blocks of modern cryptography to the Wolfram Language with functions using OpenSSL for key generation, symmetric encryption/decryption, and asymmetric encryption/decryption.

The notion of a key in cryptography is similar to the way we use keys in everyday life, in that only someone with a certain key can perform a certain action. One very simple way of arranging this is to have a single key that is used to encrypt as well as decrypt, much like the locking and unlocking of a door:

Making one key to encrypt and decrypt

This is called symmetric cryptography because both the party encrypting and the party decrypting share a single key. Symmetric cryptography is great for encrypting large amounts of information very securely and very efficiently, but there needs to be a preexisting relationship between both parties to be able to share a key in the first place. Asymmetric cryptography does not require a preexisting relationship—both parties have different keys, typically a public key and a private key. Something encrypted with the public key can only be decrypted with the private one:

Decrypting with a private key versus a public key

Asymmetric cryptography is usually used for exchanging small amounts of information, for instance, a symmetric key that can then be used for transferring a larger message.

These functions have been designed to be usable by those without a technical understanding of cryptography, but still retain enough flexibility to satisfy those who do. For example, to generate a secure symmetric key, you could simply run this:

Creating a secure symmetric key

But if you wanted to generate a more specific kind of key, you could do this:

Generating a specific secure symmetric key

This flexibility is carried over to encryption and decryption, as those functions can use any generated key:

Flexibility with encryption and decryption functions

In the Wolfram Language, encryption isn’t limited to text. You can actually encrypt any expression:

Encrypting an expression

One of the main motivations for adding cryptographic functionality to the Wolfram Language was the arrival of the Wolfram Cloud. The cloud is inherently communication based. Both in the internal workings of the cloud and in almost anything utilizing it, cryptography has the potential to play an important role in ensuring those communications are secure. Hopefully our combination of ease of use and power, as well as the broad user base of the Wolfram Language, will result in lots of interesting new protocols as well as a more secure cloud.

The (new) cryptographic functionality is supported in Version 10.1 of the Wolfram Language and Mathematica, and is rolling out soon in all other Wolfram products.

This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. This product includes cryptographic software written by Eric Young.

Download this post as a Computable Document Format (CDF) file.

14 May 19:28

Conversor recursivo de charset

by elcio

Fiz para um amigo um script de conversão de charset simplezinho e, como pode ser útil para mais alguém, resolvi abrir o código:

charsetconv

Era mais fácil fazer com shell script, é verdade, mas eu precisava que fosse multiplataforma.

O post Conversor recursivo de charset apareceu primeiro em Elcio Ferreira - fechaTag.

13 May 14:01

Spam: provando que Bill Gates estava errado

by Mariana Sousa

No Fórum Mundial de Economia de 2004, Bill Gates afirmou que em dois anos o spam seria um problema resolvido. Ele se referia a 2006 como o ano em que todos estaríamos livres dos spams definitivamente. Mas aqui estamos, mais de 10 anos depois, sofrendo diariamente com essa praga virtual, seja em nossos desktops, notebooks ou smartphones.

Então, fica a pergunta: se até o Bill Gates se enganou sobre o potencial de reciclagem e renovação dos spammers, será que algum dia ficaremos completamente livres dos e-mails indesejados? Mas, antes de falarmos mais sobre esse assunto, vamos ver alguns fatos e curiosidades sobre esse universo.

Um pouquinho de história: o primeiro spam a gente nunca esquece

Existem diversas teorias sobre quem enviou o primeiro spam e as mais famosas afirmam que Gary Thuerk, ex-gerente de marketing da Digital Equipment Corporation (DEC) pode ser chamado de pai do spam.

Gary foi o primeiro profissional de marketing que idealizou e operacionalizou a divulgação de seus produtos para uma lista de potenciais clientes em 1978. Ele divulgou uma nova linha de computadores e produtos de informática para aproximadamente 400 endereços de email, o que na época representava quase todos os usuários da ARPANET da costa oeste americana.

Porém, outras pessoas acreditam que o primeiro spam foi disparado por Laurence Canter e Martha Siegel, advogados de imigração norte-americanos em 1994 e ficou conhecido como Green Card Lotery Spam. Considerado o primeiro spam em larga escala, esta mensagem foi disparada para 6 mil e-mails através de um software/código escrito por um hacker contratado exclusivamente para esse objetivo.

Os spammers – quem são e o que fazem

Segundo a Spamhaus, uma das maiores organizações que lutam contra os spams no mundo, dos 10 maiores spammers em atuação, apenas 3 tiveram suas identidades descobertas: Yair Shalev (Kobeni Solutions), Dante Jimenez (Aiming Invest aka Neo Web Marketing) e Michael Lindsay (iMedia Networks aka Dialwave International e Data Advantage). Todos dos EUA, mais especificamente da Flórida.

O que você já sabe sobre antispam

A maioria dos profissionais responsáveis pelos e-mails, pelas redes e pela cibersegurança já reconhece a importância dos filtros antispam e sabe o quanto essa ferramenta pode colaborar no aumento da segurança dos ambientes de e-mail corporativos. Eles sabem que além de prevenir contra ataques, o antispam também pode contribuir na economia de recursos de hardware, banda, economizando tempo e dinheiro.

A cada dia que passa, as ferramentas antispam estão mais acessíveis, automatizadas e independentes, exigindo cada vez menos recursos financeiros (tanto para aquisição quanto para a manutenção) e tempo dos administradores, que só precisam configurar a ferramenta uma única vez. E grande parte dessa evolução deve-se pela popularização da modalidade em nuvem dos antispams.

Veja o caminho que um e-mail percorre até chegar a caixa de entrada de seus usuários:

artigo - unodata - fluxograma2

O antispam na nuvem reduz consideravelmente o custo de aquisição e proporciona uma mobilidade/ acessibilidade impensável com a modalidade de software.

Entretanto, muitos profissionais ainda não migraram seus sistemas para a nuvem alegando que a falta de controle do perímetro e a perda da privacidade ainda são fatores que geram duvidas na confiabilidade desse sistema, influenciando negativamente na escolha entre as modalidades.

Por isso é tão importante as organizações que lidam com segurança de email oferecerem soluções híbridas, para que os técnicos e analistas escolham a modalidade que mais se encaixa nas suas necessidades e restrições.

Pagar ou não pagar, eis a questão

Vale lembrar que existem diversas opções de antispam gratuitos, como por exemplo, SpamAssassin e Spam Fighter, além das opções embutidas nas plataformas de e-mail, como o Zimbra, Exchange e Google Apps. Porém, dependendo do volume de spam recebido, tais opções apresentam sérias limitações e em alguns casos, não apresentam resultados satisfatórios.

As opções gratuitas são mais indicadas para aqueles profissionais que possuem grande conhecimento e experiência na área e que já possuem certo grau de autonomia em relação à configurações básicas, como por exemplo: whitelist, greylist, blacklists, RBL e quarentena. Agora, se você não é especialista no assunto e não tem muito tempo para aprender, as opções pagas oferecem um grande diferencial: o suporte e as atualizações diárias. Em alguns casos, ter uma equipe para solucionar eventuais problemas pode fazer toda a diferença…

O que você (talvez) não sabia sobre antispam

O que você talvez não saiba sobre as ferramentas antispam é que, além de servir de gateway, pode exercer diversas funções complementares, como por exemplo, backup e criptografia de e-mails confidenciais, além de facilitar auditorias e proteger a reputação do seu IP/domínio com os filtros de saída.

Backup de e-mails: precaução ou obrigação?

Se você trabalha com provedores de internet ou de aplicações, você já deve saber que o Marco Civil da Internet, aprovado em abril do ano passado, obriga os provedores a guardar os registros de conexão por 1 ano e os registros de acesso a aplicações de internet pelo período de 6 meses, conforme as Subseções I e III do Capítulo III da Lei Nº 12.965, de 23 de abril de 2014.

E a pergunta que não quer calar: um dia, os spams vão acabar?

Infelizmente, não. Conforme surgem novas tecnologias, surgem novos tipos de spam. Os spammers possuem um enorme potencial e uma criatividade ainda maior para reinventar essa prática, que por incrível que pareça existe há quase 40 anos.

Mesmo com todo o avanço que as ferramentas antispam tiveram nos últimos anos, acompanhando a evolução dos demais serviços de telecomunicações (como telefonia e internet), atualizando-se constantemente e agregando cada vez mais serviços, existe um diferencial nos spammers que os fazem crescer mais rápido do que as soluções: a busca por reconhecimento na comunidade (causada pelos roubos de dados e invasões), além de gerar uma receita rápida e com baixo risco de punição.

E a facilidade de acesso a softwares maliciosos no Brasil e o baixo custo para manter uma operação dessas, aliado ao grande volume de mensagens de e-mail trocadas diariamente (mesmo após o surgimento do Facebook e WhatsApp) faz com que enviar spams ainda seja uma atividade relativamente lucrativa e longe de ser extinta.

Parece que dessa vez o Bill Gates errou na previsão…

***

Este artigo foi escrito com a colaboração de Eder Miranda é CEO e fundador da Unodata, possui mais de 8 anos de anos de experiência na área de segurança para e-mail. eder@unodata.com.br

13 May 13:15

Não perca tempo rastreando débito técnico

by Jim Bird

Nos últimos dois anos, nós temos acompanhado o débito técnico em nosso backlog de desenvolvimento. Adicionar o pagamento do débito para o backlog, tornar o custo e o risco de débito técnico visíveis para a equipe e para o dono do produto, priorizar pagamentos com outro trabalho: isso tudo deveria garantir que o débito seja pago.

Mas eu não estou convencido de que vale a pena. Eis o porquê:

Débitos que não valem a pena rastrear porque não valem a pena pagar

Não vale a pena se preocupar com alguns débitos.

Um pouco (mas não muito) de copiar e colar. Questões de estilo de codificação espalhafatosos pegas por algumas ferramentas de análise estática (importa realmente onde estão os colchetes?). Método pobres e nomenclatura de variáveis. Os métodos que são muito grandes. Código que não segue corretamente as normas ou os padrões de codificação. Outras inconsistências. Codificação difícil. Números mágicos. Falhas triviais.

Isso é irritante, mas não é o tipo de débito que você precisa controlar no backlog. É possível resolvê-los na refatoração oportunista do dia a dia. A próxima vez que você estiver no código, limpe-o. Se não for alterar o código, então quem se importa? Não custa nada. Se você fechar os olhos e fingir que ele não está lá, nada de ruim vai realmente acontecer.

Débito de outra pessoa

Software livre ou software de terceiros desatualizados. O tipo de coisas que Sonatype CLM ou a Verificação da Dependência do OWASP vai falar sobre.

Parte disso é ruim – gravemente ruim. Vulnerabilidades de segurança exploráveis. Pense em Heartbleed. Isso não deve mesmo levá-lo para o backlog. Deve ser corrigido imediatamente. Certifique-se de que você sabe que pode construir e implantar uma biblioteca corrigida rapidamente e com confiança (como parte do seu pipeline de build/integração/entrega contínua).

Todo o resto é de baixa prioridade. Importa realmente se tiver uma versão mais recente com algumas correções de bugs, mas o código funcionar do jeito que você quer? Atualizar para o bem de atualização é um desperdício de tempo, e há uma chance de que você possa trazer novos problemas, quebrar alguma coisa de que você depende agora, com pouco ou nenhum retorno. Lembre-se: você tem o código-fonte – se realmente precisa reparar algo ou acrescentar alguma coisa, você pode sempre fazer isso sozinho.

Débito que você não sabe que tem

Alguns débitos mais assustadores são os que você não sabe que tem. Débito que você assumiu inconscientemente porque não sabia de nada… e ainda não sabe. Você tomou algumas decisões ruins de design. Você não sabia como usar corretamente o seu framework de aplicativo. Você não conhecia o Top 10 do OWASP e como se proteger contra ataques de segurança comuns.

Esse débito não pode estar no seu backlog. Se alguma coisa muda – uma pessoa nova e com mais experiência se junta à equipe, ou você é auditado, ou hackeado – esse débito pode se expor de repente. Caso contrário, continua se somando, silenciosamente, nos bastidores.

O débito que é muito grande para se lidar

Existe outro débito que é muito grande para se lidar efetivamente. Tal como o débito nacional dos EUA. O débito que você adquiriu no início, fazendo as premissas erradas ou tomando as decisões erradas. Talvez você não soubesse que estava errado, mas agora você sabe. Você – ou alguém antes de você – escolheu a arquitetura errada. Ou o idioma errado, ou o framework errado. Ou a tecnologia errada. O sistema não escala. Ou não é confiável sob carga. Ou ele está cheio de falhas de segurança. Ou é frágil e difícil de mudar.

Você não pode refatorar seu caminho para fora disso. Você tem que aguentá-lo da melhor forma possível, ou começar tudo de novo. Rastreá-lo em seu backlog parece sem sentido:

Como um desenvolvedor, quero reescrever o sistema, para que tudo não seja um saco…

Corrija agora, ou não será consertado em momento algum

O débito técnico que você pode fazer algo a respeito é a dívida que você assumiu, consciente e deliberadamente – às vezes de forma responsável, às vezes não.

Você pegou atalhos para ter o e obter o feedback rapidamente (testes A/B, prototipagem etc.). Existe uma boa chance de que você vai ter que reescrevê-lo ou até mesmo jogá-lo fora, então por que se preocupar em obter o código direito na primeira vez? Esse é o débito estratégico – que você pode se dar ao luxo de levá-lo, pelo menos por um tempo.

Ou você estava sob pressão e não podia se dar ao luxo de fazê-lo direito na época. Você tinha que fazê-lo rápido, e os resultados não são bonitos.

O código funciona, mas é um trabalho de hack. Você copiou e colou muito. Não seguiu as convenções, não teve o código revisado, não escreveu testes, ou pelo menos não o suficiente. Você deixou algum código de depuração. Vai ser um saco de manter.

Se você não chegar a isso em breve, se não limpá-lo ou reescrevê-lo em algumas semanas ou alguns meses, então existe uma boa chance de que esse débito nunca será pago. Quanto mais tempo ele permanece, mais difícil é para justificar a fazer alguma coisa a respeito, afinal de contas, ele está funcionando bem e todo mundo tem outras coisas para fazer.

A prioridade de fazer algo sobre isso continuará caindo, até que fique como limo, estabelecendo-se no fundo. Eventualmente, você vai esquecer que está lá. Quando você o vir, ele vai te deixar um pouco triste, mas você vai superar isso. Tal como compradores em Nova York, olhando para o relógio da dívida nacional dos Estados Unidos, em seu caminho para a loja para comprar uma nova TV a crédito.

E, se você tiver sorte, esse débito pode ser pago à vista, sem você saber dele. Alguém refatora algum código enquanto faz uma mudança, ou talvez até mesmo o apaga porque o recurso não é usado mais, e o débito desapareceu da base de código. Mesmo que ainda esteja em seus livros.

Não monitore débito técnico – lide com ele

Monitorar débito técnico soa como a coisa responsável a fazer. Se você não rastreá-lo, não é possível compreender o alcance dele. Mas qualquer coisa que você gravar em seu backlog nunca será um registro exato ou completo do quanto de débito você realmente tem – por causa do débito oculto que você obteve involuntariamente, aquele que você não entende ou ainda não encontrou.

Mais importante ainda, o trabalho de rastreamento que você não vai fazer é um desperdício do tempo de todos. Apenas monitore os débitos que todos concordam que são importantes o suficiente para serem pagos. Em seguida, certifique-se de pagar o débito o mais rápido possível. Dentro de 1 ou 2 ou talvez 3 sprints. Caso contrário, você pode ignorá-lo. Gaste seu tempo refatorando lixo em vez de recuperar o atraso. Isso não é ser irresponsável. É ser prático.

***

Jim Bird faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://swreflections.blogspot.com.br/2015/02/dont-waste-time-tracking-technical-debt.html

Mensagem do anunciante:

A Mundipagg lança a inovadora API em REST, garantindo flexibilidade e simplicidade na integração. Conheça as nossas funcionalidades.

12 May 17:02

How to setup SSL connections and authentication?

by depesz
I had to setup it relatively recently, and hit some roadblocks, so figured I'll write about my experiences – for myself in the future, or for anyone else that might want to set it up. First, let's state goals: remote communication to PostgreSQL (as in: not within localhost) should go via ssl encrypted channels to […]
11 May 15:37

Shaun M. Thomas: PG Phriday: Functions and Performance Attributes

Users both old and new are likely aware that PGDB has functions. Some lucky readers may have even written one or two. For those without much experience in this area, or are thinking about contributing a few functions to your database for the first time, there are a couple things you should know. This week, I want to cover a few of the function declaration decorators. If you have no idea what I’m talking about, then this is probably something you’ll want to read regardless of your experience level.

Let’s begin with a very basic function that, as usual, does nothing:

CREATE OR REPLACE FUNCTION f_do_nothing(myvar INT)
RETURNS INT AS
$$
BEGIN
    RETURN myvar;
END;
$$ LANGUAGE plpgsql;
 
\timing ON
 
SELECT f_do_nothing(1)
  FROM generate_series(1, 1000000) AS s (id);

Now, hopefully we can agree that a function with one parameter which returns the contents of the parameter and does no work, is pretty simple. Calling this function one million times in a test VM required about two seconds. Some astute readers might point out that I could have used the SQL language instead of PL/pgSQL, and they’d be absolutely right. Doing so would reduce run time to about 600ms for this example.

Fortunately, the decorators I’ll be discussing will be much more powerful than even switching to a much simpler and subsequently less versatile language. As of PostgreSQL 9.4, there are four that will actually impact performance: VOLATILE, STABLE, STRICT, and IMMUTABLE.

The VOLATILE hint isn’t strictly necessary, as it’s the default. Effectively it means that the function can’t be optimized at all, as its effects can vary wildly based on table contents and parameters, even during execution. This causes PostgreSQL to invoke the function each time it’s used, regardless of what the function is actually doing. For our function, that’s clearly a detrimental approach. So what other hints do we have available?

The STABLE attribute is a bit more stringent. The implication in this case, is that the function can not modify the contents of the database in any way. Within the context of a transaction, this means the function can be semi-optimized by preventing extra calls because the data should not change. For this particular example, the STABLE keyword reduced run-time by about ten percent, to about 1.7 seconds on average. Here’s what that looks like:

CREATE OR REPLACE FUNCTION f_do_nothing(myvar INT)
RETURNS INT AS
$$
BEGIN
    RETURN myvar;
END;
$$ LANGUAGE plpgsql STABLE;
 
\timing ON
 
SELECT f_do_nothing(1)
  FROM generate_series(1, 1000000) AS s (id);

After this comes the IMMUTABLE keyword, which takes the optimization one step further. This tells the planner that the function will always return the same result for the same parameters. Due to that guarantee, function calls can tacitly be replaced by the the cached results where that benefits the planner. One way to look at this is by examining the performance:

CREATE OR REPLACE FUNCTION f_do_nothing(myvar INT)
RETURNS INT AS
$$
BEGIN
    RETURN myvar;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
 
\timing ON
 
SELECT f_do_nothing(1)
  FROM generate_series(1, 1000000) AS s (id)

The IMMUTABLE decorator reduced run time to 400ms in this case, but we don’t really know how many times the function was actually called. Just because a function call can be replaced by its results, doesn’t mean that will happen. Let’s modify the situation slightly by adding an explicit pause in the code:

CREATE OR REPLACE FUNCTION f_do_nothing(myvar INT)
RETURNS INT AS
$$
BEGIN
    PERFORM pg_sleep(1);
    RETURN myvar;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
 
\timing ON
 
SELECT f_do_nothing(1), f_do_nothing(1);

If your system is like mine, this example ran for two seconds, despite the fact that PostgreSQL could have replaced the second call with the results of the first. This seems counter-intuitive, but the real power of the IMMUTABLE keyword is not simply in the replacement structure, but in the parameter/result equivalence. It’s a fundamental misunderstanding that the database must prevent excess function calls, though that would be ideal. Let’s modify the code again to make the situation more explicit:

CREATE OR REPLACE FUNCTION f_do_nothing_imm(myvar INT)
RETURNS INT AS
$$
BEGIN
    RAISE NOTICE 'IMMUTABLE RAN!';
    RETURN myvar;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
 
CREATE OR REPLACE FUNCTION f_do_nothing_vol(myvar INT)
RETURNS INT AS
$$
BEGIN
    RAISE NOTICE 'VOLATILE RAN!';
    RETURN myvar;
END;
$$ LANGUAGE plpgsql VOLATILE;
 
SELECT 1
  FROM generate_series(1, 5) AS s (id)
 WHERE f_do_nothing_imm(10) = 10
   AND f_do_nothing_vol(10) = 10;

And the NOTICE output in this case is very telling:

NOTICE:  IMMUTABLE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  VOLATILE RAN!

While it seems that the SELECT section being optimized is somewhat arbitrary, the WHERE portion appears to have been reduced appropriately. It all comes down to how the planner decides to simplify the execution plan of the query, and the conditionals in the WHERE clause are much more likely to experience substitution.

Finally comes the STRICT decorator. It goes one step further than IMMUTABLE and assumes that a function with any NULL parameters will also return NULL. Again, this is an optimization where the planner can completely substitute a function call with NULL, thereby not only removing the function execution itself, but avoiding the requisite function parameter and return value mapping. Its nature also means it can be combined with the other keywords, like so:

CREATE OR REPLACE FUNCTION f_do_nothing(myvar INT)
RETURNS INT AS
$$
BEGIN
    RETURN myvar;
END;
$$ LANGUAGE plpgsql VOLATILE STRICT;
 
\timing ON
 
SELECT f_do_nothing(NULL)
  FROM generate_series(1, 1000000) AS s (id);

This time, even though the function is clearly marked VOLATILE, it doesn’t execute even once. The run time is merely the time required to generate and return one million values.

Now, regardless of power these keywords provide, there are some important caveats that apply. When we used notices to view the call path of our functions, the planner did what we expected, and prevented extra calls to the immutable version. So let’s try that example one more time with a variable in place and examine those notices again:

SELECT 1
  FROM generate_series(1, 5) AS s (id)
 WHERE f_do_nothing_imm(s.id % 2) = s.id % 2
   AND f_do_nothing_vol(s.id % 2) = s.id % 2;
 
NOTICE:  VOLATILE RAN!
NOTICE:  IMMUTABLE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  IMMUTABLE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  IMMUTABLE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  IMMUTABLE RAN!
NOTICE:  VOLATILE RAN!
NOTICE:  IMMUTABLE RAN!

Doh! Though the immutable function should have only run twice, we see that it ran just as often as the volatile version. It would appear that the function declaration documentation clears this up (emphasis mine):

If this option is given, any call of the function with all-constant arguments can be immediately replaced with the function value.

The problem in this case, is that the PostgreSQL planner automatically considers variables as their worst-case scenario: any number of infinite values could be substituted here. Even though we know only two of the five values were passed to the function, the PostgreSQL does not until execution time. Since the planner is determining how IMMUTABLE affects execution, that keyword is ignored for dynamic parameters.

With that said, the IMMUTABLE keyword exists beyond the context of optimization. In Set Theory, functions must be deterministic. Thus for indexes to operate as expected, functions must always return the same values when given the same inputs. Since PostgreSQL supports functional indexes, this means that only IMMUTABLE functions are candidates for providing indexable values.

This is also why the substitution process used by the planner seems arbitrary from an outside perspective; we’re not privy to the entire decision tree that generates the final execution plan. In many cases, either multiple function executions can not be reduced, or the cost and complexity of doing so would be computationally prohibitive.

In the end, it comes down to opportunity cost. By using these decorators, we give PostgreSQL as much information as possible to make decisions that affect the query execution plan. Consequentially, while we may reduce query run time, we do increase the amount of possible function candidates for table indexing. This last part is especially important, because such indexes do reduce the computational overhead of executing the function on the indexed value.

As the query planner improves over successive generations, these decorators only get more powerful. Try to use them where appropriate, and you can only improve the situation.

07 May 14:35

Containers: você ainda vai usar um!

by Kemel Zaidan

Não faz nem dois anos que o projeto Docker teve início, em março de 2013, e os containers já parecem ser a nova hype tecnológica do momento. Não é para menos, pois em tempos de computação em nuvem, uma ferramenta que permite tamanha flexibilidade quanto o Docker é muito bem-vinda.

Tecnologias de containers permitem virtualizar um sistema operacional sem que seja necessário utilizar um hypervisor. Para isso, o sistema guest compartilha o mesmo kernel com o host e executa as camadas superiores de bibliotecas e aplicações em uma espécie de sandbox que, na prática, isola ambos os sistemas de forma a executá-los de maneira independente. Contudo, como ambos compartilham do mesmo kernel, só é possível “virtualizar” sistemas do mesmo tipo, o que torna a técnica muito propícia à compartimentação de aplicações. Além disso, como não é preciso carregar o kernel do guest nem traduzir as chamadas de um sistema para o outro, como ocorre, por exemplo, na paravirtualização, há muito menos overhead, o que resulta em um maior desempenho e menor uso de memória.

A ideia não é nova. O FreeBSD já detinha uma implementação de containers chamada Jails e o (Open)Solaris também possuía os Solaris Containers, ou Zones há alguns anos. Mesmo no mundo Linux, o OpenVZ já existe desde 2005, enquanto que o kernel Linux incorporou o suporte à tecnologia, ainda de forma experimental, em 2009, na versão 2.6.24, com a implementação do LXC, ou Linux Containers.

Então, o que há de tão interessante em relação ao Docker e o que justifica tanto buzz? O que diferencia o Docker de outras implementações de containers são suas APIs, o que permite utilizar a ferramenta em um fluxo de trabalho muito semelhante a algo que boa parte dos desenvolvedores já estão acostumados quando fazem uso de utilitários de linha de comando como o Git. De forma semelhante a este, no Docker, é possível facilmente criar um ambiente de desenvolvimento, “commitar” as mudanças e, uma vez concluído o trabalho, distribuí-lo através da rede para que colegas possam “clonar” o container através da Internet, testá-lo e após a conclusão do mesmo, colocar toda a aplicação em produção simplesmente copiando o container de um ambiente para outro.

Da mesma forma que os contêineres facilitaram o comércio internacional ao padronizar uma unidade de distribuição e transporte de bens materiais, containers como o Docker têm a intenção de padronizar a distribuição de aplicações e serviços de software de maneira uniforme. Daí vem o nome Docker, ou estivador, em português. Em ambientes de cloud, o Docker é especialmente interessante, pois facilita o processo de deploy e mesmo a migração entre diferentes fornecedores de nuvem, sejam elas públicas ou privadas. Basta mover o container que pode, inclusive, conter todas as dependências (interpretadores, frameworks e até mesmo bancos de dados) para que sua aplicação execute de maneira autônoma.

Em dezembro de 2014,foi anunciado, durante a primeira DockerConf, o suporte ao Docker no Jelastic, a plataforma de cloud como serviço (PaaS) da Locaweb, que deve estar disponível aos clientes ainda no primeiro semestre de 2015. Por isso, se você ainda não utilizou o Docker, preparamos um tutorial rápido para que você possa tirar proveito de todo o poder que essa tecnologia oferece.

Tutorial

A primeira coisa a fazer é instalar o Docker em seu sistema. No site do projeto há instruções de instalação para diversas plataformas. Tenha em mente que o Docker é uma implementação de containers para o GNU/Linux e, caso esteja realizando a instalação em outro sistema operacional, este executará virtualização convencional sob o Docker, o que obviamente tem impacto no desempenho, apesar de permitir o uso da ferramenta em outras plataformas.

Uma vez com o Docker instalado, digite docker em uma janela do terminal para ver as opções disponíveis para a execução. Você verá que a sintaxe de muitas delas se assemelham com termos do git, como commit, diff, logs, pull, tag, etc.

O Docker possui um repositório online de imagens prontas, semelhante ao Github: o Docker Registry. A forma mais simples de iniciarmos é baixando uma dessas imagens para a nossa máquina.

Que tal baixarmos uma imagem com o NodeJS instalado? Dessa vez, digite docker pull node e veja que o download terá início. Ao final do processo você poderá confirmar que a imagem foi instalada digitando docker images:

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
node                latest              b8a47fa0cdf3        10 days ago         778.7 MB

Agora, se digitarmos docker run node npm -v e docker run node node -v veremos as versões do npm e do node sendo impressa em nosso terminal. O próximo passo é acessar o shell de nosso container. Para isso, digite:

# docker run -t -i node /bin/bash
root@8fe314df42de:/#

A opção -t diz ao Docker para reservar um terminal virtual (TTY) e o -i para abrir uma sessão interativa. Vamos utilizar o npm para instalar o pacote bitly-cli, que é uma ferramenta simples, para encurtarmos uma URL através da linha de comando. Após a instalação, podemos testar o uso da ferramenta:

root@8fe314df42de:/# npm install bitly-cli -g
/usr/local/bin/bitly -> /usr/local/lib/node_modules/bitly-cli/lib/bitly-cli.js
bitly-cli@0.0.1 /usr/local/lib/node_modules/bitly-cli
└── bitly@1.2.5
root@8fe314df42de:/# bitly -u "http://www.locaweb.com.br"
You can access your short url at http://bit.ly/1qS5jSL

Se na sequência abrirmos uma nova aba do terminal e digitarmos docker ps veremos o resultado abaixo:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
8fe314df42de        node:latest         "/bin/bash"         12 minutes ago      Up 12 minutes                           stoic_almeida

Perceba que CONTAINER ID exibido é o mesmo que aparece no prompt onde estamos conectados na outra aba. Está na hora de criarmos o nosso próprio container com o bitly-cli embarcado. Crie uma conta ou faça login no Docker Registry ao digitar docker login e preencha os dados da conta. Em seguida, faça commit das alterações:

$ docker commit 8fe314df42de kemelzaidan/artigo_docker
c30cbbdfe63ed6448b9fbb8f88cbb7d46e39af2f1e898b153790a32e9fc867cd

Você deve utilizar o seu próprio login e nome da imagem. Se digitar docker images novamente, poderá ver a imagem recém-criada. Para compartilhar a imagem com alguém, basta publicá-la no Registry: docker push kemelzaidan/artigo_docker. Se você digitar docker search artigo_docker, poderá ver a imagem que eu acabei de enviar:

$ docker search artigo_docker
NAME                        DESCRIPTION   STARS     OFFICIAL   AUTOMATED
kemelzaidan/artigo_docker                 0

Gostou? Pois há muito mais o que aprender sobre o Docker. Instale o programa, siga o tutorial, baixe a imagem que acabamos de subir e conte para nós o que achou do artigo. Suas aplicações estão esperando para subir às nuvens!

***

Texto publicado originalmente na Revista iMasters.

06 May 18:47

As mudanças do HTTP nos últimos 16 anos

by Kemel Zaidan

json_api

Há uma mudança gradual e silenciosa acontecendo no mundo do desenvolvimento web. O protocolo HTTP pode não ter mudado muito nos últimos 16 anos, mas seu uso evoluiu bastante. No início, ele era apenas um protocolo em modo texto para o envio de conteúdo estático. O próprio Tim Bernes Lee, criador da web, relatou em seu livro “Weaving the Web” (ainda sem tradução no Brasil) que sua criação foi concebida como “um formato universal para documentação”.

Com o passar do tempo, passamos a utilizar linguagens de script como Perl e ASP para manipular código fonte HTML e criar páginas com conteúdo “personalizado”: a aplicação inseria o conteúdo recuperado de uma base de dados no código HTML e entregava para o servidor web que, por sua vez, enviava a página para o navegador do cliente.

Com a inclusão do XMLHttpRequest no ECMAScript, foi possível transferir dados também no formato XML e JSON ao navegador. Com isso, surgiu o Ajax e, a partir dos verbos originais do HTTP (como GET, POST e PATCH e DELETE) dar origem ao modelo REST.

Até pouco tempo atrás era comum ver um desenvolvedor ser responsável tanto pelo frontend quanto pelo backend de uma aplicação web. Esses mundos evoluiram de tal maneira que ficou difícil acompanhar duas áreas de conhecimento com especificidades cada vez maiores entre si. Neste contexto, é cada vez mais frequente nos depararmos com arquiteturas onde há uma clara separação entre backend e frontend.

O rápido crescimento das plataformas móveis aumentou a necessidade de consumir dados através de REST e o número de APIs explodiu. Já que há uma API entregando esses dados aos dispositivos móveis, por que não utilizá-los também no frontend?

Projetos como Angular e Ember tornam muito mais fácil consumir APIs no lado do cliente e inserir estes dados em uma página web. Como não há a necessidade de renderizar HTML no lado do servidor, é possível enviar uma quantidade muito menor de bytes e de forma assíncrona, o que torna o tráfego mais rápido, melhorando o desempenho e aumentando a sensação de responsividade por quem faz uso do aplicativo.

Ferramentas como o Meteor (um web framework em NodeJS) já trazem esse conceito para dentro do processo de desenvolvimento de forma nativa, o que provavelmente justifica parte de sua rápida popularidade.

Muitas dessas mudanças não passaram de “hacks” com a finalidade de tirar do HTTP mais do que os fins para os quais ele havia sido concebido originalmente. Meu palpite é que há uma tendência cada vez menor de se renderizar HTML e enviá-lo ao browser. Ao invés disso, vamos consumir JSON e renderizar tudo no lado do cliente. Resta saber como o HTTP 2.0 influenciará essa evolução. O futuro porém, nem mesmo o Google pode te dizer.

***

Texto publicado originalmente na revista Locaweb #51

Mensagem do anunciante:

Quer expor APIs com segurança e impulsionar parcerias ? Conheça o API Management Suite da Sensedia

06 May 18:30

Sword in the Stone

That seems like an awful lot of hassle when all I wanted was a cool sword.
05 May 20:42

Túnel SSH reverso para acessar um servidor dentro de uma rede interna

by Augusto Campos

Se o servidor que você deseja acessar via SSH tem acesso à Internet mas não pode ser acessado diretamente a partir dela (por exemplo, se ele está atrás de uma firewall ou um roteador com NAT), o túnel SSH reverso pode ser uma solução interessante a considerar.

O artigo do Xmodulo explica como usar essa técnica de 2 maneiras: com um servidor intermediário, ou diretamente entre as 2 máquinas envolvidas. (via xmodulo.com - “How to access a Linux server behind NAT via reverse SSH tunnel - Xmodulo”)

O artigo "Túnel SSH reverso para acessar um servidor dentro de uma rede interna" foi originalmente publicado no site BR-Linux.org, de Augusto Campos.

04 May 17:29

Como trabalhar com array de checkboxes opcionais

by William Bruno

Imagine a seguinte situação: você precisa fazer um sistema de cadastro de veículos para uma vitrine virtual.

describe-table-vehicles

O nosso sistema venderá “carros famosos”, por isso cada carro possui um nome (Batmóvel 1941, Tumbler, Herbie, Dick Vigarista, Penélope Charmosa…), um ano de fabricação (1941, 2008…) e um modelo (esporte, clássico, corrida, tanque de guerra…).

Um das primeiras coisas que vêm a nossa mente é modelar os atributos dessa entidade veículo.

mysql> DESCRIBE vehicles_wrong;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(50) | YES  |     | NULL    |                |
| year  | int(4)      | YES  |     | NULL    |                |
| model | varchar(30) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

Tranquilo, certo? Um id para identificarmos cada veículo como único, o nome, ano e modelo. Mas o Batmóvel de 1941 tem acessórios diferentes do Tumbler de 2008 (o tanque de guerra do filme Batman Begins), certo?

E precisamos mostrar isso para nossos usuários, dizendo se o veículo possui ou não: ar condicionado, travas elétricas, tiro de canhão, moto acoplada para fuga… no caso do carro do Dick Vigarista, turbinas propulsoras!

O problema

Poderíamos adicionar estes itens à entidade veículos:

mysql> DESCRIBE vehicles_wrong;
+-----------+-------------+------+-----+---------+----------------+
| Field     | Type        | Null | Key | Default | Extra          |
+-----------+-------------+------+-----+---------+----------------+
| id        | int(11)     | NO   | PRI | NULL    | auto_increment |
| name      | varchar(50) | YES  |     | NULL    |                |
| year      | char(4)     | YES  |     | NULL    |                |
| model     | varchar(30) | YES  |     | NULL    |                |
| air_con   | bit(1)      | YES  |     | NULL    |                |
| bluetooth | bit(1)      | YES  |     | NULL    |                |
+-----------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

Só que aí precisamos ainda colocar:

mysql> DESCRIBE vehicles_wrong;
+------------------+-------------+------+-----+---------+----------------+
| Field            | Type        | Null | Key | Default | Extra          |
+------------------+-------------+------+-----+---------+----------------+
| id               | int(11)     | NO   | PRI | NULL    | auto_increment |
| name             | varchar(50) | YES  |     | NULL    |                |
| year             | char(4)     | YES  |     | NULL    |                |
| model            | varchar(30) | YES  |     | NULL    |                |
| air_con          | bit(1)      | YES  |     | NULL    |                |
| bluetooth        | bit(1)      | YES  |     | NULL    |                |
| parking_sensors  | bit(1)      | YES  |     | NULL    |                |
| escape_motorcyle | bit(1)      | YES  |     | NULL    |                |
| plasma_cannon    | bit(1)      | YES  |     | NULL    |                |
| cruise_control   | bit(1)      | YES  |     | NULL    |                |
| leather          | bit(1)      | YES  |     | NULL    |                |
| metallic_paint   | bit(1)      | YES  |     | NULL    |                |
| electric_locks   | bit(1)      | YES  |     | NULL    |                |
| xenon_lights     | bit(1)      | YES  |     | NULL    |                |
| nitro            | bit(1)      | YES  |     | NULL    |                |
| wings            | bit(1)      | YES  |     | NULL    |                |
+------------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

E para cada opcional extra que precisamos cadastrar, iríamos adicionando uma nova coluna na tabela veículos, mesmo que o motor de propulsão seja um item específico do carro do Dick Vigarista, e os demais carros irão sempre deixar essa coluna em branco (como false). Isso, por si só, já mostra um grave problema de modelagem: não está escalável!

Ter que alterar o modelo e possuir diversas colunas sem valores no banco de dados é um exemplo de modelagem desnormalizada que devemos evitar.

A solução

Em vez de ficarmos criando novas colunas a cada opcional novo que precisamos listar, temos que modelar melhor nossa entidade e dividir a entidade veículos em duas: veículos e opcionais, veja:

mysql> DESCRIBE optional; DESCRIBE vehicle_optional; DESCRIBE vehicles;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(50) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

+-------------+---------+------+-----+---------+-------+
| Field       | Type    | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| id_vehicle  | int(11) | YES  |     | NULL    |       |
| id_optional | int(11) | YES  |     | NULL    |       |
+-------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(50) | YES  |     | NULL    |                |
| year  | char(4)     | YES  |     | NULL    |                |
| model | varchar(30) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

Assim, fica a cargo de uma terceira tabela de relacionamento o cruzamento da informação, sobre quais opcionais cada carro tem. E para cada novo opcional extra que tivermos que cadastrar no sistema, apenas adicionamos um linha (registro) na tabela optional, em vez de uma coluna na tabela veículos. Entendeu a diferença?

  • Não precisamos alterar nada no banco de dados para disponibilizar um novo opcional.
  • Não precisamos mexer nos nossos códigos backend.
  • Nem na nossa query SQL!

O impacto de adicionar opcionais (mostrar mais checkboxes) é apenas cadastrar um novo registro na tabela.

mysql> SELECT * FROM optional;
+----+------------------+
| id | name             |
+----+------------------+
|  1 | air_con          |
|  2 | bluetooth        |
|  3 | parking_sensors  |
|  4 | escape_motorcyle |
|  5 | plasma_cannon    |
|  6 | cruise_control   |
|  7 | leather          |
|  8 | metallic_paint   |
|  9 | electric_locks   |
| 10 | xenon_lights     |
| 11 | nitro            |
| 12 | wings            |
+----+------------------+
12 rows in set (0.00 sec)

Show me the code

Iremos escrever enfim os códigos para fazer esse CRUD com checkboxes.

Lembrando que o intuito do artigo não é ensinar programação orientada a objetos. Então irei deixar o script php o mais simples possível, para que você possa extrair e então montar o seu dentro da sua estrutura/framework.

Listando os opcionais do banco

A listagem dos itens é super trivial:

<?php
  $query = $mysqli->query('SELECT id, name FROM optional');
  while($row = $query->fetch_object()) {
?>
    <label>
      <input type="checkbox" name="optional[]" value="<?php echo $row->id; ?>" />
    <?php echo $row->name; ?></label>
<?php
  }
?>

Apenas conecto no MySQL com a lib mysqli e imprimo um checkbox para cada registro da tabela.

DESCRIBE TABLE optional_vehicle

Ainda não estamos preocupados com trazer os checkboxes marcados, pois precisamos entender como utilizar a tabela optional_vehicle. Ela faz um relacionamento 1:N (um veículo para N opcionais).

Para inserir nessa tabela, precisamos mais ou menos da seguinte string sql:

INSERT INTO vehicle_optional (id_vehicle, id_optional) 
VALUES (42, 1),(42 2)

Nessa query acima, estamos inserindo o opcional 1 e 2 para o veículo 42. Se quiséssemos inserir mais o opcional 5, bastaria:

INSERT INTO vehicle_optional (id_vehicle, id_optional) 
VALUES (42, 1),(42, 2),(42, 5)

Okay?
Fazer essa string é uma simples manipulação de array. Lembra que o name do checkbox é name=”optional[]” então, no php, irá chegar um array:

$_POST['optional'][0], $_POST['optional'][1], $_POST['optional'][2]

Visto isso, vamos montar o INSERT:

  //optional insert
  $values = [];
  foreach($optionals AS $id_optional) {
    $values[] = "({$id}, {$id_optional})";
  }
  $values = implode(',', $values);

  $sql = "INSERT INTO vehicle_optional (id_vehicle, id_optional) 
      VALUES {$values}";
  echo $sql;
  $query = $mysqli->query($sql)or die($mysqli->error);

Simples, não? A saída do echo $sql, será a string que escrevi acima. Comecei pelos opcionais para matar logo a sua curiosidade, mas para deixar registrado o INSERT simples do veículo há apenas as colunas que são atributos da entidade veículo:

  $sql = "INSERT INTO vehicles (id, name, year, model) 
VALUES(NULL, '{$name}', '{$year}', '{$model}')";
  echo $sql, '<br /><br />';
  $query = $mysqli->query($sql)or die($mysqli->error);

  $id = $mysqli->insert_id;//ultimo id inserido no banco

Os truques

As duas queries acima são bem triviais, e você já está mais do que acostumado com elas no seu dia a dia. Só existem dois truques no server-side para essa modelagem.

O primeiro é:

Delete tudo, depois insira novamente

O comportamento do input type=”checkbox” do html é enviar para o server-side apenas os checkboxes marcados. Então aqueles que não estiverem no estado checked não serão enviados.

Tente imaginar a lógica de atualizar os opcionais de um carro. Ele já possui alguns cadastrados no banco. No submit do formulário, você precisa remover os que não foram marcados, inserir os novos e com os que não alteraram não fazer nada. Complexo, não?

Um truque muito mais simples é remover tudo e depois inserir tudo o que vier.

DELETE FROM vehicle_optional WHERE id_vehicle = 42;
INSERT INTO vehicle_optional (id_vehicle, id_optional) 
VALUES (42, 1),(42, 2),(42, 5);

Marque os checkboxes

Agora precisamos marcar os checkboxes na listagem durante a edição de um registro. O truque aqui é puramente SQL e se baseia em uma subquery.

SELECT `id`, `name`, 
(SELECT 'checked' FROM vehicle_optional 
  WHERE id_vehicle = 1 AND id_optional = optional.id) 
AS `checked` FROM `optional`;

Pense no seguinte:

  • Temos que listar todos os opcionais – fácil, já tinhamos feito isso.
  • E dizer quem está marcado ou não – aqui que entra a subquery.
(SELECT 'checked' FROM vehicle_optional 
  WHERE id_vehicle = 1 AND id_optional = optional.id)

ela vai trazer a string ‘checked’ apelidada como a coluna `checked` AS `checked` apenas nos registros do veiculo id = 1 que tiverem marcados. o/

mysql> SELECT `id`, `name`, 
 (SELECT 'checked' FROM vehicle_optional 
   WHERE id_vehicle = 1 AND id_optional = optional.id) 
AS `checked` FROM `optional`;
+----+------------------+---------+
| id | name             | checked |
+----+------------------+---------+
|  1 | air_con          | checked |
|  2 | bluetooth        | checked |
|  3 | parking_sensors  | NULL    |
|  4 | escape_motorcyle | NULL    |
|  5 | plasma_cannon    | NULL    |
|  6 | cruise_control   | checked |
|  7 | leather          | NULL    |
|  8 | metallic_paint   | NULL    |
|  9 | electric_locks   | NULL    |
| 10 | xenon_lights     | NULL    |
| 11 | nitro            | NULL    |
| 12 | wings            | NULL    |
+----+------------------+---------+
12 rows in set (0.00 sec)

E então vamos simplesmente imprimir isto no nosso input:

<input type="checkbox" name="optional[]" 
  value="<?php echo $row->id; ?>" 
   <?php echo $row->checked; ?>/>

Mágica.

O código está disponível no GitHub.

Mensagem do anunciante:

Em apoio à evangelização do WordPress, os cursos da Apiki são gratuitos para que você possa se especializar na plataforma que mais cresce no mundo. Vagas limitadas, Inscreva-se agora.

28 Apr 15:19

Um bolo pequeno

by Francisco Nunes
Farinha e azeite

“Faze dele primeiro para mim um bolo pequeno” (1Rs 17.13).

A época era sombria. Por causa dos pecados de Acabe, rei de Israel, a fome assolava o país. O profeta de Deus permanecia escondido, assim como outros cem profetas. Além disso havia entre o povo sete mil homens conhecidos somente por Deus que não haviam dobrado os joelhos diante de Baal, o falso deus (19.18).

Os recursos faltavam por todo lado; no entanto, em uma família fora dos limites do país, não o alimento diário para toda a casa. Durante todo um ano, “da panela a farinha não se acabou e da botija o azeite não faltou, conforme a palavra do Senhor, que Ele falara pelo ministério de Elias” (v. 16). De onde provinha essa abundância? Era claramente uma bênção material; no entanto, segundo os ensinamentos da Palavra de Deus, podemos tomá-la em seu aspecto espiritual: a farinha nos lembra das perfeições do Senhor Jesus; o azeite é figura do Espírito Santo.

Por que justamente nessa casa, ao contrário de tantas outras, havia alimento e sustento? Um dia, o homem de Deus havia encontrado essa viúva e lhe havia pedido um pouco de água e um pedaço de pão. A água escasseava, mas ela estava disposta a dá-la; no entanto, o pão faltava totalmente. Ela não tinha mais que um punhada do farinha e um pouco de azeite. Assim, a morte os esperava, a ela e a seu filho. O profeta lhe disse: “Faze dele primeiro para mim um bolo pequeno e traze-mo aqui” (v. 13). Como? Desse pouco que lhe sobrava, de seus últimos recursos, devia preparar algo para o profeta, sem deixar nada para ela e seu filho? Sim, e era necessária a fé, a fé na palavra de Deus pronunciada por Seu servo. “Ela foi e fez conforme a palavra de Elias” (v. 15). Esse foi o segredo da bênção.

“Faze dele primeiro para mim um bolo pequeno”. O Senhor não nos tem feito freqüentemente este pedido? “Ao começar o dia, reserva primeiramente um momento para estar a Meus pés e ouvir Minha voz; para fazer silêncio e dizer como outrora disse o jovem Samuel: ‘Fala, porque o Teu servo ouve’ (1Sm 3.10)”. E no transcorrer de nossas ocupações comuns, provavelmente temos ouvido com freqüência uma voz nos dizer: “Você pensa primeiramente no Senhor?” Pode tratar-se de um assunto relativo à retidão, de realizar um trabalho com esmero, de prestar um serviço a favor de alguém, de pronunciar uma palavra ou, antes, de calar. Busquemos primeiramente a vontade do Senhor quando estamos diante de uma escolha, seja para o trabalho profissional ou com relação a reunir-se ao redor do Senhor, fazer um gasto supérfluo ou dedicar esse dinheiro para o Senhor e Sua obra ou a favor de algum necessitado…

“Faze dele primeiro para mim um bolo pequeno”. Parece pouca coisa; no entanto, um punhado de farinha e um pouco de azeite era muito para a viúva, pois era todo o sustento que tinha (comp. Lc 21.4). Quanto o profeta apreciou isso, e muito mais o próprio Deus! “Quem é fiel no mínimo, também é fiel no muito; quem é injusto no mínimo, também é injusto no muito” (16.10). Pode ser um escrito, uma palavra, uma oração que primeiramente tivemos o desejo de apresentar a Ele. E, se não tomamos o cuidado de fazê-lo, que perda!

A entrega de si mesmo nada mais é do que o simples fato de pôr à disposição constante de Deus aquilo que Lhe pertence.

Faze dele primeiro para mim um bolo pequeno”. “Eu te mostrarei a minha fé pelas minhas obras” (Tg 2.18). É bom, sem dúvida, expressar sua confiança em Deus, cantar hinos que celebrem Sua bondade e Sua fidelidade, mas a fé não consiste somente em palavras: ela se traduz em atos. Por exemplo: um jovem, em meio a seus estudos na época de provas, consagrará o domingo, dia do Senhor, ao Senhor ou aos estudos? Se ele dá provas de sua fé entregando primeiramente a Deus o lugar que Lhe pertence e deixando seu próprio trabalho para os dias da semana, certamente ele será recompensado. À primeira vista, é uma perda, como parecia acontecer com a farinha e o azeite da viúva. Mas Deus pode resolver uma prova ou um trabalho igual ou melhor se, por a Ele, Lhe é reservado o tempo que Ele pede, mesmo que essas horas tenham sido “perdidas” no que diz respeito ao estudo.

“Faze dele primeiro para mim um bolo pequeno”. Pela voz do profeta ouvimos a voz do Senhor e Seu desejo de que façamos primeiramente para Ele. As Escrituras relatam que os macedônios “se deram primeiramente ao Senhor” (2Co 8.5). Este é o ponto importante do assunto: “Dá-me, filho meu, o teu coração” (Pv 23.26). O punhado de farinha e o azeite no fundo de uma vasilha representavam todos os recursos da viúva. Dando-os primeiramente ao profeta, a viúva não tinha nada além da morte diante dela… ou a salvação de Deus. De fato, colocando-nos verdadeira e inteiramente à disposição do Senhor Jesus, conscientes de que fomos “comprados por preço” (1Co 6.20), parece que perdemos a vida ao entregá-la ao Senhor, mas “qualquer que quiser salvar a sua vida perdê-la-á, mas, qualquer que perder a sua vida por amor de mim e do evangelho, esse a salvará” (Mc 8.35). E essa entrega de si mesmo (que nada mais é do que o simples fato de pôr à disposição constante de Deus aquilo que Lhe pertence) se traduzirá, não por explosões de entusiasmo ou por sonhos de missões em terras distantes, mas pelo primeiro lugar que corajosamente Lhe daremos nos detalhes de nossa vida. Talvez seja um “bolo pequeno”, mas é o segredo da bênção que nos acompanhará dia após dia até que, tendo passado a “fome”, entraremos na casa do Pai.

G. A.

Para que em tudo [Jesus Cristo] tenha a preeminência” (Cl 1.18).

Quando Deus nos pede que façamos qualquer coisa (como, neste caso, a de alimentar o profeta Elias), ao mesmo tempo nos dá tudo o que é necessário para cumprir. Mas deve-se estar disposto a fazer primeiramente e sem discutir o que Deus pede. É o que nos ensina esse pequeno bolo, prova da fé daquela mulher e “primícias” de uma abundância divina em sua casa.

J. Kn.

(Traduzido por Francisco Nunes de Un mensaje bíblico para todos, 07/2014, publicado por Ediciones Bíblicas Para Todos (Suíça). Este artigo pode ser distribuído e usado livremente, desde que não haja alteração no texto, sejam mantidas as informações de autoria e de tradução e seja exclusivamente para uso gratuito.)

Send to Kindle

O post Um bolo pequeno apareceu primeiro em Campos de Boaz.

27 Apr 18:04

DatabaseCast 56: Sintaxe SQL

by DatabaseCast

VitrineDatabaseCast56

Olá, pessoal! Neste episódio do DatabaseCast, Mauro Pichiliani (Twitter | Blog), Wagner Crivelini (@wcrivelini) e os ouvintes Alex Zaballa (@alexzaballa) e Henrique Jardim (@henriquejardim) quebram a cabeça tentando descobrir o problema na sintaxe do comando SQL. Você também vai saber um pouco mais sobre o padrão SQL, descobrir por que fugir da álgebra relacional, evitar colocar hints de instrução na forma de comentários, odiar a sintaxe (+)= e =(+) e não dar ouvidos ao diabinho e ao anjinho que ficam em cima dos ombros.

LANÇAMENTO: Veja a caneca Datas SQL com a sintaxe para manipulação de datas no Oracle, SQL Server, Mysql e PostgreSQL.MontagemCanecaDatabaseCast

Compre esta caneca clicando aqui!

Não deixe de nos incentivar digitando o seu comentário no final deste artigo, mandando um e-mail para  databasecast@gmail.com, seguindo o nosso twitter @databasecast, vendo informações de bastidores e as músicas do programa no nosso Tumblr e curtindo a nossa página no Facebook e no Google+.

feed-rssVeja no gráfico abaixo a duração e os tempos aproximados de início e fim de cada bloco:

GraficoTamanhoDatabaseCastEpisodio56Veja na tag cloud abaixo a contagem das palavras mais usadas nos emails, comentários e tweets do episódio anterior:TagCloudEp56

Livro Conversando sobre Banco de dados do Mauro Pichiliani (Impresso e PDF, EPUB e MOBI)

Você pode comprar a camiseta com estampa fractal Fluxo Matrix e Sonho Fractal diretamente neste link. Veja também:

Links do episódio:

27 Apr 14:52

Waiting for 9.5 – Reduce lock levels of some trigger DDL and add FKs

by depesz
On 5th of April, Simon Riggs committed patch: Reduce lock levels of some trigger DDL and add FKs   Reduce lock levels to ShareRowExclusive for the following SQL CREATE TRIGGER (but not DROP or ALTER) ALTER TABLE ENABLE TRIGGER ALTER TABLE DISABLE TRIGGER ALTER TABLE … ADD CONSTRAINT FOREIGN KEY   Original work by Simon […]
27 Apr 13:18

Chaves artificiais ou naturais no banco de dados?

by Ivo Nascimento

Faça essa pergunta em uma mesa de DBAs ou arquitetos de software no bar, se afaste, pegue um chopp e assista a um episódio live de Spartacus!

Alguns dizem que se uma chave natural está disponível, ela deve ser utilizada. Eu prefiro pensar que não, não tenho o hábito de usar cpf, rg, dog-tag-id e outros como identificador de uma pessoa num banco de dados.

Meu argumento é que esses campos podem ser únicos, mas não são controlados por mim, o freak dev/dba/designer/barista e, se não está sob meu controle, eu não sei o que pode acontecer com ele.

Pode ser que o Bolsonado acorde um dia com desejo de colocar uma letra no início de cada RG e aí já viu… nem disco da Xuxa sendo tocado ao contrário junto a um coral de apitos astecas pode impedi-lo.

Já a chave artificial é minha, e sob essa ótica artificial deixa de ser quase pejorativo para ser carinhoso.

“Essa chave aí?… Eu que criei. Tá crescendo linda, já passou dos 18″.

Pois é, chave artificial é o ideal pra mim, mas por quê?

the-treachery-of-images-this-is-not-a-pipe-19482Minha obrigação não é com a realidade, mas com a porção da realidade que escolhi representar e com a representação que escolhi fazer.

Essa representação não é a realidade, não toda ela, então por que identificar por um dado natural? O dado natural é um dado da realidade representada pelo objeto, uma propriedade.

 

 

Alguns diriam, “mas Ivo, RG ou CPF não são naturais,  são chaves  artificiais, criadas pelo governo”.

tio-patinhasSe não é controlada por mim, e quando digo isso não me imagine segurando primary keys como o tio Patinhas segura a moeda número um e rindo como um maníaco-depressivo. Quando eu falo sobre controle, falo sobre ser criada do meu lado do sistema.

 

 

Eu também respeito as chaves artificiais porque produzem situações interessantes. Se alguém quiser mudar uma chave artificial, seus argumentos terão de ser muito bons, vai dizer que a chave corre o risco de mudar?

Artificial brother… Pouco do mundo real pode afetá-la, talvez nada.

Mas talvez ela evolua, se torne uma chave composta ou não – representando entidades únicas com dimensões diferentes e exclusivamente excludentes e únicas. Isso é uma outra dimensão do problema, de simples para composto tem muita história e motivação, vide Darwin.

E baseado nesse estado posso tomar decisões de modelagem.

E, mais que isso, chaves artificiais são armazenadas numericamente porque uma string teria mais questões que uma criança de 7 anos em tipos escalares gerados em auto_increment, sequencies e afins, e esses recursos têm estado. Eu sei onde estão, onde estiveram, para onde vão e como vão. E, baseado nesse estado, posso tomar decisões de modelagem.

Um pattern de identitiy, por exemplo, depende dessa chave, a instância de um objeto que implementa identitiy depende dessa chave. Se tenho uma sequence no postgresql, por exemplo, posso recuperar facilmente o próximo valor usando nextval e, se estiver usando mysql, vou precisar olhar para o esquema, mas também é possível.

Vai lá e pergunta qual vai ser o próximo número de RG!

O lado ruim de chaves artificiais, alguém diria, é que você precisa conhecer o banco de dados para conseguir relacionar os dados em busca de informação.

Como programador, você não precisa conhecer a modelagem para resolver um problema… hummm, me fale mais sobre isso.

Como programador, você não precisa conhecer a modelagem para resolver um problema, não se os domínios são escritos de maneira concisa e existe uma layer de serviço demonstrando o relacionamento de uma maneira explícita, tipo Angelina Jolie saindo da banheira de recuperação ou aquela loira do Star Trek trocando de roupa.

mulheres

E se o software não tem essa camada de serviço e os relacionamentos bem explicitados… bem, não vai ser conhecer o banco de dados que vai fazer o software melhor; na realidade, conhecer o modelo do banco vai evitar que ele se torne pior.

Veja o risco: se você programar com o modelo do banco em mente, é provável que ocorram mais acoplamentos. Isso porque você está relacionando dados, e não os conceitos que deseja representar com orientação a objetos.

E tem a turma que considera o modelo do banco um repositório chique, e as regras, todas elas, devem estar no código da aplicação. O que você acha disso?

Ou aquela outra turma da aplicação multidatabase? Acho que isso é motivo pra outro texto de busão, não este, porque meu ponto de parada está chegando e já vou puxar a cordinha aqui.

Até a próxima!

26 Apr 03:16

HTTP2 para Desenvolvedores de Web

by diego@tableless.com.br (Tableless.com.br)

http2_sm

HTTP2 significa uma mudança na forma como construímos websites. As boas práticas de HTTP1 são prejudiciais no mundo do HTTP2.

HTTP1 é lento e ineficiente para a maioria dos casos de uso de hoje na web.

HTTP1.x é a versão do HTTP que nós já conhecemos quando entramos o endereço de um site. É um protocolo antigo que foi concebido antes mesmo de sabermos o que essa imensa rede mundial de computadores se tornaria. Apesar desse protocolo continuar funcionando como esperado, simplesmente não é tão eficiente como no início, porque ultimamente estamos exigindo algo muito mais complexo do que este protocolo foi projetado originalmente.

Nós estamos hackeando o HTTP1

Para que os sites carreguem em tempo aceitável usando HTTP1, desenvolvemos uma série de técnicas; hacks na verdade; para conseguirmos extrair um bom desempenho deste protocolo antigo. São eles:

  1. Usando CSS Sprites: Combine várias imagens em uma só imagem e utilizando CSS para mostrar apenas uma parte dessa imagem num devido lugar da página.
  2. Concatenando o Código: Tornando vários arquivos de CSS ou JS e consolidá-los em um único arquivo maior.
  3. Cookieless – Servindo arquivos de um domínio sem o uso de cookies, através de servidores estáticos.
  4. Usando Partições de Shard: Criando registros de Alias no DNS de diferentes domínios ou sub-domínios para hospedagem dos arquivos de imagens.

As duas primeiras técnicas visam evitar várias solicitações HTTP. Em HTTP1 um pedido é uma coisa muito cara e leva muito tempo, cada pedido pode ser baixados com os cookies que devem ser enviados como parte do pedido, e nada disso é compactado. É mais rápido agrupar um monte de coisas e fazer tudo de uma só vez no lado do cliente do que continuar enviando pedidos para o servidor cada momento que o código precisa de um arquivo.

A terceira técnica é usada para minimizar o tempo necessário para obter os arquivos; cookies, se estiver definido, deve ser enviado para o domínio solicitado junto com cada pedido – que acrescenta-se a um monte de espaço ‘desperdiçado’ na linha. Se os seus arquivos estão em um domínio diferente (exemplo: imagens.meusite.com) que não usa cookies, então o pedido desses arquivos não precisará enviar cookies com eles, o que será um pouco mais rápido.

A última técnica, sharding, é porque os navegadores costumavam permitir apenas duas solicitações HTTP simultâneas fossem feitas por domínio. Se você criar um novo domínio para alguns de seus arquivos, então você dobra a quantidade de conexões simultâneas o navegador irá permitir a fim de obter seus arquivos. Assim, você pode baixar o conteúdo do site mais rapidamente. Na realidade, sharding não tem sido muito útil nos últimos anos, pois os fabricantes de navegadores decidiram eliminar essa restrição das duas conexões ‘era tonto, e eles ignoraram.


O que esperar do HTTP2?

Não use as boas práticas do HTTP1 como base para um site que está sendo hospedado em HTTP2.

O protocolo HTTP2 está quase aqui, ele é baseado no SPDY®, e isso torna tudo muito mais eficiente. Significa também que todas as técnicas de desempenho HTTP1 são prejudiciais. Eles irão fazer um site HTTP2 mais lento, e não mais rápido. Portanto, não use as boas práticas do HTTP1 como base para um site que está sendo hospedado em HTTP2.

HTTP2 faz com que o custo de múltiplos pedidos diminua por causa de um número de técnicas já incluídas:

  • HTTP2 pode deixar a conexão em aberta para reutilização por um longo de tempo, para que não haja a necessidade daquela negociação cara que HTTP1 faz com o servidor em cada solicitação.
  • Ao contrário do HTTP1, o novo protocolo usa compactação de arquivos e assim o tamanho da solicitação é significativamente menor – e, como resultado, mais rápida.
  • HTTP2 é multiplex, ou seja, pode enviar e receber várias coisas ao mesmo tempo através de uma única conexão.

O que tudo isso significa, é que não só as técnicas que usamos no HTTP1 estão obsoletas, mas como também, farão as coisas ficarem mais lentas. Você poderá estar baixando arquivos desnecessários para a página a ser servida (concatenação de código e CSS sprites são suscetíveis à isso), e a técnica de sharding invoca pesquisas de DNS que irão retardar as coisas, na verdade, no HTTP2 voce não precisar de usar shard de forma alguma.

Resumindo, quando você desenvolver o front-end (html/css/js) para um site que será servido através do HTTP2, tenha a certeza de que você não está usando velhas técnicas de desempenho do HTTP1, o que irão prejudicar o seu HTTP2 site.

Aprendendo mais sobre o HTTP2

Aqui está um excelente artigo (em inglês), escrito por Daniel Stenberg, no qual ele detalha mais profundamente esse assunto.

Tradução

A tradução deste artigo para o Português foi devidamente autorizada pelo autor, Matt Wilcox.

---
Este artigo foi escrito por Marcelo Paiva.

Visite o nosso site para mais posts sobre desenvolvimento web! Tableless.

20 Apr 20:51

QUIC – A Google está a acelerar a Internet

by Pedro Pinto
20 Apr 20:43

homem

by Francisco Nunes

Condição do homem: inconstância, tédio, inquietação.

(Blaise Pascal)

Send to Kindle
20 Apr 20:43

poeta

by Francisco Nunes

O poeta goza deste privilégio incomparável: pode, ao seu capricho, ser o mesmo e ser outro.

(Charles-Pierre Baudelaire)

Send to Kindle
20 Apr 20:42

Aurélien Gâteau: Extensive Source Comments or Extensive Commit Messages?

If you consider yourself as a serious developer, you know writing good commit messages is important. You don't want to be that guy:

XKCD #1296: Git Commit

XKCD #1296

This applies to source comments as well: good comments save time, bad comments can be worse than no comments.

For a long time, I usually favored source comments over commit messages: whenever I was about to commit a change which needed some explanations, I would often start to write a long commit message, then pause, go back to the code, write my long explanation as a comment and then commit the changes with a short message. After all, we are told we should not repeat ourselves.

Recently I was listening to Thom Parkin talking about rebasing on Git Minutes #33 (Git Minutes is a great podcast BTW, highly recommended) and he said this: "Commits tell a story". That made me realize one thing: we developers read code a lot, but we also read a lot of commit histories, either when tracking a bug or when reviewing a patchset. Reading code and reading history can be perceived as two different views of a project, and we should strive to make sure both views are readable. Our readers (which often are our future selves...) will thank us. It may require duplicating information from time to time, but that is a reasonable trade-off in my opinion.

So, "Write extensive source comments or extensive commit messages?" I'd say: "Do both".

Flattr this

19 Apr 14:33

Recife aprova lei que obriga qualquer prédio com mais de 4 pavimentos a ter telhado verde

by Redação Hypeness
Já falamos aqui no Hypeness sobre a aprovação da lei da França que obriga os prédios comerciais a terem tetos verdes ou placas solares. Aderindo a esta ideia sustentável, foi sancionada uma lei em Recife que obriga novos prédios residenciais, com mais de 4 pavimentos e com área coberta acima de 400 metros quadrados, a implantarem […]