Most parts of NetBeans do not manipulate windows directly, but use
special
top components.
The
Window System API
provides the ability for modules to create window-like components which
can, among other things:
- Be docked as tabs into a container window.
- Provide popup menus in their window icon or tab.
- Handle activation and node selections.
- Provide the ability to be cloned, and the clones monitored.


|
Jala 1.3 | ||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
Object | +--jala.Utilities
This class contains various convenience methods
which do not fit in any other class.
Defined in Utilities.js
| Field Summary | |
<static> <final> Number |
VALUE_ADDED
Static field indicating ad added object property. |
<static> <final> Number |
VALUE_MODIFIED
Static field indicating a modified object property. |
<static> <final> Number |
VALUE_REMOVED
Static field indicating a removed object property. |
| Constructor Summary | |
jala.Utilities
()
Construct a utility object. |
| Method Summary | |
String
|
createPassword(<Number> len, <Number> level)
Creates a random password with different levels of security. |
Object
|
diffObjects(<Object> obj1, <Object> obj2)
Returns an array containing the properties that are added, removed or modified in one object compared to another. |
Object
|
patchObject(<Object> obj, <Object> diff)
Patches an object with a "diff" object created by the diffObjects() method. |
String
|
toString()
Return a string representation of the utitility object. |
| Field Detail |
<static> <final> Number VALUE_ADDED
<static> <final> Number VALUE_MODIFIED
<static> <final> Number VALUE_REMOVED
| Constructor Detail |
jala.Utilities()
| Method Detail |
String createPassword(<Number> len, <Number> level)
len
- The length of the password (default: 8)
level
- The security level Object diffObjects(<Object> obj1, <Object> obj2)
obj1
- The first of two objects which should be compared
obj2
- The second of two objects which should be compared
Object patchObject(<Object> obj, <Object> diff)
obj
- The Object the diff should be applied to
diff
- A "diff" object created by the diffObjects() method
String toString()
|
Jala 1.3 | ||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |


Next: type-error-datum, Previous: typep, Up: Types and Classes Dictionary
type-error, error, serious-condition, condition, t
The type type-error represents a situation in which an object is not of the expected type. The "offending datum" and "expected type" are initialized by the initialization arguments named :datum and :expected-type to make-condition, and are accessed by the functions type-error-datum and type-error-expected-type.
‘type-error-datum; type-error-expected-type’ , type-error-expected-type


--- title: "Domain Management" lastmod: 2019-03-06T00:00:00.000Z draft: false type: docs maths: true linktitle: "Domain Management" menu: docs: parent: Website Management weight: 30 ---
Domain management is done through Netlify. Once logged in on the methods101 account go to the domain settings page shown below:
Under the custom domains heading you will see three website addresses.
The default subdomain is the address that Netlify gives you. Netlify can give you a .netlify.com address which is undesirable for a professional page. For Methods101 a domain name was purchased (which is the one currently used).
The primary domain is the one that was purchased for the site. It is easily set by pressing the setting button on the right (the three little dots).
The address needs to be made secure which is done through the HTTPS page under the Domain Management tab on the sidemenu. Set custom certificate and follow the instructions.
(This page needs work and the Netlify documentation may be more useful right now. If someone needs to do this again please update the page with better instructions).


Pep Linked With Shock Bayern Return
by David KappelWith Manchester City dropping nine points behind Liverpool and reports suggesting that Pep Guardiola might leave the club at the end of this season, the Spaniard has now been linked with a shock return to Bayern Munich.
Read: Pep: What I Told City Stars Amid Handball Controversy
The German giants are currently without a full-time manager after Niko Kovac departed the Allianz Arena nine days ago. Hans-Joachim Flick has since taken over in the interim and guided the Bavarians to two comfortable wins, over Olympiacos and Borussia Dortmund.
Flick, a former assistant coach to Germany boss Joachim Low, will likely lead Bayern until the end of the season, when the club's bosses will present a new coach, unless the incumbent can convince all doubters otherwise with silverware.
Download The Soccer Laduma Social Media App & Earn Airtime for Selfies!
According to The Athletic, though, the Bundesliga champions' dream candidate to lead the club into a new era of success, in particular in the UEFA Champions League, would be Tottenham Hotspur coach Mauricio Pochettino.
Bayern "would do anything to get him" if he and Spurs were to go their separate ways, but Manchester United and Real Madrid could also be after his services.
However, the same report also suggested that there is a chance that Bayern will lure Guardiola back to Germany.
It is the feeling in some circles that this season is his last with City, and he still has a strong relationship with Bavarian club's leadership and also retains a property in Munich.
Read: The Top Goalscorers This Season So Far
Guardiola, though, did say in June that he would not return to Bayern or FC Barcelona.


Base currency:
Refresh rate:
10 seconds
30 seconds
1 minute
5 minutes
10 minutes
1 hour
2 hours
Public key to track:


Funding competition Reusability of waste material rocketry components
Innovate UK is investing up to £15 million in innovation projects to stimulate the new products and services of tomorrow
- Competition opens: Saturday 14 May 2016
- Competition closes: Wednesday 3 June 2020 12:00pm
Or go to your dashboard to continue an existing application.
Summary
Description
Innovate UK is investing up to £15 million in innovation projects to stimulate the new products and services of tomorrow. The aim of this competition is to help businesses innovate to find new revenue sources. Proposals should show how to achieve a step change in business growth, productivity and export opportunities for at least one UK small and medium-sized enterprise (SME). We expect projects to range from total costs of £35,000 to £2 million. Projects should last between 6 months and 3 years. There are 2 options to apply into this competition, dependent on project size and length, these are referred to as streams. Stream 1 is for projects under 12 months duration and under £100,000. Stream 2 is for projects lasting longer than 12 months or costing over £100,000.
Funding type
Grant
Project size
£15 million
Need help with this service? Contact us


You are switching authentication system to LDAP to Crowd to Single Sign-On
Do you want to continue?
Updated Settings haven't been verified. Information may be incorrect. Are you sure you want to skip this test and continue to save?
Updated Settings haven't been verified. Information may be incorrect. Are you sure you want continue to save?
It is not recommended to change the authentication system type frequently as this may lead to memory issues.


I ate about 1g of mushrooms before going into a dance party. I had eaten this batch before with positive experiences. And most of the trip was very pleasant: giggles, pretty colors, swirling shapes, beautiful music, deep thoughts. Then after a while I got tired and went up above the dance floor and leaned against the railing. My trip was pretty much over, or in it's last stages. The music was loud, the railing was shaking from the bass, and a green laser was all over the place.
After a few I started to feel nauseous and dizzy. I leaned over to my friend and said I was going outside, I was dizzy. I stumbled, so she caught me and led me outside. She tells me that my eyes were rolling back in my head and my arms were convulsing. I could barely see at times, I had the sort of tunnel vision that you get when you stand up too fast. I could hear, my friend was asking me what else I had taken. The answer to that question is: I am on Celexa, an SSRI anti-depressant, 30 mg, and nothing else. I was well-hydrated and otherwise sober. I couldn't answer her, though; I couldn't speak. She led me to a chair and I closed my eyes and waited for the unpleasantness to pass. And it did, as suddenly as it came. I was mildly headachy but alert and not dizzy or nauseous for the rest of the evening.
I am not epileptic, as far as I know at 22, and I have never had problems with lights or music at dance parties. I do not know if the seizure was due to the shrooms or the lights, but I just wanted to share my experience.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


I had the opportunity to work with a number of partners on this project and engaged with a number of senior managers across the authority.


The difficulties of the past year have proven just one thing, love always prevails.
We will be having our small ranch wedding at Upper Place, the ranch where Kate was raised, on Saturday, May 1, 2021 at 4 p.m (MT).
With all of the unknowns of 2020-21, the only thing that we know for sure is that we do not want to wait any longer.
The outpour of love and support has meant the world to us. We can’t wait to safely celebrate with each of you soon!
Thank you for joining us virtually!
Please consider making a donation to the Burke Center for Youth. Will & Kate are also registered at Pottery Barn/William Sonoma.
Burke Center for Youth
The Burke Center for Youth Pathfinder’s Ranch was just next door to Austin Equine Hospital where Kate lived and worked.
It is an incredible facility with a mission, “to promote healing and inspire hope for children in crisis.”
The horse program became near and dear to Kate’s heart as she witnessed the impact that her horse patients made on the boys in the Pathfinder Program.
If you would like to donate to the Burke Center’s Pathfinder’s Ranch/Horse Program in lieu of a gift, please click the link below.
Burke Center Donation
Pottery Barn/William Sonoma
Pottery Barn/William Sonoma Registry


Enter your Direct (From) email address, this will be used to send a validation report to a normal email account. The validation reports are sent back to the email account for review by the user during testing. This panel allows the user to register their contact email account and link one or more Direct (From) account to it. Direct messages sent from a non-registered Direct (From) address will not be validated nor reported on.


{% extends "base.html" %} {% load static %} {% block title %} 1XPredict Free Tips {% endblock %} {% block content %}
Pay Bill
Business Number 247247
Account Number<PHONE_NUMBER>770
Amount KSH.999
On your Vodacom line
Dial *150*00#
Receiptient number:
+254702290881
On your MTN line
Dial *165#
Receiptient number:
+254702290881
Ugochukwu Samuel Osinachi
Account Number<PHONE_NUMBER>
Amount 5,000 Naira
Kindly make your 15$ monthly subscription fee through PayPal to this email
<EMAIL_ADDRESS>On your MTN line
Dial *830#
Receiptient number:
+254702290881


