Skip to content

CP-49135: speed up UUID generation #6018

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 30, 2024

Conversation

edwintorok
Copy link
Contributor

2 optimizations here:

  • open /dev/urandom just once, speeds up session uuid/ref creation by ~3.8x
  • use a PRNG for non-secret UUIDs. We cannot use these for sessions (those UUIDs/opaquerefs are effectively the authentication tokens), or Pool secrets, but we can use it for everything else.

The latter is not yet enabled by default, needs more testing/auditing on whether we have any other secret opaquerefs/UUIDs in the codebase, but the 1st one is enabled by default (we still use /dev/urandom for generating the UUIDs, we just don't keep closing and reopening it).

We generate uuids frequently, e.g. when creating sessions.

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
@edwintorok edwintorok marked this pull request as draft September 26, 2024 14:37
@edwintorok
Copy link
Contributor Author

Back to draft, this seems to reveal a bug somewhere else in the codebase (someone closed the wrong file descriptor and ended up closing the urandom one...)

@edwintorok
Copy link
Contributor Author

edwintorok commented Sep 26, 2024

Back to draft, this seems to reveal a bug somewhere else in the codebase (someone closed the wrong file descriptor and ended up closing the urandom one...)

Ah crowbar runs at_exit so I can't use at_exit to close this, because that'll then run before the Crowbar tests, making them all fail (fixed).

And forkexecd isn't happy about the extra FD either (fixed).

TODO: split up the last commit that adds type safety for session into its own commit.

@edwintorok edwintorok force-pushed the private/edvint/prng branch 3 times, most recently from 472bc3e to 8e605e3 Compare September 26, 2024 17:26
@psafont
Copy link
Member

psafont commented Sep 27, 2024

Thanks, It's interesting to clearly see the types weren't used properly before, and now they are being tracked as they should

Reduces the amount of file descriptors kept open,
and avoids 2 syscalls when creating sessions.

Creating session uuids/refs is ~3.8x faster now:

Before:
```
uuidx creation/Uuidx.make_uuid_urnd (ns):
 { monotonic-clock per run = 5224.741331 (confidence: 5263.321869 to 5187.101530);
   r² = Some 0.997606 }
```

After:
```
uuidx creation/Uuidx.make_uuid_urnd (ns):
 { monotonic-clock per run = 1369.962560 (confidence: 1378.331674 to 1362.496608);
   r² = Some 0.998731 }
```

Adjust forkexecd test, where the called program is an OCaml program linked with Uuidx,
and thus has 1 extra FD open (that it didn't receive/inherit from the parent).

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
This will allow restricting which refs can be created from strings,
without breaking RPC
(the RPC code inside ref.ml will have access to the string conversion functions,
but external users can get a more restricted interface that e.g. wouldn't allow
constructing secret Refs from arbitrary strings)

No functional change.

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
Introduce an `all` type that contains all possible type parameters.

This'll ensure that spelling is consistent, e.g. we've got both VM and Vm,
and both Session and session currently.
It also prepares for using separate types for secret and non-secret refs/uuids.

Fix type signatures:
* some signatures just specified 'a Ref.t, these must now be explicit
* some signatures specified `API.ref_task Uuidx.t`, instead of `task Uuidx.t`, this is fixed
* spelling of VM, Vm, Session, session, Console, console, Crashdump, crashdump, User, user, PBD, pbd is made consistent
* test code must chose a concrete type, cannot leave the parameter abstract, use Generic

No functional change.

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
Split Uuidx.all into without_secret and secret parts.
This ensures that all sessions are created only with Uuidx.make_uuid_rnd which is guaranteed to use a CSPRNG.
Currently Uuidx.make uses the same generator, but that will change in a future commit.

Introduce Ref.make_secret, Ref.of_secret_string, and change the other function's signatures so that none of them
return a secret (except Ref.null).

It is important that uuids/refs that are used as a secret/auth token are generated in a cryptographically safe way,
if a user can guess them then they can impersonate an authenticated user.

Ref.of_secret_string bypasses these checks, but it is only used in a limited set of places, where we receive
session ids from an external source (HTTP requests, or CLI parameters), or test code.

No functional change.

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
Not yet enabled by default, behind xapi.conf feature flag.
Will be enabled by default after more testing/auditing of the code.

Pool secret and Session are the only secret uuids/refs,
for everything else avoid the overhead of reading from /dev/urandom every time,
and use an internal PRNG.

Both the opaqueref and UUID of a Session is a secret, because you can retrieve one if you can guess the other,
so both must be created using CSPRNG.

This is about 15x faster than using /dev/urandom:
```
╭───────────────────────────────────────┬───────────────────────────┬───────────────────────────┬───────────────────────────╮
│name                                   │  major-allocated          │  minor-allocated          │  monotonic-clock          │
├───────────────────────────────────────┼───────────────────────────┼───────────────────────────┼───────────────────────────┤
│  uuidx creation/Uuidx.make            │             0.0001 mjw/run│             8.0001 mnw/run│             88.1485 ns/run│
│  uuidx creation/Uuidx.make_uuid_urnd  │             0.0000 mjw/run│            10.0014 mnw/run│           1369.9626 ns/run│
╰───────────────────────────────────────┴───────────────────────────┴───────────────────────────┴───────────────────────────╯
```

Uses the type system to ensure that we cannot create Refs or Uuids of type 'session' with the wrong function.
Example type error from a testcase that has ben fixed:
```
File "ocaml/tests/test_client.ml", line 18, characters 47-57:
18 |       Test_common.make_session ~__context ~ref:session_id
                                                    ^^^^^^^^^^
Error: This expression has type [< Uuidx.not_session ] Ref.t
       but an expression was expected of type [ `session ] Ref.t
       Type [< Uuidx.not_session ] = [< `session of [ `use_make_uuid_urnd ] ]
       is not compatible with type [ `session ]
       Types for tag `session are incompatible
```

Signed-off-by: Edwin Török <edwin.torok@cloud.com>
@edwintorok
Copy link
Contributor Author

I've split the type safety changes into their own commits, and also introduced a 'Ref.of_secret_string' to more clearly separate the two.

Of course the type system can't tell us if we use secret UUIDs anywhere other than Session, it is up to us to tell it the list of classes that have secret uuids.
Also if you immediately convert a Uuidx.t to a string you wouldn't get an error that you generated it wrongly (but usually Ref and UUID generation are close together, as in the session code).

@edwintorok edwintorok marked this pull request as ready for review September 27, 2024 16:29
x);"
; "end"
]
; ["module Ref = Ref"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there still any point to this line, as it is not renaming anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I got a build failure without it, we could probably find what generates Foo.Ref and make it generate just Ref instead.

@edwintorok edwintorok added this pull request to the merge queue Sep 30, 2024
Merged via the queue into xapi-project:master with commit 25bb1b5 Sep 30, 2024
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants