Low-overhead, non-blocking I/O, external Process implementation for Java

Related tags

Native NuProcess
Overview

NuProcess

NuProcess is proud to power Facebook's Buck build.  

A low-overhead, non-blocking I/O, external Process execution implementation for Java. It is a replacement for java.lang.ProcessBuilder and java.lang.Process.

Have you ever been annoyed by the fact that whenever you spawn a process in Java you have to create two or three "pumper" threads (for every process) to pull data out of the stdout and stderr pipes and pump data into stdin? If your code starts a lot of processes you can have dozens or hundreds of threads doing nothing but pumping data.

NuProcess uses the JNA library to use platform-specific native APIs to achive non-blocking I/O on the pipes between your Java process and the spawned processes:

  • Linux: uses epoll
  • MacOS X: uses kqueue/kevent
  • Windows: uses IO Completion Ports

Maven

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>nuprocess</artifactId>
    <version>2.0.0</version>
    <scope>compile</scope>
</dependency>

👉 Note: Versions 1.0.0 and above contain breaking API changes from 0.9.7.

It's mostly about the memory

Speed-wise, there is not a significant difference between NuProcess and the standard Java Process class, even when running 500 concurrent processes. On some platforms such as MacOS X or Linux, NuProcess is 20% faster than java.lang.Process for large numbers of processes.

However, when it comes to memory there is a significant difference. The overhead of 500 threads, for example, is quite large compared to the one or few threads employed by NuProcess.

Additionally, on unix-based platforms such as Linux, when creating a new process java.lang.Process uses a fork()/exec() operation. This requires a temporary copy of the Java process (the fork), before the exec is performed. When running tests on Linux, in order to spawn 500 processes required setting the JVM max. memory to 3Gb (-Xmx3g). NuProcess uses a variant of fork() called vfork(), which does not impose this overhead. NuProcess can comfortably spawn 500 processes even when running the JVM with only 128Mb.

Example

Like the Java ProcessBuilder, NuProcess offers NuProcessBuilder, so building a process is fairly simple. Let's make a simple example where we use the Unix "cat" command. When launched with no parameters, cat reads from STDIN and echos the output to STDOUT. We're going to start the cat process, write "Hello world!" to its STDIN, and read the echoed reply from STDOUT and print it. Let's build and start the process.

NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("/bin/cat"));
ProcessHandler handler = new ProcessHandler();
pb.setProcessListener(handler);
NuProcess process = pb.start();
process.wantWrite();
process.waitFor(0, TimeUnit.SECONDS); // when 0 is used for waitFor() the wait is infinite

You'll notice the ProcessHandler in code above. This is a class you provide which receives callbacks from the process to handle input, output, termination, etc. And notice the wantWrite() call, this expresses that we have something we want to write to the process, so our ProcessHandler will be called back to perform the write. Here's what ProcessHandler looks like for our example:

class ProcessHandler extends NuAbstractProcessHandler {
   private NuProcess nuProcess;

   @Override
   public void onStart(NuProcess nuProcess) {
      this.nuProcess = nuProcess;
   }
   
   @Override
   public boolean onStdinReady(ByteBuffer buffer) {
      buffer.put("Hello world!".getBytes());
      buffer.flip();
      return false; // false means we have nothing else to write at this time
   }

   @Override
   public void onStdout(ByteBuffer buffer, boolean closed) {
      if (!closed) {
         byte[] bytes = new byte[buffer.remaining()];
         // You must update buffer.position() before returning (either implicitly,
         // like this, or explicitly) to indicate how many bytes your handler has consumed.
         buffer.get(bytes);
         System.out.println(new String(bytes));

         // For this example, we're done, so closing STDIN will cause the "cat" process to exit
         nuProcess.closeStdin(true);
      }
   }
}

Synchronous Operation

NuProcess does allow you to perform synchronous writes to the stdin of the spawned process. Even though the writes are synchronous they are non-blocking; meaning the write returns immediately. In this model, you do not use NuProcess.wantWrite() and your onStdinReady() method will not be called. If you extend the NuAbstractProcessHandler you do not need to provide an implementation of onStdinReady(). Use the NuProcess.writeStdin() method to write data to the process. This method will return immediately and writes are queued and occur in order. Read the JavaDoc for the NuProcess.writeStdin() method for cautions and caveats.

In the synchronous model, the above example would look like this:

NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("/bin/cat"));
ProcessHandler handler = new ProcessHandler();
pb.setProcessListener(handler);
NuProcess process = pb.start();