Get Started
First we want to thank you for purchasing Alia theme, we will guide you through these documentation to let you know how to get started with Alia, if you have any problem with the theme after reading read this documentation, please feel free to contact us from our Themeforest Profile, and we will answer to you in few minutes.
Installation
To install Alia Wrodpress theme, you need:
Wordperss 3.5 or higher.
Running webserver and working version of WordPress installed.
PHP and MySQL Installed on the server.
Purchasing Alia theme licence from Themeforest.
Install Alia Core plugin (Included with theme).
Now we assume that you already have WordPress installed and working on your server and you have Alia package you purchased from Themeforest, if you didn’t have WordPress Site working yet, we advice you to follow this page which has been provided by WordPress to show you installation process details. if you already have it installed, so it’s time to start installing Alia, simply choose one of the following 2 ways to install the theme.
Install Via WordPress Control Panel (Recommended)
- Download the package you purchased from Themeforest, and extract the .zip in your computer.
- Login to your WordPress control panel and click on the Appearance tab then go to Themes page.
- From the top of the page click on Add New button then click on Upload Theme.
- Choose alia.zip file from your computer and click Install Now.
- After theme installation complete click Activate The Theme and now theme should be ready to work.
Some times you are not able to install the theme by uploading alia.zip file like the previous steps, it’s due to your server limitations which limit the max size upload of the file, so if you have an access to your server configuration files we recommend that you increase max_execution_time – memory_limit – post_max_size – upload_max_filesize. if you can’t modify them then you need to go to the second method of installation.
Uploading Via FTP
- Download the package you purchased from Themeforest, and extract the .zip in your computer.
- Use FTP client to access your WordPress installation directory.
- Now you need to extract alia.zip file in your computer.
- Then upload the entire folder alia to this path inside WordPress installation ‘wp-content/themes’.
- From WordPress control panel go to Appearance then Themes and you will find Alia theme in the listed themes
- Click Activate and now theme should be ready to work.
Adding Plugins
After installing and activating the theme, you have to install some plugins to get your site working as expected, just after activating you will get a notification to begin installing plugins, some of plugins are required and others are optional, but we recommend you to install all of them if you want your site to work as our demo site. to install plugins:
Start instaling
From your WordPress site dashaboard
- Click on Begin Installing Plugins.
- Then from Bulk Actions box select Install then click Apply.
- Plugins will take a while to be installed and required plugins will get activate automatically.
- Now click on Return to Required Plugins Installer to see plugins needing activation manually.
- Check all of them then select Activate from Bulk Actions box.
- Click Apply and now Alia is ready to use.
Build Your Site
Now you have everything up and running to start building your site, and in this section we will help you to get your site looks as you expect.
If you are building a test site
If you are using Alia theme in a site which is not live for testing it's features only, you may need to install demo content to make the test site looks like Alia demo site.
- First you need to make sure that Alia Core plugin has been installed & activated from the previous step.
- Then from site dashboard go to Tools > Import.
- Scroll to the end of the page to find WordPress Importer and click Install Now if it's not installed already.
- Now click on Run Importer.
- Choose import file, you will find it iside the Folder you downloaded from Themeforest in Demo Content > alia.demo-xxxx-xx-xx.xml and click Upload file and import.
- Check Download and import file attachments then Submit.
This will take few minutes depending on your internet connection speed then you will get All Done! message to let you know that content import is complete.
If your site is live and has content already
If you are switching from another theme and you site is live and already has content then you may find some issues with image sizes showing on your pages, this is normal because all images was uploaded before was resized to match your old theme. to get all images resized again to match Alia theme you need to install a plugin called Force Regenerate Thumbnails. after installing and activation the plugin go to Tools > Force Regenerate Thumbnails then click Regenerate all Thumbnails, wait until the process is finished and you will find all images in your site are showing perfect with Alia.
If you are buidling a new site
If you are building a new live website then you need to customize it first before you start adding content, in the next section we will help you customize every part of your website.
Customizing Your Site
To start customize your site open Customizer from your top bar, or from Appearance > Customize and you will find a blue pencil icon beside every customizable part of the page. but some times you need to make customization to elements that are not currently exist in the page, here are few tips to customize your site.
Adding Pages
Adding pages in WordPress is a simple task, Just go to Pages > Add New, then write your page title and content, you can use pages to add any static content to your site like About Me or Contact Me.
Adding Menus
To create a main menu and add pages to it you need to:
- From Wordperss control panel go to Apperance then Menus.
- Click Create New Menu and type any name then click Create Menu button.
- From the left side you can drag any category, pages or add new links to the menu.
- Drag and sort them in the right area and you can add some menus under other menu to create dropdown.
- Click Save Menu to save the items you sorted.
- After saving the menu, new settings will appear below.
- From Display Locations select Top Menu to add it to your site header and click Save Menu again.
Adding Widgets to Sidebars
Alia theme has 3 location to add widgets:
- Intro Sidebar: This appears in you homepage before blog posts, and you can any widgets to introduce yourself or your site, you can add your image, text about yourself, social icons or stories circles.
- Sliding Sidebar: This sidebar is hidden to prevent distraction of the readers and can be opened but a menu icon on the header.
- Default Sidebar: This sidebar will Appear only if you set posts layout to Right Sidebar Layout.
You get multiple Widgets with Alia theme to use in your site, and you can get more widgets from WordPress plugins if you like, to start adding widgets to any of sidebars go to Appearance > Widgets and drag widgets to the sidebar you want. In some widgets you need to add some information and save them to appear in your page.
Adding Your Social Profiles
To add your social profiles go to Customize > Social Settings and add the URL of each social profile you use. Later you can where will this social icons appear. you can select to show or hide them in header from Header Settings.
Adding Social Profile To Authors
If you want to add Social Profile to a user, You will find inputs to add social profile links in each user settings page.
Change your blog layout
By default your blog posts will appear as wide list in Homepage and Archive pages, if you want to change this list view you can do this from General Settings > Blog Layout.
Upload your logo
To upload a logo you need to open Customizer from your top bar, or from Appearance > Customize then go to Header Settings and upload 2 vesions of your logo, Default and Retina. the default one is the logo image with original size, and the retina one is the same logo image as default but with double size to appear in devices with Retina screen, so if you default logo is 120X40 the retina logo should be 240X80. Then from the same menu you need to set Logo Width and Logo Height to the dimensions of the default logo not the retina one, so you the last example you need to set width to 120 and height 40.
Adding Posts
Adding posts in WordPress site is a simple, all you need to do is to go Posts > Add New and start writing your content. with WordPress you can add tags to the post, add posts to category, or set a format to the post from the right side of the page, each post format is set to look in unique design on your blog list, and here are all formats we support:
- Standard: This is the default post, with text only content, if you set a featured image, the image will appear in your blog list, but it will not appear inside the post, and you can set an option from customizer > post settings to show fearured image inside the post too in case you already have many standard posts with featured image already, so you don't need to change the format each one, or you want to add all posts as Standard without bothering yourself with changing post format each time you add a post.
- Image: This is a post with Image banner, the featured image will appear in blog list and insdie post.
- Video: This will show a video banner, video can be uploaded to your site or shown from a video provider like Youtube or Vimoe, all you need to do is to paste the video URL to the beginning of your post content and it will change automatically to a Video Player
- Gallery: This will show multiple images a banner for your post, to add the gallery you need to click Add Media > Create Gallery, upload or add images to the gallery you created and insert it in the post.
- Audio: This will use audio player as a banner for your post in blog list, audio can be hosted on your site or shown from audio provider like soundcloud. All you need to do is to paste audio URL to the beginning of your post content and it will change automatically to audio player.
- Status: This post will embed a social network status as a post banner, you use this post format to promote a Facebook or Twitter post in your post banner. All you need to do is to paste post URL to the beginning of your post content and it will change automatically to embeded format.
- Aside: This post format is useful if you want to write short content with no title, something like posts in social media, but added to your blog.
Adding Stories
Alia introduces new feature to the market. You can add stories, stories can be used to add quick images without writing content to improve relation with your readers. Stories can be added for the all site, and per author. but first you need to make sure that Alia-Core plugin is installed and activated.
You can add Stories circles to any sidebar from Widgets page, or just go to footer settings from customizer and select stories to appear in Static Footer
To start adding stories just go to


This is a multi-part message in MIME format.
Dear JOSE,You have an upcoming loan payment on 2014-05-24=
for $359.52 for your Prosper loan. No action is required on your part:=
the payment will be automatically deducted from your primary bank accou=
nt on file on 2014-05-24.Automatic payment date: 2014-05-24Paym=
ent amount: $359.52Bank account: WELLS FARGO BANK, ****9543Plea=
se make sure that your bank account has sufficient funds to cover your=
payment. If insufficient funds exist, you may be charged a failed payme=
nt fee by Prosper and an insufficient funds fee by your bank.Since=
the payment transaction is initiated on the business day prior to the=
payment due date, any changes to your primary bank account must be comp=
leted two (2) business days prior to the payment due date.Thank=
you for being a part of the Prosper community.Regards,Prosper
Dear JOSE,
You have an upcoming loan payment on=
2014-05-24 for $359.52 for your Prosper loan. No action is requ=
ired on your part: the payment will be automatically deducted=
from your primary bank account on file on 2014-05-24.
Automatic payment date: 2014-05-24
Payment=
amount: $359.52
Bank account: WELLS=
FARGO BANK, ****9543
Please make sure that=
your bank account has sufficient funds to cover your payment. If insuff=
icient funds exist, you may be charged a failed payment fee by Prosper=
and an insufficient funds fee by your bank.
Since the=
payment transaction is initiated on the business day prior to the payme=
nt due date, any changes to your primary bank account must be completed=
two (2) business days prior to the payment due date.
T=
hank you for being a part of the Prosper community.
Reg=
ards,
Prosper


