Em 26-04-2012 22:46, Jeremy Evans escreveu:
On Thursday, April 26, 2012 3:41:26 PM UTC-7, Rodrigo Rosenfeld Rosas wrote:

    Em 26-04-2012 18:44, Jeremy Evans escreveu:
    On Thursday, April 26, 2012 12:58:57 PM UTC-7, Rodrigo Rosenfeld
    Rosas wrote:

        Actually I've learned about the sandbox version of the
        console today and I found it very interesting. It would be
        great to be able to use it with Sequel too.


    That's fairly easy to do by adding an additional option to
    bin/sequel, and having IRB.start run inside a
    DB.transaction(:rollback=>:always) block.  I haven't done it yet
    as it has corner cases (multiple threads and/or sharding, where a
    separate connection outside of the transaction could be created).

    Rails console is not just an IRB instance. It loads the Rails
    environment and will make some other variables and methods
    available to the console. What is bin/sequel all about?


Well, it depends on what you mean by "not just an IRB instance". By default, it does call IRB.start: https://github.com/rails/rails/blob/master/railties/lib/rails/commands/console.rb

Of course Rails console will have to delegate to IRB, Pry or other interactive shell at some point. But none of them (including bin/sequel) will load the Rails environment alone. For example, you won't have access to the 'helper' method (or variable, not sure) inside a normal IRB or sequel session. Also, they won't run the initializers, etc.

So, I'd have to create my own console. This is not a great thing to do. For example, if I wanted to create a gem for integrating Sequel to Rails, this wouldn't be the right approach to take. Currently I would depend on asking the Rails core team to add a hook that would allow me to call "Console.start" inside a block and they accept it.

Another approach would be creating another command like "rails sequel-console" aliased as "rails sc", but that is clearly a desperate solution.

But I guess there is another reason why ActiveRecord preferred the at_exit approach. I guess that block will only run after all threads are finished running. So, even if you start some thread in the console and changed the database from there it would still rollback all changes. Particularly I would be fine not to support such edge case, but maybe I won't be able to convince them to add such a hook. I'll see.

Doing transaction{console.start} would be a perfectly valid way to implement the sandbox.

bin/sequel refers to sequel's command line tool. If you've installed the sequel gem, just run "sequel" on the command line. It's mentioned near the top of the README.

        I don't think every block-based API should provide an
        alternative API, but I don't see any issues in being able to
        manipulate connections and transactions directly through a
        public API:

        conn = DB.pool.pop
        conn.start_transaction
        # then in other context that has a reference to conn:
        conn.rollback_transaction # or conn.commit_transaction
        conn.release

        It is up to the developer using such API to be sure
        connections won't leak.


    This I don't agree with.  The error handling for transactions is
    very complex, and few people would be able to get it correct.

    So, what? Those people will never search for such methods. It is
    more likely that they will only use the block-based API. The
    additional API would be only used for special cases usually. In
    such cases, like RSpec or Rails console, leaking isn't a real
    problem and the implementation is so trivial that it would never leak.


You would be surprised how often people will use an inferior tool even if a superior tool is available.

If you think an implementation is so trivial it would not cause problems, I think you are wrong. Let's say you lost your database connection in the middle of your spec (someone kill -9 the wrong process), what do you think would happen? Possibly: connection pool removes connection, new connection for next spec (not in a transaction!). Running each spec in it's own transaction fixes this issue.

Each spec is already running on its own transaction. I'm just trying to get the before(:all) code to also run inside the outer-most transaction.

I guess RSpec don't implement the around(:all) because maybe it will run the after(:all) inside an at_exit block maybe for preventing running threads side effects, but I'm just guessing. I didn't look at RSpec code yet.

Note I think that may be a general problem with your idea of using a transaction around all the specs and a savepoint for each individual spec.

Could you please elaborate on that? Would you suggest me another approach (rather than DatabaseCleaner.truncate) for avoiding creating the same records all over again for usage in some examples?

I have already explained you the reasons why I think transactions and savepoints would be a better option. It would be much faster and I could still have some unchanged data always set in the database instead of recreating them in my specs.

    I think it is a mistake to try to hide complexity from developers.
    They should be given the choice and should be able to understand
    the pros and contras of each approach and then make the decision.
    You're not responsible for other's mistake.


You are entitled to your own opinion. However, in my opinion much of good programming is hiding the underlying complexity from developers.

Yeah, but this rules applies to my situation as well. It would much less complex for me to just call transaction.begin and transaction.rollback instead of having to dig into Sequel's source code to call private methods for doing what I want to. This is much more error prone and it makes it much more difficult for other developers to understand it. Just like if other developers are required to run "rails sc" instead of "rails c" if they want to use a sandboxed version.

So, when you hide complexity by limiting the usage to the most-common form you will be increasing complexity for other less common usage.

You would be surprised how often I get asked to help fix other people's mistakes. I may not be responsible, but that doesn't mean it wouldn't take up time better spent doing other things.

    Providing a non-block based public API would encourage developers
    to use it,

    Why do you think so? Just state in the API documentation:

    "WARNING: We really advice you against the use of such methods. It
    is very hard to prevent connections from leak if you're not using
    the block-based approach. In some rare situations (like
    interacting with other badly designed software) it is necessary to
    have this kind of control. But please don't use this unless you
    absolutely know what you're doing. You've been warned!"

    Then, only adventurers or cautions developers would use such an API.


With the current situation, only such developers will do what you are proposing. Basically, I like the current situation. The people smart enough to implement it properly can do so themselves, anyone who needs someone else to write a public API for them probably wouldn't be able to implement it properly.

This is not a matter of being able or not. Other developers that looked at my code should also be able to understand the code intention taking a glance at it. Also, working with public API is much more future proof in the sense that it is less likely that a Sequel upgrade would break our hack.

    which they would probably do poorly, things would break, and they
    would come here for support.  I don't want to support that use
    case, it's just not worth it.

    Then add to the notes that those methods aren't officially
    supported. :)


If it isn't officially supported, it shouldn't be added at all. You already have code that does what you want and isn't officially supported.

Okay, I won't insist on that.

      Block-based APIs solve this issue so that developers don't have
    to worry about it.

    But it also leaves other issues opened.

    This remembers me a recent discussion in the ruby-core list where
    some core developers think that thread-programming is  too
    complicate for users to make it correctly. So they don't bother in
    providing a virtual machine that will allow you to use the full
    power of your CPUs when running multi-threaded code due to a
    global lock. They say that you can get more CPU power by using
    processes instead and that dealing with locks by yourself while
    programming IPC can be too dangerous and they want a simpler
    approach for Ruby users. Clearly I don't agree with this
    complexity hiding policy.


I'm with the ruby developers. I think process-based design is the correct approach, and threads should only be used when absolutely necessary. Virtually all threaded code has race conditions in it. Anyone following the sequel commit log can see just this month I've been fixing issues with Sequel's thread safety that only occur on non-GVL ruby implementations (such as JRuby).

And JRuby was exactly what I had to resort at that time when I needed to use my full CPU power for a processing intensive task that took about 2 hours to complete. It would take about 12 hours to complete on MRI Ruby. If you're curious for more details:

http://rosenfeld.herokuapp.com/en/articles/ruby-rails/2012-03-04-how-nokogiri-and-jruby-saved-my-week

--
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sequel-talk?hl=en.

Reply via email to