ByteBuffer buffer = ByteBuffer.wrap("Hello, World!".getBytes());
process.writeStdin(buffer);

process.waitFor(0, TimeUnit.SECONDS); // when 0 is used for waitFor() the wait is infinite

And the handler:

class ProcessHandler extends NuAbstractProcessHandler {
   private NuProcess nuProcess;

   @Override
   public void onStart(NuProcess nuProcess) {
      this.nuProcess = nuProcess;
   }
   
   public void onStdout(ByteBuffer buffer, boolean closed) {
      if (!closed) {
         byte[] bytes = new byte[buffer.remaining()];
         // You must update buffer.position() before returning (either implicitly,
         // like this, or explicitly) to indicate how many bytes your handler has consumed.
         buffer.get(bytes);
         System.out.println(new String(bytes));

         // For this example, we're done, so closing STDIN will cause the "cat" process to exit
         nuProcess.closeStdin(true);
      }
   }
}

JavaDocs

You can read the JavaDoc here. Make sure you read and fully understand the JavaDoc for the NuProcessHandler interface as it is your primary contract with NuProcess.

Settings

These are settings that can be defined as System properties that control various behaviors of the NuProcess library. You typically do not need to modify these.

com.zaxxer.nuprocess.threads

This setting controls how many threads are used to handle the STDIN, STDOUT, STDERR streams of spawned processes. No matter how many processes are spawned, this setting will be the maximum number of threads used. Possible values are:

  • auto (default) - this sets the maximum number of threads to the number of CPU cores divided by 2.
  • cores - this sets the maximum number of threads to the number of CPU cores.
  • <number> - the sets the maximum number of threads to a specific number. Often 1 will provide good performance even for dozens of processes.

The default is auto, but in reality if your child processes are "bursty" in their output, rather than producing a constant stream of data, a single thread may provide equivalent performance even with hundreds of processes.

com.zaxxer.nuprocess.softExitDetection

On Linux and Windows there is no method by which you can be notified in an asynchronous manner that a child process has exited. Rather than polling all child processes constantly NuProcess uses what we call "Soft Exit Detection". When a child process exits, the OS automatically closes all of it's open file handles; which is something about which we can be notified. So, on Linux and Windows when NuProcess determines that both the STDOUT and STDERR streams have been closed in the child process, that child process is put into a "dead pool". The processes in the dead pool are polled to determine when they have truly exited and what their exit status was. See com.zaxxer.nuprocess.deadPoolPollMs

The default value for this property is true. Setting this value to false will completely disable process exit detection, and the ``NuProcess.waitFor()" API MUST be used. Failure to invoke this API on Linux will result in an ever-growing accumulation of "zombie" processes and eventually an inability to create new processes. There is very little reason to disable soft exit detection unless you have child process that itself closes the STDOUT and STDERR streams.

com.zaxxer.nuprocess.deadPoolPollMs

On Linux and Windows, when Soft Exit Detection is enabled (the default), this property controls how often the processes in the dead pool are polled for their exit status. The default value is 250ms, and the minimum value is 100ms.

com.zaxxer.nuprocess.lingerTimeMs

This property controls how long the processing thread(s) remains after the last executing child process has exited. In order to avoid the overhead of starting up another processing thread, if processes are frequently run it may be desirable for the processing thread to remain (linger) for some amount of time (default 2500ms).

Related Projects

Charles Duffy has developed a Clojure wrapper library here. Julien Viet has developed a Vert.x 3 library here.

Limitations

The following limitations exist in NuProcess:

  • Currently only supports Linux, Windows, and MacOS X.
  • Java 7 and above
  • Linux support requires at least kernel version 2.6.17 or higher (kernels after June 2006)
