Pro Yearly is on sale from $80 to $50! »

Eight Point Oh

Eight Point Oh

Bfa97d786f12ee3381f97bc909b88e11?s=128

Sebastian Riedel

September 08, 2018
Tweet

Transcript

  1. Eight Point Oh

  2. Sebastian Riedel @kraih

  3. 8.0

  4. None
  5. 26 New Features

  6. None
  7. openQA • 100+ Contributors • 320 Minion Workers • 2.7M

    Tests executed • 25 Years of Test Runtime • 700 Test Modules • 800 Bugs found
  8. Cavil • 110K Packages per Year • 27K License Patterns

    • 2TB of License Data • 122M Minion Jobs
  9. None
  10. New Logo

  11. None
  12. None
  13. None
  14. Subprocesses

  15. get '/slow' => sub { my $c = shift; sleep

    3; $c->render(text => 'Hello Mojo!'); };
  16. Manager Worker Worker Worker Worker 7.0

  17. Manager Worker Worker Worker Worker Subprocess Subprocess Subprocess 8.0

  18. Mojo::IOLoop->subprocess( sub { my $subprocess = shift; sleep 3; return

    'Hello Mojo!'; }, sub { my ($subprocess, $error, @results) = @_; ... } );
  19. get '/slow' => sub { my $c = shift; Mojo::IOLoop->subprocess(

    sub { sleep 3; return 'Hello Mojo!'; }, sub { my ($subprocess, $error, $message) = @_; return $c->reply->exception($error) if $error; $c->render(text => $message); } ); };
  20. Mojo::File

  21. use Cwd qw(getcwd realpath); use File::Copy qw(copy move); use File::Find

    'find'; use File::Spec::Functions qw(abs2rel canonpath catfile file_name_is_absolute rel2abs splitdir); use File::Temp qw(tempdir tempfile);
  22. use Mojo::File 'path';

  23. my $path = path('/home/sri/.vimrc'); # ["home", "sri", ".vimrc"] $path->to_array; #

    "/home/sri" $path->dirname; # ".vimrc" $path->basename;
  24. my $path = path('/home/sri'); # "/home/sri/.ssh/known_hosts" $path->child('.ssh', 'known_hosts'); # "/home/jberger/.bashrc"

    $path->sibling('jberger', '.bashrc');
  25. my $path = path('/home/sri'); $path->child('myapp')->make_path; $path->child('myapp', 'README')->spurt('Hello Mojo!');

  26. my $path = path('/home/sri'); for my $file ($path->list_tree->each) { say

    $file; } for my $file ($path->list->each) { say $file; }
  27. path('/home/sri')->list->each(sub { say $_->slurp });

  28. use Mojo::File qw(tempdir tempfile);

  29. my $dir = tempdir; $dir->child('test.txt')->spurt('Hello Mojo!'); undef $dir;

  30. $path->basename; $path->child(...); $path->copy_to(...); $path->dirname; $path->is_abs; $path->list; $path->list_tree; $path->make_path; $path->move_to(...); $path->open(...);

    $path->realpath; $path->remove_tree; $path->sibling(...); $path->slurp; $path->spurt(...); $path->to_abs; $path->to_array; $path->to_rel(...); $path->to_string;
  31. UNIX Domain Sockets

  32. None
  33. None
  34. None
  35. Configuration Overrides

  36. package MyApp; use Mojo::Base 'Mojolicious'; sub startup { my $self

    = shift; my $config = $self->plugin('Config'); my $name = $config->{name}; my $r = $self->routes; $r->get('/' => {inline => '%= $name'}); } 1;
  37. 7.0

  38. use Test::More; use Test::Mojo; my $t = Test::Mojo->new(MyApp => {name

    => 'foo'}); $t->get_ok('/')->content_is('foo'); $t = Test::Mojo->new(MyApp => {name => 'bar'}); $t->get_ok('/')->content_is('bar'); done_testing; 8.0
  39. Systemd Logging

  40. [Unit] Description=MyApp After=network.target [Service] Type=simple ExecStart=/home/sri/myapp/myapp.pl daemon [Install] WantedBy=multi-user.target

  41. None
  42. $app->log->short(1);

  43. None
  44. Roles

  45. my $t = Test::Mojo->new('MyApp'); my $location_is = sub { my

    ($t, $value, $desc) = @_; $desc ||= "Location: $value"; local $Test::Builder::Level = $Test::Builder::Level + 1; return $t->success(is($t->tx->res->headers->location, $value, $desc)); }; $t->get_ok('/') ->status_is(302) ->$location_is('https://mojolicious.org'); 7.0
  46. None
  47. package Test::Mojo::Role::Location; use Mojo::Base -role; use Test::More; sub location_is {

    my ($t, $value, $desc) = @_; $desc ||= "Location: $value"; local $Test::Builder::Level = $Test::Builder::Level + 1; return $t->success( is($t->tx->res->headers->location, $value, $desc)); } 1; 8.0
  48. my $t = Test::Mojo->new('MyApp')->with_role('+Location'); $t->get_ok('/') ->status_is(302) ->location_is('https://mojolicious.org');

  49. Test::Mojo->with_role('+Location'); Test::Mojo->with_role('Test::Mojo::Role::Location');

  50. Mojo::Log->with_role('+Clearable'); Mojo::UserAgent->with_role('+Queued'); Mojo::Promise->with_role('+Futurify'); Mojo::DOM->with_role('+PrettyPrinter'); Mojo::Collection->with_role('+UtilsBy'); Mojo::IOLoop::Stream->with_role('+LineBuffer'); Mojo::IOLoop::Subprocess->with_role('+Sereal'); Mojo::UserAgent::CookieJar->with_role('+Persistent');

  51. Promises/A+

  52. None
  53. $ua->get('https://mojolicious.org' => sub { my ($ua, $tx) = @_; ...

    }); 7.0
  54. my $promise = $ua->get_p('https://mojolicious.org'); 8.0

  55. pending fulfilled rejected

  56. my $followup_promise = $promise->then(sub { my $result = shift; ...

    return 1 + 1; }, sub { my $error = shift; ... return 2 + 2; });
  57. my $followup_promise = $promise->then(sub { my $result = shift; ...

    return $ua->get_p('https://mojolicious.org'); }, sub { my $error = shift; ... });
  58. $ua->get('https://mojolicious.org' => sub { my ($ua, $tx) = @_; say

    $tx->res->code; $ua->get('https://mojolicious.org' => sub { my ($ua, $tx) = @_; say $tx->res->code; $ua->get('https://mojolicious.org' => sub { my ($ua, $tx) = @_; say $tx->res->code; }); }); }); 7.0
  59. $ua->get_p('https://mojolicious.org')->then(sub { my $tx = shift; say $tx->res->code; return $ua->get_p('https://mojolicious.org');

    })->then(sub { my $tx = shift; say $tx->res->code; return $ua->get_p('https://mojolicious.org'); })->then(sub { my $tx = shift; say $tx->res->code; }); 8.0
  60. $ua->get_p('https://mojolicious.org')->then(sub { my $tx = shift; say $tx->res->code; return $ua->get_p('https://mojolicious.org');

    })->then(sub { my $tx = shift; say $tx->res->code; return $ua->get_p('https://mojolicious.org'); })->then(sub { my $tx = shift; say $tx->res->code; })->catch(sub { my $error = shift; ... });
  61. $promise->then(undef, sub {...}); $promise->catch(sub {...});

  62. my $promise = Mojo::Promise->new; $promise->resolve($result); $promise->reject($error);

  63. my $followup = Mojo::Promises->all(@promises); my $followup = Mojo::Promises->race(@promises); $promise =

    $promise->resolve(...); $promise = $promise->reject(...); my $followup = $promise->then(sub {...}, sub {...}); my $followup = $promise->catch(sub {...}); my $followup = $promise->finally(sub {...}); $promise->wait;
  64. Subroutine Signatures

  65. package MyApp::Controller; use Mojo::Base 'Mojolicious::Controller'; use 5.20.0; use experimental 'signatures';

    sub foo($self) { $self->render(text => 'Hello Mojo!'); } 1; 7.0
  66. package MyApp::Controller; use Mojo::Base 'Mojolicious::Controller', -signatures; sub foo($self) { $self->render(text

    => 'Hello Mojo!'); } 1; 8.0
  67. use Mojolicious::Lite -signatures; get '/' => sub ($c) { $c->render(text

    => 'Hello Batman!’); }; app->start;
  68. None
  69. timing->* Helpers

  70. None
  71. None
  72. HTTP/1.1 200 OK Date: Sat, 13 Sep 2014 16:48:29 GMT

    Content-Length: 11 Server: Mojolicious (Perl) Server-Timing: cpu;desc=CPU;dur=0.03, db;desc=PostgreSQL;dur=0.6 Hello Mojo!
  73. $c->timing->begin('db'); sleep 3; my $elapsed = $c->timing->elapsed('db'); $c->timing->server_timing('db', 'PostgreSQL', $elapsed);

  74. $c->timing->begin('stuff'); sleep 3; my $elapsed = $c->timing->elapsed('stuff'); my $rps =

    $c->timing->rps($elapsed); $c->app->log->debug("Stuff took $elapsed seconds ($rps/s)");
  75. Placeholder Types

  76. GET /bender GET /leela

  77. $r->get('/:name' => [name => ['bender', 'leela']]);

  78. $r->get('/:name' => [name => ['bender', 'leela']]); $r->put('/:name' => [name =>

    ['bender', 'leela']]);
  79. $r->add_type(futurama => ['bender', 'leela']); $r->get('/<name:futurama>'); $r->put('/<name:futurama>');

  80. $r->add_type(futurama => qr/(?:bender|leela)/); $r->get('/<name:futurama>'); $r->put('/<name:futurama>');

  81. Secure User Agent

  82. None
  83. None
  84. None
  85. None
  86. None
  87. use Mojo::UserAgent; my $ua = Mojo::UserAgent->new(insecure => 1); say $ua->get('https://127.0.0.1:3000')->res->text;

  88. before_server_start

  89. $app->hook(before_server_start => sub { my ($server, $app) = @_; return

    unless $server->isa('Mojo::Server::Daemon'); say "Manager $$ started"; $server->on( spawn => sub { my ($server, $pid) = @_; say "Worker $pid started"; } ) if $server->isa('Mojo::Server::Prefork'); });
  90. plugin 'Status';

  91. Request ID

  92. my $id = $c->req->request_id;

  93. None
  94. None
  95. $app->hook(before_dispatch => sub { my $c = shift; my $id

    = $c->req->headers->header('X-Request-Id'); $c->req->request_id($id); });
  96. Cpanel::JSON::XS

  97. 0 75 150 225 300 JSON::PP Mojo::JSON

  98. 0 1750 3500 5250 7000 JSON::PP Mojo::JSON Cpanel::JSON::XS

  99. None
  100. None
  101. Mojo::Pg

  102. ???

  103. use DBD::Pg ':async'; use Mojo::IOLoop; my $dbh = DBI->connect( 'dbi:Pg:dbname=postgres;host=127.0.0.1;port=5432',

    'postgres', '', { AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, PrintWarn => 0, RaiseError => 1 } ); my $sth = $dbh->prepare('select 1', {pg_async => PG_ASYNC}); $sth->execute; open my $handle, '<&', $dbh->{pg_socket} or die "Can't dup: $!"; Mojo::IOLoop->singleton->reactor->io( $handle => sub { return unless $dbh->pg_ready; my $result = $sth->pg_result; my $data = $sth->fetchall_arrayref; ... Mojo::IOLoop->stop; } ); Mojo::IOLoop->start;
  104. use Mojo::Pg; my $pg = Mojo::Pg->new('postgres://sri@127.0.0.1:5432/myapp'); $pg->db->query_p('select 1')->then(sub { my

    $results = shift; ... })->catch(sub { my $error = shift; ... })->wait;
  105. my $db1 = $pg->db; my $db2 = $pg->db; $db1->query_p('select 1')->then(...);

    $db2->query_p('select 1')->then(...);
  106. my $results = $pg->db->query('select * from foo where bar =

    ?', 'baz');
  107. my $arrays = $results->arrays; my $hashes = $results->hashes; my $array

    = $results->array; my $hash = $results->hash; my $text = $results->text; my $names = $results->columns; my $num = $results->rows;
  108. my $users = $pg->db->query('select * from users')->hashes; for my $user

    ($users->each) { say $user->{first_name}; say $user->{last_name}; }
  109. SQL::Abstract::Pg

  110. $db->insert('users', {name => 'Joel'}); my $id = $db->select('users', '*', {name

    => 'Joel'})->hash->{id}; $db->update('users', {name => 'jberger'}, {id => $id}); $db->delete('users', {name => 'jberger'});
  111. $db->select_p('users', '*', {name => ‘Marcus’})->then( sub { my $results =

    shift; say $results->hash->{id}; }, sub { my $error = shift; warn $error; } );
  112. # "select * from users limit 10 offset 20" $db->select('users',

    '*', undef, {limit => 10, offset => 20});
  113. # "insert into t (a) values ('b') on conflict (a)

    do update set a = 'c'" $db->insert('t', {a => 'b'}, {on_conflict => [a => {a => 'c'}]}); # "select * from foo join bar on (bar.foo_id = foo.id)" $db->select(['foo', ['bar', foo_id => 'id']]); # "select * from some_table where id = 1 for update" $db->select('some_table', '*', {id => 1}, {for => 'update'});
  114. None
  115. Pub/Sub

  116. $pg->pubsub->listen(foo => sub { my ($pubsub, $payload) = @_; say

    "foo: $payload"; }); $pg->pubsub->notify(foo => 'PostgreSQL rocks!');
  117. None
  118. Migrations

  119. -- 1 up create table if not exists posts (

    id serial primary key, title text, body text ); -- 1 down drop table if exists posts;
  120. -- 1 up create table if not exists posts (

    id serial primary key, title text, body text ); -- 2 up alter tabl posts add column slug text; -- 1 down drop table if exists posts;
  121. $pg->migrations->from_file('migrations/blog.sql'); $pg->migrations->from_data('MyApp'); $pg->migrations->from_string('....');

  122. $pg->migrations->migrate; $pg->migrations->migrate(2); $pg->migrations->migrate(0);

  123. None
  124. Minion

  125. PostgreSQL

  126. None
  127. SKIP LOCKED JSONB ARRAY LISTEN/NOTIFY Upsert Transactional DDL

  128. package Minion::Backend::MyBackend; use Mojo::Base 'Minion::Backend'; sub broadcast {...} sub dequeue

    {...} sub enqueue {...} sub fail_job {...} sub finish_job {...} sub history {...} sub list_jobs {...} sub list_locks {...} sub list_workers {...} sub lock {...} sub note {...} sub receive {...} sub register_worker {...} sub remove_job {...} sub repair {...} sub reset {...} sub retry_job {...} sub stats {...} sub unlock {...} sub unregister_worker {...}
  129. 3rd Party

  130. Admin UI

  131. plugin 'Minion::Admin';

  132. None
  133. None
  134. None
  135. None
  136. Concurrency

  137. Job Job Job

  138. Job Job Job Job

  139. None
  140. Job Results

  141. $minion->add_task(foo => sub { my ($job, @args) = @_; sleep

    5; say $job->finish({hello => 'mojo'}); });
  142. None
  143. my $result = $minion->job($id)->info->{result}; say $result->{hello};

  144. Job Dependencies

  145. resize pack resize resize

  146. my $id_one = $minion->enqueue('resize', [...]); my $id_two = $minion->enqueue('resize', [...]);

    my $pack_id = $minion->enqueue( 'pack', [...], {parents => [$id_one ,$id_two]});
  147. None
  148. Remote Control

  149. None
  150. None
  151. None
  152. None
  153. $worker->add_command(jobs => sub { my ($worker, $num) = @_; $worker->status->{jobs}

    = $num; });
  154. Foreground

  155. $minion->add_task(foo => sub { my ($job, @args) = @_; warn

    'Some important information!'; die 'Hello Mojo!'; });
  156. None
  157. None
  158. Thanks, have fun at Mojoconf!