Giter Site home page Giter Site logo

learn-onyx's Introduction

Onyx Workshop

These are the material needed to conduct the Onyx workshop. All dependencies are included, no external set up beyond the JVM and Leiningen are needed. Please read all the instructions before beginning!

Things you need to know

  • The workshop is divided into levels and challenges.
  • Each level is dedicated to a specific Onyx feature.
  • Each level has several challenges that you can work through.
  • Every challenge has one test file named challenge_<level>_<challenge>_test.clj and one implementation file named challenge_<level>_<challenge>.
  • Every level has a "challenge 0". This challenge is already working, and serves as an example. You don't need to do anything here but read.
  • IMPORTANT Onyx outputs its log messages to onyx.log in this project's root directory. Follow it with tail -F onyx.log.
  • Exception messages printed to the log are also printed to standard out for convenience.
  • All the answers can be found on the answers branch of this repository. Check it out and run lein test. All the tests should pass.
  • This workshop often leaves vars that need to be filled in with values unbound. If you see an exception complaining about an Unbound Var, track it down and fill in the implementation.
  • Invoking lein test on the master with no changes will cause it to hang. You need to fill in some code to make it run all the way through.

How to work on a challenge

  1. Read the How to Start documentation, listed below, to run the environment sanity check.
  2. Open the cheat sheet at src/workshop/cheat_sheet.clj for your own reference.
  3. Open the test file in test/ for the challenge. Read the comments.
  4. Examine the input and expected output.
  5. Run the test, watch it fail. If you aren't using something like Cider or nrepl, you can run lein repl, and invoke the appropriate test var. Run (reset) to hot reload your code.
  6. Open the corresponding challenge source file in src/ for the test.
  7. Locate the <<< BEGIN FILL ME IN >>> and <<< END FILL ME IN >>>.
  8. Fix the source file with the appropriate changes.
  9. Run the test file and pass the test. Refer to Onyx's documentation (links below) and the answers branch as needed. I recommend running the tests in a repl to avoid restarting the JVM between test runs. Starting and stopping the Onyx environment only takes about two seconds, so you can iterate much faster. You may wish to call (reset) (clojure.test/run-tests 'workshop.jobs.challenge-0-0-test) via the repl to reset your environment before running each test.
  10. Load up the cheat sheet documentation guide: http://www.onyxplatform.org/docs/cheat-sheet/latest/

Remember that challenge 0 on every level is already working!

How to start

Begin the workshop with level 0, challenge 0. This is a test to ensure that your environment is sane and working. If all is well, you should see the following on standard out:

Starting Onyx development environment
==== Start job output ====
[{:sentence "Getting started with Onyx is easy"}
 {:sentence "This is a segment"}
 {:sentence "Segments are Clojure maps"}
 {:sentence "Sample inputs are easy to fabricate"}
 :done]
==== End job output ====
Stopping Onyx development environment

Once you see this, proceed to level 1, challenge 0. Run the test and observe the already-working example in challenge 0, then proceed to the next challenge. Once you finish all the challenges, proceed to the next level and repeat.

Reading

Each file has a moderate amount of comments to frame the discussion. Still, here are some links that will be helpful if you're approaching Onyx from scratch or need more information to help solve a challenge:

Gotchas

  • Some of the tests capture standard out using clojure.core/with-out-str to make verifying assertions as easy as possible. If you're wondering where your printlns are going, check the test harness for the challenge. Feel free to drop it while you debug.
  • If your environment starts and then hangs on the Onyx log line INFO [onyx.log.zookeeper] - Starting ZooKeeper server, your ZooKeeper port is in use. Switch the port to an unused port in workshop.workshop-utils/zk-port.

Guide

Below is the table of contents for the levels and challenges you'll be working through. Feel free to skip around.

  • Level 0: Sanity Check
    • Challenge 0: Makes sure your environment is working okay
  • Level 1: Workflows
    • Challenge 0: Observe a minimal workflow
    • Challenge 1: Implement a linear workflow
    • Challenge 2: Implement a branched workflow
    • Challenge 3: Implement a larger DAG workflow
  • Level 2: Catalogs
    • Challenge 0: Observe a minimal catalog
    • Challenge 1: Implement a catalog entry for a function
    • Challenge 2: Parameterize a function through the catalog
    • Challenge 3: Implement a grouping task
    • Challenge 4: Bound the number of peers executing a task
  • Level 3: Functions
    • Challenge 0: Observe a basic function
    • Challenge 1: Implement 3 task functions
    • Challenge 2: Write a function that emits multiple segments
    • Challenge 3: Write a catalog entry and parameterized function
    • Challenge 4: Implement a batch function
  • Level 4: Lifecycles
    • Challenge 0: Observe a lifecycle example
    • Challenge 1: Use lifecycles for logging
    • Challenge 2: Compute an aggregate
    • Challenge 3: Parameterize a function with state
  • Level 5: Flow Conditions
    • Challenge 0: Observe a flow conditions example
    • Challenge 1: Use flow conditions for basic routing
    • Challenge 2: Create a composite flow conditions predicate
    • Challenge 3: Write an exception handler with flow conditions
    • Challenge 4: Use key exclusion
  • Level 6: Windows and Triggers
    • Challenge 0: Observe a fixed windowing example
    • Challenge 1: Create a fixed windowed that maintains a progressive sum
    • Challenge 2: Use sliding windows to take an average
    • Challenge 3: Create a fixed window with a watermark trigger

License

Copyright © 2015 Distributed Masonry LLC

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

learn-onyx's People

Contributors

bridgethillyer avatar codonnell avatar freethejazz avatar jackdempsey avatar kevingreene avatar lbradstreet avatar mariusz-jachimowicz-83 avatar michaeldrogalis avatar moquist avatar owengalenjones avatar ricardojmendez avatar superstevenz avatar tvanhens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

learn-onyx's Issues

learn-onyx SG issues

My notes from the Singapore Onyx Dojo.

TO IMPROVE:
challenge-2-2: mention batch-size / batch-timeout required i.e. partial task-map entry
challenge-2-0: information model link is outdated now
challenge-1-3 DAG is a bit too crazy or hard to understand

  • challenge-2-3: grouping is not required to pass the test - test doesn’t properly track whether the correct peers have the correct output.
  • Even after adding :onyx/group-by-key, no error was printed for missing flux policy

Once added, but with invalid min-max-peers, this was printed:

  • actual: java.lang.IllegalArgumentException: No method in multimethod 'predicate-error-msg' for dispatch value: valid-flux-policy-min-max-n-peers

  • I've fixed this in core by now.

    actual: clojure.lang.ExceptionInfo: Task :identity failed validation. Error: {:onyx/flux-policy missing-required-key}
    and
    actual: clojure.lang.ExceptionInfo: Value does not match schema: (not (valid-flux-policy-min-max-n-peers a-clojure.lang.PersistentArrayMap))

  • this one is due to capturing stdout. Should go to stderr plus, we shouldn't depend on capturing standard out in learn-onyx.

GENERAL

  • n-peers is broken when you set n-peers in a catalog entry

Challenge 6_0's tests fail

I get the following error when I try to run Challenge 6_0's tests:

ERROR in (test-level-6-challenge-0) (RocksDB.java:47)
Uncaught exception, not in assertion.
expected: nil
  actual: java.lang.ExceptionInInitializerError: null
 at org.rocksdb.RocksDB.loadLibrary (RocksDB.java:47)
    org.rocksdb.RocksDB.<clinit> (RocksDB.java:23)
    onyx.state.filter.rocksdb$eval22380$fn__22382.invoke (rocksdb.clj:53)
    clojure.lang.MultiFn.invoke (MultiFn.java:233)
    onyx.peer.task_lifecycle$resolve_filter_state.invokeStatic (task_lifecycle.clj:51)
    onyx.peer.task_lifecycle$resolve_filter_state.invoke (task_lifecycle.clj:45)
    onyx.peer.task_lifecycle.TaskLifeCycle.start (task_lifecycle.clj:496)
    com.stuartsierra.component$eval10614$fn__10615$G__10604__10617.invoke (component.cljc:5)
    com.stuartsierra.component$eval10614$fn__10615$G__10603__10620.invoke (component.cljc:5)
    clojure.lang.Var.invoke (Var.java:379)
    clojure.lang.AFn.applyToHelper (AFn.java:154)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    com.stuartsierra.component$try_action.invokeStatic (component.cljc:117)

    com.stuartsierra.component$try_action.invoke (component.cljc:116)
    com.stuartsierra.component$update_system$fn__10673.invoke (component.cljc:139)
    clojure.lang.ArraySeq.reduce (ArraySeq.java:114)
    clojure.core$reduce.invokeStatic (core.clj:6544)
    clojure.core$reduce.invoke (core.clj:6527)
    com.stuartsierra.component$update_system.invokeStatic (component.cljc:135)
    com.stuartsierra.component$update_system.doInvoke (component.cljc:129)
    clojure.lang.RestFn.invoke (RestFn.java:445)
    com.stuartsierra.component$start_system.invokeStatic (component.cljc:163)
    com.stuartsierra.component$start_system.invoke (component.cljc:155)
    onyx.system.OnyxTask/fn (system.clj:140)
    onyx.system$rethrow_component.invokeStatic (system.clj:56)
    onyx.system$rethrow_component.invoke (system.clj:54)
    onyx.system.OnyxTask.start (system.clj:139)
    onyx.log.commands.common$eval15602$start_new_lifecycle__15607$fn__15608$fn__15609.invoke (common.clj:150)
    clojure.core$binding_conveyor_fn$fn__4676.invoke
 (core.clj:1938)
    clojure.lang.AFn.call (AFn.java:18)
    java.util.concurrent.FutureTask.run (FutureTask.java:266)
    java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1142)
    java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:617)
    java.lang.Thread.run (Thread.java:745)
Caused by: java.lang.UnsupportedOperationException: null
 at org.rocksdb.util.Environment.getJniLibraryName (Environment.java:40)
    org.rocksdb.NativeLibraryLoader.<clinit> (NativeLibraryLoader.java:19)
    org.rocksdb.RocksDB.loadLibrary (RocksDB.java:47)
    org.rocksdb.RocksDB.<clinit> (RocksDB.java:23)
    onyx.state.filter.rocksdb$eval22380$fn__22382.invoke (rocksdb.clj:53)
    clojure.lang.MultiFn.invoke (MultiFn.java:233)
    onyx.peer.task_lifecycle$resolve_filter_state.invokeStatic (task_lifecycle.clj:51)
    onyx.peer.task_lifecycle$resolve_filter_state.invoke (task_lifecycle.clj:45)
    onyx.peer.task_lifecycle.TaskLifeCycle.start (task_lifecycle.clj:496)
    com.stuartsierra.component$eval10614$fn__10615$G__10604__10617.invoke (component.cljc:5)
    com.stuartsierra.component$eval10614$fn__10615$G__10603__10620.invoke (component.cljc:5)
    clojure.lang.Var.invoke (Var.java:379)
    clojure.lang.AFn.applyToHelper (AFn.java:154)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    com.stuartsierra.component$try_action.invokeStatic (component.cljc:117)
    com.stuartsierra.component$try_action.invoke (component.cljc:116)
    com.stuartsierra.component$update_system$fn__10673.invoke (component.cljc:139)
    clojure.lang.ArraySeq.reduce (ArraySeq.java:114)
    clojure.core$reduce.invokeStatic (core.clj:6544)
    clojure.core$reduce.invoke (core.clj:6527)
    com.stuartsierra.component$update_system.invokeStatic (component.cljc:135)
    com.stuartsierra.component$update_system.doInvoke (component.cljc:129)
    clojure.lang.RestFn.invoke (RestFn.java:445)

Improvements

Add after-retry and after-complete to lifecycles example.
Have an example where they add submit-job (and maybe await-job-completion).
Add an example where we can manipulate catalogs/workflows/etc to show the power of data and manipulating data

Challenge 4.1 should be clarified, solution/test possibly changed

This is partially referenced in #5, but I think during the workshop it was misdiagnosed.

In the test file, it says:

;; In this challenge, we're going to log every segment that
;; we see after it's processed by the :times-three task with
;; the lifecycle hook :after-batch. Obtain the segments via
;; the key :onyx.core/batch in the event map.

I believe this should say :onyx.core/results, as it is asking for the results after it has been processed, which should indicate that :onyx.core/batch shouldn't be used.

However, this propagated to the test / solutions files as well, as the test rebuilds the input instead of using the expected output here - https://github.com/onyx-platform/learn-onyx/blob/master/test/workshop/jobs/test_4_1.clj#L47

The solution also uses :onyx.core/batch to make the tests pass as well, seen here - https://github.com/onyx-platform/learn-onyx/blob/answers/src/workshop/challenge_4_1.clj#L53

I'd be happy to change this and submit a PR, but should the change be reworking the test, solution, and hints to use :onyx.core/results, or should it be reworking the prompt to simply say "every segment that we see before it's processed..."?

Tests of level 6 challenges fail

Hi Onyx team!

When going through the excellent Onyx workshop, the level 6 challenges fail during RockDB init with the following error (example for first test):

