►
Description
Support Operations - How the Zendesk Global Ticket Round Robin works
https://gitlab.com/gitlab-com/support/support-ops/zendesk-global/ticket-round-robin
A
Hey
all
right,
so
jason,
I'm
with
the
support
operations
team,
I'm
the
sport
operations
manager
and
I
kind
of
wanted
to
go
over
what
the
ticket
round
robin
looks
like,
especially
with
the
new
counter
system,
we're
building
that
will
be
going
live
tomorrow
or
monday,
which
you
won't
see
any
changes.
But
it
will
help
more
numerically
balance,
the
load
of
tickets
going
to
each
global
group,
I'll
kind
of
go
a
little
bit
towards
the
end
and
how
it
used
to
work
and
why
we're
moving
to
the
new
one.
A
But
let
me
share
my
screen,
we'll
kind
of
start
going
over
the
code.
A
Cool,
so
this
is
kind
of
the
readme
file.
It
will
contain,
like
the
recording
link,
it'll
talk
very
very
briefly
on
the
counter
system
and
special
tickets
and
what
the
groups
are
and
how
to
add
a
new
one
and
all
that
fun
stuff,
not
as
obvious
without
looking
at
the
code.
For
some
of
that,
which
is
why
we're
doing
this
recording
so
gem
file
wise.
A
It
takes
the
response
body
and
turns
it
into
a
ruby
object
for
me
that
I
can
start
manipulating
it's
a
lot
easier
than
taking
what
is
most
the
time,
a
giant
string
and
trying
to
work
with
it,
so
that
basically
kind
of
parses
it
into
that
format.
For
me,
right
now,
it's
using
3.0.1
but
probably
tell
with
these
gems
that
I'm
using
there's
no
reason
it's
on
that
specific
ruby
version.
That
just
right
now
is
what
ops
is
using
globally
for
most
of
our
projects,
so
kind
of
go
with
that.
A
We've
got
a
robocop
file
here
to
kind
of
tweak
some
of
the
linting
settings
that
it
does.
One
linting
thing:
that's
common
in
ruby
is
any
digit.
That's
over
three
should
be
using
underscores
where
you
would
normally
use
a
comma
and
human
readable
english.
I
just
like
that.
A
I
think
it
makes
scripts
look
really
weird
and
it
makes
it
difficult
for
me
to
read,
so
I
always
turn
that
off
when
I'm
dealing
with
numbers
like
that
and
then
abc's
size
and
method
length,
I
have
some
more
complex
functions
in
there
that
couldn't
be
simplified
down
and
those
were
the
only
things
there.
So
I
kind
of
limited
them.
A
The
method
length
is
mostly
because
I'll
occasionally
set
objects
as
functions
and
super
annoying.
When,
yes,
I
get
that
it
shouldn't
be
more
than
10,
but
this
particular
json
object
has
11
lines
and
then
our
ci
file,
which
is
how
all
this
runs,
is
basically
just
calling
the
run
script
and
it
runs
on
a
schedule
for
my
before
script.
I
always
do
ruby
dash
b
by
doing
debugging.
A
It's
really
easy
to
look
at
a
ci
job
and
say:
okay
yeah,
it's
on
3.1
like
it
should
be
or
3.0.1,
I
always
install
bundler,
and
then
I
just
run
bundle
and
because
of
the
gem
file.
It'll
do
all
that.
For
me,
the
run
script
is
not
very
fancy.
It's
requiring
the
actual
scripts
and
then
just
calling
the
ticket
round
robin
zendesk
class
and
then
telling
it
to
run
dna
wise.
A
We
have
a
counter
dot
json,
which
right
now
is
all
zeroed,
but
once
this
goes
live
we'll
never
be
zeroed
again,
it'll
pretty
much
just
keep
iterating
we'll
go
over
that
a
bit
more
as
I
go
over
the
scripts
and
then
we
have
a
code
owner's
file
which
is
used
for
our
actual
project.
For
mr
approvals
stuff,
like
that,
so
the
actual
script
starts
with
this
ticket
round
robin.rb.
A
This
is
where
I'm
declaring
the
module.
Initially,
I'm
doing
some
fancy
rubying
here
to
shortcut
having
to
require
all
those
gems.
I
only
have
three
so
really
not
saving
myself
that
much
time,
but
when
you
get
to
gyms
that
are
using
hundreds
of
gender,
you
know
script
setups
that
are
using
hundreds
of
gems.
A
It's
a
great
shortcut
that
basically
says
if
I
installed
the
gym
during
bundle,
I'm
going
to
need
it
and
for
various
gems
there
are
special
ways
you
have
to
put
them
in
the
gym
file
to
require
them
the
right
way,
but
we're
dealing
with
some
very
basic
ones
here,
so
not
a
big
deal
and
then
it's
going
to
call
require
relative,
which
is
telling
it
to
require
a
relative
file.
A
This
case
it's
going
to
be
in
the
ticket
round,
robin
folder
and
it's
called
client
so
take
it
round
robin
folder
and
then
client
this
within
that
module
is
where
I
declare
a
class.
The
first
thing
I
do
is
require
the
zendesk
file,
but
we'll
go
over
that
in
a
bit
here.
In
the
client
file,
I'm
just
kind
of
declaring
some
functions,
I'm
going
to
be
using
in
more
complex
scripts.
A
A
Environment.Fetch
is
the
new
way
that
they
want
you
to
be
using
environment
variables
in
ruby,
scripting.
It
used
to
just
be
environment
square
bracket,
like
you
were
calling
an
array,
but
now
you're
supposed
to
do
dot
fetch
anything
you
can
do
there
is
this
which
we'll
say
fetch
the
that
gl
token
from
your
environment.
A
But
if
it's
not
there
set
it
to
nil,
in
this
case
this
script,
I
want
it
to
fail
if
there's
not
a
get
laptop
like
that,
there's
many
problems
occurring
if
it's
missing
all
its
environment
anyway,
here
I'm
just
kind
of
calling
something
to
make
it
easier
for
me
to
commit
to
a
project.
Basically,
I'm
using
this
client,
this
faraday
connection
we
made
I'm
making
a
public
send,
which
is
a
function
to
send
a
request,
I'm
sending
a
post
request
and
then
I'm
doing
it
to
a
specific
project.
A
This
is
where
the
round
robin
lives
and
what
it's
doing
is
this
will
be
used
to
commit
that
counter
when
it
updates
it
and
then
ops,
we
defined
yeah
it'll
be
passed
and
ops
yeah
it's
passed
later
on
in
the
script,
so
it'll
be
past
what
the
options
are,
which
will
basically
contain
the
json
for
the
actual
commit
I've
got
a
function
here
to
define
retry
options
because
typing
all
that
out.
If
I
need
it,
multiple
times
is
annoying
and
not
efficient,
I'll
kind
of
go
I'll
kind
of
skip
over
that.
A
But
it's
basically
just
a
bunch
of
settings
that
are
thrown
into
the
into
the
request
to
tell
it
how
to
handle
various
types
of
failures,
such
as
I
failed
to
connect.
I
time
down,
tell
it
try
again
with
this
interval
this
randomness
back
off.
If
you
hit,
you
know
the
same
error
this
many
times,
etc.
A
Here
I'm
using
faraday
to
create
a
zendesk
api
connection.
In
this
case,
I
do
it
with
these
environment
variables,
because
we
have
you
know
the
global
zendesk,
the
us
federal
and
then
both
of
them
have
a
sandbox
so
really
annoying
to
type
that
url
out
and
then
have
to
change
it
constantly.
So
it's
much
easier
to
set
an
environment
variable
and
just
kind
of
swap
that
when
I'm
running
scripts
locally,
when
it
runs
the
cicd,
it
has
a
set
variable.
A
This
is
where
those
retry
options
come
into
play
once
again
default.
Faraday
adapter,
zendesk's
api
requires
you
set
the
content
type
to
application
json.
I
don't
know
why
everything
they
reply
back
to
is
in
json.
That
just
feels
like
a
weird
arbitrary
thing
to
have
to
do,
but
it
will
fail
if
you
don't
and
then
I'm
creating
a
basic
auth,
because
they
don't
use
like
a
token
thing
unless
you're
using
like
an
oauth
application.
A
Here,
I'm
calling
making
a
function
called
request
which
will
basically
use
this
faraday
connection
we
made
for
zendesk
and
do
another
public
send,
but
this
one
I'm
making
a
little
more
ambiguous.
So
I
can
call
different
forms
of
it.
A
In
this
case
the
http
method,
so
get
post
delete,
put
probably
another
that
I'm
forgetting
off
the
top
of
my
head
and
then
it
gives
it
the
url,
which
will
essentially
be
the
api
url.
So
you
can
see
how
here,
when
I
did
the
get
lab
one
I
didn't
have
to
tell
it
get
dot
com,
api
v4.
I
could
just
start
immediately
with
the
new
part
of
it.
A
This
works
the
same
way
and
then,
whatever
parameters
we're
passing
in
and
then
this
one
I'm
using
oj
to
actually
load
that
response
body
so
that
I
have
a
you
know,
a
ruby
object
to
work
with
or
a
structured
ruby
object
is
the
way.
I
should
say
that,
because
everything
in
ruby
is
an
object
for
here
I
have
a
function
called
counter,
which
literally
is
just
using
json
to
read
that
counter.json
file
we
keep
in
the
data
folder
and
then
here
I
define
our
ticket
our
global
group.
A
So
right
now
you
know
we
have
baobab
ginkgo,
capoc,
maple
and
pine
hoping
I
pronounced
all
those
right
if
we
needed
to
add
some
remove
some
we
wanted
to
test,
not
routing
every
third
ticket
to
you
know
not
applicable
or
something
like
that.
We
would
use
this
kind
of
logic,
so
the
real
meat
potatoes
of
this
comes
from
this
zendesk
script,
which
the
run
function
is
just
calling
three
other
functions,
sort
assign
and
commit
it
kind
of
starts
by
setting
an
empty
tickets
array.
A
A
This
is
super
useful
so
that
I
can
call
tickets
once
and
it's
it's
an
empty
array.
That's
what
I
needed
and
then
I
can
just
push
to
it
without
having
to
have
to
find
it
per
function
or
anything
weird
like
that.
So
it
allows
me
to
just
say:
tickets
and
tickets
will
keep
growing,
as
I
add
to
it
in
this,
but
for
this
we've
got
sauce,
self-managed
and
special,
and
I
abbreviate
where
I
can,
but
sauce
tickets
is
making
requests
which
you'll
remember
from
here
is
a
zendesk
request
and
it's
calling
a
specific
view.
A
This
view
id
is
something
only
ops
can
see,
but
it
is
a
very
aptly
named
sauce
to
be
sorted
and
it
literally
is
just
taking
every
new
sauce
ticket
that
comes
in,
which
would
be
you
know,
sauce
and
sauce
accounts
and
throwing
them
into
that
view
which
is
filtered
based
on.
They
have
no
global
group
and
they're
not
set
to
not
applicable,
and
it's
essentially
just
giving
me
an
array
of
okay.
A
Here's
all
the
sauce
tickets
to
sort
self
manage
is
doing
the
same
thing,
different
view,
which
just
has
the
self-managed
tickets,
which
would
be
self-managed,
get
lab,
dedicated,
open
partner,
select
partner
alliance
partner,
and
it's
just
getting
an
array
of
those
tickets
too.
So
starting
out,
we've
got
basically
two
arrays
there
special
tickets,
which
is
a
thing
we're
not
currently
using,
but
it
might
be
used
in
the
future.
So
this
is
kind
of
unused
code
right
now,
but
it
could
be
used.
A
Basically,
it's
going
to
first
go
through.
You
know
it's
going
to
make
it's
going
to
kind
of
I'm
actually
not
even
sure.
Why
I'm
doing
that?
Because
that's
kind
of
a
oh
okay,
you
know,
I
see
what
I'm
doing
so.
I've
got
an
objects
variable
there,
which
probably
not
the
best
name
for
it,
but
that's
what
I
just
called
it
and
I'm
kind
of
initiating
each
of
the
initiating
this
hash
with
basically
the
last
part
of
the
group.
A
So
if
we
go
back
to
the
groups,
it's
splitting
on
underscore
it's
getting
the
last,
so
it's
basically
just
gonna
get.
You
know
these
four
right
here
or
these
five
sorry.
So
it's
gonna
get
each
of
those
just
as
a
string
by
itself
and
kind
of
initiate
this
object.
So
it's
basically
doing
like
baobab
is
an
empty
array.
A
A
So
from
there
I'm
making
an
array
of
both
the
I'm
making
an
array
containing
two
different
arrays,
the
sauce
and
the
self-managed
tickets,
and
I'm
going
through
each
list
on
each
list.
I'm
then
iterating
through
each
group
and
essentially
I'm
looking
for
that
ticket
to
contain
that
group's
organizational
sgt
tag,
which
again,
would
just
be
the
name
of
it
if
it
contains
that
it's
going
to
sort
it
into
a
special
ticket
object
which
is
keeping
it
away
from
the
rest
of
them.
A
The
idea
here
is,
let's
say,
jason's
awesome,
org
every
ticket
from
that
they
file
goes
to
pine.
They
would
have
that
pine
tag
in
their
org
level
and
then,
when
they
file
tickets
they
would
have
a
pine
tag,
and
that
kind-
and
that
would
tell
this
script-
hey
every
one
of
their
tickets
goes
to
the
pine
group.
Do
not
send
it
to
other
groups.
A
We
have
this
remove
thing
which
will
then
go
back
over
the
sauce
itself
manage
tickets
to
remove
them
from
those
objects
if
they're
a
special
ticket.
The
idea
here
is,
we
could
specialize
route
these
tickets
if
desired
again.
None
of
this
is
currently
in
use,
it's
something
that
is
being
toyed
with,
so
I
wanted
to
make
sure
the
script
could
support
it
when
and
if
we
decided
to
do
that.
A
So
once
it's
kind
of
done.
All
of
this,
which
you
can
tell
how
like
remove
special
tickets,
is
where
it
starts
and
that's
gonna
automatically
call
special
tickets,
so
it's
gonna
run
all
of
this
that
calls
sauce
and
self-managed.
So
it's
calling
these
up
here,
but
essentially
after
it's
doing
that,
it's
then
going
to
go
through
and
sort
these
tickets
and
each
one's
going
to
kind
of
use.
The
same
method,
I
probably
should
have
made
this
a
little
cleaner
and
not
had
three
functions
to
quintessentially.
A
Do
the
same
thing,
but
what
I'm
basically
doing
here
is
referring
back
to
this
counter,
json
that
we
started
with,
and
it
is
sorting
them
based
on
these
counts
of
tickets
that
have
been
assigned
to
them
so
starting
out,
obviously
everything's
zero,
and
that's
just
for
this
going
live.
Like
I
said
once
it
goes,
live
they'll
never
be
at
zero
again.
A
Unless,
for
some
reason
we
decide
to
zero
them
all
out
and
then
we
could
and
it'll
kind
of
start
back
over,
but
essentially
what
it's
gonna
start
by
doing
is
sorting
them
out
and
the
way
it's
kind
of
doing
this
I'll
I'll
just
use
the
sauce
one
just
a
little
easier
to
read
it's
finding.
Who
has
the
lowest
number
of
tickets
first
and
sorting
by
that.
So
in
this
particular
case,
well,
they're
all
tied,
so
it's
just
going
to
go
by
their
regular
order.
A
So
you
know
the
order
they're
in,
but
in
a
situation
where,
like
everybody
had
four
but
baobab
had
zero,
it
would
say:
okay,
babe
has
zero.
So
in
this
particular
case
you
know
it
would
say:
okay,
well,
they
get
this
first
ticket
and
then
it's
going
to
add
that
they've
gotten
a
you
know
sauce
ticket.
So
it's
going
to
up
their
account
by
one
and
then
it's
going
to
repeat
this
iteration
and
this
grouping
every
time.
So,
every
time
it's
going
to
say
who
has
the
least
okay?
They
get
the
next.
A
Who
has
the
least
they
have
the
next
one.
So
it's
going
to
distribute
these
numerically
in
a
balanced
way.
I
say
numerically
because
obviously
it's
not
a
ticket.
You
know
one
ticket
can
be
harder
than
the
other,
but
there's
no
real
great
way
to
calculate
that.
Looking
at
a
brand
new
ticket
there
could
be
a
system
we
build
in
the
future
where
we
build
like
weights
based
on
categorization
or
something
along
those
lines
at
which
point
the
counter
system
would
be
not
adding
one
but
adding
a
weight,
and
they
would
determine
by
that.
A
But
right
now,
big
ticket
is
a
ticket
as
far
as
this
counter
is
assumed.
So
it's
just
kind
of
throwing
one
sauce
to
one
sauce,
one
sm
to
one
sm,
but
essentially
it's
going
through
all
of
this
and
while
it's
doing
it,
it
is
creating
what
we
call
an
update,
object
which
I'll
go
down
here
to
show
super
fancy.
But
this
is
what
it's
going
to
be
passing
to
zendesk
the
ticket
id.
A
Is
this
and
then
we're
editing
this
particular
custom
field,
which
it
has
an
id
of
this
and
we're
setting
the
group
to
that
s?
That
to
that
sgg,
that
custom
field
is
what
you
will
see
on
a
ticket
called
sgg,
because
I'm
not
that
original
on
naming.
So
basically,
this
is
just
setting
the
sgt
for
all
of
these,
but
it's
making
this
giant
kind
of
array
of
here's
all
the
tickets.
You
need
to
update.
You
see
how
it's
pushing
to
that
empty
tickets
array.
A
A
So
after
it's
sorted
and
figured
out
where
every
ticket's
going
assign
groups
is
going
to
make
a
zdesk
request
using
a
quick
method
and
it's
going
to
be
a
tickets
update
mini,
which
is
a
nice
little
wrapper
that
allows
me
to
update
100
tickets
up
to
100
tickets
at
a
time
with
one
api
call.
A
Instead
of
calling
100
api
calls
in
theory,
if
we
ever
got
such
a
giant
flood
of
tickets,
that
we
had
more
than
100
to
update
this
actually
would
have
an
issue
and
would
throw
an
error,
but
being
that
this
is
set
to
run
about
every
10
minutes
that
that
would
we
have
bigger
concerns
at
that
point.
A
At
that
point,
we
would
have
much
bigger
concerns.
Hey
we
just
had
more
than
100
tickets
created
in
a
10
minute
span,
something's
very,
very
wrong,
but
essentially
it's
going
to
do
this
update
call
unless
there's
no
tickets
to
sort
that's,
I
don't
obviously
don't
need
to
tell
it
to
update
zero
tickets.
That's
just
a
wasted.
Api
call,
no
point
in
that
and
then
just
for
debug
purposes.
It
tells
me
what
the
job
id
is.
So
I
can
go
check.
A
Ci
cd
logs
grab
that
job
id
and
go
check
and
zdesk's
api
to
see
if
there
was
a
problem
with
that,
job
tends
to
be
pretty
easy
to
notice,
though
with
the
sorted
cues
are
not
in
the
single
digits
like
they
historically
are.
If
they're
suddenly
double
digits.
I
start
looking
for
problems,
but
we
have
not
had
that
problem
really
there.
You
know
we've
not
had
that
problem
at
all.
I
should
say.
A
Yeah
so
I'll
stop
sharing,
because
that's
basically
the
code
and
how
it's
going
to
be
working
when
we
implement
this
counter
system.
So
before
I
kind
of
go
into
the.
Why
we're
moving
to
this
and
what
the
old
one
did
any
questions,
any
thoughts.
A
All
right
cool,
so
the
current
method.
What
it's
currently
doing
is
using
what
we
call
the
last
used
method
based
on
the
index
of
the
sgg
and
that
array
of
groups.
It
is
committing
a
number
to
the
repo
to
say:
baobab
was
used.
Last
ginkgo
was
used
last
and
then
it's
saying,
okay,
well,
the
used
last
index
was
you
know
two.
So
I
need
to
start
at
three
and
that's
how
it's
currently
doing
that
works
mostly
fine.
A
A
It
technically
balances
it
the
same
way,
with
the
caveat
of
if
the
script
were
to
screw
up
and
throw
baobab
five
tickets,
it's
gonna,
say:
baobab
got
that
it's
just
saying.
As
far
as
the
current
last
use
method
is
concerned,
that's
one
they
just
consider
that
they
got
one
next
is
ginkgo,
I
think
alphabetically.
A
Whereas
this
counter
system
is
saying
well,
I
just
threw
baobab
five
tickets:
that's
five
tickets.
They
got
not.
They
were
just
used
last.
They
got
five.
A
You
can
see
how,
with
like
the
special
routing
system,
that
this
script
has
in
it
that
we're
not
using
but
could
potentially
use
that
could
quickly
get
to
be
an
unfair
balancing
if
every
ticket's
going
to
baobab
for,
like
five
organizations
and
they're
getting
rounded
normal
tickets.
On
top
of
that,
it's
very
quickly
going
to
spiral
to
where
you
know
that
one
group
is
getting
way
more
than
the
rest.
It's
not
balanced
with
the
last
use
system.
There
really
wasn't
a
there,
isn't
a
way
to
do
any
form
of
specialized
routing
like
that.
A
A
So
we
kind
of
like
I've
been
looking
at
it
going.
I
get
my
logic,
but
it
was
kind
of
bad.
I
could
see
where
a
situation
can
occur,
so
we've
been
moving
to
a
counter
system,
I've
gotten
questions
of.
Could
we
do
specialized
routing,
and
I
said
theoretically
and
as
I
was
go
moving
to
the
counter
system,
I
said
I
should
theoretically
put
the
code
in
there
so
that
we
are
not
a
blocker
if
support
wants
to
move
forward
with
that.
A
Cool
anybody,
questions,
thoughts,
comments,
ideas,
things
you
want
to
kind
of
express.
A
So
yeah
I'll
get
this
kind
of
closed
up,
put
onto
youtube
and
put
into
the
readme
for
this
counter
system.
And
then,
when
that's
live,
there
will
be
a
readme
with
the
recording
in
case.
You
forget
any
of
this
or
just
want
to
hear
my
lovely
voice,
go
over
ruby
coding
again
for
whatever
reason.
A
Right
on
well,
I
appreciate
y'all
joining.
I
hope
this
was
educational
and
helpful
to
learning
how
this
system
works.
I
know
there's
been
a
lot
of
confusion
of
well.
How
does
it
work
and
I'm
like
we
have
this
script
and
people
like
that's
great,
but
how
does
it
work
and
I
kind
of
tried
typing
up
a
couple,
different
drafts
to
explain
it.
I
was
like
I'm
typing
up
to
explain
how
code
works
after
having
already
coded
it
and
I'm
doing
it
as
a
coder.
So
I'm
staring
at
code
going
well
yeah.