0. Basic concepts
- Every request submitted by log index to a Raft group is serialized into a log entry. Each log entry has an ID, which monotonically increases within the Raft group, and the log entries are replicated to every Raft node in the group.
- Term is a long-type number that monotonically increases within the Raft group. You can simply take it as the number of votes. The term of an elected leader is called the leader term. Before this leader steps down, log entries submitted during this period have the same term.
1. Configuration and supporting classes
This topic mainly describes the configuration and utility methods and classes. The core objects are:
- Endpoint, which refers to a service address
- PeerId, which refers to ID of a Raft node
- Configuration, which refers to the configuration of a Raft group, or a node list in other words.
1.1 Endpoint
Endpoint refers to a service address, including the IP address and the port number. Raft nodes must not be started on the IPv4 address <IP_ADDRESS>. The startup IP address must be clearly specified Create a address, and bind it to port 8080 of the local host, as shown in the following example:
Endpoint addr = new Endpoint("localhost", 8080);
String s = addr.toString(); // The result is localhost:8080
boolean success = addr.parse(s); // Specifies whether parsing the endpoint from a string is supported. The result is true.
1.2 PeerId
A PeerId indicates a participant (leader, follower, or candidate) of the Raft protocol. It comprises three elements in the format of ip:port:index, where ip is the IP address of the node, port is the port number, and index is the serial number of the same port. Currently, the index is not used, and is always set to 0. This field is reserved to allow starting different Raft nodes from the same port and to differentiate them by index.
Create a PeerId and set the index to 0, the IP to localhost, and the port to 8080:
PeerId peer = new PeerId("localhost", 8080);
EndPoint addr = peer.getEndpoint(); // Gets the endpoint of a node
int index = peer.getIdx(); // Gets the index of a node, which is always set to 0 currently
String s = peer.toString(); // The result is localhost:8080
boolean success = peer.parse(s); // Specifies whether PeerId parsing from a string is supported. The result is true.
1.3 Configuration
It refers to the configuration of a Raft group, or a participant list in other words.
PeerId peer1 = ...
PeerId peer2 = ...
PeerId peer3 = ...
// A Raft group that consists of three nodes
Configuration conf = new Configuration();
conf.addPeer(peer1);
conf.addPeer(peer2);
conf.addPeer(peer3);
1.4 JRaftUtils utility class
To enable users conveniently create objects such as Endpoint, PeerId, and Configuration, Jraft provides the JRaftUtils class to help users quickly create the required objects from strings.
Endpoint addr = JRaftUtils.getEndpoint("localhost:8080");
PeerId peer = JRaftUtils.getPeerId("localhost:8080");
// A Raft group that consists of three nodes. Separate the nodes with commas (,).
Configuration conf = JRaftUtils.getConfiguration("localhost:8081,localhost:8082,localhost:8083");
1.5 Closure and status
Closure is a simple callback interface. Most methods provided by Jraft are asynchronous callbacks. The result is sent to users through this interface:
public interface Closure {
/**
* Called when task is done.
*
* @param status the task status.
*/
void run(Status status);
}
The status method Status#isOk()
is used to get the status of the request, which can be a success or failure. You can use two other methods to get the error code and error messages:
boolean success= status.isOk();
RaftError error = status.getRaftError(); // Gets the error code. RaftError is an enumeration class.
String errMsg = status.getErrorMsg(); // Gets the error message
Status provides some methods to:
// Conveniently create a successful status
Status ok = Status.OK();
// Conveniently create an error for a failure. Error messages support string templates.
String filePath = "/tmp/test";
Status status = new Status(RaftError.EIO, "Fail to read file from %s", filePath);
1.6 Task
Task is one of the most important classes used by JRaft users. Users can use the Task class to submit a task from a Raft group. The task will then be submitted to the leader and replicated to the follower nodes. A task contains the following information:
ByteBuffer data
The task data to be submitted. The user should serialize the business data to be replicated into a ByteBuffer record by using a serialization method (such as Java and Hessian2), and place the record into the task.long expected term =-1
The expected leader term when the task is submitted. If not specified (the default value -1 will be used), the client does not check whether the leader has changed before the task is applied to the state machine. If this field is specified (obtained from the state machine callback as described later in this topic), the client will check whether the term matches the actual leader term before the task is applied to the state machine. It will decline this task for a term mismatch.Closure done
The callback of the task. Regardless of whether a task is successful or failed, the result of the task is sent to this object upon completion of the task. This closure can be get and called when theStateMachine#onApply(iterator)
method is applied to the state machine. It is generally used for returning responses to the client.
Create a simple task:
Closure done = ...;
Task task = new Task();
task.setData(ByteBuffer.wrap("hello".getBytes());
task.setClosure(done);
The closure of a task can also use the special TaskClosure
interface, which provides an additional onCommitted
callback method.
public interface TaskClosure extends Closure {
/**
* Called when task is committed to majority peers of the RAFT group but before it is applied to state machine.
*
* Note: user implementation should not block this method and throw any exceptions.
*/
void onCommitted();
}
When JRaft detects a task’s done callback is TaskClosure
, JRaft will call the onCommitted
method after the Raft log entry is submitted to the Raft group (and replicated to a majority of nodes) and before it is applied to the state machine.
2. Server
This section mainly describes the major interfaces and classes in JRaft server programming. The core interfaces and classes are:
- StateMachine: The main interface that implements users’ business logic. The state machine is run on every Raft node. Generally, successfully submitted tasks will eventually be applied to the state machine on each node.
- Raft node: A Raft node that can be used to submit tasks and query for the Raft group information, such as the current state, the current leader, and the current term.
- RPC service: The remote control call (RPC) service which is responsible for communication (such as leader election and log replication) between Raft nodes.
- RaftGroupService: A supporting programming framework class that is used to conveniently assemble a Raft group node.
2.1 Iterator
Tasks submitted by clients to the Raft group are submitted in batches within JRaft, and then applied to the state machine in the form of a task iterator, which is represented by the com.alipay.sofa.jraft.Iterator
interface. Here is a typical example:
Iterator it = ....
// Traverse the iteration task list
while(it.hasNext()){
ByteBuffer data = it.getData(); // Get the current task data
Closure done = it.getDone(); // Get the closure callback of the current task
long index = it.getIndex(); // Get the unique log number of the task, which monotonically increases, and is automatically assigned by JRaft
long term = it.getTerm(); // Get the leader term of the task
... Logical processing...
it.next(); // Move to the next task
}
Note that if no closure is set for the task, done may be null. In addition, done is null when it ends up on a follower node, because done will not be replicated to other Raft nodes except for the leader node.
Here is an optimization solution. Generally, the Closure done obtained by the leader can be extended to wrap a closure class, which contains user requests that are not serialized. Therefore, the leader can directly get user requests from closure in the logical processing stage, without data
deserialization, which reduces the CPU overhead of the leader. For more information, see the counter example.
2.2 StateMachine
Tasks submitted to a Raft group will eventually be replicated and applied to the state machine of each Raft node. A state machine is represented by the StateMachine
interface. Its core methods include:
void onApply(Iterator iter)
is the most important method that applies tasks to the state machine in their submission order. Note that when this method returns, we consider that this batch of tasks have been successfully applied to the state machine. If they are not completely applied (in the case of errors or exceptions), a critical error will be reported to the state machine’sonError
method, and the error type would beERROR_TYPE_STATE_MACHINE
. For more information about the handling of faults and errors, see Section 6 of this topic.void onError(RaftException e)
This method is called when a critical error occurs. RaftException contains detailed error information such as the status. When this method is called, no new tasks will be allowed to be applied to the state machine until the error is fixed and the node is restarted. Therefore, all errors encountered during the development stage should be promptly fixed. Please let us know if there are any JRaft problems.void onLeaderStart (long term)
This method is called when the Raft node of the state machine becomes a leader. The current term of the leader is passed through the parameter.void onLeaderStop (Status status)
This method is called when the Raft node of the current state machine loses the leadership position. Thestatus
field describes the detailed reasons, such as proactive leadership transfer and re-election.void onStartFollowing (LeaderChangeContext ctx)
This method is called when a Raft follower or candidate node starts to follow a leader.LeaderChangeContext
contains contextual information of the leader, such as the PeerId, term, and status. In addition, the leaderId attribute of the current Raft node is then set as the PeerId of the new leader node.void onStopFollowing (LeaderChangeContext ctx)
A Raft follower stops following a leader node. This is usually caused by a leadership transfer, for example, a new leader is elected, or an election starts. Likewise,LeaderChangeContext
describes the event that the follower stops following the leader, and the status field describes the detailed reasons.void onConfigurationCommitted (Configuration conf)
This method is called when a Raft group node configuration is submitted to the raft group log. It is usually unnecessary to implement this method, or this can be replaced with log printing.void onShutdown ()
This method is called when the Raft node of the state machine is closed. It can be used for resource cleanup, such as closing files, on some state machines.For more information about the snapshot saving and loading methods onSnapshotSave
andonSnapshotLoad
, see Section 2.6.
The StateMachine interface has_many methods, and most of them are not required for business processing. Therefore, JRaft provides a StateMachineAdapter class to help users quickly adapt to the state machine. Except for the onApply
method that must be implemented, other methods offer the default implementation, which is simply log printing. Users can choose specific methods to implement.
public TestStateMachine extends StateMachineAdapter {
private AtomicLong leaderTerm = new AtomicLong(-1);
@Override
public void onApply(Iterator iter) {
while(iter.hasNext()){
// Apply a task to state machine
iter.next();
}
}
@Override
public void onLeaderStart(long term) {
// Save the leader term
this.leaderTerm.set(term);
super.onLeaderStart(term);
}
}
2.3 Raft nodes
The Node interface refers to a Raft node, the role of which can be a leader, follower, or candidate. The role may change in the election progress.
The most important methods of the Node interface are:
void apply(Task task)
Submits a new task to the Raft group. This method is thread safe and nonblocking. Regardless of whether the task is successfully submitted to raft group, the result will be sent to the user through the Closure done callback associated with the task. If the current node is not the leader, a failure notification will be directly sent to the Closure done.PeerId getLeaderId()
Gets the current leader peerId of the Raft group. If the peerId is unknown, null is returned.shutdown
andJoin
. The former is used to stop a Raft node, and the latter can wait for the shutdown process to end after the shutdown call.void snapshot (Closure done)
Triggers the current node to perform a snapshot save operation. The result is sent to the user through Closure done. For more information, see Section 2.6.
Other methods are used to query node information and changing the node configuration in a Raft group. For more information, see Section 5.
You can use the RaftServiceFactory.createRaftNode(String groupId, PeerId serverId)
static method to create a Raft node, where
- the groupId is the Raft group ID of this Raft node.
- The serverId is the PeerId of this Raft node.
After you create the Raft node, you need to initialize it. To initialize the node, you can call the boolean init (NodeOptions opts)
method and pass the NodeOptions
configuration.
Main configuration items of NodeOptions are as follows:
// Specify a period of time, during which
// the leader is expected to send AppendEntries RPCs for log replication or heartbeats (AppendEntries RPCs that carry no log entries) to followers. If a follower fails to receive communication within this period,
// the follower transitions to the candidate state, starts a leader election, and remains in the candidate state until it wins the election or waits for a new leader to appear. Default value: 1s (1000 ms).
private int electionTimeoutMs = 1000;
// Specify the snapshot interval. Default value: 1 hour.
private int snapshotIntervalSecs = 3600;
// When a node is started from the empty state (no snapshots or log entries available), it uses this initial configuration as the startup configuration of the Raft group.
// Otherwise, it loads existing configuration from the storage.
private Configuration initialConf = new Configuration();
// Specify the most important component - the state machine instance of this Raft node.
private StateMachine fsm;
// Required. Specify the log storage path of the Raft node.
private String logUri;
// Required. Specify the meta information storage path of the Raft node.
private String raftMetaUri;
// Optional. Specify the snapshot storage path of the Raft node. The snapshot feature is disabled if the path is not specified.
private String snapshotUri;
// Specify whether to disable the CLI service. Default value: false. For more information, see Section 3.2.
private boolean disableCli = false;
// Specify the internal timer pool size. The default value is subject to the number of CPU cores. You need to adjust the value based on the actual situation.
private int timerPoolSize = Utils.cpus() * 3 > 20 ? 20 : Utils.cpus() * 3;
// Specify the configuration options of some internal Raft implementations, especially the performance related options. For more information, see Section 6.
private RaftOptions raftOptions = new RaftOptions();
The most important configuration items in NodeOptions are the three storage paths (of the log, meta information, and snapshots) and the state machine instance. When you start a node for the first time, you need to set the initialConf node list.
Then you can initialize the node that you have created.
NodeOptions opts = ...
Node node = RaftServiceFactory.createRaftNode(groupId, serverId);
if(!node.init(opts))
throw new IllegalStateException("Unable to restart the Raft node. For more information about the error, see the log.") ;
You can also directly use the createAndInitRaftNode
method to create and initialize a Raft node:
Node node = RaftServiceFactory.createAndInitRaftNode(groupId, serverId, nodeOpts);
2.4 RPC service
One Raft node is not enough. You can test a single node, but generally, a Raft group should consist of at least three nodes. If remote disaster recovery is required, scale it up to five nodes.
Communication between different nodes is implemented by the RPC service of the SOFABolt framework.
First, you need to create Raft nodes, and add the endpoints of the nodes to the NodeManager.
NodeManager.getInstance().addAddress(serverId.getEndpoint());
NodeManager’s address collection consists of endpoints of Raft nodes to which the RPC service is provided.
Second, create a Raft-specific RPC server, which is built in with a set of processors to process communication protocols between internal nodes:
RPCServer rpcServer = RaftRpcServerFactory.createRaftRpcServer(serverId.getEndPoint());
// Start the RPC service.
rpcServer.start();
You can combine the RPC server creation and startup steps into one operation.
RPCServer rpcServer = RaftRpcServerFactory.createAndStartRaftRpcServer(serverId.getEndPoint());
You can call this operation to provide RPC Server service to the specified node. You can connect other nodes to this one for RPC-based communication. For example, issuing leader election RPCs, heartbeat RPCs, and log replication RPCs.
In actual practice, most application servers also provide the RPC service to end users. JRaft allows Raft nodes to use the RPCServer of the application, which means the RPC service and the application share the same server port. In this case, you need to register Raft protocol processors with the RPC server of the application:
RpcServer rpcServer = ... // Specify the RPCServer object of the application.
...Register processors with the application...
// Register processors with the RPC server for processing Raft protocols.
RaftRpcServerFactory.addRaftRequestProcessors(rpcServer);
// Start the RPC server with the same server port as that of the application server.
rpcServer.start();
Service communication between application server nodes may also be implemented by RpcClient of SOFABolt. You can also directly use the rpcClient object of JRaft to meet this requirement:
RpcClient rpcClient = ((AbstractBoltClientService) (((NodeImpl) node).getRpcService())).getRpcClient();
This increases the resource usage efficiency and reduces the costs, but it also increases JRaft dependency and reduces custom configurations.
2.5 RaftGroupService framework class
To sum up, main steps required for creating and starting a Raft group are as follows:
- Implement and create a state machine instance.
- Create and set the NodeOptions instance. Specify the storage paths of the log, meta information, and snapshots. In addition, specify the initial configuration for nodes that are started without configuration.
- Create nodes and then initialize them by using NodeOptions.
- Create and start the RpcServer to provide communication service between nodes.
If all these procedures are completely left to be implemented by the application, it would be rather complex. Therefore, JRaft provides the RaftGroupService utility class to simplify this process:
String groupId = "jraft";
PeerId serverId = JRaftUtils.getPeerId("localhost:8080");
NodeOptions nodeOptions = ... // Configure the node options.
RaftGroupService cluster = new RaftGroupService(groupId, serverId, nodeOptions);
Node node = cluster.start();
// Use a node to apply a task.
Task task = ....
node.apply(task);
You can use the start method to implement Step 3 and Step 4, and return the created nodes.
RaftGroupService
also offers some other constructors, for example, to allow shared use of the RpcServer with an application.
public RaftGroupService(String groupId, PeerId serverId, NodeOptions nodeOptions, RpcServer rpcServer)
In this case, the passed RpcServer must be registered with Raft-protocol processors with the RaftRpcServerFactory.addRaftRequestProcessors(rpcServer)
operation.
2.6 Snapshot service
When you restart a Raft node, all states of the state machine will be lost from the memory. During the startup, all log entries stored in the node’s local log file is re-applied to the state machine to rebuild the state machine instance. This leads to the following problems:
- In scenarios where tasks are applied very frequently, for example, in the case of message oriented middleware, rebuilding the state machine will be time-consuming and result in a very slow startup.
- In scenarios with large amounts of log entries, storing all log entries on a node requires considerable storage space and is not sustainable.
- When a new node is added to an existing Raft group, the new node must replicate all log entries from the leader and apply them to the state machine, posing an unignorable burden on both the leader and the network bandwidth.
Therefore, JRaft introduces the snapshot mechanism to solve these problems. The basic idea is to create a snapshot of the latest state of a state machine, and save this snapshot separately. After that, you can delete all previous log entries to reduce the storage space usage. When you restart the node, you can directly load the latest snapshot, and then apply log entries written in the log after the time of the snapshot. If the snapshot interval is reasonable, the time required for reapplying the log entries should be short, and the startup process is sped up. When you add a new node to an existing Raft group, you can first replicate the latest snapshot from the leader, install the snapshot on the state machine of the new node, and then replicate the log entries newly added after the snapshot time. This allows the new node to quickly catch up the progress of the entire Raft group.
To start a snapshot, in NodeOptions, you need to set the snapshotUri
parameter, which specifies the storage path of snapshots. A timer will be started to create snapshots. You can specify the interval of snapshots by setting the snapshotIntervalSecs
parameter in NodeOptions. The default value is 3600s, or 1 hour.
You can also use the done closure to of the Node interface to proactively trigger snapshot creation.
Node node = ...
Closure done = ...
node.snapshot(done);
The result will be sent to the closure.
You need to implement the following two methods on the state machine:
// Save the snapshot of the latest state. You can write the information of the saved snapshot to the SnapshotWriter. Remember to call the done.run(status) method after the snapshot is saved.
void onSnapshotSave(SnapshotWriter writer, Closure done);
// Load or install the snapshot on the local state machine. Read the snapshot files from the SnapshotReader, and select the latest one.
boolean onSnapshotLoad(SnapshotReader reader);
For more information about the detailed implementation, see Use case of a counter.
3. Client
After creating a Raft group, clients need to interact with the Raft group. This section mainly describes some client services provided by JRaft.
3.1 RouteTable
The RouteTable class is used to maintain the route information for the access to the Raft group. It is a global singleton and is easy to use. For more information, see the example below:
// Initialize the RPC service.
CliClientService cliClientService = new BoltCliClientService();
cliClientService.init(new CliOptions());
// Get the route table.
RouteTable rt = RouteTable.getInstance();
// Configure nodes of the Raft group.
Configuration conf = JRaftUtils.getConfiguration("localhost:8081,localhost:8082,localhost:8083");
// Update the route table configuration.
rt.updateConfiguration("jraft_test", conf);
// Refresh the leader information, and return the success or error message after the timeout of 10s.
boolean success = rt.refreshLeader(cliClientService, "jraft_test", 10000).isOk();
if(success){
// Get the leader information of the cluster, and return null if the leader is unknown.
PeerId leader = rt.selectLeader("jraft_test");
}
If an application needs to raise requests to the leader or query the latest data from the leader, it needs to regularly call the refreshLeader
method to update the route table information, or proactively update the leader information when the server returns the redirect message (a custom protocol). For more information, see Use case of a counter.
The RouteTable class also provides methods to query and delete configuration. For more information, see the API section.
3.2 CLI service
In JRaft, the client command line (CLI) service provides some service interface methods to manage the Raft group, for example, to add or delete nodes, change the node configuration list, reset node configuration, and to transfer the leader. These methods can be called by the RPC service for Raft nodes.
The methods are very straight forward and are explained as follows:
public interface CliService extends Lifecycle<CliOptions> {
// Add a node to an existing Raft group
Status addPeer(String groupId, Configuration conf, PeerId peer);
// Remove a node from the Raft group
Status removePeer(String groupId, Configuration conf, PeerId peer);
// Smoothly change nodes of the Raft group
Status changePeers(String groupId, Configuration conf, Configuration newPeers);
// Reset the configuration of a specific node. Use this method only in special circumstances. For more information, see Section 4.
Status resetPeer(String groupId, PeerId peer, Configuration newPeers);
// Transfer the leadership of a leader to a peer (follower)
Status transferLeader(String groupId, Configuration conf, PeerId peer);
// Trigger the snapshot creation
Status snapshot(String groupId, PeerId peer);
}
To use the CLI service, you need to first create a CliService instance:
// Create and initialize a CliService instance
CliService cliService = RaftServiceFactory.createAndInitCliService(new CliOptions());
// Use the CliService instance
Configuration conf = JRaftUtils.getConfiguration("localhost:8081,localhost:8082,localhost:8083");
Status status = cliService.addPeer("jraft_group", conf, new PeerId("localhost", 8083));
if(status.isOk()){
System.out.println ("Node added successfully");
}
3.3 RPC service
Communication of the clients is dependent on RpcClient of SOFABolt, which is encapsulated in the CliClientService
interface. The implementation class is BoltCliClientService
.
You can call the getRpcClient
method of the BoltCliClientService class to get the underlying Bolt RpcClient instance, and use it for other communication services. This increases the resource usage efficiency.
You need to pass the CliClientService
instance, when you refresh the RouteTable or leader information at the client side. You should do your best to reuse such underlying communication components, instead of repetitively creating new ones.
4. Node configuration change
For more information, see Section 3.2. You can use either CliService or Node methods of the leader node to change node configuration. Actually, all CliService methods are forwarded to and implemented on the leader node.
5. Linearizable read
Here is a simple example. Assume that we write a value at the moment t1. We can certainly read this value after t1, but we cannot read values written earlier than t1.
The Raft consensus algorithm is designed to implement linear consensus in a distributed environment. Therefore, we can conveniently use Raft to implement linerizable read. We can go through the Raft log process for any read requests at any time. After a log entry of the read request is applied to the state machine, we can ensure that the result read from the state machine meets the linear consensus requirement.
However, as you know, if every read request needs to go through the Raft process, the efficiency can be very low. Therefore, this method is seldom used.
To solve this problem, JRaft implements the ReadIndex and Lease Read optimization solutions as mentioned in the Raft paper, to provide more efficient linearizable read solutions.
For more information about linearizable read, see the PingCap blog https://www.pingcap.com/blog-cn/lease-read/
The following code shows how to initiate a linearizable read request in JRaft:
// Implement linearizable read of KV data.
public void readFromQuorum(final String key, AsyncContext asyncContext) {
// Pass the request ID as the context of the request.
final byte[] reqContext = new byte[4];
Bits.putInt(reqContext, 0, requestId.incrementAndGet());
// Call the ReadIndex method, and wait for the execution of the ReadIndexClosure.
this.node.readIndex(reqContext, new ReadIndexClosure() {
@Override
public void run(Status status, long index, byte[] reqCtx) {
if (status.isOk()) {
try {
// If the ReadIndexClosure returns a success message, the latest data can be read from the state machine and returned to the client.
// If your state implementation is subject to version control, you can read the data based on the log index ID.
asyncContext.sendResponse(new ValueCommand(fsm.getValue(key)));
} catch (final KeyNotFoundException e) {
asyncContext.sendResponse(GetCommandProcessor.createKeyNotFoundResponse());
}
} else {
// In special cases, for example a leader election, the read request fails.
asyncContext.sendResponse(new BooleanCommand(false, status.getErrorMsg()));
}
}
});
}
Use Node#readIndex(byte [] requestContext, ReadIndexClosure done)
to initiate a linearizable read request. When the data can be securely read, the passed closure will be called. In normal circumstances, the data can be read from the state machine and returned to the client. This is how JRaft ensures linearizable read by using the readIndex method. The request context provided by requestContext
to the user can be obtained in the done closure for follow-up processing.
Note that linearizable reads can be initiated on any nodes within a Raft node cluster. You do not have to direct all read requests on the leader node. You can handle read requests on followers, too. This can significantly reduce the read stress of the leader.
Linearizable read provided by JRaft is implemented based on ReadIndex of the Raft protocol by default. The performance of this solution is acceptable. When higher performance is required and CPU clock synchronization can be ensured, you can apply the clock-based LeaseRead optimization and further improve the performance. To implement this solution, set ReadOnlyOption
to ReadOnlyLeaseBased
in RaftOptions
.
public enum ReadOnlyOption {
// ReadOnlySafe guarantees the linearizability of the read only request by
// communicating with the quorum. It is the default and suggested option.
ReadOnlySafe,
// ReadOnlyLeaseBased ensures linearizability of the read only request by
// relying on the leader lease. It can be affected by clock drift.
// If the clock drift is unbounded, leader might keep the lease longer than it
// should (clock can move backward/pause without any bound). ReadIndex is not safe
// in that case.
ReadOnlyLeaseBased;
}
The LeaseRead solution improves the performance of the ReadIndex solution by approximately 15%.
6. Failures and countermeasures
This section describes possible failures that a Raft group may encounter, and JRaft’s countermeasures to ensure the distributed consensus and availability of the Raft group. Possible failures are:
- Server power outage
- App force-stop
- Service unavailability caused by slow node operation, for example, in the case of OOM exceptions
- Network failures such as network partitioning and slow network speed
- Other issues that may hinder Raft nodes from normal operation
In this document, a raft group should consist of at least three nodes. The use of a single node cannot guarantee the availability and must be avoided in the production environment.
Moreover, we divide services that a Raft node provides to clients into two types:
- Read service: Clients can read data of the state machine from both the leader node and follower nodes. However, data read from a follower node may not be the latest because of the time gap, which may lead to stale read. JRaft enables linearizable read to ensure linear consensus, and supports reading data from follower nodes.
- Write service: Clients can send requests to the leader node only, to update data on the state machine.
6.1 Single node failure
In the case of a single node failure, the Raft group as a whole can continue providing the read service but cannot provide the write service temporarily. This does not affect data consistency:
- If the failed node is the leader, the followers of the Raft group starts a new election to elect a new leader after the specified timeout. Before the new leader appears, the Raft group does not provide the write service, but continues to provide the read service. However, this may frequently cause stale read. Linearizable read cannot be provided either.
- If the failed node is a follower, the Raft group continues to provide both read and write services. The only difference is that some read requests sent to the failed follower node will not be responded. The application should try sending these requests to other nodes.
6.2 Failures of a minority of nodes
In the case of failures of a minority of nodes, less than half of the nodes become unavailable. This situation is similar to that of single node failures.
6.3 Failures of a majority of nodes
In the case of failures of a majority of nodes, more than half of the nodes become unavailable. In this case, the Raft group as a whole becomes unavailable. Although some nodes continue to provide the read-only service, they cannot elect a new leader to recover the write service (because the remaining nodes cannot provide more than half of votes that are required to elect a new leader). You need to recover the unavailable nodes, and make sure that more than half of the nodes of the group are available.
If you cannot quickly recover unavailable nodes, you may use the resetPeers(Configuration newPeers)
method of CliService to change the configuration of the remaining nodes and discard unavailable nodes to quickly elect a new leader. This may cause data loss and compromise data consistency. Do not use this method unless in extremely urgent circumstances where availability is more important.
6.4 Failures and the state machine
In the case of a single node failure without irrevertible storage failures such as disk damage, perform the following after restarting this node:
- If snapshot is enabled, load the latest snapshot on the state machine, and re-apply log entries written after snapshot generation to the state machine, until it catches up with the progress of the Raft group.
- If snapshot is disabled, reload all local log entries to the state machine, and catch up with the progress of the Raft group.
In the case of storage failures, such as disk damage, and storage failures of the log file and snapshots, fix the storage problem, reload the node, and pull the latest snapshot and log entries from the leader, load the snapshot on the state machine, and apply the log entries to it to ensure it reaches the latest state.
6.6 Failures and storage
NodeOptions provides the raftOptions
configuration item to allow you to set parameters related to performance and data reliability. The
/** call fsync when need*/
private boolean sync = true;
sync
parameter specifies whether to call the fsync method to flush the data to the local disk of the node when you write log entries and meta information of the Raft group and snapshots to the local storage of a node. Generally, you should set this parameter to true. If not, you may permanently lose the data when a majority of nodes fail.
You can set it to false only when you are sure that data loss is acceptable.
7. Metrics monitoring
JRaft provides the performance metric statistics based on the metrics class library. This function is disabled by default. You can call the setEnableMetrics(true)
method in NodeOptions
to enable it.
Node node = ...
NodeOptions nodeOpts = ...
nodeOpts.setEnableMetrics(true);
node.init(nodeOpts);
// Report the metrics to the console every 30s.
ConsoleReporter reporter = ConsoleReporter.forRegistry(node.getNodeMetrics().getMetricRegistry())
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(30, TimeUnit.SECONDS);
You can also make the reporter to report metrics to log libraries (such as Log4j) and time series databases. For more information, see the Metrics document.
Example:
-- Histograms ------------------------------------------------------------------
append-logs-bytes
count = 4
min = 0
max = 42
mean = 17.50
stddev = 15.52
median = 18.00
75% <= 42.00
95% <= 42.00
98% <= 42.00
99% <= 42.00
99.9% <= 42.00
append-logs-count
......
-- Timers ----------------------------------------------------------------------
append-logs
count = 4
mean rate = 44.24 calls/second
1-minute rate = 0.00 calls/second
5-minute rate = 0.00 calls/second
15-minute rate = 0.00 calls/second
min = 0.00 milliseconds
max = 3.00 milliseconds
mean = 1.25 milliseconds
stddev = 1.09 milliseconds
median = 1.00 milliseconds
75% <= 3.00 milliseconds
95% <= 3.00 milliseconds
98% <= 3.00 milliseconds
99% <= 3.00 milliseconds
99.9% <= 3.00 milliseconds
The definitions of metrics are as follows (all metrics contain min, max, avg, p95, and p99):
|
Metric
|
Definition
|
Unit and comment
|
|
append-logs-bytes
|
The size of log entries to be appended to the Raft log file.
|
In bytes
|
|
append-logs-count
|
The number of log entries to be appended to the Raft log file.
|
Count
|
|
append-logs
|
The TPS and time consumption for appending log entries to the log file.
|
TPS in batch/s, and time consumption in ms
|
|
replicate-entries-bytes
|
The size of log entries to be replicated to follower nodes in each batch.
|
In bytes
|
|
replicate-entries-count
|
The number of log entries to be replicated to follower nodes in each batch.
|
Count
|
|
replicate-entries
|
The TPS and time consumption for replicating log entries to follower nodes.
|
TPS in batch/s, and time consumption in ms
|
|
fsm-apply-tasks
|
The TPS and time consumption for applying tasks the state machine.
|
TPS in batch/s, and time consumption in ms
|
|
fsm-apply-tasks-count
|
The number of tasks to be applied to the state machine.
|
Count
|
|
fsm-commit
|
The total TPS and time consumption for committing log entries and applying them to the state machine (including the time for internal processing and the time of fsm-apply-tasks).
|
TPS in batch/s, and time consumption in ms
|
|
pre-vote
|
The TPS and time consumption for the pre-vote.
|
TPS in times/s, and time consumption in ms
|
|
request-vote
|
The TPS and time consumption for leader election.
|
TPS in times/s, and time consumption in ms
|
|
handle-append-entries
|
The TPS and time consumption for a follower to handle the log replication request.
|
TPS in batch/s, and time consumption in ms
|
|
handle-append-entries-count
|
The number log entries to be replicated by the follower in each batch.
|
Count
|
|
install-snapshot
|
The TPS and time consumption for a follower to handle a snapshot installation request.
|
TPS in times/s, and time consumption in ms
|
|
truncate-log-prefix and
truncate-log-suffix
|
The TPS and time consumption for the log storage module to delete log entries.
|
TPS in times/s, and time consumption in ms
|
|
replicate-inflights-count
|
The number of in-flight requests in the replicator pipeline.
|
Count
|
|
read-index
|
The TPS and time consumption for a node to handle a ReadIndex request.
|
TPS in times/s, and time consumption in ms
|
|
handle-read-index-entries
|
The number of ReadIndex requests to be handled in each batch.
|
Count
|
|
handle-read-index
|
The TPS and time consumption for a node to handle a batch of ReadIndex requests.
|
TPS in batch/s, and time consumption in ms
|
|
raft-rpc-client-thread-pool.{metric}
|
The number of RPC client thread pools.
|
|
|
raft-utils-closure-thread-pool.{metric}
|
The number of internal Closure thread pools.
|
|
|
replicator-{node}.{metric}
|
Replicator statistics
|
append-entries-times: The number of log replication requests.
hearbeat-times: The number of heartbeat requests.
install-snapshot-times: The number of snapshot installation requests.
log-lags: The number of log entries that have not been replicated.
next-index: The index of the next log entry to be sent to a node
|
8. Performance optimization suggestions
8.1 Performance-related configuration of Raft nodes
NodeOptions provides the raftOptions
configuration item to allow you to set parameters related to performance and data reliability, including
/** Specifies the maximum size of each file replication RPC (snapshot replication) between the leader and followers. Default value: 128 KB. */
private int maxByteCountPerRpc = 128 * 1024;
/** Specifies whether to check file holes in replicated files. It is currently not supported. */
private boolean fileCheckHole = false;
/** Specifies the largest number of log entries to be replicated from the leader to followers. Default value: 1024. */
private int maxEntriesSize = 1024;
/** Specifies the maximum size of the request body of AppendEntries RPCs to be sent from the leader to followers. Default value: 512 KB.*/
private int maxBodySize = 512 * 1024;
/** Specifies the maximum buffer size of log storage. Default value: 256 KB. */
private int maxAppendBufferSize = 256 * 1024;
/** Specifies the maximum election delay beyond the specified time. Default value: 1s.*/
private int maxElectionDelayMs = 1000;
/**
* Specifies the ratio of the specified election timeout to the heartbeat interval between the leader and followers.
* Default value: 10.
*/
private int electionHeartbeatFactor = 10;
/** Specifies the maximum number of requests submitted to the leader to write data to the log storage in each batch. Default value: 32.
private int applyBatch = 32;
/** Specifies whether to call fsync when writing log entries and meta information. Default value: true.*/
private boolean sync = true;
/**
* Specifies whether to call fsync when writing the snapshot or Raft meta information. Default value: false.
* When sync is set to true, the sync mode is used.
*/
private boolean syncMeta = false;
/**
* Specifies the internal disruptor buffer size. Increase this value for applications with high write throughput. Default value: 16384.
*/
private int disruptorBufferSize = 16384;
/** Specifies whether to enable the replicator pipeline. Default value: true.*/
private boolean replicatorPipeline = true;
/** Specifies the maximum number of inflight requests in an enabled replicator pipeline. Default value: 256.*/
private int maxReplicatorInflightMsgs = 256;
/** Specifies the ReadIndex request option. Default: ReadOnlySafe. For more information, see Section 5 Linearizable read*/
private ReadOnlyOption readOnlyOptions = ReadOnlyOption.ReadOnlySafe;
For applications with high throughput, you need to appropriately adjust the values of parameters such as the buffer size and batch size. The actual values should be subject to the actual performance of your servers based on tests.
8.2 Suggestions for applications
8.2.1 Suggestions for state machine implementation
- We recommend that you first inherit the
StateMachineAdapter
instead of directly implementing theStateMachine
interface. The adapter provides default implementations for most state machines. - Before you start your state machine, be sure to clear data on it, because JRaft will load a snapshot on and re-apply Raft log entries to it. Legacy data and non-idempotent operations may lead to data inconsistency.
- Do your best to optimize the
onApply(Iterator)
method, avoid blocking the machine, and accelerate the log entry application performance of the state machine. - We recommend that you enable the snapshot function to avoid re-applying all log entries every time you restart a node. In addition, log entries cannot be compressed, and they will take considerable storage space in the long run.
- The save and load methods of Snapshot both block the state machine, you should do your best to optimize them and avoid blocking the state machine. It will be the best if you can implement enhanced backup to save your snapshots.
onSnapshotSave
needs to call theclosure.run(status)
method after saving a snapshot, to indicate whether the snapshot is successfully saved. You can implement this as follows:
@Override
public void onSnapshotSave(SnapshotWriter writer, Closure done) {
// Synchronously get the current snapshot state of the state machine.
// Asynchronously save the state.
// Send the result to JRaft regardless of whether the snapshot is successfully saved by calling the done.run(status) method.
}
8.2.2 Suggestions for RPC service
- We recommend that you enable CliService to conveniently query and manage the Raft cluster.
- You can decide on whether to share the RPC server based on your application. If your application uses Bolt RPC, we recommend that you share the RPC server with your application to control the resource usage.
- Use a task data serialization solution with relatively balanced performance and storage space, for example protobuf.
- The application RPC processor should not share the same thread pool with the JRaft RPC processor, which may affect the interaction between Raft nodes.
8.2.3 Suggestions for clients
- Use
RouteTable
to manage the cluster information, and regularly call therefreshLeader
andrefreshConfiguration
methods to get the latest state of the cluster. - The service protocol should include the redirect protocol to allow the Raft group to return the latest leader information to the client when the client sends a write request to a non-leader node. Then the client can send the write request to the leader node. In addition to the redirect protocol, the clients can proactively refresh the leader information on a regularly basis. The combined use of the redirect protocol and proactive refresh can improve the clients’ availability.
- We recommend that you implement linearizable read to distribute read requests to all nodes to shift burden from the leader.


What is Salsa?
Salsa is an open source library that renders iOS views and exports them into a Sketch file. We built Salsa to help bridge the gap between design and engineering in an effort to create a single source of truth for visual styling of UI.
How it works
Running Salsa inside of an iOS simulator will output two things into a specified directory: a .salsa file and an images folder. You can then pass these two inputs into the salsa command line tool to compile them into a .sketch file.
Why two steps?
Certain macOS-only APIs need to be used to encode text for .sketch files. Having two steps allows us to define our own intermediate file format that’s easier to work with than the full sketch file format. This means we can leverage this file format in the future if we want to expand this tool for other platforms.
Installing Salsa
pod 'Salsa'
brew tap yelp/salsa
brew install salsa
Using Salsa
import Salsa
Converting a view to a Sketch Group
// Configure the export directory
SalsaConfig.exportDirectory = "/some_directory"
// Convert a view into a group
let myGroup = myView.makeSketchGroup()
Putting a group into a sketch document and exporting to a salsa file
// Create a page containing the generated group, and insert it into a Document
let document = Document(pages: [Page(layers: [myGroup])])
// Export the document to disk
try? document.export(fileName: "my_file")
Converting a salsa file to a sketch file
In your terminal of choice run the following:
$ salsa -f /some_directory/my_file.salsa -e /some_directory/my_file.sketch
Creating a Sketch file documenting your standard UI elements
We provide some helpers to help you document your elements out of the box. You organize examples of your views into an Artboard by conforming your view class to ArboardRepresentable
.
extension View1: ArtboardRepresentable {
static func artboardElements() -> [[ArtboardElement]] {
...
}
}
If you would like to also create Symbols of your views to go along with the generated Artboards you can instead conform your views to SymbolRepresentable
.
extension View2: SymbolRepresentable {
static func artboardElements() -> [[ArtboardElement]] {
...
}
}
Create your Artboards and Symbols from these ArboardRepresentable
and SymbolRepresentable
views
// Configure the export directory
SalsaConfig.exportDirectory = "/some_directory"
// Generate the artboards and symbols
let artboardsAndSymbols = makeArtboardsAndSymbols(from: [[View1.self], [View2.self]])
// Put the artboards and symbols onto their own dedicated pages
let artboardPage = Page(name: "Artboards", layers: artboardsAndSymbols.artboards)
let symbolPage = Page(name: "Symbols", layers: artboardsAndSymbols.symbols)
// Create a document with the generated pages and export it
let document = Document(pages: [artboardPage, symbolPage])
try? document.export(fileName: "my_file")
Example Project
Check out the Example project to see how Sasla can be used in production. The Example app uses a test target to generate Sketch files without manually launching Xcode.
To generate a Sketch file for the Example project run the following after cloning the repo:
cd Example
pod install
./generate_sketch
This should create a new file called ExampleSketch.sketch
inside the project directory
Open up generate_sketch
with a text editor to see how this is done.


Home
Welcome
Welcome to the CS161 Lab Page
These are the up to date labs:
Posts
Office Hours: 1-2pm Wed/Thurs, Young 404- Lab 7: Exact Cover - November 22, 2015
- Lab 6: Using Randomness - November 16, 2015
- Lab 5: Applicative Reverse Polish Notation Calculator - November 2, 2015
- Lab 4: Counting Words - October 26, 2015
- Lab 3: More Haskell Calculators - October 19, 2015
- Lab 2: Intersections and Computational Geometry - October 12, 2015
- Lab 0: Haskell Install Party - September 27, 2015
- Lab 1: Simple Reverse Polish Notation Calculator - August 5, 2015
…or you can find more in the archives.


Loveable mutt needs help
by Darren HandschuhTeki is in need of some human help.
The loveable senior dog needs the public's help to cover medical costs.
Teki had surgery to remove two painful lumps and has received treatment for a severe dental disease.
At almost 13 years old, Teki arrived at the SPCA underweight, but has been slowly gaining weight and recovering her strength.
“Teki loves people and gets along well with other dogs. She is a very laid-back girl who loves nothing more than to fall asleep on the couch in front of a warm fire,” said Chelsea Taylor, SPCA Vernon and District Branch manager.
Her estimated cost of care is $1,690.
“We need to raise funds for Teki and the many other senior animals that end up in shelters in their twilight years,” said Taylor.
Staff are hopeful that Teki will be able to spend the holidays in her new forever home and she is currently available for adoption.
To help Teki and other animals in need, visit the branch at 4800 Haney Rd., call<PHONE_NUMBER> or click here.


John Mager
highly specialized language (sometimes)
wide variety of publishers (and paywalls)
public abstracts


|
QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.30
|
Pricing engine for barrier options using analytical formulae. More...
#include <ql/pricingengines/barrier/analyticbarrierengine.hpp>
Public Member Functions | |
| AnalyticBarrierEngine (ext::shared_ptr< GeneralizedBlackScholesProcess > process) | |
| void | calculate () const override |
| Public Member Functions inherited from GenericEngine< BarrierOption::arguments, BarrierOption::results > | |
| PricingEngine::arguments * | getArguments () const override |
| const PricingEngine::results * | getResults () const override |
| void | reset () override |
| void | update () override |
| Public Member Functions inherited from PricingEngine | |
| ~PricingEngine () override=default | |
| virtual arguments * | getArguments () const =0 |
| virtual const results * | getResults () const =0 |
| virtual void | reset ()=0 |
| virtual void | calculate () const =0 |
| Public Member Functions inherited from Observable | |
| Observable () | |
| Observable (const Observable &) | |
| Observable & | operator= (const Observable &) |
| virtual | ~Observable ()=default |
| void | notifyObservers () |
| Public Member Functions inherited from Observer | |
| Observer ()=default | |
| Observer (const Observer &) | |
| Observer & | operator= (const Observer &) |
| virtual | ~Observer () |
| std::pair< iterator, bool > | registerWith (const ext::shared_ptr< Observable > &) |
| void | registerWithObservables (const ext::shared_ptr< Observer > &) |
| Size | unregisterWith (const ext::shared_ptr< Observable > &) |
| void | unregisterWithAll () |
| virtual void | update ()=0 |
| virtual void | deepUpdate () |
Private Member Functions | |
| Real | underlying () const |
| Real | strike () const |
| Volatility | volatility () const |
| Real | barrier () const |
| Real | rebate () const |
| Real | stdDeviation () const |
| Rate | riskFreeRate () const |
| DiscountFactor | riskFreeDiscount () const |
| Rate | dividendYield () const |
| DiscountFactor | dividendDiscount () const |
| Rate | mu () const |
| Real | muSigma () const |
| Real | A (Real phi) const |
| Real | B (Real phi) const |
| Real | C (Real eta, Real phi) const |
| Real | D (Real eta, Real phi) const |
| Real | E (Real eta) const |
| Real | F (Real eta) const |
Private Attributes | |
| ext::shared_ptr< GeneralizedBlackScholesProcess > | process_ |
| CumulativeNormalDistribution | f_ |
Additional Inherited Members | |
| Public Types inherited from Observer | |
| typedef set_type::iterator | iterator |
| Protected Member Functions inherited from BarrierOption::engine | |
| bool | triggered (Real underlying) const |
| Protected Attributes inherited from GenericEngine< BarrierOption::arguments, BarrierOption::results > | |
| BarrierOption::arguments | arguments_ |
| BarrierOption::results | results_ |
Pricing engine for barrier options using analytical formulae.
The formulas are taken from "Option pricing formulas", E.G. Haug, McGraw-Hill, p.69 and following.
Definition at line 46 of file analyticbarrierengine.hpp.
| AnalyticBarrierEngine | ( | ext::shared_ptr< GeneralizedBlackScholesProcess > | process | ) |
|
overridevirtual |
Implements PricingEngine.
Definition at line 36 of file analyticbarrierengine.cpp.
|
private |
Definition at line 117 of file analyticbarrierengine.cpp.
|
private |
Definition at line 121 of file analyticbarrierengine.cpp.
|
private |
Definition at line 128 of file analyticbarrierengine.cpp.
|
private |
Definition at line 140 of file analyticbarrierengine.cpp.
|
private |
Definition at line 144 of file analyticbarrierengine.cpp.
|
private |
Definition at line 134 of file analyticbarrierengine.cpp.
|
private |
Definition at line 148 of file analyticbarrierengine.cpp.
|
private |
Definition at line 155 of file analyticbarrierengine.cpp.
|
private |
Definition at line 160 of file analyticbarrierengine.cpp.
|
private |
Definition at line 167 of file analyticbarrierengine.cpp.
|
private |
Definition at line 172 of file analyticbarrierengine.cpp.
|
private |
Definition at line 177 of file analyticbarrierengine.cpp.
Definition at line 181 of file analyticbarrierengine.cpp.
Definition at line 191 of file analyticbarrierengine.cpp.
Definition at line 200 of file analyticbarrierengine.cpp.
Definition at line 213 of file analyticbarrierengine.cpp.
Definition at line 226 of file analyticbarrierengine.cpp.
Definition at line 243 of file analyticbarrierengine.cpp.
|
private |
Definition at line 52 of file analyticbarrierengine.hpp.
|
private |
Definition at line 53 of file analyticbarrierengine.hpp.


--- layout: post title: Summer date: '2016-07-11T18:30:38+02:00' categories: - Sketches - Ello Mobile picture_url: https://film-Storyboards.tumblr.com/post/147260923838 ---
Ello Mobile newsletter, Summer holidays.


Copland Directed by James Mangold
Starring: Sylvester Stallone, Harvey Keitel, Ray Liotta, Robert De Niro, Peter Berg, Janeane Garofalo
Synopsis: A small town sheriff who feels obligated to a popular but dirty cop must decide whether or not to betray him when a victim of attempted murder asks for protection.
This is a drama, not an action flick. This time, nstead of playing Rambo, or Rocky or any other superheroic type of guy, Sylvester Stallone plays a sweet, slow-witted, pot-bellied, likeable sheriff of a small town who wanders sadly an gently through a town that doesnt have much excitement. The film is called "Cop Land" because the New Jersey town that Sylvester patrols is mostly inhabited by cops who work across the bridge in New York. Theres an atmosphere of conpsiracy since throughout the film, it is made known that the mob has connections with dirty cops who have connections with dirty politicians, but all of these clues or not followed up on or explained. I loved the film and thought it was a great bit of normal-guy type heroism and drama.
Opinion: Whether or not you enjoy the film hinges entirely on one key point - - it depends on how you interpret Sylvester Stallones character. If you interpret Stallones character as a smart guy, then youre going to wonder why he didnt investigate all those conspiratorial threads and why things are left "hanging" unexplained. If you interpret him as a slow-witted guy, then youll be happy with this film, because the film is essentially a morality play, with the moral that even a gentle, confused guy can make a moral decision to do the right thing.
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


|
AAX SDK
2.3.1
Avid Audio Extensions Development Kit
|
How to transfer data between different parts of an AAX plug-in.
AAX is a highly modular architecture. This section describes the various means by which AAX plug-in modules may communicate with one another and with the host.
There are two fundamental categories of communication in AAX:
Most communication between the AAX host and the plug-in is accomplished via the AAX_IController interface. This interface contains methods for such things as:
In addition, the GUI uses a separate interface for managing view and event details with the host. This interface, AAX_IViewContainer, includes methods for:
Often it is necessary to transmit arbitrary blocks of custom plug-in data between different plug-in modules. In AAX, this is accomplished by "pushing" data to and "pulling" it from the plug-in's data model.
The abstract data model interface includes two custom data methods for this:
It is the data model's job to act as a go-between when custom data must be transmitted between a plug-in's other modules.
For example, a plug-in may wish to send analysis data from its direct data module to its GUI. In this situation, the Direct Data object would call SetCustomData() to update the data model whenever new data was available, while the GUI would "pull" the most up-to-date data via GetCustomData() whenever an update was required.
Note that the default implementations of these methods are empty and thus all implementation details, including thread safety guards, are left to the plug-in.
The data model and GUI interfaces include notification hook methods. These methods used for host-to-Effect notifications by default, but may also be called with custom notification IDs in order to create custom notifications within a plug-in.
If co-location is guaranteed, plug-in modules may directly share data pointers. For example, a non-real-time plug-in's Host Processor object may share a this
pointer with its data model object.
To guarantee co-location between modules that could normally be placed into different memory spaces by the host, use "constraint" properties:
To help avoid forwards-compatibility issues with future devices that support AAX, these constraints should be set whenever a plug-in requires co-location of its components. Note, however, that using a design that relies on co-location will prevent the plug-in from running in distributed environments and should therefore be avoided when possible.
An AAX plug-in's algorithm is essentially a stateless callback and, therefore, all of its state data must at some level be managed by the host. This model is fundamentally different from the other plug-in modules, which are each objects with their own memory and state.
Most algorithmic data management is performed via the algorithm's context structure. More information about memory management in AAX real-time algorithms can be found here.
The most common form of communication with a plug-in's real-time algorithm callback is the transmission of read-only data from the data model to the context structure.
AAX includes a dedicated API for this task that provides buffered, optimized delivery of read-only data packets to the algorithm. For more information, see Communicating with the algorithm .
Algorithms can also send data to the host and receive environment information through dedicated context fields. For example, the host can provide access to DMA facilities through an object accessed via a DMA field, and a plug-in can report meter values to the host via a dedicated meter field. For more information, see Communicating with the algorithm .
When other modules in the plug-in must interact directly with the algorithm's state information this is accomplished via the Direct Data interface. This interface provides an idle-time context in which the plug-in may read from or write to the algorithm's private data memory. These transfers are unbuffered and therefore the plug-in must handle any appropriate thread-safety considerations.


Prerequisites
PlugBoard needs at least Python 2.4.
Also you need zope_interface. Unfortunately this package hasn't a correct behavior as a setuptools dependency, then you need to install it manually
Download and Install
- Download the ez_setup.py bootstrap script.
-
Once downloaded
ez_setup.py
run it on your machine, using the parameters that follows:python ez_setup.py -f http://plugboard.berlios.de/install/ PlugBoard
- If everthing went well you have installed PlugBoard, and you can procede with reading the documentation.
Note
The ez_setup.py
script will scan this page and will download and install for you the following packages:
- PlugBoard 0.2 (released Feb 27, 2006)
Give it a spin
Once installed, set up your first project:
plugboardctl.py create my_project
Your project will be created in
the directory my_project
.
Note
The creation of the project is nothing else a beginning directory structure with a setup.py
script automatically written. This means you are not forced to use this structure for
your projects.
Other downloads
Here you can find source and binary packages of all versions of PlugBoard.
For getting the development version from the SVN trunk, please visit the development page.


Image Credits and Special Thanks
Here are the credits for all images used plus some special thanks from the development team!
Image Credits
Home Page
Photo by
Erriko Boccia on Unsplash
About Page
Photo by
Marvin Meyer on Unsplash
Resources Page
Image from Canva, Design by Jennifer Weiner
Privacy Page
Photo by
Markus Spiske on Unsplash
Terms of Use Page
Image from Canva, Design by Jennifer Weiner
Special Thanks Page
Image from Canva, Design by Jennifer Weiner
Special Thanks
A general special thanks to the Lambda School staff. From Team Leads to the CEO, access is not limited. This non-traditional way of learning is working for so many. To all the leaders we interact with on a daily basis, thank you for your time and skills paying it forward to others.


A supporting section of code for REDCap to allow use of Local External Module Repositories.
The local EM Repo would be set up on another REDCap server internal to your organization.
This mimics the standard REDCap EM Manager for retrieval of zip files to add external modules with the difference that it goes to your configured local repository URL location.
Created at Washington University School of Medicine Institute for Informatics
Author: David L. Heskett
Create CUSTOM REDCap Repo server
Add directory under redcap, /customrepo/
Add the REPO handler files.
Add your External Module zip files to /redcap/customrepo/modules/filesarea/
Create a project and use data dictionary to configure the project.
Create a bookmark to the generate listing process.
Example: REPO: Generate Listing - http://wwwrepo/customrepo/modules/generatelist.php
Create a bookmark to show the listing.
Example: REPO: Show Listing - http://wwwrepo/customrepo/modules/getlisting.php
Edit configuration file: /redcap/customrepo/modules/configuration.php
Change the $customRepoProjectId = 0; // PROJECT ID VALUE
Example: $customRepoProjectId = 123;
Change the $customInstituteName = '';
Example: $customInstituteName = 'Your Institute Name Here';
Change the $customEmailSupport = '';
Example: $customEmailSupport =<EMAIL_ADDRESS>Change the $instituteRepoCustomText = '';
Example: $instituteRepoCustomText = 'Department';
Edit the configurationKeys file: /redcap/customrepo/modules/configurationKeys.php
Change the $customRepoSecretKey = 'testingkey'; // CUSTOMIZE
Example: $customRepoSecretKey = 'yourdifferenttexthere';
Go to the project and add entries for your EMs
(module short name) the EM snake case short name without the version number
(module system version) the correct latest version number of each related zip file
and other information
finally (active flag) the availability of the module, set to "Yes"
and set to Complete and SAVE
Add your External Module zip file to /redcap/customrepo/modules/filesarea/
When done with all the add and edit changes, run the
Verify you have a good listing as expected.
When you have a new version, edit the related EM record and update the version number (module system version) and other information as needed if changed. SAVE it.
REMEMBER to again run the REPO Generate Listing!
Requires a web server with REDCap and access http(s) from DEV, QA, PROD to communicate to web service hosted on the server allowing External Module zip downloads and management.
System will be using http(s) post and get to request data about available external modules, and what client has, to compare versions and other information, and transfer zip files. (Content-Type: application/zip).
Will need access to place zip files on to the server, path will be, /redcap/customrepo/modules/filesarea/*.zip
Web service uses a token to restrict access to service along with other data elements being required to present any data to client requests.