[user] λ: (reset) (clojure.test/run-tests 'workshop.jobs.challenge-6-0-test)

Testing workshop.jobs.challenge-6-0-test                                                                                                                                             
Starting Onyx test environment
Stopping Onyx test environment

ERROR in (test-level-6-challenge-0) (ClassLoader.java:-2)
Uncaught exception, not in assertion.
expected: nil
  actual: java.lang.UnsatisfiedLinkError: /tmp/librocksdbjni2552824838766080783..so: /tmp/librocksdbjni2552824838766080783..so: failed to map segment from shared object
 at java.lang.ClassLoader$NativeLibrary.load (ClassLoader.java:-2)
    java.lang.ClassLoader.loadLibrary0 (ClassLoader.java:1938)
    java.lang.ClassLoader.loadLibrary (ClassLoader.java:1821)
    java.lang.Runtime.load0 (Runtime.java:809)
    java.lang.System.load (System.java:1086)
    org.rocksdb.NativeLibraryLoader.loadLibraryFromJar (NativeLibraryLoader.java:105)
    org.rocksdb.NativeLibraryLoader.loadLibrary (NativeLibraryLoader.java:56)
    org.rocksdb.RocksDB.loadLibrary (RocksDB.java:47)
    org.rocksdb.RocksDB.<clinit> (RocksDB.java:23)
    onyx.state.filter.rocksdb$eval21879$fn__21881.invoke (rocksdb.clj:53)
    clojure.lang.MultiFn.invoke (MultiFn.java:233)
    onyx.peer.task_lifecycle$resolve_filter_state.invokeStatic (task_lifecycle.clj:51)
    onyx.peer.task_lifecycle$resolve_filter_state.invoke (task_lifecycle.clj:45)
    onyx.peer.task_lifecycle.TaskLifeCycle.start (task_lifecycle.clj:519)
    com.stuartsierra.component$eval11444$fn__11445$G__11434__11447.invoke (component.cljc:5)
    com.stuartsierra.component$eval11444$fn__11445$G__11433__11450.invoke (component.cljc:5)
    clojure.lang.Var.invoke (Var.java:379)
    clojure.lang.AFn.applyToHelper (AFn.java:154)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invokeStatic (core.clj:648)
    clojure.core$apply.invoke (core.clj:641)
    com.stuartsierra.component$try_action.invokeStatic (component.cljc:117)
    com.stuartsierra.component$try_action.invoke (component.cljc:116)
    com.stuartsierra.component$update_system$fn__11503.invoke (component.cljc:139)
    clojure.lang.ArraySeq.reduce (ArraySeq.java:114)
    clojure.core$reduce.invokeStatic (core.clj:6544)
    clojure.core$reduce.invoke (core.clj:6527)
    com.stuartsierra.component$update_system.invokeStatic (component.cljc:135)
    com.stuartsierra.component$update_system.doInvoke (component.cljc:129)
    clojure.lang.RestFn.invoke (RestFn.java:445)
    com.stuartsierra.component$start_system.invokeStatic (component.cljc:163)
    com.stuartsierra.component$start_system.invoke (component.cljc:155)
    onyx.system.OnyxTask/fn (system.clj:125)
    onyx.system$rethrow_component.invokeStatic (system.clj:80)
    onyx.system$rethrow_component.invoke (system.clj:78)
    onyx.system.OnyxTask.start (system.clj:124)
    onyx.log.commands.common$eval14531$start_new_lifecycle__14536$fn__14537$fn__14538.invoke (common.clj:162)
    clojure.core.async$thread_call$fn__7501.invoke (async.clj:434)
    clojure.lang.AFn.run (AFn.java:22)
    java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1142)
    java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:617)
    java.lang.Thread.run (Thread.java:745)

Ran 1 tests containing 1 assertions.
0 failures, 1 errors.
{:test 1, :pass 0, :fail 0, :error 1, :type :summary}

OS: Fedora release 24 (Twenty Four)
java version "1.8.0_60"
git commit 0ad1557

I guess this issue is closely related to #21.

This error can be resolved by issuing sudo mount /tmp -o remount,exec on a cmd line, but this is not something desirable.

It seems the shared library librocksdbjni needs a directory with exec rights, and /tmp does not provides them for security reasons.

There may be something to change in the RocksDB config or Onyx's one in order to use another directory for the JNI link?
Or maybe something with this obscure tools named SELinux?

Cheers!

Error when testing the solution for 4 2

  Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (0 frames hidden)

