Giter VIP home page Giter VIP logo

vfps's Introduction

vfps

Latest Version License OpenSSF Scorecard SLSA 3

A very fast and resource-efficient pseudonym service.

Supports horizontal service replication for highly-available deployments.

Run it

Warning Using the provided docker-compose.yaml is not a production-ready deployment but merely used to get started and testing quickly. It sets very restrictive resource limits uses the default password for an included, unoptimized PostgreSQL deployment.

docker compose -f docker-compose.yaml --profile=test up

Visit http://localhost:8080/ to view the OpenAPI specification of the Vfps API:

Screenshot of the OpenAPI specification

You can use the JSON-transcoded REST API described via OpenAPI or interact with the service using gRPC. For example, using grpcurl to create a new namespace:

grpcurl \
  -plaintext \
  -import-path src/Vfps/ \
  -proto src/Vfps/Protos/vfps/api/v1/namespaces.proto \
  -d '{"name": "test", "pseudonymGenerationMethod": "PSEUDONYM_GENERATION_METHOD_SECURE_RANDOM_BASE64URL_ENCODED", "pseudonymLength": 32}' \
  127.0.0.1:8081 \
  vfps.api.v1.NamespaceService/Create

And to create a new pseudonym inside this namespace:

grpcurl \
  -plaintext \
  -import-path src/Vfps/ \
  -proto src/Vfps/Protos/vfps/api/v1/pseudonyms.proto \
  -d '{"namespace": "test", "originalValue": "to be pseudonymized"}' \
  127.0.0.1:8081 \
  vfps.api.v1.PseudonymService/Create

Production-grade deployment

See https://github.com/miracum/charts/tree/master/charts/vfps for a production-grade deployment on Kubernetes via Helm.

Configuration

Available configuration options which can be set as environment variables:

Variable Type Default Description
ConnectionStrings__PostgreSQL string "" Connection string to the PostgreSQL database. See https://www.npgsql.org/doc/connection-string-parameters.html for options.
ForceRunDatabaseMigrations bool false Run database migrations as part of the startup. Only recommended when a single replica of the application is used.
Tracing__IsEnabled bool false Enable distributed tracing support.
Tracing__Exporter string "jaeger" The tracing export format. One of jaeger, otlp.
Tracing__ServiceName string "vfps" Tracing service name.
Tracing__RootSampler string "AlwaysOnSampler" Tracing parent root sampler. One of AlwaysOnSampler, AlwaysOffSampler, TraceIdRatioBasedSampler
Tracing__SamplingProbability double 0.1 Sampling probability to use if Tracing__RootSampler is set to TraceIdRatioBasedSampler.
Tracing__Jaeger object {} Jaeger exporter options.
Tracing__Otlp__Endpoint string "" The OTLP gRPC Endpoint URL.
Pseudonymization__Caching__Namespaces__IsEnabled bool false Set to true to enable namespace caching.
Pseudonymization__Caching__Pseudonyms__IsEnabled bool false Set to true to enable pseudonym caching.
Pseudonymization__Caching__SizeLimit int 65534 Maximum number of entries in the cache. The cache is shared between the pseudonyms and namespaces.
Pseudonymization__Caching__AbsoluteExpiration D.HH:mm:nn 0.01:00:00 Time after which a cache entry expires.

Observability

The service exports metrics in Prometheus format on :8082/metrics. Health-, readiness-, and liveness-probes are exposed at :8080/healthz, :8080/readyz, and :8080/livez respectively.

FHIR operations

The service also exposes a FHIR operations endpoint. Sending a FHIR Parameters resource to /v1/fhir/$create-pseudonym of the following schema:

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "namespace",
      "valueString": "test"
    },
    {
      "name": "originalValue",
      "valueString": "hello world"
    }
  ]
}

will create a pseudonym in the test namespace. The expected response looks as follows:

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "namespace",
      "valueString": "test"
    },
    {
      "name": "originalValue",
      "valueString": "hello world"
    },
    {
      "name": "pseudonymValue",
      "valueString": "8KWwnm3TXR5R9iUDVVKD-jUezE4DEyeydOeq4v_a_b5ejSLmqOlT8g"
    }
  ]
}

Development

Prerequisites

Build & run

Start an empty PostgreSQL database for development (optionally add -d to run in the background):

docker compose -f docker-compose.yaml up

To additionally start an instance of Jaeger Tracing, you can specify the jaeger profile:

docker compose -f docker-compose.yaml --profile=jaeger up

Restore dependencies and run in Debug mode:

dotnet restore
dotnet run -c Debug --project=src/Vfps

Open https://localhost:8080/ to see the OpenAPI UI for the JSON-transcoded gRPC services. You can also use grpcurl to interact with the API:

Note In development mode gRPC reflection is enabled and used by grpcurl by default.

grpcurl -plaintext \
    -d '{"name": "test", "pseudonymGenerationMethod": "PSEUDONYM_GENERATION_METHOD_SECURE_RANDOM_BASE64URL_ENCODED", "pseudonymLength": 32}' \
    127.0.0.1:8081 \
    vfps.api.v1.NamespaceService/Create

grpcurl -plaintext \
    -d '{"namespace": "test", "originalValue": "a test value"}' \
    127.0.0.1:8081 \
    vfps.api.v1.PseudonymService/Create

Run unit tests

dotnet test src/Vfps.Tests \
  --configuration=Release \
  --collect:"XPlat Code Coverage" \
  --results-directory=./coverage \
  -l "console;verbosity=detailed" \
  --settings=src/Vfps.Tests/runsettings.xml

Generate Code coverage report

If not installed, install the report generation too:

dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:"./coverage/*/coverage.cobertura.xml" -targetdir:"coveragereport" -reporttypes:Html
# remove the coverage directory so successive runs won't cause issues with their random GUID.
# See <https://github.com/microsoft/vstest/issues/2378>
rm -rf coverage/

Build container image

docker build -t ghcr.io/miracum/vfps:latest .

Run iter8 SLO experiments locally

kind create cluster

export IMAGE_TAG="iter8-test"

docker build -t ghcr.io/miracum/vfps:${IMAGE_TAG} .

kind load docker-image ghcr.io/miracum/vfps:${IMAGE_TAG}

helm upgrade --install \
  --set="image.tag=${IMAGE_TAG}" \
  -f tests/iter8/values.yaml \
  --wait \
  --timeout=15m \
  --version=^1.0.0 \
  vfps oci://ghcr.io/miracum/charts/vfps

kubectl apply -f tests/iter8/experiment.yaml

iter8 k assert -c completed --timeout 15m
iter8 k assert -c nofailure,slos
iter8 k report

Benchmarks

Micro benchmarks

The pseudonym generation methods are continuously benchmarked. Results are viewable at https://miracum.github.io/vfps/dev/bench/.

E2E load testing

Create a pseudonym namespace used for benchmarking:

grpcurl \
  -plaintext \
  -import-path src/Vfps/ \
  -proto src/Vfps/Protos/vfps/api/v1/namespaces.proto \
  -d '{"name": "benchmark", "pseudonymGenerationMethod": "PSEUDONYM_GENERATION_METHOD_SECURE_RANDOM_BASE64URL_ENCODED", "pseudonymLength": 32}' \
  127.0.0.1:8081 \
  vfps.api.v1.NamespaceService/Create

Generate 100.000 pseudonyms in the namespace from random original values:

ghz -n 100000 \
    --insecure \
    --import-paths src/Vfps/ \
    --proto src/Vfps/Protos/vfps/api/v1/pseudonyms.proto \
    --call vfps.api.v1.PseudonymService/Create \
    -d '{"originalValue": "{{randomString 32}}", "namespace": "benchmark"}' \
    127.0.0.1:8081

Sample output running on

OS=Windows 11 (10.0.22000.978/21H2)
12th Gen Intel Core i9-12900K, 1 CPU, 24 logical and 16 physical cores
32GiB of DDR4 4800MHz RAM
Samsung SSD 980 Pro 1TiB
PostgreSQL running in WSL2 VM on the same machine.
.NET SDK=7.0.100-rc.1.22431.12
Summary:
  Count:        100000
  Total:        16.68 s
  Slowest:      187.81 ms
  Fastest:      2.52 ms
  Average:      8.00 ms
  Requests/sec: 5993.51

Response time histogram:
  2.522   [1]     |
  21.051  [99748] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  39.580  [201]   |
  58.109  [0]     |
  76.639  [0]     |
  95.168  [0]     |
  113.697 [0]     |
  132.226 [0]     |
  150.755 [0]     |
  169.285 [0]     |
  187.814 [50]    |

Latency distribution:
  10 % in 6.26 ms
  25 % in 6.91 ms
  50 % in 7.72 ms
  75 % in 8.93 ms
  90 % in 9.57 ms
  95 % in 10.01 ms
  99 % in 11.86 ms

Status code distribution:
  [OK]   100000 responses

Sub-10ms P99-latency

By default, each pseudonym creation requests executes two database queries: one to fetch the namespace configuration and a second one to persist the pseudonym if it doesn't already exist. There is an opt-in way to avoid the first query by caching the namespaces in a non-distributed in-memory cache. It can be enabled and configured using the following environment variables:

Variable Type Default Description
Pseudonymization__Caching__Namespaces__IsEnabled bool false Set to true to enable namespace caching.
Pseudonymization__Caching__SizeLimit int 32 Maximum number of entries in the cache.
Pseudonymization__Caching__AbsoluteExpiration D.HH:mm:nn 0.01:00:00 Time after which a cache entry expires.

Warning Deleting a namespace does not automatically remove it from the in-memory cache. Pseudonym creation requests against such a stale cached namespace will fail until either the entry expired or the service is restarted.

Using the same setup as above but with namespace caching enabled, we can lower the per-request latencies and increase throughput:

Summary:
  Count:        100000
  Total:        11.70 s
  Slowest:      27.23 ms
  Fastest:      1.55 ms
  Average:      5.47 ms
  Requests/sec: 8549.22

Response time histogram:
  1.546  [1]     |
  4.114  [17418] |∎∎∎∎∎∎∎∎∎∎
  6.682  [72827] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  9.251  [9382]  |∎∎∎∎∎
  11.819 [122]   |
  14.387 [0]     |
  16.956 [0]     |
  19.524 [0]     |
  22.092 [0]     |
  24.661 [49]    |
  27.229 [201]   |

Latency distribution:
  10 % in 4.00 ms
  25 % in 4.34 ms
  50 % in 5.81 ms
  75 % in 6.00 ms
  90 % in 6.66 ms
  95 % in 7.00 ms
  99 % in 8.00 ms

Status code distribution:
  [OK]   100000 responses

Resource efficiency

The sample deployment described in docker-compose.yaml sets strict resource limits for both the CPU (1 CPU) and memory (max 128MiB). Even under these constraints > 1k RPS are possible, although with significantly increased P99 latencies:

Summary:
  Count:        100000
  Total:        73.99 s
  Slowest:      268.06 ms
  Fastest:      5.26 ms
  Average:      36.69 ms
  Requests/sec: 1351.51

Response time histogram:
  5.257   [1]     |
  31.537  [57298] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  57.817  [21327] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  84.097  [17685] |∎∎∎∎∎∎∎∎∎∎∎∎
  110.377 [3395]  |∎∎
  136.656 [243]   |
  162.936 [0]     |
  189.216 [1]     |
  215.496 [0]     |
  241.776 [0]     |
  268.055 [50]    |

Latency distribution:
  10 % in 14.62 ms
  25 % in 18.47 ms
  50 % in 29.46 ms
  75 % in 47.53 ms
  90 % in 71.96 ms
  95 % in 79.95 ms
  99 % in 97.22 ms

Status code distribution:
  [OK]   100000 responses

Image signature and provenance verification

Prerequisites:

All released container images are signed using cosign and SLSA Level 3 provenance is available for verification.

IMAGE=ghcr.io/miracum/vfps:v1.3.5
DIGEST=$(crane digest "${IMAGE}")
IMAGE_DIGEST_PINNED="ghcr.io/miracum/vfps@${DIGEST}"
IMAGE_TAG="${IMAGE#*:}"

cosign verify \
   --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
   --certificate-identity-regexp="https://github.com/miracum/.github/.github/workflows/standard-build.yaml@.*" \
   --certificate-github-workflow-name="ci" \
   --certificate-github-workflow-repository="miracum/vfps" \
   --certificate-github-workflow-trigger="release" \
   --certificate-github-workflow-ref="refs/tags/${IMAGE_TAG}" \
   "${IMAGE_DIGEST_PINNED}"

slsa-verifier verify-image \
    --source-uri github.com/miracum/vfps \
    --source-tag ${IMAGE_TAG} \
    "${IMAGE_DIGEST_PINNED}"

See also https://github.com/slsa-framework/slsa-github-generator/tree/main/internal/builders/container#verification for details on verifying the image integrity using automated policy controllers.

vfps's People

Contributors

chgl avatar miracum-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

vfps's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): update docker.io/bitnami/kubectl:1.30.3 docker digest to fc6e485
  • chore(deps): update mcr.microsoft.com/dotnet/sdk:8.0.303-noble docker digest to e1713ba
  • chore(deps): update all non-major dependencies (BenchmarkDotNet, EntityFrameworkCore.Exceptions.PostgreSQL, EntityFrameworkCore.Exceptions.Sqlite, Hl7.Fhir.R4, docker.io/jaegertracing/all-in-one, docker.io/library/postgres)

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

docker-compose
docker-compose.yaml
  • docker.io/library/postgres 16.3@sha256:d0f363f8366fbc3f52d172c6e76bc27151c3d643b870e1062b4e8bfe65baf609
  • docker.io/jaegertracing/all-in-one 1.59.0@sha256:e369bd9a8e4a212bfed67aaff59b77ce0676df32828aaccca468a866efcb732b
dockerfile
Dockerfile
  • mcr.microsoft.com/dotnet/aspnet 8.0.7-noble-chiseled@sha256:1f6d3352e053938ab1496d30a03b0f2fb6d45a7359aa0979ace5eca71cb3fec4
  • mcr.microsoft.com/dotnet/sdk 8.0.303-noble@sha256:0ece4e7951bc9ab64577b5a56e541a618c67d6623e9104e9cc5ab7d762f16d75
  • docker.io/bitnami/kubectl 1.30.3@sha256:c20ccfdedb71c0c5374dba6841a09e10e9941d6e776e14d43d99c2113585c9ba
grpc-utils.Dockerfile
  • docker.io/library/ubuntu 24.04@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
github-actions
.github/workflows/benchmarks.yaml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • actions/setup-dotnet v4.0.1@6bd8b7f7774af54e05809fcc5431931b3eb1ddee
  • benchmark-action/github-action-benchmark v1.20.3@4de1bed97a47495fc4c5404952da0499e31f5c29
  • ubuntu 22.04
.github/workflows/buf.yaml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • bufbuild/buf-setup-action v1.34.0@35c243d7f2a909b1d4e40399b348a7fdab27d78d
  • bufbuild/buf-lint-action v1.1.1@06f9dd823d873146471cfaaf108a993fe00e5325
  • ubuntu 22.04
.github/workflows/build-grpc-utils-image.yaml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • docker/metadata-action v5@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
  • docker/setup-qemu-action v3@5927c834f5b4fdf503fca6f4c7eccda82949e1ee
  • docker/setup-buildx-action v3@4fd812986e6c8c2a69e18311145f9371337f27d4
  • docker/login-action v3@0d4c9c5ea7693da7b068278f7b52bda2a190a446
  • docker/build-push-action v5@ca052bb54ab0790a636c9b5f226502c73d547a25
  • ubuntu 22.04
.github/workflows/ci.yaml
  • miracum/.github v1.11.2@028aedc2d5806bb362562560f5e871d9364902cd
  • actions/download-artifact v4.1.8@fa0a91b85d4f404e444e00e005971372dc801d16
  • irongut/CodeCoverageSummary v1.3.0@51cc3a756ddcd398d447c044c02cb6aa83fdae95
  • marocchino/sticky-pull-request-comment v2.9.0@331f8f5b4215f0445d3c07b4967662a32a2d3e31
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • actions/download-artifact v4.1.8@fa0a91b85d4f404e444e00e005971372dc801d16
  • marocchino/sticky-pull-request-comment v2.9.0@331f8f5b4215f0445d3c07b4967662a32a2d3e31
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • actions/download-artifact v4.1.8@fa0a91b85d4f404e444e00e005971372dc801d16
  • actions/setup-dotnet v4.0.1@6bd8b7f7774af54e05809fcc5431931b3eb1ddee
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • helm/kind-action v1.10.0@0025e74a8c7512023d06dc019c617aa3cf561fde
  • actions/download-artifact v4.1.8@fa0a91b85d4f404e444e00e005971372dc801d16
  • marocchino/sticky-pull-request-comment v2.9.0@331f8f5b4215f0445d3c07b4967662a32a2d3e31
  • actions/upload-artifact v4.3.4@0b2256b8c012f0828dc542b3febcab082c67f72b
  • actions/upload-artifact v4.3.4@0b2256b8c012f0828dc542b3febcab082c67f72b
  • miracum/.github v1.11.2@028aedc2d5806bb362562560f5e871d9364902cd
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
.github/workflows/lint-pr-title.yaml
  • amannn/action-semantic-pull-request v5.5.3@0723387faaf9b38adef4775cd42cfd5155ed6017
  • ubuntu 22.04
.github/workflows/nightly-chaos.yaml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • docker/setup-buildx-action v3@4fd812986e6c8c2a69e18311145f9371337f27d4
  • arduino/setup-task v2.0.0@b91d5d2c96a56797b48ac1e0e89220bf64044611
  • actions/upload-artifact v4.3.4@0b2256b8c012f0828dc542b3febcab082c67f72b
  • ubuntu 22.04
.github/workflows/release-please.yaml
  • google-github-actions/release-please-action v3.7.13@db8f2c60ee802b3748b512940dde88eabd7b7e01
  • ubuntu 22.04
.github/workflows/schedule.yaml
  • miracum/.github v1.11.2@028aedc2d5806bb362562560f5e871d9364902cd
.github/workflows/scorecards.yaml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • ossf/scorecard-action v2.3.3@dc50aa9510b46c811795eb24b2f1ba02a914e534
  • actions/upload-artifact v4.3.4@0b2256b8c012f0828dc542b3febcab082c67f72b
  • github/codeql-action v3.25.11@b611370bb5703a7efb587f9d136a52ea24c5c38c
nuget
.config/dotnet-tools.json
  • dotnet-outdated-tool 4.6.4
  • csharpier 0.28.2
src/Vfps.Benchmarks/Vfps.Benchmarks.csproj
  • BenchmarkDotNet 0.13.12
src/Vfps.IntegrationTests/Vfps.IntegrationTests.csproj
  • coverlet.collector 6.0.2
  • xunit.runner.visualstudio 2.8.2
  • xunit 2.9.0
  • Testcontainers.PostgreSql 3.9.0
  • Testcontainers 3.9.0
  • Microsoft.NET.Test.Sdk 17.10.0
  • FluentAssertions 6.12.0
  • coverlet.msbuild 6.0.2
src/Vfps.StressTests/Vfps.StressTests.csproj
  • coverlet.collector 6.0.2
  • xunit.runner.visualstudio 2.8.2
  • FluentAssertions 6.12.0
  • NBomber 5.7.0
  • xunit 2.9.0
  • Microsoft.NET.Test.Sdk 17.10.0
src/Vfps.Tests/Vfps.Tests.csproj
  • coverlet.collector 6.0.2
  • xunit.runner.visualstudio 2.8.2
  • xunit 2.9.0
  • Microsoft.NET.Test.Sdk 17.10.0
  • Microsoft.EntityFrameworkCore.Sqlite 8.0.7
  • Microsoft.Data.Sqlite.Core 8.0.7
  • Microsoft.AspNetCore.Mvc.Testing 8.0.7
  • FluentAssertions 6.12.0
  • FakeItEasy.Analyzer.CSharp 6.1.1
  • FakeItEasy 8.3.0
  • EntityFrameworkCore.Exceptions.Sqlite 8.1.2
  • coverlet.msbuild 6.0.2
src/Vfps/Vfps.csproj
  • prometheus-net.AspNetCore.HealthChecks 8.2.1
  • prometheus-net.AspNetCore.Grpc 8.2.1
  • prometheus-net.AspNetCore 8.2.1
  • OpenTelemetry.Instrumentation.AspNetCore 1.9.0
  • OpenTelemetry.Extensions.Hosting 1.9.0
  • OpenTelemetry.Exporter.OpenTelemetryProtocol 1.9.0
  • OpenTelemetry.Exporter.Jaeger 1.5.1
  • Microsoft.EntityFrameworkCore.Sqlite 8.0.7
  • Microsoft.Data.Sqlite.Core 8.0.7
  • Npgsql.OpenTelemetry 8.0.3
  • Npgsql.EntityFrameworkCore.PostgreSQL 8.0.4
  • Npgsql 8.0.3
  • Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 8.0.7
  • Microsoft.EntityFrameworkCore.Design 8.0.7
  • Microsoft.EntityFrameworkCore 8.0.7
  • Microsoft.AspNetCore.Grpc.Swagger 0.8.7
  • Hl7.Fhir.R4 5.9.0
  • Grpc.AspNetCore.Server.Reflection 2.65.0
  • Grpc.AspNetCore.HealthChecks 2.65.0
  • Grpc.AspNetCore 2.65.0
  • EntityFrameworkCore.Exceptions.PostgreSQL 8.1.2
  • EFCore.NamingConventions 8.0.3

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.