Comments
  • Optimize epoll_wait loop to avoid memory churn.

    Optimize epoll_wait loop to avoid memory churn.

    • Disabled autoRead and/or autoWrite on EpollEvent structs depending on how they're used, in ProcessEpoll
      • EpollEvents in the eventPool have autoRead disabled because they are used a write-only context (Calling epoll_ctl)
      • The triggeredEvent EpollEvent has both autoRead/autoWrite disabled
        • When epoll_wait returns 1, read() is called explicitly, but if it returns 0 native memory is not read
        • Prior to calling epoll_ctl to register for further stdin events, write() is called explicitly
    • Fixed memory alignment on EpollEvent and added a unit test to verify the size of the struct is 12 bytes
    • Removed EpollEvent.EpollData.ptr because, even though it's never used, JNA still allocates it
    • Replaced Integers with ints using Integer.MIN_VALUE as a marker, when acquiring descriptors, to avoid allocating 3 per process() loop
      • If the process soft-exits, Integers will still be created then

    Extra bits:

    • Added IntelliJ IDEA files to .gitignore
    • Updated JNA to 4.5.1
    opened by bturner 21
  • current working directory (cwd) support

    current working directory (cwd) support

    The README states that "NuProcess does not currently permit setting an alternate current working directory (cwd) for the child process."

    Why does this limitation exist, and will be solved in near future?

    question 
    opened by okapies 17
  • Null injection hardening

    Null injection hardening

    In NuProcessBuilder throw an IllegalArgumentException if commands that include a null character are passed. This hardens against command line injection type attacks in applications that use NuProcess.

    The issue this aims to fix is described below. On Linux if you start a process with this list of arguments:

    NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("/tmp/main", "--foo", "--bar=\0--bad", "--extra"));
    

    One might expect you'd get arguments like this at the forked process (and indeed on MacOS you do, more on that later):

    argv[0]: /tmp/main
    argv[1]: --foo
    argv[2]: --bar=
    argv[3]: --extra
    

    Or perhaps you'd expect an exception is thrown. But instead you get this:

    argv[0]: /tmp/main
    argv[1]: --foo
    argv[2]: --bar=
    argv[3]: --bad
    

    The problem exists in NuProcess since 1.2.0 when it switched to using Java_java_lang_UNIXProcess_forkAndExec() and happens because the command array gets turned into a null separated string here: https://github.com/brettwooldridge/NuProcess/blob/master/src/main/java/com/zaxxer/nuprocess/linux/LinuxProcess.java#L141-L147

    That is necessary because Java_java_lang_UNIXProcess_forkAndExec() takes a string. And then in Java_java_lang_UNIXProcess_forkAndExec() it gets converted back to an array/vector, with the extra/injected null obviously being interpreted as a delimiter too: https://github.com/adoptium/jdk8u/blob/master/jdk/src/solaris/native/java/lang/UNIXProcess_md.c#L608 https://github.com/adoptium/jdk8u/blob/master/jdk/src/solaris/native/java/lang/childproc.c#L166-L176

    On MacOS this problem doesn't exist the forked process gets:

    argv[0]: /tmp/main
    argv[1]: --foo
    argv[2]: --bar=
    argv[3]: --extra
    

    This is because MacOS uses posix_spawnp() which takes an array, so doesn't have this conversion to a null delimited string. I'm not sure how Windows behaves, I didn't test it.

    The solution I used in our NuProcess fork was just to apply the same approach as Java's ProcessBuilder uses: https://github.com/adoptium/jdk8u/blob/master/jdk/src/share/classes/java/lang/ProcessBuilder.java#L1022-L1026

    There is some scope to do some deduplication of code in the constructors of NuProcessBuilder. But I've just kept it simple for now. Happy to do that deduplication, or any other changes should that be desirable.

    opened by benhumphreys 14
  • unshare(CLONE_FS) failed, return code: -1, last error: 1

    unshare(CLONE_FS) failed, return code: -1, last error: 1

    Exception in thread "NuProcessLinuxCwdChangeable-1" java.lang.RuntimeException: unshare(CLONE_FS) failed, return code: -1, last error: 1
            at com.zaxxer.nuprocess.internal.BasePosixProcess.checkReturnCode(BasePosixProcess.java:793)
            at com.zaxxer.nuprocess.internal.BasePosixProcess$LinuxCwdThreadFactory$1.run(BasePosixProcess.java:97)
            at java.lang.Thread.run(Thread.java:748)
    
    opened by MasseGuillaume 14
  • New classes NuCharsetDecoder and NuCharsetEncoder to handle conversion of bytes to string data

    New classes NuCharsetDecoder and NuCharsetEncoder to handle conversion of bytes to string data

    Thanks for the nice library! I'm an engineer on the Buck Build team at Facebook. Buck currently has some pretty crummy and non-performant abstractions around process management, and NuProcess is just what the doctor ordered.

    When using NuProcess, I found it's very common to want to use CharsetDecoder to decode stdout/stderr bytes provided to NuProcessHandler into String data (and to encode String data to bytes for stdin).

    Right now, it's pretty inconvenient to do that: if you don't consume all the bytes in the ByteBuffer passed to onStdout() (say, because a UTF-8 code point boundary straddles two reads), the buffer is wiped before the next invocation. That means you have to do your own byte buffering in order to decode the data.

    Since this seems like a pretty common use-case, this pull request adds a new concrete implementation of NuProcessHandler called NuProcessDecoder which handles streaming decoding and encoding of stdin, stdout, and stderr bytes to and from Java strings.

    Previously, stdout and stderr shared a single buffer in BasePosixProcess. After this diff, they are separate buffers.

    In addition, we now optionally support keeping unused data in the buffers after onStdout() or onStderr() returns, using ByteBuffer.compact() to move the unused data to the front of the buffer so it can be consumed on the next invocation of that method. This new behavior is off by default, and can be enabled by returning true from NuProcessHandler.shouldCompactBuffersAfterUse().

    Since we can now have unused data in the stdout and stderr buffers at exit, I added two new callbacks onPreExitStdout() and onPreExitStderr() to allow callers one last chance to consume stdout/stderr data at process exit time. This is necessary for CharsetDecoder to finalize any decoding to Unicode.

    I included docs and unit tests, but I only tested this on OS X 10.10. I think it ought to work on Windows as well, but I hope you (or CI) can test that for me.

    opened by bhamiltoncx 13
  • Starting daemonized processes

    Starting daemonized processes

    We are trying to switch from using the regular Runtime.exec() from java to the NuProcess lib. We are using this solely on Linux. We don't realy use async processes, e.g. when we execute a process we always need to wait untill its "done". Most of the time this is to read and parse output, this works great.

    However, we also have a couple of daemon processes we start (more specific: kvm/qemu). The regular behavior in Java was to return when the process was executed correctly without writing anything to stdout/stdin or return when the process could not be started (it would write output to stderror then).

    After our switch to NuProcess we can't "execute and return when started correctly" anymore. We use waitFor(0, ...) directly after executing to wait untill the process is don e but this call hangs untill we kill the executed process. I understand why this is (the process is still running) but im wondering if there is a solution so we get the same behavior as with Runtime.exec?

    opened by bramklg 13
  • Release 2.0.2 is broken on Java 8

    Release 2.0.2 is broken on Java 8

    Hi there,

    It seems 2.0.2 release was built with JDK 9+, causing the dreaded java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer; on Java 8.

    Regards

    opened by slandelle 11
  • Adds ability to run processes synchronously (#95).

    Adds ability to run processes synchronously (#95).

    As per discussed in #95, this one implements ability to run processes synchronously with pumping happening on the client thread.

    To achieve that, it extends builder, factory and process classes with run method that starts the process and then creates new event processor per client to listen for events. Processor then executes event loop the same way as it does for asynchronous code path.

    The change is tested with new runProcessSynchronously test in CatTest.

    Additionally it bumps minor version, because we've added a new feature here.

    opened by tnymlr 9
  • Compatibility with JNA 5.2.0

    Compatibility with JNA 5.2.0

    Facing issue when running with latest version of JNA.

    Exception faced

    Exception in thread "main" java.lang.NoSuchFieldError: SIZE at com.zaxxer.nuprocess.windows.NuWinNT$ULONG_PTR.(NuWinNT.java:128) at com.zaxxer.nuprocess.windows.NuWinNT$ULONG_PTRByReference.(NuWinNT.java:141) at com.zaxxer.nuprocess.windows.ProcessCompletions.(ProcessCompletions.java:79) at com.zaxxer.nuprocess.windows.WindowsProcess.(WindowsProcess.java:90) at com.zaxxer.nuprocess.windows.WinProcessFactory.createProcess(WinProcessFactory.java:41) at com.zaxxer.nuprocess.NuProcessBuilder.start(NuProcessBuilder.java:266)

    opened by anantharaman93 9
  • Obtain PID

    Obtain PID

    Using a library (or similar hackery) like this Flapdoodle, it is possible to obtain the PID of a standard Java Process object.

    Is that possible to do with a NuProcess object?

    enhancement 
    opened by MTyson 9
  • Fix wrong position and limit value on inBuffer caused by incorrect inBuffer clear.

    Fix wrong position and limit value on inBuffer caused by incorrect inBuffer clear.

    This bug happens when these three conditions happen together: no inBuffer remaining, no pending writes and no user wantsWrite. It results loading of whole empty inBuffer from position (0) to limit (BUFFER_CAPACITY).

    opened by pfxuan 9
  • FreeBSD Support - Fix syscall(SYS__pthread_chdir) failed to set current directory

    FreeBSD Support - Fix syscall(SYS__pthread_chdir) failed to set current directory

    Hello guys!

    I am a developer and co-founder of Wiselabs Software in Brazil. We have some mission-critical systems that we chose to migrate to FreeBSD. In some of these systems we use NuProcess, and in the quality assurance process we found the following problem:

    "syscall(SYS__pthread_chdir) failed to set current directory"

    We also found a bug with the order in the process registration call (Kevent).

    We chose to contribute to the project and resolve this bug. I hope it will be useful to you and other users.

    Thank you so much for this wonderful library.

    opened by fronald 1
  • Thread-safety contract should be properly documented for NuProcessHandler

    Thread-safety contract should be properly documented for NuProcessHandler

    When it comes to having some state inside NuProcessHandler implementation, some questions arise that cannot be answering by reading the Javadoc. Moreover, some examples in this repo contradicts the ideas about thread-safety that you can get from Javadoc.

    • Are writes done in onPreStart visible in onStart/onStd*/onExit? This is most clear of all< Javadoc says

      this method is invoked before the process is spawned, and is guaranteed to be invoked before any other methods are called. Can we treat this "before" as JMM's "happens-before"? Are any writes done is this method visible in other method calls? Discussion in #106 is quite contradictory on this matter.

    • Are writes done in onStart visible in onStd*/onExit Discussion in #106 and Javadoc suggest that the answer is no. However an example in README.md shows storing nuProcess instance in onStart. Discussion in #106 suggest that this is wrong.
    • **Are writes done in onStdOut visible in subsequent onStdOut calls? Are they visible in onExit call? In other words, can we have something like a giant StringBuilder to collect stdout of the process?
    • (if yes) ** Can we share state between e. g. onStdOut/onStdErr calls?
    • **Can we assume that all writes done in the handler visible to the thread that got non-Integer.MIN_VALUE from waitFor I'd suggest this to be a naive thing to assume, yet this example shares the state without any synchronization.
    opened by gagarski 1
  • Undocumented exit code MAX_INT-1

    Undocumented exit code MAX_INT-1

    I've noticed that when my Kubernetes pod shuts down, child processes often die with exit code 2147483646 ie MAX_INT-1, which they did not do under commons-exec. (Commons-exec would sometimes return its own INVALID_EXITVALUE in this case.) I couldn't find this code documented.

    Moreover, there is a bit of a discrepancy in the docs. waitFor says it will return Integer.MIN_VALUE if the timeout is reached while NuProcessHandler.onExit returns A value of Integer.MIN_VALUE indicates some kind of launch failure. A value of Integer.MAX_VALUE indicates an unknown or expected failure mode.

    It would great be to have better clarity on possible exit codes and when they should be expected.

    I should say that a Java library should try to communicate exceptional conditions with exceptions (or return an object with properties), and not by overloading the exit code which the command itself might return.

    opened by almson 0
  • Use Job objects in lieu of a shutdown hook on Windows.

    Use Job objects in lieu of a shutdown hook on Windows.

    Shutdown hooks have bad behavior on Windows. Using Windows Job objects ensure child processes die when the parent process (the NuProcess caller) dies, regardless of the circumstances that caused the JVM running NuProcess died.

    opened by doctorpangloss 0
  • subprocess under the main process, is that possible?

    subprocess under the main process, is that possible?

    Hi,

    I would like to keep the terminal open and sending few commands over. 1 of the command will open a pipe and waiting for the input. is that possible?

    I had tried ProcessBuilder. it cant.

    Thanks

    opened by ivanooi 0
  • Add Java 9/JPMS module support

    Add Java 9/JPMS module support

    Currently even latest version 2.0.1 of the library does not have support for java modules. Since this is a utility, its will everyone who want to use this with JPMS modules post Java-9

    opened by thekalinga 2
Owner
Brett Wooldridge
Vice President and Lead Architect of Software Development. Father of an angel who fell to Earth and somehow into my life.
Brett Wooldridge
The missing bridge between Java and native C++

JavaCPP Commercial support: Introduction JavaCPP provides efficient access to native C++ inside Java, not unlike the way some C/C++ compilers interact

Bytedeco 4k Jan 8, 2023
Java Native Access

Java Native Access (JNA) The definitive JNA reference (including an overview and usage details) is in the JavaDoc. Please read the overview. Questions

Java Native Access 7.6k Jan 1, 2023
Java Abstracted Foreign Function Layer

jnr-ffi jnr-ffi is a Java library for loading native libraries without writing JNI code by hand, or using tools such as SWIG. Example package hellowor

The Java Native Runtime Project 1.1k Dec 31, 2022
Compile Java byte-code to native CPU's.

Java Grinder Compile Java bytecode to microcontroller assembly. Currently supporting MSP430, dsPIC, 6502/6510, 68000, MIPS, TMS9900, and Z80 with plat

Michael Kohn 396 Dec 23, 2022
Java Bindings for V8

J2V8 J2V8 is a set of Java bindings for V8. J2V8 focuses on performance and tight integration with V8. It also takes a 'primitive first' approach, mea

EclipseSource 2.3k Jan 4, 2023
Jssembly is a library that allows you to execute native assembly from Java.

jssembly Jssembly is a library that allows you to execute native assembly from Java via a JNI bridge. The goal is to provide wrappers and parsers for

David Titarenco 121 Jun 3, 2022
A tool to help eliminate NullPointerExceptions (NPEs) in your Java code with low build-time overhead

NullAway: Fast Annotation-Based Null Checking for Java NullAway is a tool to help eliminate NullPointerExceptions (NPEs) in your Java code. To use Nul

Uber Open Source 3.2k Dec 29, 2022
Slicer4J is an accurate, low-overhead dynamic slicer for Java programs.

Slicer4J This repository hosts Slicer4J, an accurate, low-overhead dynamic slicer for Java programs. Slicer4J automatically generates a backward dynam

The Reliable, Secure, and Sustainable Software Lab 25 Dec 19, 2022
Easy to use, very low overhead, Java APM

Glowroot Requirements Java 8+ Quick start Download and unzip glowroot-0.14.0-beta.2-dist.zip Add -javaagent:path/to/glowroot.jar to your application's

null 1.1k Dec 14, 2022
High performance non-blocking webserver

Undertow Undertow is a Java web server based on non-blocking IO. It consists of a few different parts: A core HTTP server that supports both blocking

null 3.3k Jan 1, 2023
Magician is an asynchronous non-blocking network protocol analysis package, supports TCP, UDP protocol, built-in Http, WebSocket decoder

An asynchronous non-blocking network protocol analysis package Project Description Magician is an asynchronous non-blocking network protocol analysis

贝克街的天才 103 Nov 30, 2022
Library for composability of interdependent non-blocking I/O tasks

Composer Composer helps you to organize and execute multiple interdependent asynchronous input/output tasks such as webservice calls, database read/wr

krupal 19 Oct 8, 2021
Non-Blocking Reactive Foundation for the JVM

Reactor Core Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactive Extensions inspired API and efficient event streaming s

Reactor 4.4k Dec 30, 2022
vʌvr (formerly called Javaslang) is a non-commercial, non-profit object-functional library that runs with Java 8+. It aims to reduce the lines of code and increase code quality.

Vavr is an object-functional language extension to Java 8, which aims to reduce the lines of code and increase code quality. It provides persistent co

vavr 5.1k Jan 3, 2023
Kyrestia, named after Kyrestia the Firstborne, is a process engine supporting mainstream process definition standards.

Kyrestia Kyrestia, named after Kyrestia the Firstborne, is a process engine supporting mainstream process definition standards. It is not only lightwe

Weiran Wu 32 Feb 22, 2022
DatasetCreator is a lightweight RESTFul client implementation of the Salesforce CRM Analytics External Data API.

DatasetCreator is a lightweight RESTFul client implementation of the Salesforce CRM Analytics External Data API. It has been deliberately developed with no 3rd party jars with the goal of being a lean, reliable and scalable solution.

Salesforce Platform 6 Dec 16, 2022
Library for creating In-memory circular buffers that use direct ByteBuffers to minimize GC overhead

Overview This project aims at creating a simple efficient building block for "Big Data" libraries, applications and frameworks; thing that can be used

Tatu Saloranta 132 Jul 28, 2022
Curated Collection of all Low level design Questions and implementation asked in major Tech companies , Get yourself prepared for the LLD round and ace the interview.

Low level Design / Machine Coding Question Collections What is Machine Coding Round ? Machine Coding Round has become very popular interview round in

Kumaran gowthaman 619 Dec 31, 2022