1. Unhandled clojure.lang.ExceptionInfo
   onyx.messaging.serializers.base_encoder.Encoder cannot be cast to
   onyx.messaging.serializers.base_encoder.Encoder
   {:original-exception :java.lang.ClassCastException}

             publisher.clj:  109  onyx.messaging.aeron.publisher.LocalDispatch/offer_segments
             publisher.clj:  234  onyx.messaging.aeron.publisher.Publisher/offer_segments_BANG_
      messaging_output.clj:   38  onyx.plugin.messaging-output/try-sends
      messaging_output.clj:   35  onyx.plugin.messaging-output/try-sends
      messaging_output.clj:  126  onyx.plugin.messaging-output.MessengerOutput/write_batch
        task_lifecycle.clj:  160  onyx.peer.task-lifecycle/write-batch
        task_lifecycle.clj:  156  onyx.peer.task-lifecycle/write-batch
        task_lifecycle.clj: 1076  onyx.peer.task-lifecycle/wrap-lifecycle-metrics/fn
        task_lifecycle.clj: 1049  onyx.peer.task-lifecycle.TaskStateMachine/exec
        task_lifecycle.clj:  524  onyx.peer.task-lifecycle/iteration
        task_lifecycle.clj:  521  onyx.peer.task-lifecycle/iteration
        task_lifecycle.clj:  542  onyx.peer.task-lifecycle/run-task-lifecycle!
        task_lifecycle.clj:  532  onyx.peer.task-lifecycle/run-task-lifecycle!
        task_lifecycle.clj: 1142  onyx.peer.task-lifecycle/start-task-lifecycle!/fn
                 async.clj:  442  clojure.core.async/thread-call/fn
                  AFn.java:   22  clojure.lang.AFn/run
   ThreadPoolExecutor.java: 1149  java.util.concurrent.ThreadPoolExecutor/runWorker
   ThreadPoolExecutor.java:  624  java.util.concurrent.ThreadPoolExecutor$Worker/run
               Thread.java:  748  java.lang.Thread/run

I resorted to checking the solution initially because I didn't understand that I needed a lifecycle to inject the atom and another to compute the max. After I redid my solution I got the error above, then after triple-checking everything, just replaced my content with the content of the solution and got the same error.

The workshop doesn't work on Ubuntu 14.04 + OpenJDK 7

Hey @MichaelDrogalis!

First off, thanks for taking the time to create this wonderful workshop!
I've encountered the same issue that was outlined here

The error messages were quite cryptic, but I had a hunch it might have something to do with my Java version. It turns out that upgrading to Java 8 did solve the problem on my machine (Ubuntu 14.04).

It might be worth investigating into this issue?
Or documenting that Java 8 is required?

As a side note, I've encountered the same problem when running the onyx-examples.
I'm curious which component requires Java 8.

Thanks again!

Itay

Tiny Improvement?

Working through these, I noticed in challenge 3-1 there's two functions interpose-space and interpose-pipe. It was odd seeing two specific fn's so soon after learning about generalized fn's with :onyx/params. Not sure if it's useful but seems like it could reinforce best-(?)-practices.

challenge 6.0 is confusing / incorrect

The comments mention that the toy data set has exactly 10 elements, which corresponds to the trigger threshold, ensuring it will fire. However, the test set only has 9 elements in it and actually fires because the job was completed (if you inspect the event-type out of the state-event record you will see this).

Refactor tests to follow Clojure conventions

One thing that would be beneficial for people using Cider is refactoring the test namespaces to be namespace.to.test-test, as that will allow people to use built in auto-running functionality, such as C-c , in Cider.

Another option would be to include documentation in the readme on how to configure this for various environments. For example, cider lets users define cider-test-infer-test-ns.

Test namespace missmatch

Is it intentional that the tests are namespaced as workshop.jobs.challenge-x-x-test while src files are namespaced workshop.challenge-x-x?

This prevents cider from being able to run the test directly from the src files. If the directory and namespace structure would match, it would solve this issue.

Challenge 2 4 gives bad error on Windows

While running Challenge 2 4, the check

(is (= ["Peer executing task :identity"
              "Peer executing task :identity"
              "Peer executing task :identity"
              "Peer executing task :read-segments"
              "Peer executing task :write-segments"]
             (sort (butlast (rest results))))))))

fails. On windows, each row has an appended \r.

Challenge 4.2 has confusing wording

In the comments in the test for challenge 4.2, it says that we are to "create an atom in the :before-task-start lifecycle method"; however, the implementation file already has an atom created and that is the one the test checks. It would seem to be that what I'm supposed to do is just swap! the already-extant atom in :after-batch but am I mistaken?

Thank you, really love this resource for learning Onyx, it is fantastic!

`segments-equal?` seems to be insufficient

segments-equal converts inputs and outputs to a set. That means that for example challenge 1-3 might show a valid test without duplicated outputs for the paths E-G, E-H,E-I.
Maybe this function should be

(defn segments-equal?
  "Onyx is a parallel, distributed system - so ordering isn't guaranteed.
   Does an unordered comparison of segments to check for equality."
  [expected actual]
  (is (= (frequencies expected) (frequencies actual))))

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.