1. 01 6月, 2016 1 次提交
    • S
      Make Active Record emit significantly smaller YAML · c4cb6862
      Sean Griffin 提交于
      This reduces the size of a YAML encoded Active Record object by ~80%
      depending on the number of columns. There were a number of wasteful
      things that occurred when we encoded the objects before that have
      resulted in numerous wins
      
      - We were emitting the result of `attributes_before_type_cast` as a hack
        to work around some laziness issues
      - The name of an attribute was emitted multiple times, since the
        attribute objects were in a hash keyed by the name. We now store them
        in an array instead, and reconstruct the hash using the name
      - The types were included for every attribute. This would use backrefs
        if multiple objects were encoded, but really we don't need to include
        it at all unless it differs from the type at the class level. (The
        only time that will occur is if the field is the result of a custom
        select clause)
      - `original_attribute:` was included over and over and over again since
        the ivar is almost always `nil`. We've added a custom implementation
        of `encode_with` on the attribute objects to ensure we don't write the
        key when the field is `nil`.
      
      This isn't without a cost though. Since we're no longer including the
      types, an object can find itself in an invalid state if the type changes
      on the class after serialization. This is the same as 4.1 and earlier,
      but I think it's worth noting.
      
      I was worried that I'd introduce some new state bugs as a result of
      doing this, so I've added an additional test that asserts mutation not
      being lost as the result of YAML round tripping.
      
      Fixes #25145
      c4cb6862
  2. 12 5月, 2016 1 次提交
  3. 02 10月, 2015 1 次提交
    • S
      Further encapsulate dirty checking on `Attribute` · 07723c23
      Sean Griffin 提交于
      We can skip the allocation of a full `AttributeSet` by changing the
      semantics of how we structure things. Instead of comparing two separate
      `AttributeSet` objects, and `Attribute` is now a singly linked list of
      every change that has happened to it. Since the attribute objects are
      immutable, to apply the changes we simply need to copy the head of the
      list.
      
      It's worth noting that this causes one subtle change in the behavior of
      AR. When a record is saved successfully, the `before_type_cast` version
      of everything will be what was sent to the database. I honestly think
      these semantics make more sense, as we could have just as easily had the
      DB do `RETURNING *` and updated the record with those if we had things
      like timestamps implemented at the DB layer.
      
      This brings our performance closer to 4.2, but we're still not quite
      there.
      07723c23
  4. 29 9月, 2015 1 次提交
    • S
      Inline `Attribute#original_value` · e950c4b4
      Sean Griffin 提交于
      The external uses of this method have been removed, and I'd like to
      internally re-use that name, as I'm planning to encapsulate `changed?`
      into the attribute object itself.
      e950c4b4
  5. 25 9月, 2015 1 次提交
    • S
      Clean up the implementation of AR::Dirty · 8e633e50
      Sean Griffin 提交于
      This moves a bit more of the logic required for dirty checking into the
      attribute objects. I had hoped to remove the `with_value_from_database`
      stuff, but unfortunately just calling `dup` on the attribute objects
      isn't enough, since the values might contain deeply nested data
      structures. I think this can be cleaned up further.
      
      This makes most dirty checking become lazy, and reduces the number of
      object allocations and amount of CPU time when assigning a value. This
      opens the door (but doesn't quite finish) to improving the performance
      of writes to a place comparable to 4.1
      8e633e50
  6. 18 2月, 2015 3 次提交
  7. 01 2月, 2015 1 次提交
    • S
      Attribute assignment and type casting has nothing to do with columns · 70ac0729
      Sean Griffin 提交于
      It's finally finished!!!!!!! The reason the Attributes API was kept
      private in 4.2 was due to some publicly visible implementation details.
      It was previously implemented by overloading `columns` and
      `columns_hash`, to make them return column objects which were modified
      with the attribute information.
      
      This meant that those methods LIED! We didn't change the database
      schema. We changed the attribute information on the class. That is
      wrong! It should be the other way around, where schema loading just
      calls the attributes API for you. And now it does!
      
      Yes, this means that there is nothing that happens in automatic schema
      loading that you couldn't manually do yourself. (There's still some
      funky cases where we hit the connection adapter that I need to handle,
      before we can turn off automatic schema detection entirely.)
      
      There were a few weird test failures caused by this that had to be
      fixed. The main source came from the fact that the attribute methods are
      now defined in terms of `attribute_names`, which has a clause like
      `return [] unless table_exists?`. I don't *think* this is an issue,
      since the only place this caused failures were in a fake adapter which
      didn't override `table_exists?`.
      
      Additionally, there were a few cases where tests were failing because a
      migration was run, but the model was not reloaded. I'm not sure why
      these started failing from this change, I might need to clear an
      additional cache in `reload_schema_from_cache`. Again, since this is not
      normal usage, and it's expected that `reset_column_information` will be
      called after the table is modified, I don't think it's a problem.
      
      Still, test failures that were unrelated to the change are worrying, and
      I need to dig into them further.
      
      Finally, I spent a lot of time debugging issues with the mutex used in
      `define_attribute_methods`. I think we can just remove that method
      entirely, and define the attribute methods *manually* in the call to
      `define_attribute`, which would simplify the code *tremendously*.
      
      Ok. now to make this damn thing public, and work on moving it up to
      Active Model.
      70ac0729
  8. 28 1月, 2015 2 次提交
  9. 21 1月, 2015 1 次提交
    • S
      Introduce `ActiveRecord::Base#accessed_fields` · be9b6803
      Sean Griffin 提交于
      This method can be used to see all of the fields on a model which have
      been read. This can be useful during development mode to quickly find
      out which fields need to be selected. For performance critical pages, if
      you are not using all of the fields of a database, an easy performance
      win is only selecting the fields which you need. By calling this method
      at the end of a controller action, it's easy to determine which fields
      need to be selected.
      
      While writing this, I also noticed a place for an easy performance win
      internally which I had been wanting to introduce. You cannot mutate a
      field which you have not read. Therefore, we can skip the calculation of
      in place changes if we have never read from the field. This can
      significantly speed up methods like `#changed?` if any of the fields
      have an expensive mutable type (like `serialize`)
      
      ```
      Calculating -------------------------------------
       #changed? with serialized column (before)
                             391.000  i/100ms
       #changed? with serialized column (after)
                               1.514k i/100ms
      -------------------------------------------------
       #changed? with serialized column (before)
                                4.243k (± 3.7%) i/s -     21.505k
       #changed? with serialized column (after)
                               16.789k (± 3.2%) i/s -     84.784k
      ```
      be9b6803
  10. 15 1月, 2015 1 次提交
  11. 17 12月, 2014 1 次提交
    • S
      `update_column` take ruby-land input, not database-land input · dd8b5fb9
      Sean Griffin 提交于
      In the case of serialized columns, we would expect the unserialized
      value as input, not the serialized value. The original issue which made
      this distinction, #14163, introduced a bug. If you passed serialized
      input to the method, it would double serialize when it was sent to the
      database. You would see the wrong input upon reloading, or get an error
      if you had a specific type on the serialized column.
      
      To put it another way, `update_column` is a special case of
      `update_all`, which would take `['a']` and not `['a'].to_yaml`, but you
      would not pass data from `params` to it.
      
      Fixes #18037
      dd8b5fb9
  12. 17 8月, 2014 1 次提交
  13. 16 8月, 2014 1 次提交
  14. 26 6月, 2014 3 次提交
  15. 25 6月, 2014 1 次提交
    • S
      Move behavior of `read_attribute` to `AttributeSet` · a89f8a92
      Sean Griffin 提交于
      Moved `Builder` to its own file, as it started looking very weird once I
      added private methods to the `AttributeSet` class and the `Builder`
      class started to grow.
      
      Would like to refactor `fetch_value` to change to
      
      ```ruby
      self[name].value(&block)
      ```
      
      But that requires the attributes to know about their name, which they
      currently do not.
      a89f8a92
  16. 24 6月, 2014 1 次提交
    • Y
      add missing `:nodoc:` for recent refactorings. [ci skip] · b27e856d
      Yves Senn 提交于
      Adding `# :nodoc:` to the parent `class` / `module` is not going
      to ignore nested classes or modules.
      
      There is a modifier `# :nodoc: all` but sadly the containing class
      or module will continue to be in the docs.
      
      /cc @sgrif
      b27e856d
  17. 17 6月, 2014 1 次提交
  18. 14 6月, 2014 1 次提交
    • S
      Introduce an Attribute object to handle the type casting dance · 6f08db05
      Sean Griffin 提交于
      There's a lot more that can be moved to these, but this felt like a good
      place to introduce the object. Plans are:
      
      - Remove all knowledge of type casting from the columns, beyond a
        reference to the cast_type
      - Move type_cast_for_database to these objects
      - Potentially make them mutable, introduce a state machine, and have
        dirty checking handled here as well
      - Move `attribute`, `decorate_attribute`, and anything else that
        modifies types to mess with this object, not the columns hash
      - Introduce a collection object to manage these, reduce allocations, and
        not require serializing the types
      6f08db05